A python-lsp-server plugin that adds workspace symbol search, inlay hints, code lens, semantic tokens, call/type hierarchy, document links and document colors via Jedi.
Why?
pylspdoes not implement several LSP features natively. This plugin fills those gaps โ enabling "Go to Symbol in Workspace", rich type inference hints, code lens overlays, semantic token highlighting, call/type hierarchy navigation, clickable import links, and inline color previews in any LSP client โ including CudaText, Neovim, Emacs, and others โ with broad client compatibility out of the box.
- ๐ Workspace-wide symbol search โ find functions, classes, and modules across all files in the project
- ๐ก Inlay hints โ inline type annotations inferred by Jedi for assignments, return types, raised exceptions, and parameter names at call sites
- ๐ญ Code lens โ per-definition overlays showing reference counts (
๐ฅ N references), subclass/override counts (๐ N implementations), run entry points (โถ Run), and test markers (๐งช Run test) - ๐จ Semantic tokens โ Jedi-backed token classification for editors that support
textDocument/semanticTokens(opt-in, disabled by default) - ๐ณ Call hierarchy โ navigate callers and callees of any function via
callHierarchy/incomingCallsandcallHierarchy/outgoingCalls - ๐งฌ Type hierarchy โ explore supertypes and subtypes of any class via
typeHierarchy/supertypesandtypeHierarchy/subtypes - ๐ Document links โ clickable links for URLs in comments/strings and import statements (resolves to stdlib source when available)
- ๐จ Document colors โ inline color previews for CSS/hex/RGB/HSL color literals in source files
- ๐ Broad client compatibility โ capabilities announced via proper LSP channel (works with Neovim, eglot, and any client that does not support experimental capabilities), with automatic fallback to the experimental channel
- โก Fast โ results in ~130ms after the first call (Jedi cache warm)
- ๐ค Case-insensitive substring match โ
areafindscalculate_area,CalfindsCalculator - ๐ Smart folder exclusion โ automatically skips
.git,__pycache__,node_modules,.venv,dist,build, and more - โ๏ธ Configurable โ tune all options via pylsp settings
- ๐ Python 3.8+ โ compatible with all modern Python versions
pip install pylsp-workspace-symbolsThe plugin is discovered automatically by pylsp via its entry point โ no manual configuration needed.
Add to your LSP client's pylsp settings (e.g. in settings.json or equivalent):
{
"pylsp": {
"plugins": {
"jedi_workspace_symbols": {
"enabled": true,
"max_symbols": 500,
"ignore_folders": []
},
"inlay_hints": {
"enabled": true,
"show_assign_types": true,
"show_return_types": true,
"show_raises": true,
"show_parameter_hints": true,
"max_hints_per_file": 200
},
"code_lens": {
"enabled": true,
"show_references": true,
"show_implementations": true,
"cross_file_implementations": false,
"show_run": true,
"show_tests": true,
"max_definitions": 150
},
"semantic_tokens": {
"enabled": false
},
"call_hierarchy": {
"enabled": true
},
"type_hierarchy": {
"enabled": true
},
"document_links": {
"enabled": true
},
"document_colors": {
"enabled": true
}
}
}
}| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable/disable workspace symbol search |
max_symbols |
int | 500 |
Maximum symbols returned. 0 means no limit |
ignore_folders |
list | [] |
Extra folder names to skip (merged with built-in list) |
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable/disable all inlay hints |
show_assign_types |
bool | true |
Show inferred types for unannotated assignments (x = 42 โ : int) |
show_return_types |
bool | true |
Show inferred return types for unannotated functions (def f(): โ -> str) |
show_raises |
bool | true |
Show raised exception types (raise ValueError(...) โ Raises: ValueError) |
show_parameter_hints |
bool | true |
Show parameter names at call sites (f(1, 2) โ a=1, b=2) |
max_hints_per_file |
int | 200 |
Maximum hints per file. 0 means no limit |
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable/disable all code lenses |
show_references |
bool | true |
Show ๐ฅ N references above every function, method, and class |
show_implementations |
bool | true |
Show ๐ N implementations on classes with subclasses and methods with overrides |
cross_file_implementations |
bool | false |
Extend ๐ counts to subclasses/overrides in other files. Opt-in โ adds one get_references() call + file I/O per class/method |
show_run |
bool | true |
Show โถ Run above if __name__ == "__main__": blocks |
show_tests |
bool | true |
Show ๐งช Run test above test_* functions and Test* classes |
max_definitions |
int | 150 |
Maximum number of definitions to process per file |
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enable/disable semantic token highlighting. Opt-in โ can be slow on very large files |
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable/disable call hierarchy (callHierarchy/incomingCalls, callHierarchy/outgoingCalls) |
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable/disable type hierarchy (typeHierarchy/supertypes, typeHierarchy/subtypes) |
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable/disable document links (URLs in comments/strings and import resolution) |
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable/disable document color previews (hex, RGB, HSL, CSS named colors) |
.git, .hg, .svn, __pycache__, .mypy_cache, .ruff_cache, .pytest_cache,
node_modules, .venv, venv, .env, env, dist, build, .eggs, egg-info
Once installed, your LSP client will receive workspaceSymbolProvider: true in the server capabilities.
Use your client's "Go to Symbol in Workspace" command (typically Ctrl+T or # in the symbol picker).
Your LSP client will receive inlayHintProvider: true in the server capabilities. Hints are
rendered inline by the client automatically. The following hint types are supported:
- Assignment hints โ unannotated variable assignments, including
self.attrin__init__ - Return hints โ unannotated
defandasync deffunctions, inferred from the firstreturnstatement - Raise hints โ
raise ExceptionType(...)statements - Parameter hints โ positional argument names at call sites (keyword args are skipped as self-documenting)
Inlay hints respect type annotations already present in the source โ annotated functions and variables are never hinted twice.
Your LSP client will receive codeLensProvider: true. The following overlays are shown above
definitions:
๐ฅ N referencesโ every top-level function, method, and class. Inheritance usages (class Dog(Animal)) are excluded from the reference count and counted as implementations instead. Cross-file inheritance usages are also excluded whencross_file_implementations=true.๐ N implementationsโ classes with direct subclasses; methods overridden in at least one subclass. Intra-file always; cross-file whencross_file_implementations=true(opt-in).โถ Runโif __name__ == "__main__":entry point blocks. Firesworkspace/executeCommandwith commandpylsp_workspace_symbols.run_file.๐งช Run testโtest_*functions andTest*/unittest.TestCasesubclasses. Firesworkspace/executeCommandwith commandpylsp_workspace_symbols.run_test, passing{"path": ..., "name": ..., "kind": "function"|"class"}as arguments.
Your LSP client will receive semanticTokensProvider when semantic_tokens.enabled is true.
Token types follow an extended LSP legend: namespace, type, class, enum, interface,
struct, typeParameter, parameter, variable, property, enumMember, function,
method, macro, keyword, comment, string, number, regexp, operator,
decorator, plus Python-specific selfParameter and clsParameter.
Modifiers include declaration, readonly, static, deprecated, async,
modification, documentation, defaultLibrary, builtin, classMember, and parameter.
Disabled by default โ enable explicitly if your client supports it and you want Jedi-backed
token classification in addition to your editor's built-in lexer.
Your LSP client will receive callHierarchyProvider: true. Place the cursor on any function name
and invoke "Show Call Hierarchy" to see incoming callers and outgoing callees. Compatible with
any LSP client that supports the standard callHierarchy/* requests (Neovim, eglot, CudaText, etc.).
Your LSP client will receive typeHierarchyProvider: true. Place the cursor on any class name
and invoke "Show Type Hierarchy" to explore supertypes and subtypes. Compatible with
any LSP client that supports the standard typeHierarchy/* requests (Neovim, eglot, CudaText, etc.).
Your LSP client will receive documentLinkProvider: true. The following are turned into clickable links:
- URLs โ
http://andhttps://links in comments and strings - Import statements โ resolved to the corresponding
.pysource file in the system Python'sLib/directory (when Python is installed and available on PATH); modules without a.pysource (C extensions, frozen modules, embedded-only.pyc) are silently skipped - Workspace path literals โ relative path strings (e.g.
"./config.json","../data/file.csv") andopen()/Path()calls that reference files present in the workspace
Your LSP client will receive colorProvider: true. Inline color swatches are shown for:
hex (#rgb, #rrggbb, #rrggbbaa), rgb()/rgba(), hsl()/hsla(), and CSS named colors.
pylsp does not define a pylsp_workspace_symbols hookspec, so this plugin uses a two-pronged approach:
- Capability injection (preferred) โ at import time, monkey-patches
PythonLSPServer.capabilities()to insertworkspaceSymbolProvider: trueandinlayHintProviderdirectly into the proper LSP capabilities dict. This makes the plugin work out-of-the-box with clients that require proper capabilities, such as Neovim and eglot. - Experimental fallback โ if the injection fails (e.g. pylsp changes its internal API), capabilities are announced via
pylsp_experimental_capabilitiesinstead. Clients that honour the experimental channel (CudaText, VSCode with pylsp, etc.) will still work. pylsp_dispatchersโ registers a custom JSON-RPC handler forworkspace/symbolthat calls Jedi'sproject.complete_search()and filters results client-side by case-insensitive substring match.
Results are limited to files inside the known workspace folders. All open workspace roots are
read from the live server at query time via server.workspaces, so folders added after startup
(workspace/didChangeWorkspaceFolders) are included correctly. Each root is searched with
sys_path=[root], restricting Jedi's indexing to that folder only โ avoiding the full Python
environment (stdlib + site-packages). This yields an ~80x speedup on complete_search compared
to the default Jedi project. A _is_relative_to() guard (Python 3.8-compatible replacement for
Path.is_relative_to, which requires 3.9+) provides a second layer of filtering.
Note:
workspace/symbolreturns module-level definitions (functions, classes, modules). Local variables inside functions are not indexed โ this is standard LSP behaviour, consistent with pyright and other Python language servers.
The plugin handles the textDocument/inlayHint request using a hybrid approach:
- Regex scan โ fast pass over the source to locate
def, assignment,raise, and call patterns. _literal_typefast-path โ resolves common literals ("str",42,True,[...], etc.) without calling Jedi.- Jedi inference โ for non-literal expressions,
script.infer()andscript.get_signatures()are used to resolve types. - Signature fallback โ for
self.attr = paramassignments, the enclosingdefsignature is inspected for type annotations or default values.
Handled via the native pylsp_code_lens hookspec. Uses a two-pass approach:
- AST pass โ single
ast.walkover the file to collect all definitions and build intra-file maps of subclass relationships (class_subclasses) and method overrides (method_overrides). Also pre-computes inheritance usage positions (intra-file) to correctly separate reference counts from implementation counts. - Jedi reference pass โ one
script.get_references()call per definition to count non-definition references. Cross-file inheritance positions are excluded from the reference count using_find_cross_file_subclasses(only whencross_file_implementations=True). Results are cached by(uri, hash(source))so repeated requests on an unchanged file skip all work.
When cross_file_implementations=True, _find_cross_file_subclasses uses
script.get_references() with the workspace project to find subclasses in other files,
verifying each ref against the AST of the referenced file. A per-request _cf_subclass_cache
ensures the I/O is shared between the references filter and the implementations count.
Handled via textDocument/semanticTokens/full, full/delta, and range dispatchers.
Uses a two-phase O(n) approach โ no per-token goto calls:
- AST pass โ single
ast.parse()walk to build lookup tables for token types and modifiers that Jedi alone cannot determine:enumMember,typeParameter,decorator,@classmethod/@staticmethodโstatic,ClassVar/Finalโreadonly,deprecated(via decorator or docstring),async, and augmented assignments โmodification. - Jedi pass โ
jedi.Script.get_names(all_scopes=True)provides the base token stream. A two-sub-pass strategy resolves statement references via lookup dicts built in phase 1, avoiding any per-token inference calls.
Beyond the two main passes, a token injection stage handles symbols that neither Jedi nor
the AST walk emit directly: @ decorator markers, names inside forward-reference annotation
strings, type parameter attributes (e.g. ParamSpec.args), and regexp patterns in
re.compile() calls. Each injected token receives the correct type and modifiers using the
same lookup tables built in the AST pass. Delta computation uses SequenceMatcher to
produce minimal SemanticTokensEdit[] arrays.
Handled via callHierarchy/incomingCalls and callHierarchy/outgoingCalls dispatchers. Uses Jedi's script.goto() and script.get_references() to resolve callers and callees, building LSP-compliant CallHierarchyItem structures with correct range information. Functions decorated with @overload are handled correctly โ Jedi returns all overload stubs as references to the real implementation (shared qualified name); incoming calls filters them out via ref.is_definition(), which is True for all definition sites and False for actual call sites.
Handled via typeHierarchy/supertypes and typeHierarchy/subtypes dispatchers. Uses Jedi's script.goto() and static class inheritance analysis to build the type tree.
Three-pass collection over the source:
- URL pass โ regex scan for
http://andhttps://URLs in comments, docstrings, and string literals. - Import pass โ AST parse to find all
importandfrom ... importstatements; resolves each module name to a.pyfile by querying the system Python'ssys.prefixvia a single cached subprocess call. - Path literal pass โ detects relative path strings,
open()calls andPath()calls whose argument resolves to an existing file inside the workspace root.
Modules without a .py source (C extensions, frozen modules, .pyc-only embedded builds) are silently skipped.
Regex-based scan over the source for color literals: hex (#rgb, #rrggbb, #rrggbbaa), rgb()/rgba(), hsl()/hsla(), and the full set of CSS named colors. Each match is returned as an LSP ColorInformation with normalised [0.0, 1.0] RGBA components.
pip install -e ".[dev]"
pytestIssues and pull requests are welcome! Please open an issue before submitting a large change.
- python-lsp-server
- Jedi
- LSP workspace/symbol specification
- LSP inlay hints specification
- LSP code lens specification
- LSP semantic tokens specification
- LSP call hierarchy specification
- LSP type hierarchy specification
- LSP document links specification
- LSP document colors specification
Bruno Eduardo โ github.com/Hanatarou
MIT