|
| 1 | +# Minimal LSP Server for param |
| 2 | + |
| 3 | +A minimal Language Server Protocol (LSP) implementation that provides IDE features for `param.Parameterized` classes. |
| 4 | + |
| 5 | +## Current Status |
| 6 | + |
| 7 | +This is a **proof-of-concept** LSP server demonstrating what's possible for param tooling. It uses simple regex-based parsing (not a full Python AST parser) and only analyzes the current file. |
| 8 | + |
| 9 | +### Features |
| 10 | + |
| 11 | +| Feature | Status | Description | |
| 12 | +|---------|--------|-------------| |
| 13 | +| **Hover (declarations)** | ✅ | Hover over `param.Integer(...)` to see type info | |
| 14 | +| **Hover (attribute access)** | ✅ | Hover over `person.name` to see param info (same file only) | |
| 15 | +| **Completion (param types)** | ✅ | Type `param.` to see all param types | |
| 16 | +| **Completion (kwargs)** | ✅ | Inside `param.Number(...)`, get suggestions for `bounds`, `doc`, etc. | |
| 17 | +| **Completion (instantiation)** | ✅ | Type `Person(` to see parameter names | |
| 18 | +| **Diagnostics** | ✅ | Warnings for unknown types, missing `objects` in Selectors | |
| 19 | +| **Cross-file resolution** | ❌ | Only works within the current file | |
| 20 | +| **Type checking** | ❌ | Does not replace Pylance/Pyright | |
| 21 | + |
| 22 | +### Limitations |
| 23 | + |
| 24 | +- **Regex-based parsing**: May fail on complex multi-line declarations |
| 25 | +- **Same-file only**: Cannot resolve classes or variables from imports |
| 26 | +- **No inheritance tracking**: Doesn't resolve inherited parameters from parent classes in other files |
| 27 | +- **Runs alongside Pylance**: Pylance may still show type errors (see [Pylance Coexistence](#pylance-coexistence)) |
| 28 | + |
| 29 | +## Installation |
| 30 | + |
| 31 | +### Prerequisites |
| 32 | + |
| 33 | +```bash |
| 34 | +pip install pygls lsprotocol param |
| 35 | +``` |
| 36 | + |
| 37 | +### Quick Test |
| 38 | + |
| 39 | +Run the tests to verify the server works: |
| 40 | + |
| 41 | +```bash |
| 42 | +cd examples/param-lsp |
| 43 | +python test_param_lsp.py |
| 44 | +``` |
| 45 | + |
| 46 | +Expected output: |
| 47 | +``` |
| 48 | +✓ test_parse_param_args_with_kwargs |
| 49 | +✓ test_parse_param_args_with_positional |
| 50 | +... |
| 51 | +PASSED: All 11 tests passed! |
| 52 | +``` |
| 53 | + |
| 54 | +## Building the VS Code Extension |
| 55 | + |
| 56 | +The `vscode-client/` folder contains a minimal VS Code extension that launches the LSP server. |
| 57 | + |
| 58 | +### Build Steps |
| 59 | + |
| 60 | +```bash |
| 61 | +cd examples/param-lsp/vscode-client |
| 62 | + |
| 63 | +# Install dependencies |
| 64 | +npm install |
| 65 | + |
| 66 | +# Package as VSIX |
| 67 | +npx vsce package --allow-missing-repository -o param-lsp.vsix |
| 68 | +``` |
| 69 | + |
| 70 | +This creates `param-lsp.vsix` (~32 MB). |
| 71 | + |
| 72 | +> **Note**: The extension uses an absolute path to `param_lsp.py`. If you move the files, update the path in `extension.js`. |
| 73 | +
|
| 74 | +## Installing the Extension |
| 75 | + |
| 76 | +### VS Code (Desktop) |
| 77 | + |
| 78 | +```bash |
| 79 | +# Install |
| 80 | +code --install-extension examples/param-lsp/vscode-client/param-lsp.vsix |
| 81 | + |
| 82 | +# Or for development (hot reload) |
| 83 | +code --extensionDevelopmentPath=/full/path/to/examples/param-lsp/vscode-client |
| 84 | +``` |
| 85 | + |
| 86 | +### code-server (Web) |
| 87 | + |
| 88 | +```bash |
| 89 | +# Install |
| 90 | +code-server --install-extension examples/param-lsp/vscode-client/param-lsp.vsix |
| 91 | + |
| 92 | +# Force reinstall (after updates) |
| 93 | +code-server --install-extension examples/param-lsp/vscode-client/param-lsp.vsix --force |
| 94 | +``` |
| 95 | + |
| 96 | +After installation, **reload the window**: |
| 97 | +- Press `Ctrl+Shift+P` |
| 98 | +- Type "Reload Window" |
| 99 | +- Press Enter |
| 100 | + |
| 101 | +### Verify Installation |
| 102 | + |
| 103 | +1. Open a Python file with `param.Parameterized` classes |
| 104 | +2. Open the Output panel: `Ctrl+Shift+U` (or View → Output) |
| 105 | +3. Select "Param LSP" from the dropdown |
| 106 | +4. You should see: `Starting Param LSP server...` |
| 107 | + |
| 108 | +## Uninstalling the Extension |
| 109 | + |
| 110 | +### VS Code (Desktop) |
| 111 | + |
| 112 | +```bash |
| 113 | +code --uninstall-extension undefined_publisher.param-lsp-client |
| 114 | +``` |
| 115 | + |
| 116 | +### code-server (Web) |
| 117 | + |
| 118 | +```bash |
| 119 | +code-server --uninstall-extension undefined_publisher.param-lsp-client |
| 120 | +``` |
| 121 | + |
| 122 | +### Via UI |
| 123 | + |
| 124 | +1. Open Extensions panel: `Ctrl+Shift+X` |
| 125 | +2. Search for "Param LSP" |
| 126 | +3. Click the gear icon → Uninstall |
| 127 | + |
| 128 | +## Usage |
| 129 | + |
| 130 | +Open `example_parameterized.py` and try: |
| 131 | + |
| 132 | +1. **Hover** over `param.Integer` → see type info and kwargs |
| 133 | +2. **Hover** over `person.name` → see the parameter's type and metadata |
| 134 | +3. **Type** `param.` → see completion for all param types |
| 135 | +4. **Inside** `param.Number(`, press `Ctrl+Space` → see kwargs like `bounds`, `doc` |
| 136 | +5. **Type** `Person(` → see completion for `name`, `age`, `height`, etc. |
| 137 | + |
| 138 | +## Pylance Coexistence |
| 139 | + |
| 140 | +This LSP runs **alongside** Pylance (VS Code's Python language server). They don't share information: |
| 141 | + |
| 142 | +``` |
| 143 | +┌─────────────────┐ ┌─────────────────┐ |
| 144 | +│ Pylance │ │ param-lsp │ |
| 145 | +│ "name unknown" │ │ "name: str" │ |
| 146 | +└────────┬────────┘ └────────┬────────┘ |
| 147 | + │ │ |
| 148 | + └───────────┬───────────┘ |
| 149 | + ▼ |
| 150 | + Both show in VS Code |
| 151 | +``` |
| 152 | + |
| 153 | +**Pylance may still show errors** like "Property 'name' not defined" because it doesn't understand param descriptors. |
| 154 | + |
| 155 | +### Workarounds |
| 156 | + |
| 157 | +1. **Add type annotations** (recommended): |
| 158 | + ```python |
| 159 | + name: str = param.String(default="Alice") |
| 160 | + ``` |
| 161 | + |
| 162 | +2. **Disable Pylance for specific files**: |
| 163 | + ```json |
| 164 | + // .vscode/settings.json |
| 165 | + { |
| 166 | + "python.analysis.ignore": ["**/my_parameterized_file.py"] |
| 167 | + } |
| 168 | + ``` |
| 169 | + |
| 170 | +3. **Create type stubs** (`param.pyi`) - more complex but fully fixes the issue |
| 171 | + |
| 172 | +## Architecture |
| 173 | + |
| 174 | +``` |
| 175 | +examples/param-lsp/ |
| 176 | +├── param_lsp.py # LSP server (Python) |
| 177 | +│ ├── PARAM_TYPE_INFO # Map: param.Type → Python type |
| 178 | +│ ├── hover() # textDocument/hover handler |
| 179 | +│ ├── completion() # textDocument/completion handler |
| 180 | +│ └── analyze_document() # Diagnostics |
| 181 | +├── test_param_lsp.py # Unit tests |
| 182 | +├── example_parameterized.py # Example file to test with |
| 183 | +├── README.md # This file |
| 184 | +└── vscode-client/ # VS Code extension |
| 185 | + ├── package.json # Extension manifest |
| 186 | + ├── extension.js # Extension entry point |
| 187 | + └── node_modules/ # Dependencies |
| 188 | +``` |
| 189 | + |
| 190 | +## Development |
| 191 | + |
| 192 | +### Running the Server Standalone |
| 193 | + |
| 194 | +For debugging, run the server directly: |
| 195 | + |
| 196 | +```bash |
| 197 | +python param_lsp.py |
| 198 | +``` |
| 199 | + |
| 200 | +The server communicates via stdio using JSON-RPC. You can test with: |
| 201 | + |
| 202 | +```json |
| 203 | +{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}} |
| 204 | +``` |
| 205 | + |
| 206 | +### Modifying the Server |
| 207 | + |
| 208 | +1. Edit `param_lsp.py` |
| 209 | +2. No need to rebuild the extension - it reads the Python file directly |
| 210 | +3. Reload VS Code window to pick up changes |
| 211 | + |
| 212 | +### Adding New Param Types |
| 213 | + |
| 214 | +Edit `PARAM_TYPE_INFO` in `param_lsp.py`: |
| 215 | + |
| 216 | +```python |
| 217 | +PARAM_TYPE_INFO = { |
| 218 | + "param.MyNewType": ("my_python_type", "Description of the type"), |
| 219 | + # ... |
| 220 | +} |
| 221 | +``` |
| 222 | + |
| 223 | +## Future Improvements |
| 224 | + |
| 225 | +To make this production-ready: |
| 226 | + |
| 227 | +1. **Use AST parsing** instead of regex for robust detection |
| 228 | +2. **Cross-file resolution** via workspace indexing |
| 229 | +3. **Runtime introspection** to get actual Parameter metadata |
| 230 | +4. **Mypy/Pyright plugin** to provide types to static checkers |
| 231 | +5. **Type stubs generation** for param library |
| 232 | + |
| 233 | +## Related Resources |
| 234 | + |
| 235 | +- [pygls](https://pygls.readthedocs.io/) - Python Language Server library |
| 236 | +- [param](https://param.holoviz.org/) - Parameter library for Python |
| 237 | +- [LSP Specification](https://microsoft.github.io/language-server-protocol/) - Protocol documentation |
| 238 | +- [vscode-languageclient](https://github.com/microsoft/vscode-languageserver-node) - VS Code LSP client |
0 commit comments