Documentation Index
Fetch the complete documentation index at: https://docs.reasonos.com/llms.txt
Use this file to discover all available pages before exploring further.
LSP SDK
The LSP SDK provides target-aware Language Server Protocol integration. Unlike traditional LSP setups that configure paths globally, RBS resolves dependencies per-target — your editor’s autocomplete, go-to-definition, and diagnostics reflect only the dependencies your specific target can actually import.
Why target-aware LSP?
Traditional LSP setups give you completions for every package installed on your system. RBS does it differently:
| Traditional LSP | RBS LSP |
|---|
| Scope | All installed packages | Only your target’s declared deps |
| Accuracy | May suggest unrelated packages | Only suggests what you can actually import |
| Consistency | Varies by developer machine | Same results for everyone (hermetic) |
| Configuration | Manual path setup | Automatic from build graph |
How it works
- You open a file — e.g.,
src/main.py in your editor.
- RBS resolves the target — determines that
src/main.py belongs to //src:app.
- Dependencies are analyzed —
//src:app declares deps = ["@pip//requests", "@pip//flask", ":utils"].
- Paths are resolved — dependencies map to filesystem paths in
.rbs/external-deps/.
- LSP is configured — the language server receives only the relevant paths.
- You get accurate completions — autocomplete suggests
requests and flask, but not unrelated packages.
Quick start
1. Load a built-in provider
In your WORKSPACE.rbs:
# Python LSP support
load("@rbs//rules/python:lsp.rbs", "python_lsp_provider")
# TypeScript LSP support
load("@rbs//rules/typescript:lsp.rbs", "typescript_lsp_provider")
# Go LSP support
load("@rbs//rules/go:lsp.rbs", "go_lsp_provider")
2. Sync dependencies
# Sync all dependencies for LSP resolution
rbs sync //...
# Sync specific targets
rbs sync //myapp:server //lib:utils
3. Connect your editor
Your editor connects to the LSP through the RBS server automatically when you open files. If you’re building a custom integration, connect via:
- WebSocket:
ws://your-server/ws/lsp/{language}
- REST API:
http://your-server/api/lsp/*
Built-in providers
Python
- Server: python-lsp-server (pylsp)
- Extensions:
.py, .pyi
- Features: Jedi-based completion, rope refactoring, pyflakes linting
TypeScript / JavaScript
- Server: typescript-language-server
- Extensions:
.ts, .tsx, .js, .jsx, .mjs, .cjs
- Features: Full TypeScript/JavaScript support with type checking
- Server: gopls
- Extensions:
.go
- Features: Official Go language server with full LSP support
Creating custom LSP providers
The SDK-first design means you can define LSP support for any language through configuration:
native.lsp_provider(
name = "my_language",
language = "my_language",
file_extensions = [".ml", ".mli"],
server = "@custom//my-lsp-server",
server_args = ["--stdio"],
resolve_paths = """
def resolve(ctx):
paths = []
for dep in ctx.target.deps:
paths.append(ctx.get_dep_path(dep))
return paths
""",
initialization_options = {
"my.option": True,
},
root_markers = ["my_project.json", "BUILD.rbs"],
)
Parameters
| Parameter | Type | Required | Description |
|---|
name | string | Yes | Provider name. |
language | string | Yes | Language identifier. |
file_extensions | list | Yes | File extensions to handle. |
server | string | Yes | Server binary reference. |
server_args | list | No | Arguments passed to the server. |
resolve_paths | string | No | RBS DSL function to resolve dependency paths. |
initialization_options | dict | No | LSP initialization options sent to the server. |
root_markers | list | No | Files that indicate the project root. |
Server references
The server parameter supports multiple formats:
# From an external dependency
server = "@pip//python-lsp-server"
server = "@npm//typescript-language-server"
# From a toolchain binary
server = ":gopls"
# From system PATH (for development/testing)
server = "gopls"
Path resolution function
The resolve_paths function receives a context object with access to the target’s build graph:
resolve_paths = """
def resolve(ctx):
# ctx.target — the target owning the file
# ctx.target.deps — list of declared dependencies
# ctx.target.srcs — list of source files
# ctx.workspace — workspace root path
# ctx.platform — current platform (e.g., "darwin-arm64")
# ctx.get_dep_path(dep) — filesystem path for a dependency
paths = []
for dep in ctx.target.deps:
paths.append(ctx.get_dep_path(dep))
return paths
"""
Initialization options
Use ${RESOLVED_PATHS} as a placeholder that will be replaced with the resolved dependency paths:
initialization_options = {
"python.analysis.extraPaths": "${RESOLVED_PATHS}",
}
REST API
List providers
{
"providers": [
{
"name": "python",
"language": "python",
"file_extensions": [".py", ".pyi"],
"server": "@pip//python-lsp-server",
"root_markers": ["pyproject.toml", "BUILD.rbs"]
}
]
}
Get a provider
GET /api/lsp/providers/{language}
List running servers
{
"servers": [
{
"language": "python",
"provider": "python",
"server": "@pip//python-lsp-server",
"pid": 12345,
"configured": 3
}
]
}
Start / stop a server
POST /api/lsp/{language}/start
POST /api/lsp/{language}/stop
Resolve a file to its target
Find which target a file belongs to and what dependency paths are resolved:
POST /api/lsp/resolve
Content-Type: application/json
{
"path": "src/main.py"
}
{
"file": "src/main.py",
"language": "python",
"target": {
"name": "app",
"package": "//src",
"kind": "py_binary"
},
"deps": ["@pip//requests", "@pip//flask", ":utils"],
"paths": [
".rbs/external-deps/darwin-arm64/python/requests",
".rbs/external-deps/darwin-arm64/python/flask"
]
}
WebSocket protocol
Connect
WebSocket: /ws/lsp/{language}
On connection:
{
"type": "connected",
"language": "python",
"server": "python"
}
Open a file (triggers path resolution)
{
"type": "file_opened",
"path": "src/main.py"
}
Response:
{
"type": "file_resolved",
"file": "src/main.py",
"language": "python",
"target": { "name": "app", "package": "//src", "kind": "py_binary" },
"deps": ["@pip//requests", "@pip//flask", ":utils"],
"paths_configured": 3
}
LSP requests/responses
Standard JSON-RPC 2.0 LSP messages are proxied through the WebSocket:
// Request: autocomplete at line 10, character 5
{
"jsonrpc": "2.0",
"id": 1,
"method": "textDocument/completion",
"params": {
"textDocument": { "uri": "file:///src/main.py" },
"position": { "line": 10, "character": 5 }
}
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"items": [
{ "label": "requests", "kind": 9 },
{ "label": "flask", "kind": 9 }
]
}
}
Editor SDK integration
import { RBSLSPClient } from "@rbs/editor-sdk";
const client = new RBSLSPClient("python", {
serverUrl: "https://your-project.rbs.dev",
});
await client.connect();
// Open a file (triggers target-aware path resolution)
client.fileOpened("src/main.py");
// Get completions
const completions = await client.completion(
"file:///src/main.py", 10, 5
);
// Go to definition
const definition = await client.definition(
"file:///src/main.py", 15, 12
);
# List providers
curl https://your-project.rbs.dev/api/lsp/providers \
-H "Authorization: Bearer $RBS_TOKEN"
# Resolve a file to its target
curl -X POST https://your-project.rbs.dev/api/lsp/resolve \
-H "Authorization: Bearer $RBS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"path": "src/main.py"}'
# Check running servers
curl https://your-project.rbs.dev/api/lsp/servers \
-H "Authorization: Bearer $RBS_TOKEN"
Troubleshooting
No completions
- Run
rbs sync //your:target to ensure dependencies are downloaded.
- Verify the file belongs to a target with declared
deps.
- Check that paths are resolved:
POST /api/lsp/resolve with your file path.
LSP server not starting
# Check if the provider is registered
curl http://your-server/api/lsp/providers
# Check server status
curl http://your-server/api/lsp/servers
Missing dependencies
# Sync all dependencies
rbs sync //...
# Verify external-deps directory
ls -la .rbs/external-deps/