Language Server Protocol implementation for xonsh
Project description
xonsh-lsp
A Language Server Protocol (LSP) implementation for xonsh, the Python-powered shell.
Features
- Syntax Highlighting (via tree-sitter-xonsh integration)
- And optional symantic tokens (contributed by @nahoj, #6)
- Code Completion
- Environment variables (
$VAR,${expr}) - Subprocess commands from PATH
- Shell builtins (cd, echo, jobs, etc.)
- Xonsh builtins and aliases (source, xontrib, aliases, etc.)
- Python completions (via Jedi or external backend)
- Path completions with directory traversal
- Glob pattern completions (
`...`) - At-object completions (
@.env,@.imp) - Path literal completions (
p"...",pf"...")
- Environment variables (
- Diagnostics
- Syntax errors (via tree-sitter)
- Undefined environment variables (with quick fix)
- Unknown commands (hint severity)
- Empty subprocess warnings
- Python errors (via Jedi or external backend)
- Hover Information
- Environment variable values
- Xonsh operator documentation (
$(),!(),$[],![],@(),@$()) - Xonsh builtin documentation
- Command paths and
--helppreview - Python symbol information with signatures
- Path literal documentation
- Go to Definition
- Python definitions (via Jedi or external backend)
- Environment variable assignments
- Alias definitions
- Function definitions
- Find References
- Python references (via Jedi or external backend)
- Environment variable references
- Symbol references
- Signature Help
- Python function signatures (via Jedi or external backend)
- Parameter tracking
- Document Symbols
- Functions, classes, variables, modules
- Code Actions
- Quick fix for undefined environment variables
- Inlay Hints
- Type annotations after xonsh expressions (
$(),!(),p"…",$VAR, …) - Env-var values after
$VAR(opt-in viainlayHints.envVarValues) - Python inlay hints forwarded from the backend (Pyright/ty)
- Type annotations after xonsh expressions (
- Typed
__xonsh_env__stub for the Python backend- Generated TypedDict from xonsh's env-var registry —
$XONSH_DEBUGtypes asint,$AUTO_CDasbool, etc. (Pyright/ty backends only) - User-defined env vars (
$X = …) added per-document asAny
- Generated TypedDict from xonsh's env-var registry —
- Multi-Backend Support
- Built-in Jedi backend (default, no external dependencies)
- LSP proxy to Pyright, basedpyright, pylsp, ty, or any LSP server
- Transparent settings passthrough (editor → child backend)
- Fallback
backendSettingsviainitializationOptions
Installation
pip install xonsh-lsp
# or just run
uvx xonsh-lsp
To install with the built-in Jedi backend (recommended for standalone use):
pip install "xonsh-lsp[jedi]"
Jedi is optional — if you configure an external backend (Pyright, ty, etc.), you don't need Jedi installed.
Usage
Command Line
# Start with stdio (default, for editor integration)
xonsh-lsp
# Start with TCP
xonsh-lsp --tcp --host 127.0.0.1 --port 2087
# Debug mode
xonsh-lsp --log-level DEBUG
# Use Pyright as the Python backend
xonsh-lsp --python-backend pyright
# Use ty as the Python backend
xonsh-lsp --python-backend ty
# Use a custom LSP server command
xonsh-lsp --python-backend lsp-proxy --backend-command my-lsp-server --stdio
# Enable LSP semantic-tokens syntax highlighting (off by default)
xonsh-lsp --semantic-tokens
Neovim Integration
xonsh-lsp is not yet in the canonical nvim-lspconfig registry. For now, configure it manually using Neovim's native LSP API (0.11+) or the nvim-lspconfig custom-server pattern.
1. Register the xonsh filetype
Place this somewhere early in your config (e.g. an autocmds.lua file):
vim.filetype.add({
extension = { xsh = 'xonsh', xonshrc = 'xonsh' },
filename = { ['.xonshrc'] = 'xonsh', ['xonshrc'] = 'xonsh' },
})
2. Configure the language server
Neovim 0.11+ (native vim.lsp)
vim.lsp.config('xonsh_lsp', {
cmd = { 'xonsh-lsp' }, -- or { 'uvx', '-n', 'xonsh-lsp' }
filetypes = { 'xonsh' },
root_markers = { '.xonshrc', 'xonshrc', '.git' },
})
vim.lsp.enable('xonsh_lsp')
nvim-lspconfig (manual server registration)
local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')
if not configs.xonsh_lsp then
configs.xonsh_lsp = {
default_config = {
cmd = { 'xonsh-lsp' },
filetypes = { 'xonsh' },
root_dir = function(fname)
return lspconfig.util.find_git_ancestor(fname) or vim.fn.getcwd()
end,
settings = {},
},
}
end
lspconfig.xonsh_lsp.setup({})
3. Choose a Python backend
xonsh-lsp delegates Python analysis to an external backend. Set it via init_options:
ty (recommended — extremely fast, written in Rust)
vim.lsp.config('xonsh_lsp', {
cmd = { 'uvx', '-n', 'xonsh-lsp' },
filetypes = { 'xonsh' },
root_markers = { '.xonshrc', 'xonshrc', '.git' },
init_options = {
pythonBackend = 'ty',
-- Optional: pin the ty binary (e.g. from Mason)
-- pythonBackendCommand = { '/path/to/ty', 'server' },
},
settings = {
environment = {
python = vim.fn.exepath('python3'),
},
},
})
vim.lsp.enable('xonsh_lsp')
Pyright
vim.lsp.config('xonsh_lsp', {
cmd = { 'uvx', '-n', 'xonsh-lsp' },
filetypes = { 'xonsh' },
root_markers = { '.xonshrc', 'xonshrc', '.git' },
init_options = {
pythonBackend = 'pyright',
},
settings = {
python = {
pythonPath = vim.fn.exepath('python3'),
analysis = {
autoSearchPaths = true,
useLibraryCodeForTypes = true,
},
},
},
})
vim.lsp.enable('xonsh_lsp')
Your Python backend settings (e.g. settings.python.*, settings.environment.*) are
forwarded transparently to the child LSP — xonsh-lsp does not detect or override paths itself.
4. (Optional) Tree-sitter highlighting
For syntax highlighting, install the tree-sitter-xonsh parser:
-- Register the parser so :TSInstall xonsh works
vim.api.nvim_create_autocmd('User', {
pattern = 'TSUpdate',
callback = function()
require('nvim-treesitter.parsers').xonsh = {
install_info = {
url = 'https://github.com/FoamScience/tree-sitter-xonsh',
queries = 'queries/',
},
}
end,
})
-- Start treesitter highlighting for xonsh buffers
vim.api.nvim_create_autocmd('FileType', {
pattern = 'xonsh',
callback = function(args)
if not require('nvim-treesitter.parsers').xonsh then
vim.treesitter.start(args.buf, 'xonsh')
end
end,
})
5. (Optional) UV project Python detection
If you use uv for project management, you can
auto-detect the correct Python interpreter and notify the backend via
workspace/didChangeConfiguration in your on_attach:
vim.lsp.config('xonsh_lsp', {
cmd = { 'uvx', '-n', 'xonsh-lsp' },
filetypes = { 'xonsh' },
root_markers = { '.xonshrc', 'xonshrc', '.git' },
init_options = { pythonBackend = 'ty' },
settings = {
environment = { python = vim.fn.exepath('python3') },
},
on_attach = function(client, bufnr)
local filepath = vim.api.nvim_buf_get_name(bufnr)
local dir = vim.fn.fnamemodify(filepath, ':h')
vim.system(
{ 'uv', 'python', 'find' },
{ text = true, timeout = 1000, cwd = dir },
vim.schedule_wrap(function(result)
if not result or not result.stdout then return end
local py = result.stdout:match('^%s*(.-)%s*$')
if py == '' or vim.fn.executable(py) ~= 1 then return end
client.settings = vim.tbl_deep_extend('force', client.settings or {}, {
environment = { python = py },
})
client:notify('workspace/didChangeConfiguration', {
settings = client.settings,
})
end)
)
end,
})
vim.lsp.enable('xonsh_lsp')
Configuration
Python Backend
xonsh-lsp supports multiple Python analysis backends. The backend handles completions, hover, go-to-definition, references, signature help, and diagnostics for Python code within xonsh files.
| Backend | Command | Description |
|---|---|---|
jedi (default) |
built-in | Uses Jedi for static analysis. Works out of the box, no external process. |
pyright |
pyright-langserver --stdio |
Microsoft's Pyright type checker. Respects pyrightconfig.json. |
basedpyright |
basedpyright-langserver --stdio |
Community fork basedpyright with additional features. |
pylsp |
pylsp |
The Python LSP Server with plugin ecosystem. |
ty |
ty server |
Astral's ty type checker — extremely fast, written in Rust. |
lsp-proxy |
custom | Any LSP server via --backend-command. |
The backend can be set via CLI args or editor initializationOptions:
CLI:
xonsh-lsp --python-backend pyright
Editor initializationOptions:
{
"pythonBackend": "pyright"
}
Initialization Options
The full set of initializationOptions accepted by xonsh-lsp:
{
// Python analysis backend: "jedi", "pyright", "basedpyright", "pylsp", "ty", or "lsp-proxy"
"pythonBackend": "jedi",
// Custom command for the "lsp-proxy" backend (not needed for named backends)
"pythonBackendCommand": ["my-lsp-server", "--stdio"],
// Fallback settings sent to the backend during initialization.
// Normally you should configure your backend via the editor's settings
// (e.g. Neovim's `settings = { ... }`), which are forwarded transparently.
// Use this only if your editor doesn't support workspace/configuration.
"backendSettings": { },
// Enable LSP semantic tokens for syntax highlighting (default: false).
// Off by default because most editors already highlight xonsh via
// tree-sitter or a TextMate grammar, and overlapping semantic tokens can
// fight with those. Turn on if your editor relies on LSP for highlighting.
"semanticTokens": false,
// Inlay-hint preferences. The xonsh server emits native hints for
// xonsh-specific syntax in addition to whatever the Python backend
// returns for Python regions.
"inlayHints": {
// ": str" / ": CommandPipeline" / ": Path" / ": bool" (from the
// xonsh env var registry) after $VAR, $(), !(), p"…", etc.
"xonshTypes": true,
// " = /home/user" after $HOME (resolved from the server's process env).
"envVarValues": false,
// Alias kind after unknown commands (reserved for v2).
"aliasResolution": false
}
}
Settings Passthrough
When the child backend requests its configuration (via workspace/configuration),
xonsh-lsp forwards the request to your editor and returns the editor's response.
This means your existing Python backend settings — pythonPath, python.analysis.*,
etc. — work exactly as if the backend were running standalone.
If the editor doesn't respond (or workspace/configuration is not supported),
xonsh-lsp falls back to backendSettings from initializationOptions.
How the LSP Proxy Works
When configured with an external backend, xonsh-lsp acts as LSP middleware:
- Receives requests from the editor (completions, hover, etc.)
- Preprocesses xonsh syntax to valid Python (e.g.,
$HOME→__xonsh_env__["HOME"]) - Forwards the preprocessed Python to the child LSP server
- Maps positions in the response back to the original xonsh source
- Merges results with xonsh-specific features (env vars, operators, commands)
Xonsh-specific features (environment variable completions, operator hover, subprocess diagnostics, etc.) are always handled natively — only Python analysis is delegated to the backend.
Diagnostics use a two-phase merge:
- Xonsh diagnostics (syntax errors, undefined env vars, unknown commands) are published immediately
- Python diagnostics from the backend arrive asynchronously and are merged with the cached xonsh diagnostics
Architecture
flowchart RL
subgraph xonsh-lsp
parser[tree-sitter-xonsh parser]
parser --> python[Python Regions]
parser --> xonsh[Xonsh Builtins]
parser --> subprocess[Subprocess Commands]
python --> backend{PythonBackend}
backend -->|default| jedi[JediBackend]
backend -->|proxy| proxy[LspProxyBackend]
proxy --> child[Child LSP: Pyright / ty / basedpyright / pylsp]
xonsh --> native1[Native Handler]
subprocess --> native2[Native Handler]
end
Contributing
Contributions are welcome! Please feel free to submit issues and pull requests; especially flagging unsupported xonsh syntax since this LSP is young.
License
MIT
Related Projects
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file xonsh_lsp-0.2.1.tar.gz.
File metadata
- Download URL: xonsh_lsp-0.2.1.tar.gz
- Upload date:
- Size: 87.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4cbf0d7c4ffc2be2850bb8ee9d63f7fbd1d6c0d3e2b5052b97e870bd1ad360ed
|
|
| MD5 |
df32083137ce585b8c694a63bf6253d8
|
|
| BLAKE2b-256 |
e712f6cb642e039720dcd11a3b5ef8bccdea4b07a94d80677977758b9e307f02
|
Provenance
The following attestation bundles were made for xonsh_lsp-0.2.1.tar.gz:
Publisher:
release.yml on FoamScience/xonsh-language-server
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xonsh_lsp-0.2.1.tar.gz -
Subject digest:
4cbf0d7c4ffc2be2850bb8ee9d63f7fbd1d6c0d3e2b5052b97e870bd1ad360ed - Sigstore transparency entry: 1563894006
- Sigstore integration time:
-
Permalink:
FoamScience/xonsh-language-server@6ee361b68342cefff3a16ce2fcca41ff0bf20c0e -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/FoamScience
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6ee361b68342cefff3a16ce2fcca41ff0bf20c0e -
Trigger Event:
push
-
Statement type:
File details
Details for the file xonsh_lsp-0.2.1-py3-none-any.whl.
File metadata
- Download URL: xonsh_lsp-0.2.1-py3-none-any.whl
- Upload date:
- Size: 68.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d60089afb49dea508c1952a37d9a581027a9527dd15f7e5d6ff5aa216ff621fe
|
|
| MD5 |
167997a509db7ee17bd4988e58980c09
|
|
| BLAKE2b-256 |
7ba2e06c450438f3708dc407c13c59550e791af73656035a4875e7554d3a1359
|
Provenance
The following attestation bundles were made for xonsh_lsp-0.2.1-py3-none-any.whl:
Publisher:
release.yml on FoamScience/xonsh-language-server
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xonsh_lsp-0.2.1-py3-none-any.whl -
Subject digest:
d60089afb49dea508c1952a37d9a581027a9527dd15f7e5d6ff5aa216ff621fe - Sigstore transparency entry: 1563894022
- Sigstore integration time:
-
Permalink:
FoamScience/xonsh-language-server@6ee361b68342cefff3a16ce2fcca41ff0bf20c0e -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/FoamScience
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6ee361b68342cefff3a16ce2fcca41ff0bf20c0e -
Trigger Event:
push
-
Statement type: