> ## 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

> Target-aware Language Server Protocol integration — accurate autocomplete, go-to-definition, and diagnostics based on your build graph

# 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

1. **You open a file** — e.g., `src/main.py` in your editor.
2. **RBS resolves the target** — determines that `src/main.py` belongs to `//src:app`.
3. **Dependencies are analyzed** — `//src:app` declares `deps = ["@pip//requests", "@pip//flask", ":utils"]`.
4. **Paths are resolved** — dependencies map to filesystem paths in `.rbs/external-deps/`.
5. **LSP is configured** — the language server receives only the relevant paths.
6. **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 theme={null}
# 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

```bash theme={null}
# 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

### Go

* **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:

```python theme={null}
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:

```python theme={null}
# 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:

```python theme={null}
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:

```python theme={null}
initialization_options = {
    "python.analysis.extraPaths": "${RESOLVED_PATHS}",
}
```

***

## REST API

### List providers

```bash theme={null}
GET /api/lsp/providers
```

```json theme={null}
{
  "providers": [
    {
      "name": "python",
      "language": "python",
      "file_extensions": [".py", ".pyi"],
      "server": "@pip//python-lsp-server",
      "root_markers": ["pyproject.toml", "BUILD.rbs"]
    }
  ]
}
```

### Get a provider

```bash theme={null}
GET /api/lsp/providers/{language}
```

### List running servers

```bash theme={null}
GET /api/lsp/servers
```

```json theme={null}
{
  "servers": [
    {
      "language": "python",
      "provider": "python",
      "server": "@pip//python-lsp-server",
      "pid": 12345,
      "configured": 3
    }
  ]
}
```

### Start / stop a server

```bash theme={null}
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:

```bash theme={null}
POST /api/lsp/resolve
Content-Type: application/json

{
  "path": "src/main.py"
}
```

```json theme={null}
{
  "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:

```json theme={null}
{
  "type": "connected",
  "language": "python",
  "server": "python"
}
```

### Open a file (triggers path resolution)

```json theme={null}
{
  "type": "file_opened",
  "path": "src/main.py"
}
```

Response:

```json theme={null}
{
  "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:

```json theme={null}
// 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

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    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
    );
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    # 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"
    ```
  </Tab>
</Tabs>

***

## Troubleshooting

### No completions

1. Run `rbs sync //your:target` to ensure dependencies are downloaded.
2. Verify the file belongs to a target with declared `deps`.
3. Check that paths are resolved: `POST /api/lsp/resolve` with your file path.

### LSP server not starting

```bash theme={null}
# 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

```bash theme={null}
# Sync all dependencies
rbs sync //...

# Verify external-deps directory
ls -la .rbs/external-deps/
```
