Skip to content

Commit 0af7827

Browse files
rwolstomv564
authored andcommitted
Browse function overloads with the up/down arrow keys (with no other stuff) (sublimelsp#162)
* Browse function overloads with the up/down arrow keys
1 parent 604df77 commit 0af7827

5 files changed

+173
-50
lines changed
+18-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[
22
{
3-
"keys": ["ctrl+."],
4-
"command": "lsp_code_actions"
3+
"keys": ["ctrl+."],
4+
"command": "lsp_code_actions"
55
},
66
{
77
"keys": ["ctrl+alt+m"],
@@ -10,5 +10,21 @@
1010
{
1111
"keys": ["shift+f12"],
1212
"command": "lsp_symbol_references"
13+
},
14+
{
15+
"keys": ["down"],
16+
"command": "noop",
17+
"context":
18+
[
19+
{ "key": "lsp.signature_help", "operator": "equal", "operand": 1 }
20+
]
21+
},
22+
{
23+
"keys": ["up"],
24+
"command": "noop",
25+
"context":
26+
[
27+
{ "key": "lsp.signature_help", "operator": "equal", "operand": -1 }
28+
]
1329
}
1430
]

Keymaps/Default (OSX).sublime-keymap

+19-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[
22
{
3-
"keys": ["super+."],
4-
"command": "lsp_code_actions"
3+
"keys": ["super+."],
4+
"command": "lsp_code_actions"
55
},
66
{
77
"keys": ["super+shift+m"],
@@ -11,5 +11,20 @@
1111
"keys": ["shift+f12"],
1212
"command": "lsp_symbol_references"
1313
},
14-
15-
]
14+
{
15+
"keys": ["down"],
16+
"command": "noop",
17+
"context":
18+
[
19+
{ "key": "lsp.signature_help", "operator": "equal", "operand": 1 }
20+
]
21+
},
22+
{
23+
"keys": ["up"],
24+
"command": "noop",
25+
"context":
26+
[
27+
{ "key": "lsp.signature_help", "operator": "equal", "operand": -1 }
28+
]
29+
}
30+
]
+18-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[
22
{
3-
"keys": ["ctrl+."],
4-
"command": "lsp_code_actions"
3+
"keys": ["ctrl+."],
4+
"command": "lsp_code_actions"
55
},
66
{
77
"keys": ["ctrl+alt+m"],
@@ -10,5 +10,21 @@
1010
{
1111
"keys": ["shift+f12"],
1212
"command": "lsp_symbol_references"
13+
},
14+
{
15+
"keys": ["down"],
16+
"command": "noop",
17+
"context":
18+
[
19+
{ "key": "lsp.signature_help", "operator": "equal", "operand": 1 }
20+
]
21+
},
22+
{
23+
"keys": ["up"],
24+
"command": "noop",
25+
"context":
26+
[
27+
{ "key": "lsp.signature_help", "operator": "equal", "operand": -1 }
28+
]
1329
}
1430
]

main.py

+95-36
Original file line numberDiff line numberDiff line change
@@ -2297,33 +2297,45 @@ def run_auto_complete(self):
22972297

22982298

22992299
class SignatureHelpListener(sublime_plugin.ViewEventListener):
2300+
2301+
css = ".mdpopups .lsp_signature { margin: 4px; } .mdpopups p { margin: 0.1rem; }"
2302+
wrapper_class = "lsp_signature"
2303+
23002304
def __init__(self, view):
23012305
self.view = view
2302-
self.signature_help_triggers = None
2306+
self._initialized = False
2307+
self._signature_help_triggers = [] # type: List[str]
2308+
self._visible = False
2309+
self._language_id = ""
2310+
self._signatures = [] # type: List[Any]
2311+
self._active_signature = -1
23032312

23042313
@classmethod
23052314
def is_applicable(cls, settings):
23062315
syntax = settings.get('syntax')
23072316
return syntax and is_supported_syntax(syntax)
23082317

2309-
def initialize_triggers(self):
2318+
def initialize(self):
23102319
client = client_for_view(self.view)
23112320
if client:
23122321
signatureHelpProvider = client.get_capability(
23132322
'signatureHelpProvider')
23142323
if signatureHelpProvider:
23152324
self.signature_help_triggers = signatureHelpProvider.get(
23162325
'triggerCharacters')
2317-
return
23182326

2319-
self.signature_help_triggers = []
2327+
config = config_for_scope(self.view)
2328+
if config:
2329+
self._language_id = config.languageId
2330+
2331+
self._initialized = True
23202332

23212333
def on_modified_async(self):
23222334
pos = self.view.sel()[0].begin()
23232335
last_char = self.view.substr(pos - 1)
23242336
# TODO: this will fire too often, narrow down using scopes or regex
2325-
if self.signature_help_triggers is None:
2326-
self.initialize_triggers()
2337+
if not self._initialized:
2338+
self.initialize()
23272339

23282340
if self.signature_help_triggers:
23292341
if last_char in self.signature_help_triggers:
@@ -2337,40 +2349,87 @@ def on_modified_async(self):
23372349
lambda response: self.handle_response(response, pos))
23382350
else:
23392351
# TODO: this hides too soon.
2340-
if self.view.is_popup_visible():
2352+
if self._visible:
23412353
self.view.hide_popup()
23422354

23432355
def handle_response(self, response, point):
23442356
if response is not None:
2345-
config = config_for_scope(self.view)
2346-
signatures = response.get("signatures")
2347-
activeSignature = response.get("activeSignature")
2348-
debug("got signatures, active is", len(signatures), activeSignature)
2349-
if len(signatures) > 0 and config:
2350-
signature = signatures[activeSignature]
2351-
debug("active signature", signature)
2352-
formatted = []
2353-
formatted.append(
2354-
"```{}\n{}\n```".format(config.languageId, signature.get('label')))
2355-
params = signature.get('parameters')
2356-
if params:
2357-
for parameter in params:
2358-
paramDocs = parameter.get('documentation')
2359-
if paramDocs:
2360-
formatted.append("**{}**\n".format(parameter.get('label')))
2361-
formatted.append("* *{}*\n".format(paramDocs))
2362-
2363-
formatted.append(signature.get('documentation'))
2364-
2365-
mdpopups.show_popup(
2366-
self.view,
2367-
preserve_whitespace("\n".join(formatted)),
2368-
css=".mdpopups .lsp_signature { margin: 4px; } .mdpopups p { margin: 0.1rem; }",
2369-
md=True,
2370-
flags=sublime.HIDE_ON_MOUSE_MOVE_AWAY,
2371-
location=point,
2372-
wrapper_class="lsp_signature",
2373-
max_width=800)
2357+
self._signatures = response.get("signatures", [])
2358+
self._active_signature = response.get("activeSignature", -1)
2359+
2360+
if self._signatures:
2361+
if not 0 <= self._active_signature < len(self._signatures):
2362+
debug("activeSignature {} not a valid index for signatures length {}".format(
2363+
self._active_signature, len(self._signatures)))
2364+
self._active_signature = 0
2365+
else:
2366+
if self._active_signature != -1:
2367+
debug("activeSignature should be -1 or null when no signatures are returned")
2368+
self._active_signature = -1
2369+
2370+
if len(self._signatures) > 0:
2371+
mdpopups.show_popup(self.view,
2372+
self._build_popup_content(),
2373+
css=self.__class__.css,
2374+
md=True,
2375+
flags=sublime.HIDE_ON_MOUSE_MOVE_AWAY,
2376+
location=point,
2377+
wrapper_class=self.__class__.wrapper_class,
2378+
max_width=800,
2379+
on_hide=self._on_hide)
2380+
self._visible = True
2381+
2382+
def on_query_context(self, key, _, operand, __):
2383+
if key != "lsp.signature_help":
2384+
return False # Let someone else handle this keybinding.
2385+
elif not self._visible:
2386+
return False # Let someone else handle this keybinding.
2387+
elif len(self._signatures) < 2:
2388+
return False # Let someone else handle this keybinding.
2389+
else:
2390+
# We use the "operand" for the number -1 or +1. See the keybindings.
2391+
new_index = self._active_signature + operand
2392+
2393+
# clamp signature index
2394+
new_index = max(0, min(new_index, len(self._signatures) - 1))
2395+
2396+
# only update when changed
2397+
if new_index != self._active_signature:
2398+
self._active_signature = new_index
2399+
mdpopups.update_popup(self.view,
2400+
self._build_popup_content(),
2401+
css=self.__class__.css,
2402+
md=True,
2403+
wrapper_class=self.__class__.wrapper_class)
2404+
2405+
return True # We handled this keybinding.
2406+
2407+
def _on_hide(self):
2408+
self._visible = False
2409+
2410+
def _build_popup_content(self) -> str:
2411+
signature = self._signatures[self._active_signature]
2412+
formatted = []
2413+
2414+
if len(self._signatures) > 1:
2415+
signature_navigation = "**{}** of **{}** overloads (use the arrow keys to navigate):\n".format(
2416+
str(self._active_signature + 1), str(len(self._signatures)))
2417+
formatted.append(signature_navigation)
2418+
2419+
label = "```{}\n{}\n```".format(self._language_id, signature.get('label'))
2420+
formatted.append(label)
2421+
2422+
params = signature.get('parameters')
2423+
if params:
2424+
for parameter in params:
2425+
paramDocs = parameter.get('documentation', None)
2426+
if paramDocs:
2427+
formatted.append("**{}**\n".format(parameter.get('label')))
2428+
formatted.append("* *{}*\n".format(paramDocs))
2429+
sigDocs = signature.get('documentation', None)
2430+
if sigDocs:
2431+
formatted.append(sigDocs)
2432+
return preserve_whitespace("\n".join(formatted))
23742433

23752434

23762435
def get_line_diagnostics(view, point):

stubs/mdpopups.pyi

+23-6
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,36 @@
11
import sublime
2+
try:
3+
from typing import Callable, Optional
4+
assert Callable and Optional
5+
except ImportError:
6+
pass
7+
28

39
def show_popup(
410
view: sublime.View,
511
content: str,
612
md=True,
7-
css=None,
13+
css=None, # type: Optional[str]
814
flags=0,
915
location=-1,
1016
max_width=320,
1117
max_height=240,
12-
on_navigate=None,
13-
on_hide=None,
14-
wrapper_class=None,
15-
template_vars=None,
16-
template_env_options=None,
18+
on_navigate=None, # type: Optional[Callable]
19+
on_hide=None, # type: Optional[Callable]
20+
wrapper_class=None, # type: Optional[str]
21+
template_vars=None, # type: Optional[dict]
22+
template_env_options=None, # type: Optional[dict]
23+
nl2br=True,
24+
allow_code_wrap=False
25+
): ...
26+
27+
def update_popup(
28+
view: sublime.View,
29+
content: str,
30+
md=True,
31+
css=None, # type: Optional[str]
32+
wrapper_class=None, # type: Optional[str]
33+
template_vars=None, # type: Optional[str]
1734
nl2br=True,
1835
allow_code_wrap=False
1936
): ...

0 commit comments

Comments
 (0)