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

Completions overhaul #2010

Merged
merged 5 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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', 0), KIND_UNSPECIFIED)
jwortmann marked this conversation as resolved.
Show resolved Hide resolved

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
predragnikolic marked this conversation as resolved.
Show resolved Hide resolved
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