-
Notifications
You must be signed in to change notification settings - Fork 182
/
references.py
133 lines (119 loc) · 5.71 KB
/
references.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
from .core.panels import ensure_panel
from .core.protocol import Location
from .core.protocol import Point
from .core.protocol import Request
from .core.registry import get_position
from .core.registry import LspTextCommand
from .core.sessions import Session
from .core.settings import PLUGIN_NAME
from .core.settings import userprefs
from .core.types import ClientConfig
from .core.types import PANEL_FILE_REGEX
from .core.types import PANEL_LINE_REGEX
from .core.typing import Dict, List, Optional, Tuple
from .core.views import get_line
from .core.views import get_uri_and_position_from_location
from .core.views import text_document_position_params
from .locationpicker import LocationPicker
import functools
import linecache
import os
import sublime
def ensure_references_panel(window: sublime.Window) -> Optional[sublime.View]:
return ensure_panel(window, "references", PANEL_FILE_REGEX, PANEL_LINE_REGEX,
"Packages/" + PLUGIN_NAME + "/Syntaxes/References.sublime-syntax")
class LspSymbolReferencesCommand(LspTextCommand):
capability = 'referencesProvider'
def run(self, _: sublime.Edit, event: Optional[dict] = None, point: Optional[int] = None) -> None:
session = self.best_session(self.capability)
file_path = self.view.file_name()
pos = get_position(self.view, event, point)
if session and file_path and pos is not None:
position_params = text_document_position_params(self.view, pos)
params = {
'textDocument': position_params['textDocument'],
'position': position_params['position'],
'context': {"includeDeclaration": False},
}
request = Request("textDocument/references", params, self.view, progress=True)
session.send_request(
request,
functools.partial(
self._handle_response_async,
self.view.substr(self.view.word(pos)),
session
)
)
def _handle_response_async(self, word: str, session: Session, response: Optional[List[Location]]) -> None:
sublime.set_timeout(lambda: self._handle_response(word, session, response))
def _handle_response(self, word: str, session: Session, response: Optional[List[Location]]) -> None:
if response:
if userprefs().show_references_in_quick_panel:
self._show_references_in_quick_panel(session, response)
else:
self._show_references_in_output_panel(word, session, response)
else:
window = self.view.window()
if window:
window.status_message("No references found")
def _show_references_in_quick_panel(self, session: Session, locations: List[Location]) -> None:
self.view.run_command("add_jump_record", {"selection": [(r.a, r.b) for r in self.view.sel()]})
LocationPicker(self.view, session, locations, side_by_side=False)
def _show_references_in_output_panel(self, word: str, session: Session, locations: List[Location]) -> None:
window = session.window
panel = ensure_references_panel(window)
if not panel:
return
manager = session.manager()
if not manager:
return
base_dir = manager.get_project_path(self.view.file_name() or "")
to_render = [] # type: List[str]
references_count = 0
references_by_file = _group_locations_by_uri(window, session.config, locations)
for file, references in references_by_file.items():
to_render.append('{}:'.format(_get_relative_path(base_dir, file)))
for reference in references:
references_count += 1
point, line = reference
to_render.append('{:>5}:{:<4} {}'.format(point.row + 1, point.col + 1, line))
to_render.append("") # add spacing between filenames
characters = "\n".join(to_render)
panel.settings().set("result_base_dir", base_dir)
panel.run_command("lsp_clear_panel")
window.run_command("show_panel", {"panel": "output.references"})
panel.run_command('append', {
'characters': "{} references for '{}'\n\n{}".format(references_count, word, characters),
'force': True,
'scroll_to_end': False
})
# highlight all word occurrences
regions = panel.find_all(r"\b{}\b".format(word))
panel.add_regions('ReferenceHighlight', regions, 'comment', flags=sublime.DRAW_OUTLINED)
def _get_relative_path(base_dir: Optional[str], file_path: str) -> str:
if base_dir:
try:
return os.path.relpath(file_path, base_dir)
except ValueError:
# On Windows, ValueError is raised when path and start are on different drives.
pass
return file_path
def _group_locations_by_uri(
window: sublime.Window,
config: ClientConfig,
locations: List[Location]
) -> Dict[str, List[Tuple[Point, str]]]:
"""Return a dictionary that groups locations by the URI it belongs."""
grouped_locations = {} # type: Dict[str, List[Tuple[Point, str]]]
for location in locations:
uri, position = get_uri_and_position_from_location(location)
file_path = config.map_server_uri_to_client_path(uri)
point = Point.from_lsp(position)
# get line of the reference, to showcase its use
reference_line = get_line(window, file_path, point.row)
if grouped_locations.get(file_path) is None:
grouped_locations[file_path] = []
grouped_locations[file_path].append((point, reference_line))
# we don't want to cache the line, we always want to get fresh data
linecache.clearcache()
return grouped_locations