Skip to content

Commit

Permalink
Fix LspShowScopeNameCommand for the new ContextStackFrame type (#1961)
Browse files Browse the repository at this point in the history
Resolves #1943
  • Loading branch information
rwols authored Apr 12, 2022
1 parent 56ee12f commit 45d4a9b
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 29 deletions.
166 changes: 138 additions & 28 deletions plugin/semantic_highlighting.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .core.registry import LspTextCommand
from .core.typing import List
from .core.typing import Any, List, Tuple, cast
import sublime
import os


class SemanticToken:
Expand All @@ -13,6 +14,12 @@ def __init__(self, region: sublime.Region, type: str, modifiers: List[str]):
self.modifiers = modifiers


def copy(view: sublime.View, text: str) -> None:
sublime.set_clipboard(text)
view.hide_popup()
sublime.status_message('Scope name copied to clipboard')


class LspShowScopeNameCommand(LspTextCommand):
"""
Like the builtin show_scope_name command from Default/show_scope_name.py,
Expand All @@ -21,14 +28,56 @@ class LspShowScopeNameCommand(LspTextCommand):

capability = 'semanticTokensProvider'

def run(self, edit: sublime.Edit) -> None:
def run(self, _: sublime.Edit) -> None:
point = self.view.sel()[-1].b

scope = self.view.scope_name(point).rstrip()
scope_list = scope.replace(' ', '<br>')

stack = self.view.context_backtrace(point)

token_type, token_modifiers = self._get_semantic_info(point)
if isinstance(stack, list) and len(stack) > 0 and not isinstance(stack[0], str):
self._render_with_fancy_stackframes(
scope,
scope_list,
cast(List[sublime.ContextStackFrame], stack),
token_type,
token_modifiers
)
else:
self._render_with_plain_string_stackframes(
scope,
scope_list,
cast(List[str], stack),
token_type,
token_modifiers
)

def _get_semantic_info(self, point: int) -> Tuple[str, str]:
session = self.best_session('semanticTokensProvider')
session_buffer = None
if session:
for sv in session.session_views_async():
if self.view == sv.view:
session_buffer = sv.session_buffer
break
token_type = '-'
token_modifiers = '-'
if session_buffer:
for token in session_buffer.get_semantic_tokens():
if token.region.contains(point) and point < token.region.end():
token_type = token.type
if token.modifiers:
token_modifiers = ', '.join(token.modifiers)
break
return token_type, token_modifiers

def _render_with_plain_string_stackframes(
self,
scope: str,
scope_list: str,
stack: List[str],
token_type: str,
token_modifiers: str
) -> None:
backtrace = ''
digits_len = 1
for i, ctx in enumerate(reversed(stack)):
Expand All @@ -44,26 +93,84 @@ def run(self, edit: sublime.Edit) -> None:
backtrace += '\n'
backtrace += '<div>%s%s</div>' % (nums, ctx)

# ------------------------------------------------------
html = """
<body id=show-scope>
<style>
h1 {
font-size: 1.1rem;
font-weight: 500;
margin: 0 0 0.5em 0;
font-family: system;
}
p {
margin-top: 0;
}
a {
font-weight: normal;
font-style: italic;
padding-left: 1em;
font-size: 1.0rem;
}
span.nums {
display: inline-block;
text-align: right;
width: %dem;
color: color(var(--foreground) a(0.8))
}
span.context {
padding-left: 0.5em;
}
</style>
<h1>Scope Name <a href="%s">Copy</a></h1>
<p>%s</p>
<h1>Context Backtrace</h1>
%s
<br>
<h1>Semantic Token</h1>
<p>Type: %s<br>Modifiers: %s</p>
</body>
""" % (digits_len, scope, scope_list, backtrace, token_type, token_modifiers)

session = self.best_session('semanticTokensProvider')
session_buffer = None
if session:
for sv in session.session_views_async():
if self.view == sv.view:
session_buffer = sv.session_buffer
break
self.view.show_popup(html, max_width=512, max_height=512, on_navigate=lambda x: copy(self.view, x))

token_type = '-'
token_modifiers = '-'
def _render_with_fancy_stackframes(
self,
scope: str,
scope_list: str,
stack: List[Any],
token_type: str,
token_modifiers: str
) -> None:
backtrace = ''
digits_len = 1
for i, frame in enumerate(reversed(stack)):
digits = '%s' % (i + 1)
digits_len = max(len(digits), digits_len)
nums = '<span class=nums>%s.</span>' % digits

if session_buffer:
for token in session_buffer.get_semantic_tokens():
if token.region.contains(point) and point < token.region.end():
token_type = token.type
if token.modifiers:
token_modifiers = ', '.join(token.modifiers)
break
if frame.context_name.startswith("anonymous context "):
context_name = '<em>%s</em>' % frame.context_name
else:
context_name = frame.context_name
ctx = '<span class=context>%s</span>' % context_name

resource_path = frame.source_file
display_path = os.path.splitext(frame.source_file)[0]
if resource_path.startswith('Packages/'):
resource_path = '${packages}/' + resource_path[9:]
display_path = display_path[9:]

if frame.source_location[0] > 0:
href = '%s:%d:%d' % (resource_path, frame.source_location[0], frame.source_location[1])
location = '%s:%d:%d' % (display_path, frame.source_location[0], frame.source_location[1])
else:
href = resource_path
location = display_path
link = '<a href="o:%s">%s</a>' % (href, location)

if backtrace:
backtrace += '\n'
backtrace += '<div>%s%s%s</div>' % (nums, ctx, link)

html = """
<body id=show-scope>
Expand Down Expand Up @@ -93,7 +200,7 @@ def run(self, edit: sublime.Edit) -> None:
padding-left: 0.5em;
}
</style>
<h1>Scope Name <a href="%s">Copy</a></h1>
<h1>Scope Name <a href="c:%s">Copy</a></h1>
<p>%s</p>
<h1>Context Backtrace</h1>
%s
Expand All @@ -103,9 +210,12 @@ def run(self, edit: sublime.Edit) -> None:
</body>
""" % (digits_len, scope, scope_list, backtrace, token_type, token_modifiers)

def copy(view: sublime.View, text: str) -> None:
sublime.set_clipboard(text)
view.hide_popup()
sublime.status_message('Scope name copied to clipboard')
self.view.show_popup(html, max_width=512, max_height=512, on_navigate=self.on_navigate)

self.view.show_popup(html, max_width=512, max_height=512, on_navigate=lambda x: copy(self.view, x))
def on_navigate(self, link: str) -> None:
if link.startswith('o:'):
window = self.view.window()
if window:
window.run_command('open_file', {'file': link[2:], 'encoded_position': True})
else:
copy(self.view, link[2:])
8 changes: 7 additions & 1 deletion stubs/sublime.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,12 @@ class HtmlSheet:
...


class ContextStackFrame:
context_name = ... # type: str
source_file = ... # type: str
source_location = ... # type: Tuple[int, int]


class View:
view_id = ... # type: Any
selection = ... # type: Any
Expand Down Expand Up @@ -801,7 +807,7 @@ class View:
def scope_name(self, pt: int) -> str:
...

def context_backtrace(self, pt: int) -> List[str]:
def context_backtrace(self, pt: int) -> Union[List[ContextStackFrame], List[str]]:
...

def match_selector(self, pt: int, selector: str) -> bool:
Expand Down

0 comments on commit 45d4a9b

Please sign in to comment.