Skip to content

Commit 0c5fb28

Browse files
committed
Implement workspace search
1 parent 4c0e99b commit 0c5fb28

File tree

8 files changed

+117
-6
lines changed

8 files changed

+117
-6
lines changed

pylsp/config/schema.json

+8-3
Original file line numberDiff line numberDiff line change
@@ -242,21 +242,26 @@
242242
"default": true,
243243
"description": "Enable or disable the plugin."
244244
},
245-
"pylsp.plugins.jedi_symbols.enabled": {
245+
"pylsp.plugins.jedi_document_symbols.enabled": {
246246
"type": "boolean",
247247
"default": true,
248248
"description": "Enable or disable the plugin."
249249
},
250-
"pylsp.plugins.jedi_symbols.all_scopes": {
250+
"pylsp.plugins.jedi_document_symbols.all_scopes": {
251251
"type": "boolean",
252252
"default": true,
253253
"description": "If True lists the names of all scopes instead of only the module namespace."
254254
},
255-
"pylsp.plugins.jedi_symbols.include_import_symbols": {
255+
"pylsp.plugins.jedi_document_symbols.include_import_symbols": {
256256
"type": "boolean",
257257
"default": true,
258258
"description": "If True includes symbols imported from other libraries."
259259
},
260+
"pylsp.plugins.jedi_workspace_symbols.enabled": {
261+
"type": "boolean",
262+
"default": true,
263+
"description": "If True includes workspace symbols."
264+
},
260265
"pylsp.plugins.mccabe.enabled": {
261266
"type": "boolean",
262267
"default": true,

pylsp/hookspecs.py

+5
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ def pylsp_execute_command(config, workspace, command, arguments):
6868
pass
6969

7070

71+
@hookspec
72+
def pylsp_workspace_symbol(config, workspace, document, query):
73+
pass
74+
75+
7176
@hookspec
7277
def pylsp_experimental_capabilities(config, workspace):
7378
pass

pylsp/plugins/symbols.py renamed to pylsp/plugins/document_symbols.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
@hookimpl
1414
def pylsp_document_symbols(config, document):
15-
symbols_settings = config.plugin_settings("jedi_symbols")
15+
symbols_settings = config.plugin_settings("jedi_document_symbols")
1616
all_scopes = symbols_settings.get("all_scopes", True)
1717
add_import_symbols = symbols_settings.get("include_import_symbols", True)
1818
definitions = document.jedi_names(all_scopes=all_scopes)

pylsp/plugins/workspace_symbol.py

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Copyright 2017-2020 Palantir Technologies, Inc.
2+
# Copyright 2021- Python Language Server Contributors.
3+
4+
import logging
5+
from pathlib import Path
6+
7+
from pylsp import hookimpl
8+
from pylsp.lsp import SymbolKind
9+
10+
log = logging.getLogger(__name__)
11+
12+
13+
@hookimpl
14+
def pylsp_workspace_symbol(config, workspace, document, query):
15+
if not query or not workspace:
16+
return []
17+
18+
return [_jedi_name_to_symbol(jedi_name) for jedi_name in workspace.search(query)]
19+
20+
21+
def _jedi_name_to_symbol(jedi_name):
22+
return {
23+
"name": jedi_name.name,
24+
"kind": _jedi_type_to_symbol_kind(jedi_name.type),
25+
"location": {
26+
"uri": "file://" + str(jedi_name.module_path),
27+
"range": {
28+
"start": {"line": jedi_name.line - 1, "character": jedi_name.column},
29+
"end": {"line": jedi_name.line - 1, "character": jedi_name.column},
30+
},
31+
},
32+
}
33+
34+
35+
def _jedi_type_to_symbol_kind(jedi_type):
36+
if jedi_type == "module":
37+
return SymbolKind.Module
38+
if jedi_type == "class":
39+
return SymbolKind.Class
40+
if jedi_type == "function":
41+
return SymbolKind.Function
42+
if jedi_type == "instance":
43+
return SymbolKind.Variable
44+
if jedi_type == "statement":
45+
return SymbolKind.Variable
46+
if jedi_type == "param":
47+
return SymbolKind.Variable
48+
if jedi_type == "path":
49+
return SymbolKind.File
50+
return SymbolKind.Variable

pylsp/python_lsp.py

+9
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ def capabilities(self):
298298
"workspaceFolders": {"supported": True, "changeNotifications": True}
299299
},
300300
"experimental": merge(self._hook("pylsp_experimental_capabilities")),
301+
"workspaceSymbolProvider": True,
301302
}
302303
log.info("Server capabilities: %s", server_capabilities)
303304
return server_capabilities
@@ -433,6 +434,11 @@ def highlight(self, doc_uri, position):
433434
or None
434435
)
435436

437+
def workspace_symbol(self, query):
438+
response = self._hook("pylsp_workspace_symbol", query=query)
439+
log.debug("Workspace symbol hook returned: %s", response)
440+
return flatten(response)
441+
436442
def hover(self, doc_uri, position):
437443
return self._hook("pylsp_hover", doc_uri, position=position) or {"contents": ""}
438444

@@ -767,6 +773,9 @@ def m_text_document__hover(self, textDocument=None, position=None, **_kwargs):
767773
def m_text_document__document_symbol(self, textDocument=None, **_kwargs):
768774
return self.document_symbols(textDocument["uri"])
769775

776+
def m_workspace__symbol(self, textDocument=None, **_kwargs):
777+
return self.workspace_symbol(_kwargs["query"])
778+
770779
def m_text_document__formatting(self, textDocument=None, options=None, **_kwargs):
771780
return self.format_document(textDocument["uri"], options)
772781

pylsp/workspace.py

+40
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,46 @@ def root_path(self):
100100
def root_uri(self):
101101
return self._root_uri
102102

103+
def search(self, query):
104+
project = self.jedi_project()
105+
return project.complete_search(query)
106+
107+
def jedi_project(self, use_document_path=False):
108+
extra_paths = []
109+
environment_path = None
110+
env_vars = None
111+
112+
if self._config:
113+
jedi_settings = self._config.plugin_settings(
114+
"jedi", document_path=self.root_path
115+
)
116+
jedi.settings.auto_import_modules = jedi_settings.get(
117+
"auto_import_modules", DEFAULT_AUTO_IMPORT_MODULES
118+
)
119+
environment_path = jedi_settings.get("environment")
120+
# Jedi itself cannot deal with homedir-relative paths.
121+
# On systems, where it is expected, expand the home directory.
122+
if environment_path and os.name != "nt":
123+
environment_path = os.path.expanduser(environment_path)
124+
125+
extra_paths = jedi_settings.get("extra_paths") or []
126+
env_vars = jedi_settings.get("env_vars")
127+
128+
# Drop PYTHONPATH from env_vars before creating the environment because that makes
129+
# Jedi throw an error.
130+
if env_vars is None:
131+
env_vars = os.environ.copy()
132+
env_vars.pop("PYTHONPATH", None)
133+
134+
sys_path = extra_paths # self.sys_path(environment_path, env_vars=env_vars) + extra_paths
135+
project_path = self.root_path
136+
137+
# Extend sys_path with document's path if requested
138+
if use_document_path:
139+
sys_path += [os.path.normpath(os.path.dirname(self.path))]
140+
141+
return jedi.Project(path=project_path, sys_path=sys_path)
142+
103143
def is_local(self):
104144
return (self._root_uri_scheme in ["", "file"]) and os.path.exists(
105145
self._root_path

pyproject.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ jedi_highlight = "pylsp.plugins.highlight"
7171
jedi_references = "pylsp.plugins.references"
7272
jedi_rename = "pylsp.plugins.jedi_rename"
7373
jedi_signature_help = "pylsp.plugins.signature"
74-
jedi_symbols = "pylsp.plugins.symbols"
74+
jedi_document_symbols = "pylsp.plugins.document_symbols"
75+
jedi_workspace_symbol = "pylsp.plugins.workspace_symbol"
7576
mccabe = "pylsp.plugins.mccabe_lint"
7677
preload = "pylsp.plugins.preload_imports"
7778
pycodestyle = "pylsp.plugins.pycodestyle_lint"

test/plugins/test_symbols.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
from pylsp import uris
1010
from pylsp.lsp import SymbolKind
11-
from pylsp.plugins.symbols import pylsp_document_symbols
11+
from pylsp.plugins.document_symbols import pylsp_document_symbols
12+
from pylsp.plugins.workspace_symbol import pylsp_workspace_symbol
1213
from pylsp.workspace import Document
1314

1415
PY2 = sys.version[0] == "2"

0 commit comments

Comments
 (0)