-
Notifications
You must be signed in to change notification settings - Fork 185
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
Improve heuristic related to showing completion labels #2189
Comments
Example payloads: {
"commitCharacters": [
".",
",",
";",
"("
],
"filterText": ". $attrs",
"insertText": ". $attrs",
"insertTextFormat": 1,
"kind": 5,
"label": "$attrs",
"sortText": "11",
"textEdit": {
"newText": " $attrs",
"range": {
"end": {
"character": 19,
"line": 7
},
"start": {
"character": 18,
"line": 7
}
}
}
} {
"commitCharacters": [
".",
",",
"("
],
"detail": "typescript",
"filterText": "import { readConfigFile$1 } from 'typescript';",
"insertText": "import { readConfigFile$1 } from 'typescript';",
"insertTextFormat": 2,
"kind": 3,
"label": "readConfigFile",
"sortText": "11",
"textEdit": {
"insert": {
"end": {
"character": 18,
"line": 7
},
"start": {
"character": 0,
"line": 7
}
},
"newText": "import { readConfigFile$1 } from 'typescript';",
"replace": {
"end": {
"character": 18,
"line": 7
},
"start": {
"character": 0,
"line": 7
}
}
}
} |
An initial idea from my side would be |
Could we dumbify it to just check for a space in I'm thinking that in case of that specific typescript example, in theory the |
I'm thinking about what would be the reason why a server would use a That's why we have the For you first example with the screenshot and the leading VSCode doesn't have this problem, because it can still filter the items even if you type something which is not contained in the label. But ST can't do this, and actually what I wrote in #2189 (comment) is wrong and ST does match over multiple words separated by spaces (in the comment above I accidentally thought about what happens when you write a space, which is of course not really relevant): So I'm doubtful that even in my suggestion from above the "more beautiful" label would outweight the drawbacks in behavior. |
There is more context in microsoft/vscode#63100 As far as I understand, VSCode scores completion higher when (In case of Volar the extra space after So based on that info, it looks like the purpose of As for |
But the server already knows from the content of the file and the position of the completion request, what the completion prefix is, i.e. in the linked VSCode issue that it is a dot. So to prepend a Under "filtering" I understand that if you type a letter which is not present in the
But then I would say that, if a server like Volar decides to ignore the purpose of the different CompletionItem fields, the "fix" for that should be in the LSP-Volar package, not in the LSP package. |
I've been discussing this more in typescript-language-server/typescript-language-server#631 but the bottom line is that I'm fairly convinced now that what vscode is doing with |
This issue has appeared before with gopls. It used the same string for all filterText fields to influence VSCode’s sorting behavior from what I can recall. |
Are you able to dig out some more info (issues)? More data points could help to make the right decision here. EDIT: Only able to find #1002 |
Although not a question pointed to me, And this comment here is useful
|
I'm not sure about that. It seems to be related to labels that span word boundaries and I don't think filterText would have any bearing on that.
Kinda useful on understanding how filterText should work but kinda surprised that he said that completion item with textEdit and without filterText should never be filtered out. In his example this completion:
shows up when typing |
The conclusion is that it's a job of the server to not set weird Also that ST should have a way to filter on a separate (invisible) text. |
Yes, that seems weird, but I think it's not really relevant for us anyway, because the only information in the completion item and what we can therefore use as "trigger" in this case is the As far as I understand the linked comment, Like in the given example if you have typed One other idea what we maybe could do to improve the "trigger", would be if {"filterText": "import { readConfigFile$1 } from 'typescript';", "label": "readConfigFile"} we would show |
Any opinions on the idea from my last comment? Proof of concept implementation (untested and might have an impact on performance): Click to revealdiff --git a/plugin/completion.py b/plugin/completion.py
index c7b9485..75e97ee 100644
--- a/plugin/completion.py
+++ b/plugin/completion.py
@@ -40,12 +40,22 @@ ResolvedCompletions = Tuple[Union[CompletionResponse, Error], 'weakref.ref[Sessi
CompletionsStore = Tuple[List[CompletionItem], CompletionItemDefaults]
+def fuzzy_match(text: str, prefix: str) -> bool:
+ for letter in prefix:
+ idx = text.find(letter)
+ if idx == -1:
+ return False
+ text = text[idx+1:]
+ return True
+
+
def format_completion(
item: CompletionItem,
index: int,
can_resolve_completion_items: bool,
session_name: str,
item_defaults: CompletionItemDefaults,
+ prefix: str,
view_id: int
) -> sublime.CompletionItem:
# This is a hot function. Don't do heavy computations or IO in this function.
@@ -74,6 +84,13 @@ def format_completion(
details.append(html.escape(lsp_label + lsp_label_detail))
if lsp_label_description:
details.append(html.escape(lsp_label_description))
+ elif " " in lsp_filter_text and lsp_label in lsp_filter_text and fuzzy_match(lsp_label, prefix):
+ trigger = lsp_filter_text[lsp_filter_text.find(lsp_label):]
+ annotation = lsp_detail
+ if lsp_label_detail:
+ details.append(html.escape(lsp_label + lsp_label_detail))
+ if lsp_label_description:
+ details.append(html.escape(lsp_label_description))
else:
trigger = lsp_filter_text
annotation = lsp_detail
@@ -166,11 +183,13 @@ class QueryCompletionsTask:
def __init__(
self,
view: sublime.View,
+ prefix: str,
location: int,
triggered_manually: bool,
on_done_async: Callable[[List[sublime.CompletionItem], int], None]
) -> None:
self._view = view
+ self._prefix = prefix
self._location = location
self._triggered_manually = triggered_manually
self._on_done_async = on_done_async
@@ -232,7 +251,14 @@ class QueryCompletionsTask:
config_name = session.config.name
items.extend(
format_completion(
- response_item, index, can_resolve_completion_items, config_name, item_defaults, self._view.id())
+ response_item,
+ index,
+ can_resolve_completion_items,
+ config_name,
+ item_defaults,
+ self._prefix,
+ self._view.id()
+ )
for index, response_item in enumerate(response_items)
if include_snippets or response_item.get("kind") != CompletionItemKind.Snippet)
if items:
diff --git a/plugin/documents.py b/plugin/documents.py
index df80930..982e8ba 100644
--- a/plugin/documents.py
+++ b/plugin/documents.py
@@ -487,18 +487,18 @@ class DocumentSyncListener(sublime_plugin.ViewEventListener, AbstractViewListene
triggered_manually = self._auto_complete_triggered_manually
self._auto_complete_triggered_manually = False # reset state for next completion popup
sublime.set_timeout_async(
- lambda: self._on_query_completions_async(completion_list, locations[0], triggered_manually))
+ lambda: self._on_query_completions_async(completion_list, prefix, locations[0], triggered_manually))
return completion_list
# --- textDocument/complete ----------------------------------------------------------------------------------------
def _on_query_completions_async(
- self, clist: sublime.CompletionList, location: int, triggered_manually: bool
+ self, clist: sublime.CompletionList, prefix: str, location: int, triggered_manually: bool
) -> None:
if self._completions_task:
self._completions_task.cancel_async()
on_done = partial(self._on_query_completions_resolved_async, clist)
- self._completions_task = QueryCompletionsTask(self.view, location, triggered_manually, on_done)
+ self._completions_task = QueryCompletionsTask(self.view, prefix, location, triggered_manually, on_done)
sessions = list(self.sessions_async('completionProvider'))
if not sessions or not self.view.is_valid():
self._completions_task.cancel_async() |
Not at the computer now but i have a feeling that ST does not report a |
@jwortmann some related info that I wasn't quite aware of (maybe you were): typescript-language-server/typescript-language-server#631 (comment) (read till the end). I've confirmed that behavior with VSCode. |
It seems like a proper thing to do would be to in the first place get the text from the document that matches the |
I was not exactly aware of that, but I've commented in that issue.
But I wonder what could we exactly do with that information? |
Hmm, I haven't thought too much of that and now I'm thinking that maybe I was too optimistic. I was thinking that in a simple case where the range includes the period (made up example below) it would somehow help to pick the
|
I also have another criterion for the completion trigger: (Assuming that For example with LSP-TexLab, and when the completion popup for a citation label appears, the filterText will include everything contained in the corresponding bibliography entry. For testing use some @book{SNPO08,
author = {Eduardo de Souza Neto and Djordje Perić and David Owen},
title = {Computational Methods for Plasticity},
subtitle = {Theory and Applications},
publisher = {John Wiley \& Sons, Ltd},
date = {2008},
isbn = {978-0-470-69452-7}
} and write in a LaTeX document \documentclass{article}
\usepackage{biblatex}
\addbibresource{bibliography.bib}
\begin{document}
\cite{ Completion response: {
"isIncomplete": false,
"items": [
{
"kind": 22,
"preselect": false,
"data": {
"citation": {
"uri": "file:///D:/code/LaTeX/LspTest/bibliography.bib",
"key": "SNPO08"
}
},
"textEdit": {
"newText": "SNPO08",
"range": {
"end": {
"character": 6,
"line": 7
},
"start": {
"character": 6,
"line": 7
}
}
},
"label": "SNPO08",
"filterText": "SNPO08 @book SNPO08 author Eduardo de Souza Neto and Djordje Perić and David Owen title Computational Methods for Plasticity subtitle Theory and Applications publisher John Wiley \\& Sons Ltd date 2008 isbn 978-0-470-69452-7",
"sortText": "00 SNPO08 @book SNPO08 author Eduardo de Souza Neto and Djordje Perić and David Owen title Computational Methods for Plasticity subtitle Theory and Applications publisher John Wiley \\& Sons Ltd date 2008 isbn 978-0-470-69452-7"
}
]
} I guess the idea is that you don't need to remember the cryptic bib-entry key (SNPO08 in this case), but instead can type the book title or the author names to filter the suggestions. But due to Sublime's strange narrowing technique for the long completion trigger, it doesn't actually work in this editor. So I guess here it would be better to directly show the |
Describe the bug
typescript-language-server
will soon introducefilterText
property which will make ST presentation of completions worse. See typescript-language-server/typescript-language-server#678 (review)The change will be spec compliant and might improve things for other editors but it's not gonna work great for us so we need to improve our heuristic.
This issue also already manifests with Volar, for example. See:
The text was updated successfully, but these errors were encountered: