From ac9144692159dea8264f4925237debb0af69cce5 Mon Sep 17 00:00:00 2001 From: jwortmann Date: Tue, 2 Aug 2022 09:43:15 +0200 Subject: [PATCH] Completions overhaul (#2010) --- plugin/core/views.py | 91 +++++++++++++++++----------------------- tests/test_completion.py | 46 ++++++-------------- 2 files changed, 50 insertions(+), 87 deletions(-) diff --git a/plugin/core/views.py b/plugin/core/views.py index 6200d55db..18e9cfe5a 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -939,66 +939,51 @@ def format_diagnostic_for_html( return "".join(formatted) -def _is_completion_item_deprecated(item: CompletionItem) -> bool: - if item.get("deprecated", False): - return True - tags = item.get("tags") - if isinstance(tags, list): - return CompletionItemTag.Deprecated in tags - return False - - -def _wrap_in_tags(tag: str, item: str) -> str: - return "<{0}>{1}".format(tag, html.escape(item)) - - def format_completion( item: CompletionItem, index: int, can_resolve_completion_items: bool, session_name: str ) -> sublime.CompletionItem: # This is a hot function. Don't do heavy computations or IO in this function. - item_kind = item.get("kind") - kind = COMPLETION_KINDS.get(item_kind, KIND_UNSPECIFIED) if item_kind else KIND_UNSPECIFIED - - if _is_completion_item_deprecated(item): - kind = (kind[0], '!', "⚠ {} - Deprecated".format(kind[2])) - - lsp_label = item["label"] - lsp_label_details = item.get("labelDetails") - lsp_filter_text = item.get("filterText") - st_annotation = (item.get("detail") or "").replace('\n', ' ') - - st_details = "" - if can_resolve_completion_items or item.get("documentation"): - st_details += make_command_link("lsp_resolve_docs", "More", {"index": index, "session_name": session_name}) - if lsp_label_details: - if st_details: - st_details += " | " - lsp_label_detail = lsp_label_details.get("detail") - lsp_label_description = lsp_label_details.get("description") - st_details += "

" - # `label` should be rendered most prominent. - st_details += _wrap_in_tags("b", lsp_label) - if isinstance(lsp_label_detail, str): - # `detail` should be rendered less prominent than `label`. - # The string is appended directly after `label`, with no additional white space applied. - st_details += html.escape(lsp_label_detail) - if isinstance(lsp_label_description, str): - # `description` should be rendered less prominent than `detail`. - # Additional separation is added. - st_details += " - {}".format(_wrap_in_tags("i", lsp_label_description)) - st_details += "

" - elif lsp_filter_text and lsp_filter_text != lsp_label: - if st_details: - st_details += " | " - st_details += _wrap_in_tags("p", lsp_label) + + lsp_label = item['label'] + lsp_label_details = item.get('labelDetails') or {} + lsp_label_detail = lsp_label_details.get('detail') or "" + lsp_label_description = lsp_label_details.get('description') or "" + lsp_filter_text = item.get('filterText') or "" + lsp_detail = (item.get('detail') or "").replace("\n", " ") + + kind = COMPLETION_KINDS.get(item.get('kind', -1), KIND_UNSPECIFIED) + + details = [] # type: List[str] + if can_resolve_completion_items or item.get('documentation'): + details.append(make_command_link('lsp_resolve_docs', "More", {'index': index, 'session_name': session_name})) + + if lsp_label_detail and (lsp_label + lsp_label_detail).startswith(lsp_filter_text): + trigger = lsp_label + lsp_label_detail + annotation = lsp_label_description or lsp_detail + elif lsp_label.startswith(lsp_filter_text): + trigger = 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 + details.append(html.escape(lsp_label + lsp_label_detail)) + if lsp_label_description: + details.append(html.escape(lsp_label_description)) + + if item.get('deprecated') or CompletionItemTag.Deprecated in item.get('tags', []): + annotation = "DEPRECATED - " + annotation if annotation else "DEPRECATED" completion = sublime.CompletionItem.command_completion( - trigger=lsp_filter_text or lsp_label, - command="lsp_select_completion_item", + trigger=trigger, + command='lsp_select_completion_item', args={"item": item, "session_name": session_name}, - annotation=st_annotation, + annotation=annotation, kind=kind, - details=st_details) - if item.get("textEdit"): + details=" | ".join(details)) + if item.get('textEdit'): completion.flags = sublime.COMPLETION_FLAG_KEEP_PREFIX return completion diff --git a/tests/test_completion.py b/tests/test_completion.py index 1ca0a79c7..0afa86947 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -142,7 +142,6 @@ def test_var_prefix_added_in_insertText(self) -> 'Generator': 'sortText': '0006USERPROFILE', 'label': 'USERPROFILE', 'additionalTextEdits': None, - 'detail': None, 'data': None, 'kind': 6, 'command': None, @@ -178,7 +177,6 @@ def test_pure_insertion_text_edit(self) -> 'Generator': } }, 'label': '$someParam', - 'filterText': None, 'data': None, 'command': None, 'detail': 'null', @@ -612,8 +610,7 @@ def test_show_deprecated_flag(self) -> None: "deprecated": True } # type: CompletionItem formatted_completion_item = format_completion(item_with_deprecated_flag, 0, False, "") - self.assertEqual('!', formatted_completion_item.kind[1]) - self.assertEqual('⚠ Method - Deprecated', formatted_completion_item.kind[2]) + self.assertIn("DEPRECATED", formatted_completion_item.annotation) def test_show_deprecated_tag(self) -> None: item_with_deprecated_tags = { @@ -622,8 +619,7 @@ def test_show_deprecated_tag(self) -> None: "tags": [CompletionItemTag.Deprecated] } # type: CompletionItem formatted_completion_item = format_completion(item_with_deprecated_tags, 0, False, "") - self.assertEqual('!', formatted_completion_item.kind[1]) - self.assertEqual('⚠ Method - Deprecated', formatted_completion_item.kind[2]) + self.assertIn("DEPRECATED", formatted_completion_item.annotation) def test_strips_carriage_return_in_insert_text(self) -> 'Generator': yield from self.verify( @@ -662,37 +658,37 @@ def check( check( resolve_support=False, - expected_regex=r"^

f

$", + expected_regex=r"^f$", label="f", label_details=None ) check( resolve_support=False, - expected_regex=r"^

f\(X& x\)

$", + expected_regex=r"^f\(X& x\)$", label="f", label_details={"detail": "(X& x)"} ) check( resolve_support=False, - expected_regex=r"^

f\(X& x\) - does things

$", + expected_regex=r"^f\(X& x\) \| does things$", label="f", label_details={"detail": "(X& x)", "description": "does things"} ) check( resolve_support=True, - expected_regex=r"^More \|

f

$", + expected_regex=r"^More \| f$", label="f", label_details=None ) check( resolve_support=True, - expected_regex=r"^More \|

f\(X& x\)

$", + expected_regex=r"^More \| f\(X& x\)$", label="f", label_details={"detail": "(X& x)"} ) check( resolve_support=True, - expected_regex=r"^More \|

f\(X& x\) - does things

$", # noqa: E501 + expected_regex=r"^More \| f\(X& x\) \| does things$", # noqa: E501 label="f", label_details={"detail": "(X& x)", "description": "does things"} ) @@ -709,41 +705,23 @@ def check( if label_details is not None: lsp["labelDetails"] = label_details native = format_completion(lsp, 0, resolve_support, "") - self.assertRegex(native.details, expected_regex) + self.assertRegex(native.trigger, expected_regex) check( resolve_support=False, - expected_regex=r"^$", + expected_regex=r"^f$", label="f", label_details=None ) check( resolve_support=False, - expected_regex=r"^

f\(X& x\)

$", + expected_regex=r"^f\(X& x\)$", label="f", label_details={"detail": "(X& x)"} ) check( resolve_support=False, - expected_regex=r"^

f\(X& x\) - does things

$", - label="f", - label_details={"detail": "(X& x)", "description": "does things"} - ) - check( - resolve_support=True, - expected_regex=r"^More$", - label="f", - label_details=None - ) - check( - resolve_support=True, - expected_regex=r"^More \|

f\(X& x\)

$", - label="f", - label_details={"detail": "(X& x)"} - ) - check( - resolve_support=True, - expected_regex=r"^More \|

f\(X& x\) - does things

$", # noqa: E501 + expected_regex=r"^f\(X& x\)$", label="f", label_details={"detail": "(X& x)", "description": "does things"} )