Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Use "filterText" (fallback to "label") for the trigger of the completion item #551

Merged
merged 1 commit into from Apr 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions LSP.sublime-settings
Expand Up @@ -355,6 +355,11 @@
// Resolve completions and apply snippet if received.
"resolve_completion_for_snippets": false,

// When presenting completions, prefer the "label" over the "filterText" key
// in the CompletionItem. By default, the "filterText" is chosen over the
// "label". If the "filterText" is not present, fall back to the "label".
"prefer_label_over_filter_text": false,

// Show symbol references in Sublime's quick panel instead of the bottom panel.
"show_references_in_quick_panel": false,

Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Expand Up @@ -10,6 +10,7 @@ Global plugin settings and settings defined at project level are merged together
* `only_show_lsp_completions` `false` *disable sublime word completion and snippets from autocomplete lists*
* `completion_hint_type` `"auto"` *override automatic completion hints with "detail", "kind" or "none"*
* `resolve_completion_for_snippets` `false` *resolve completions and apply snippet if received*
* `prefer_label_over_filter_text` `false` *always use the "label" key instead of the "filterText" key in CompletionItems*
* `show_references_in_quick_panel` `false` *show symbol references in Sublime's quick panel instead of the bottom panel*
* `show_status_messages` `true` *show messages in the status bar for a few seconds*
* `show_view_status` `true` *show permanent language server status in the status bar*
Expand Down
14 changes: 8 additions & 6 deletions plugin/completion.py
Expand Up @@ -258,7 +258,10 @@ def do_request(self, prefix: str, locations: 'List[int]'):

def format_completion(self, item: dict) -> 'Tuple[str, str]':
# Sublime handles snippets automatically, so we don't have to care about insertTextFormat.
label = item["label"]
if settings.prefer_label_over_filter_text:
trigger = item["label"]
else:
trigger = item.get("filterText", item["label"])
# choose hint based on availability and user preference
hint = None
if settings.completion_hint_type == "auto":
Expand All @@ -274,12 +277,11 @@ def format_completion(self, item: dict) -> 'Tuple[str, str]':
if kind:
hint = completion_item_kind_names.get(kind)
# label is an alternative for insertText if neither textEdit nor insertText is provided
insert_text = self.text_edit_text(item) or item.get("insertText") or label
trigger = insert_text
if len(insert_text) > 0 and insert_text[0] == '$': # sublime needs leading '$' escaped.
insert_text = '\\$' + insert_text[1:]
replacement = self.text_edit_text(item) or item.get("insertText") or trigger
if len(replacement) > 0 and replacement[0] == '$': # sublime needs leading '$' escaped.
replacement = '\\$' + replacement[1:]
# only return trigger with a hint if available
return "\t ".join((trigger, hint)) if hint else trigger, insert_text
return "\t ".join((trigger, hint)) if hint else trigger, replacement

def text_edit_text(self, item) -> 'Optional[str]':
text_edit = item.get("textEdit")
Expand Down
1 change: 1 addition & 0 deletions plugin/core/settings.py
Expand Up @@ -64,6 +64,7 @@ def update_settings(settings: Settings, settings_obj: sublime.Settings):
settings.complete_all_chars = read_bool_setting(settings_obj, "complete_all_chars", True)
settings.completion_hint_type = read_str_setting(settings_obj, "completion_hint_type", "auto")
settings.resolve_completion_for_snippets = read_bool_setting(settings_obj, "resolve_completion_for_snippets", False)
settings.prefer_label_over_filter_text = read_bool_setting(settings_obj, "prefer_label_over_filter_text", False)
settings.show_references_in_quick_panel = read_bool_setting(settings_obj, "show_references_in_quick_panel", False)
settings.log_debug = read_bool_setting(settings_obj, "log_debug", False)
settings.log_server = read_bool_setting(settings_obj, "log_server", True)
Expand Down
1 change: 1 addition & 0 deletions plugin/core/types.py
Expand Up @@ -34,6 +34,7 @@ def __init__(self) -> None:
self.complete_all_chars = False
self.completion_hint_type = "auto"
self.resolve_completion_for_snippets = False
self.prefer_label_over_filter_text = False
self.show_references_in_quick_panel = False
self.log_debug = True
self.log_server = True
Expand Down
79 changes: 41 additions & 38 deletions tests/test_completion.py
Expand Up @@ -7,6 +7,12 @@
from LSP.plugin.core.settings import client_configs, ClientConfig
from os.path import dirname, join

try:
from typing import Dict, Optional
assert Dict and Optional
except ImportError:
pass


def load_completion_sample(name: str) -> 'Dict':
return json.load(open(join(dirname(__file__), name + ".json")))
Expand All @@ -17,7 +23,7 @@ def load_completion_sample(name: str) -> 'Dict':
intelephense_completion_sample = load_completion_sample("intelephense_completion_sample")


def create_completion_item(item: str, insert_text: 'Optional[str]'=None) -> dict:
def create_completion_item(item: str, insert_text: 'Optional[str]' = None) -> dict:
return {
"label": item,
"insertText": insert_text
Expand Down Expand Up @@ -144,7 +150,7 @@ def test_prefers_insert_text(self):
handler = CompletionHandler(self.view)
result = handler.format_completion(create_completion_item("asdf", "Asdf"))
self.assertEqual(len(result), 2)
self.assertEqual("Asdf", result[0])
self.assertEqual("asdf", result[0])
self.assertEqual("Asdf", result[1])

def test_ignores_text_edit(self):
Expand All @@ -165,7 +171,7 @@ def test_ignores_text_edit(self):

result = handler.format_completion(item)
self.assertEqual(len(result), 2)
self.assertEqual("$true", result[0])
self.assertEqual("true", result[0])
self.assertEqual("\\$true", result[1])

def test_ignore_label(self):
Expand Down Expand Up @@ -235,27 +241,24 @@ def test_text_edit_clangd(self):
[
('argc\t int', 'argc'),
('argv\t const char **', 'argv'),
('alignas(${1:expression})\t Snippet', 'alignas(${1:expression})'),
('alignof(${1:type})\t size_t', 'alignof(${1:type})'),
('alignas\t Snippet', 'alignas(${1:expression})'),
('alignof\t size_t', 'alignof(${1:type})'),
('auto\t Keyword', 'auto'),
('static_assert(${1:expression}, ${2:message})\t Snippet',
'static_assert(${1:expression}, ${2:message})'),
('a64l(${1:const char *__s})\t long', 'a64l(${1:const char *__s})'),
('abort()\t void', 'abort()'),
('abs(${1:int __x})\t int', 'abs(${1:int __x})'),
('aligned_alloc(${1:size_t __alignment}, ${2:size_t __size})\t void *',
'aligned_alloc(${1:size_t __alignment}, ${2:size_t __size})'),
('alloca(${1:size_t __size})\t void *', 'alloca(${1:size_t __size})'),
('asctime(${1:const struct tm *__tp})\t char *', 'asctime(${1:const struct tm *__tp})'),
('asctime_r(${1:const struct tm *__restrict __tp}, ${2:char *__restrict __buf})\t char *',
'asctime_r(${1:const struct tm *__restrict __tp}, ${2:char *__restrict __buf})'),
('asprintf(${1:char **__restrict __ptr}, ${2:const char *__restrict __fmt, ...})\t int',
'asprintf(${1:char **__restrict __ptr}, ${2:const char *__restrict __fmt, ...})'),
('at_quick_exit(${1:void (*__func)()})\t int', 'at_quick_exit(${1:void (*__func)()})'),
('atexit(${1:void (*__func)()})\t int', 'atexit(${1:void (*__func)()})'),
('atof(${1:const char *__nptr})\t double', 'atof(${1:const char *__nptr})'),
('atoi(${1:const char *__nptr})\t int', 'atoi(${1:const char *__nptr})'),
('atol(${1:const char *__nptr})\t long', 'atol(${1:const char *__nptr})')
('static_assert\t Snippet', 'static_assert(${1:expression}, ${2:message})'),
('a64l\t long', 'a64l(${1:const char *__s})'),
('abort\t void', 'abort()'),
('abs\t int', 'abs(${1:int __x})'),
('aligned_alloc\t void *', 'aligned_alloc(${1:size_t __alignment}, ${2:size_t __size})'),
('alloca\t void *', 'alloca(${1:size_t __size})'),
('asctime\t char *', 'asctime(${1:const struct tm *__tp})'),
('asctime_r\t char *',
'asctime_r(${1:const struct tm *__restrict __tp}, ${2:char *__restrict __buf})'),
('asprintf\t int', 'asprintf(${1:char **__restrict __ptr}, ${2:const char *__restrict __fmt, ...})'),
('at_quick_exit\t int', 'at_quick_exit(${1:void (*__func)()})'),
('atexit\t int', 'atexit(${1:void (*__func)()})'),
('atof\t double', 'atof(${1:const char *__nptr})'),
('atoi\t int', 'atoi(${1:const char *__nptr})'),
('atol\t long', 'atol(${1:const char *__nptr})')
]
)

Expand All @@ -269,29 +272,29 @@ def test_missing_text_edit_but_we_do_have_insert_text_for_pyls(self):
result,
[
('abc\t os', 'abc'),
('abort\t os', 'abort'),
('access(${1:path}, ${2:mode}, ${3:dir_fd}, ${4:effective_ids}, ${5:follow_symlinks})$0\t os',
'access(${1:path}, ${2:mode}, ${3:dir_fd}, ${4:effective_ids}, ${5:follow_symlinks})$0'),
('abort()\t os', 'abort'),
('access(path, mode, dir_fd, effective_ids, follow_symlinks)\t os',
'access(${1:path}, ${2:mode}, ${3:dir_fd}, ${4:effective_ids}, ${5:follow_symlinks})$0'),
('altsep\t os', 'altsep'),
('chdir(${1:path})$0\t os', 'chdir(${1:path})$0'),
('chmod(${1:path}, ${2:mode}, ${3:dir_fd}, ${4:follow_symlinks})$0\t os',
'chmod(${1:path}, ${2:mode}, ${3:dir_fd}, ${4:follow_symlinks})$0'),
('chown(${1:path}, ${2:uid}, ${3:gid}, ${4:dir_fd}, ${5:follow_symlinks})$0\t os',
'chown(${1:path}, ${2:uid}, ${3:gid}, ${4:dir_fd}, ${5:follow_symlinks})$0'),
('chroot(${1:path})$0\t os', 'chroot(${1:path})$0'),
('chdir(path)\t os', 'chdir(${1:path})$0'),
('chmod(path, mode, dir_fd, follow_symlinks)\t os',
'chmod(${1:path}, ${2:mode}, ${3:dir_fd}, ${4:follow_symlinks})$0'),
('chown(path, uid, gid, dir_fd, follow_symlinks)\t os',
'chown(${1:path}, ${2:uid}, ${3:gid}, ${4:dir_fd}, ${5:follow_symlinks})$0'),
('chroot(path)\t os', 'chroot(${1:path})$0'),
('CLD_CONTINUED\t os', 'CLD_CONTINUED'),
('CLD_DUMPED\t os', 'CLD_DUMPED'),
('CLD_EXITED\t os', 'CLD_EXITED'),
('CLD_TRAPPED\t os', 'CLD_TRAPPED'),
('close(${1:fd})$0\t os', 'close(${1:fd})$0'),
('closerange(${1:fd_low}, ${2:fd_high})$0\t os', 'closerange(${1:fd_low}, ${2:fd_high})$0'),
('confstr(${1:name})$0\t os', 'confstr(${1:name})$0'),
('close(fd)\t os', 'close(${1:fd})$0'),
('closerange(fd_low, fd_high)\t os', 'closerange(${1:fd_low}, ${2:fd_high})$0'),
('confstr(name)\t os', 'confstr(${1:name})$0'),
('confstr_names\t os', 'confstr_names'),
('cpu_count\t os', 'cpu_count'),
('ctermid\t os', 'ctermid'),
('cpu_count()\t os', 'cpu_count'),
('ctermid()\t os', 'ctermid'),
('curdir\t os', 'curdir'),
('defpath\t os', 'defpath'),
('device_encoding(${1:fd})$0\t os', 'device_encoding(${1:fd})$0')
('device_encoding(fd)\t os', 'device_encoding(${1:fd})$0')
]
)

Expand Down