Skip to content

Commit

Permalink
Completions overhaul (#2010)
Browse files Browse the repository at this point in the history
  • Loading branch information
jwortmann authored Aug 2, 2022
1 parent da960d5 commit ac91446
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 87 deletions.
91 changes: 38 additions & 53 deletions plugin/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}</{0}>".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 += "<p>"
# `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 += "</p>"
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
46 changes: 12 additions & 34 deletions tests/test_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -178,7 +177,6 @@ def test_pure_insertion_text_edit(self) -> 'Generator':
}
},
'label': '$someParam',
'filterText': None,
'data': None,
'command': None,
'detail': 'null',
Expand Down Expand Up @@ -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 = {
Expand All @@ -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(
Expand Down Expand Up @@ -662,37 +658,37 @@ def check(

check(
resolve_support=False,
expected_regex=r"^<p>f</p>$",
expected_regex=r"^f$",
label="f",
label_details=None
)
check(
resolve_support=False,
expected_regex=r"^<p><b>f</b>\(X&amp; x\)</p>$",
expected_regex=r"^f\(X&amp; x\)$",
label="f",
label_details={"detail": "(X& x)"}
)
check(
resolve_support=False,
expected_regex=r"^<p><b>f</b>\(X&amp; x\) - <i>does things</i></p>$",
expected_regex=r"^f\(X&amp; x\) \| does things$",
label="f",
label_details={"detail": "(X& x)", "description": "does things"}
)
check(
resolve_support=True,
expected_regex=r"^<a href='subl:lsp_resolve_docs {\S+}'>More</a> \| <p>f</p>$",
expected_regex=r"^<a href='subl:lsp_resolve_docs {\S+}'>More</a> \| f$",
label="f",
label_details=None
)
check(
resolve_support=True,
expected_regex=r"^<a href='subl:lsp_resolve_docs {\S+}'>More</a> \| <p><b>f</b>\(X&amp; x\)</p>$",
expected_regex=r"^<a href='subl:lsp_resolve_docs {\S+}'>More</a> \| f\(X&amp; x\)$",
label="f",
label_details={"detail": "(X& x)"}
)
check(
resolve_support=True,
expected_regex=r"^<a href='subl:lsp_resolve_docs {\S+}'>More</a> \| <p><b>f</b>\(X&amp; x\) - <i>does things</i></p>$", # noqa: E501
expected_regex=r"^<a href='subl:lsp_resolve_docs {\S+}'>More</a> \| f\(X&amp; x\) \| does things$", # noqa: E501
label="f",
label_details={"detail": "(X& x)", "description": "does things"}
)
Expand All @@ -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"^<p><b>f</b>\(X&amp; x\)</p>$",
expected_regex=r"^f\(X& x\)$",
label="f",
label_details={"detail": "(X& x)"}
)
check(
resolve_support=False,
expected_regex=r"^<p><b>f</b>\(X&amp; x\) - <i>does things</i></p>$",
label="f",
label_details={"detail": "(X& x)", "description": "does things"}
)
check(
resolve_support=True,
expected_regex=r"^<a href='subl:lsp_resolve_docs {\S+}'>More</a>$",
label="f",
label_details=None
)
check(
resolve_support=True,
expected_regex=r"^<a href='subl:lsp_resolve_docs {\S+}'>More</a> \| <p><b>f</b>\(X&amp; x\)</p>$",
label="f",
label_details={"detail": "(X& x)"}
)
check(
resolve_support=True,
expected_regex=r"^<a href='subl:lsp_resolve_docs {\S+}'>More</a> \| <p><b>f</b>\(X&amp; x\) - <i>does things</i></p>$", # noqa: E501
expected_regex=r"^f\(X& x\)$",
label="f",
label_details={"detail": "(X& x)", "description": "does things"}
)
Expand Down

0 comments on commit ac91446

Please sign in to comment.