Skip to content

Commit be85df2

Browse files
committed
feat: quick-action buttons in the LSP hover popup (Go to Definition / Find Usages)
The hover popup now shows a pinned action footer below the (scrollable) documentation: "Go to Definition" on the left and "Find Usages" on the right, each gated on what the server actually advertises (definitionProvider / referencesProvider). Clicking places the cursor at the hovered symbol, dismisses the popup, and runs the command - so the action targets what you hovered. The keyboard shortcut is shown as a tooltip rather than inline to keep the row tidy. Also rename the commands to match common IDE wording (applies to the context menu too): "Jump to Definition" -> "Go to Definition", "Find All References" -> "Find Usages". Adds QuickViewManager.hideQuickView() so an action can dismiss the popup; hover styling lives in brackets.less (theme-aware).
1 parent 9cb53b7 commit be85df2

4 files changed

Lines changed: 132 additions & 11 deletions

File tree

src/features/QuickViewManager.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,4 +794,5 @@ define(function (require, exports, module) {
794794
exports.isQuickViewShown = isQuickViewShown;
795795
exports.lockQuickView = lockQuickView;
796796
exports.unlockQuickView = unlockQuickView;
797+
exports.hideQuickView = hidePreview;
797798
});

src/languageTools/HoverProvider.js

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ define(function (require, exports, module) {
2929

3030
// Hover styling lives in src/styles/brackets.less (.lsp-hover-quickview) so it tracks the
3131
// active light/dark theme.
32-
const marked = require("thirdparty/marked.min");
32+
const marked = require("thirdparty/marked.min"),
33+
CommandManager = require("command/CommandManager"),
34+
Commands = require("command/Commands"),
35+
KeyBindingManager = require("command/KeyBindingManager"),
36+
QuickViewManager = require("features/QuickViewManager"),
37+
Strings = require("strings");
3338

3439
/**
3540
* Convert LSP hover `contents` (MarkupContent | MarkedString | string | array of those)
@@ -68,6 +73,61 @@ define(function (require, exports, module) {
6873
}
6974
}
7075

76+
/**
77+
* Build one clickable action row (icon + label + optional shortcut). Clicking places the cursor
78+
* at the hovered position, dismisses the hover popup, then runs the command - so jump/find
79+
* operate on the symbol the user hovered, not wherever the cursor happened to be.
80+
*/
81+
function _action(iconClass, label, commandId, editor, pos, alignRight) {
82+
const $action = $("<div>").addClass("lsp-hover-action").attr("tabindex", "0");
83+
if (alignRight) {
84+
$action.addClass("lsp-hover-action--end");
85+
}
86+
$("<i>").addClass(iconClass + " lsp-hover-action-icon").appendTo($action);
87+
$("<span>").addClass("lsp-hover-action-label").text(label).appendTo($action);
88+
// The keyboard shortcut is surfaced as a tooltip (not inline) to keep the row compact.
89+
const shortcut = KeyBindingManager.getKeyBindingsDisplay(commandId);
90+
$action.attr("title", shortcut ? (label + " (" + shortcut + ")") : label);
91+
function run(e) {
92+
if (e) {
93+
e.preventDefault();
94+
e.stopPropagation();
95+
}
96+
QuickViewManager.hideQuickView();
97+
editor.setCursorPos(pos.line, pos.ch);
98+
editor.focus();
99+
CommandManager.execute(commandId);
100+
}
101+
$action.on("click", run);
102+
$action.on("keydown", function (e) {
103+
if (e.keyCode === 13 || e.keyCode === 32) { // Enter / Space
104+
run(e);
105+
}
106+
});
107+
return $action;
108+
}
109+
110+
/**
111+
* Build the action footer (Go to Definition / Find Usages), gated on what the server supports.
112+
* @return {?jQuery} the actions element, or null when no action is available.
113+
*/
114+
function _buildActions(client, editor, pos) {
115+
const caps = client.getServerCapabilities() || {};
116+
const $actions = $("<div>").addClass("lsp-hover-actions");
117+
let count = 0;
118+
if (caps.definitionProvider) {
119+
$actions.append(_action("fa-solid fa-arrow-right", Strings.CMD_JUMPTO_DEFINITION,
120+
Commands.NAVIGATE_JUMPTO_DEFINITION, editor, pos));
121+
count++;
122+
}
123+
if (caps.referencesProvider) {
124+
$actions.append(_action("fa-solid fa-magnifying-glass", Strings.FIND_ALL_REFERENCES,
125+
Commands.CMD_FIND_ALL_REFERENCES, editor, pos, true));
126+
count++;
127+
}
128+
return count ? $actions : null;
129+
}
130+
71131
/**
72132
* @param {Object} client - a LanguageClient from LSPClient.js
73133
*/
@@ -98,10 +158,16 @@ define(function (require, exports, module) {
98158
start = { line: hover.range.start.line, ch: hover.range.start.character };
99159
end = { line: hover.range.end.line, ch: hover.range.end.character };
100160
}
161+
const $content = $("<div>").addClass("lsp-hover-quickview");
162+
$("<div>").addClass("lsp-hover-doc").html(html).appendTo($content);
163+
const $actions = _buildActions(self.client, editor, pos);
164+
if ($actions) {
165+
$content.append($actions);
166+
}
101167
resolve({
102168
start: start,
103169
end: end,
104-
content: $("<div>").addClass("lsp-hover-quickview").html(html)
170+
content: $content
105171
});
106172
})
107173
.fail(function () {

src/nls/root/strings.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,7 +1498,7 @@ define({
14981498
"EDIT": "Edit",
14991499

15001500
// extensions/default/JavaScriptCodeHints
1501-
"CMD_JUMPTO_DEFINITION": "Jump to Definition",
1501+
"CMD_JUMPTO_DEFINITION": "Go to Definition",
15021502
"CMD_SHOW_PARAMETER_HINT": "Show Parameter Hint",
15031503
"NO_ARGUMENTS": "<no parameters>",
15041504
"DETECTED_EXCLUSION_TITLE": "JavaScript File Inference Problem",
@@ -1735,7 +1735,7 @@ define({
17351735
"DESCRIPTION_PHP_TOOLING_CONFIGURATION": "PHP Tooling default configuration settings",
17361736
"OPEN_PREFERENNCES": "Open Preferences",
17371737

1738-
"FIND_ALL_REFERENCES": "Find All References",
1738+
"FIND_ALL_REFERENCES": "Find Usages",
17391739
"REFERENCES_IN_FILES": "references",
17401740
"REFERENCE_IN_FILES": "reference",
17411741
"REFERENCES_NO_RESULTS": "No References available for current cursor position",

src/styles/brackets.less

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2146,9 +2146,6 @@ a, img {
21462146
.lsp-hover-quickview {
21472147
text-align: left;
21482148
max-width: 520px;
2149-
max-height: 320px;
2150-
overflow-y: auto;
2151-
overflow-x: hidden;
21522149
padding: 2px 4px;
21532150
font-size: 12.5px;
21542151
line-height: 1.5;
@@ -2159,6 +2156,13 @@ a, img {
21592156
color: @dark-bc-text;
21602157
}
21612158

2159+
// The documentation scrolls; the action footer below it stays pinned and always visible.
2160+
.lsp-hover-doc {
2161+
max-height: 320px;
2162+
overflow-y: auto;
2163+
overflow-x: hidden;
2164+
}
2165+
21622166
// First block is the function/type signature (a ```ts``` fence): the visual focal point.
21632167
pre {
21642168
margin: 0 0 9px 0;
@@ -2262,21 +2266,71 @@ a, img {
22622266
margin: 2px 0;
22632267
}
22642268

2265-
// Slim, unobtrusive scrollbar that matches both themes.
2266-
&::-webkit-scrollbar {
2269+
// Slim, unobtrusive scrollbar on the scrolling doc that matches both themes.
2270+
.lsp-hover-doc::-webkit-scrollbar {
22672271
width: 9px;
22682272
height: 9px;
22692273
}
2270-
&::-webkit-scrollbar-thumb {
2274+
.lsp-hover-doc::-webkit-scrollbar-thumb {
22712275
background: rgba(0, 0, 0, 0.18);
22722276
border-radius: 5px;
22732277
.dark & {
22742278
background: rgba(255, 255, 255, 0.18);
22752279
}
22762280
}
2277-
&::-webkit-scrollbar-track {
2281+
.lsp-hover-doc::-webkit-scrollbar-track {
22782282
background: transparent;
22792283
}
2284+
2285+
// Quick actions (Go to Definition / Find Usages) pinned below the documentation.
2286+
.lsp-hover-actions {
2287+
display: flex;
2288+
flex-wrap: wrap;
2289+
gap: 3px;
2290+
margin-top: 8px;
2291+
padding-top: 7px;
2292+
border-top: 1px solid rgba(0, 0, 0, 0.08);
2293+
.dark & {
2294+
border-top-color: rgba(255, 255, 255, 0.08);
2295+
}
2296+
}
2297+
.lsp-hover-action {
2298+
display: inline-flex;
2299+
align-items: center;
2300+
gap: 6px;
2301+
padding: 4px 9px;
2302+
border-radius: 5px;
2303+
cursor: pointer;
2304+
user-select: none;
2305+
color: @bc-text-link;
2306+
transition: background-color 0.12s ease;
2307+
2308+
.dark & {
2309+
color: @dark-bc-text-link;
2310+
}
2311+
&:hover,
2312+
&:focus {
2313+
outline: none;
2314+
background: rgba(0, 0, 0, 0.06);
2315+
.dark & {
2316+
background: rgba(255, 255, 255, 0.09);
2317+
}
2318+
}
2319+
2320+
// Push this action to the far right of the row (e.g. Find Usages).
2321+
&.lsp-hover-action--end {
2322+
margin-left: auto;
2323+
}
2324+
2325+
.lsp-hover-action-icon {
2326+
font-size: 11px;
2327+
opacity: 0.85;
2328+
}
2329+
.lsp-hover-action-label {
2330+
font-size: 12px;
2331+
font-weight: 500;
2332+
}
2333+
}
22802334
}
22812335

22822336
// LSP code-hint documentation popup (shown beside the code hint list)

0 commit comments

Comments
 (0)