Skip to content

Commit b8684a9

Browse files
authored
feat: Add a SQL language server (#225)
1 parent b248326 commit b8684a9

File tree

11 files changed

+26396
-14266
lines changed

11 files changed

+26396
-14266
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,12 @@ jobs:
9797
node-version-file: '.nvmrc'
9898

9999
- name: Install dependencies
100-
run: npm ci --prefer-offline --no-audit
100+
run: |
101+
npm ci --prefer-offline --no-audit
102+
# Verify Tailwind CSS native modules are installed for Linux (npm optional deps bug)
103+
# See: https://github.com/npm/cli/issues/4828
104+
node -e "try { require('lightningcss'); } catch { process.exit(1); }" 2>/dev/null || npm install lightningcss-linux-x64-gnu
105+
node -e "try { require('@tailwindcss/oxide'); } catch { process.exit(1); }" 2>/dev/null || npm install @tailwindcss/oxide-linux-x64-gnu
101106
102107
- name: Compile TypeScript
103108
run: npm run compile

.github/workflows/package.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ jobs:
3030
node-version-file: '.nvmrc'
3131

3232
- name: Install dependencies
33-
run: npm ci --prefer-offline --no-audit
33+
run: |
34+
npm ci --no-audit
35+
# Verify Tailwind CSS native modules are installed for Linux (npm optional deps bug)
36+
# See: https://github.com/npm/cli/issues/4828
37+
node -e "try { require('lightningcss'); } catch { process.exit(1); }" 2>/dev/null || npm install lightningcss-linux-x64-gnu
38+
node -e "try { require('@tailwindcss/oxide'); } catch { process.exit(1); }" 2>/dev/null || npm install @tailwindcss/oxide-linux-x64-gnu
3439
3540
- name: Install vsce
3641
run: npm install -g @vscode/vsce

LSP.md

Lines changed: 133 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,11 @@ Deepnote maintains forks of well-established LSP servers:
182182

183183
#### sql-language-server
184184

185-
- Provides SQL-specific intelligence
186-
- Understands database schemas
187-
- Offers query optimization suggestions
185+
- Provides SQL-specific intelligence for SQL cells in Deepnote notebooks
186+
- Runs locally as a separate process using stdio communication
187+
- Integrates with configured database integrations (PostgreSQL, MySQL, BigQuery)
188+
- Offers autocomplete, error detection, and schema awareness
189+
- Uses the [@deepnote/sql-language-server](https://github.com/deepnote/sql-language-server) package
188190

189191
## Benefits for Users
190192

@@ -334,48 +336,66 @@ When you open a `.deepnote` file in VS Code:
334336
1. **Environment Creation**: The extension creates a dedicated virtual environment for the notebook
335337
2. **Toolkit Installation**: Installs `deepnote-toolkit` and `python-lsp-server[all]` in the venv
336338
3. **Kernel Launch**: Starts the Deepnote kernel using the toolkit
337-
4. **LSP Activation**: Automatically starts the LSP client for code intelligence
339+
4. **LSP Activation**: Automatically starts LSP clients for code intelligence
340+
- **Python**: Uses python-lsp-server for Python code intelligence
341+
- **SQL**: Uses sql-language-server for SQL code intelligence
338342

339343
#### 2. LSP Client Management
340344

341345
The `DeepnoteLspClientManager` (in `src/kernels/deepnote/deepnoteLspClientManager.node.ts`) handles:
342346

343347
#### Client Lifecycle
348+
344349
```typescript
345350
// When kernel starts
346351
await lspClientManager.startLspClients(
347-
serverInfo, // Deepnote server connection info
348-
notebookUri, // Notebook file URI
349-
interpreter // Python environment from venv
352+
serverInfo, // Deepnote server connection info
353+
notebookUri, // Notebook file URI
354+
interpreter // Python environment from venv
350355
);
351356

352357
// When notebook closes
353358
await lspClientManager.stopLspClients(notebookUri);
354359
```
355360

356361
#### Per-Notebook Isolation
362+
357363
- Each notebook gets its own LSP client instance
358364
- Clients are isolated to prevent conflicts
359365
- Automatic cleanup when notebooks close
360366

361367
#### Duplicate Prevention
368+
362369
- Prevents multiple clients for the same notebook
363370
- Reuses existing clients when possible
364371
- Graceful handling of client errors
365372

366373
#### 3. Language Server Process
367374

368-
The extension uses `python-lsp-server` in stdio mode:
375+
The extension runs dedicated language servers for both Python and SQL:
376+
377+
**Python LSP:**
378+
379+
```typescript
380+
const serverOptions: Executable = {
381+
command: pythonPath, // Python from venv
382+
args: ['-m', 'pylsp'], // Start python-lsp-server
383+
options: { env: { ...process.env } }
384+
};
385+
```
386+
387+
**SQL LSP:**
369388

370389
```typescript
371390
const serverOptions: Executable = {
372-
command: pythonPath, // Python from venv
373-
args: ['-m', 'pylsp'], // Start python-lsp-server
374-
options: { env: { ...process.env } }
391+
command: sqlLspPath, // Path to sql-language-server binary
392+
args: ['up', '--method', 'stdio'],
393+
options: { env: { ...process.env } }
375394
};
376395
```
377396

378397
**Why stdio instead of TCP:**
398+
379399
- Simpler process management
380400
- Better isolation
381401
- Standard LSP pattern
@@ -387,38 +407,42 @@ The LSP client is configured to provide intelligence for Python cells in Deepnot
387407

388408
```typescript
389409
documentSelector: [
390-
{
391-
scheme: 'vscode-notebook-cell', // Notebook cells
392-
language: 'python',
393-
pattern: '**/*.deepnote'
394-
},
395-
{
396-
scheme: 'file',
397-
language: 'python',
398-
pattern: '**/*.deepnote'
399-
}
400-
]
410+
{
411+
scheme: 'vscode-notebook-cell', // Notebook cells
412+
language: 'python',
413+
pattern: '**/*.deepnote'
414+
},
415+
{
416+
scheme: 'file',
417+
language: 'python',
418+
pattern: '**/*.deepnote'
419+
}
420+
];
401421
```
402422

403423
This ensures code intelligence works in:
424+
404425
- Interactive notebook cells
405426
- Cell outputs
406427
- Notebook file contexts
407428

408429
### Features Provided
409430

410431
#### Real-Time Code Intelligence
432+
411433
- Autocomplete as you type in notebook cells
412434
- Hover documentation for functions and variables
413435
- Signature help for function parameters
414436
- Error detection before execution
415437

416438
#### Context Awareness
439+
417440
- Understands imports and dependencies from the venv
418441
- Knows about variables defined in earlier cells
419442
- Provides relevant suggestions based on cell context
420443

421444
#### Integration with Kernel
445+
422446
- LSP runs alongside the Deepnote kernel
423447
- Both share the same Python environment
424448
- Consistent experience between static analysis and execution
@@ -431,10 +455,7 @@ The LSP client manager is registered as a singleton service:
431455

432456
```typescript
433457
// In src/notebooks/serviceRegistry.node.ts
434-
serviceManager.addSingleton<IDeepnoteLspClientManager>(
435-
IDeepnoteLspClientManager,
436-
DeepnoteLspClientManager
437-
);
458+
serviceManager.addSingleton<IDeepnoteLspClientManager>(IDeepnoteLspClientManager, DeepnoteLspClientManager);
438459
```
439460

440461
#### Kernel Lifecycle Integration
@@ -455,6 +476,7 @@ await this.lspClientManager.startLspClients(
455476
#### Error Handling
456477

457478
The implementation gracefully handles:
479+
458480
- Missing `python-lsp-server` installation
459481
- LSP server crashes
460482
- Connection failures
@@ -463,16 +485,19 @@ The implementation gracefully handles:
463485
### User Experience
464486

465487
#### Transparent Operation
488+
466489
- No manual configuration required
467490
- Automatically starts with notebooks
468491
- Seamlessly integrates with VS Code features
469492

470493
#### Performance
494+
471495
- Lightweight per-notebook clients
472496
- Fast response times for code intelligence
473497
- Minimal impact on notebook execution
474498

475499
#### Reliability
500+
476501
- Robust error handling
477502
- Automatic reconnection on failures
478503
- Clean shutdown on notebook close
@@ -491,21 +516,95 @@ The extension includes comprehensive integration tests:
491516
```
492517

493518
Tests run in a real VS Code environment to ensure:
519+
494520
- `vscode-languageclient` works correctly
495521
- LanguageClient lifecycle is properly managed
496522
- Integration with VS Code APIs functions as expected
497523

524+
### SQL LSP Integration
525+
526+
The extension also provides SQL language intelligence using the `sql-language-server` package.
527+
528+
#### How SQL LSP Works
529+
530+
**Local Process Approach:**
531+
532+
- SQL LSP runs as a separate local process (similar to Python LSP)
533+
- Uses stdio communication mode for simplicity and consistency
534+
- Automatically starts when notebooks are opened
535+
- No external server or port configuration required
536+
537+
**Database Integration:**
538+
539+
```typescript
540+
// SQL LSP automatically discovers database integrations
541+
const connections = await integrationStorage.getAll();
542+
543+
// Converts extension's integration configs to sql-language-server format
544+
const sqlConnections = connections
545+
.filter((config) => isSupportedType(config.type))
546+
.map((config) => convertToSqlLspConnection(config));
547+
548+
// Passes connections to SQL LSP via initializationOptions
549+
initializationOptions: {
550+
connections: sqlConnections;
551+
}
552+
```
553+
554+
**Supported Database Types:**
555+
556+
- PostgreSQL (`pgsql`)
557+
- MySQL (`mysql`)
558+
- BigQuery (`big-query`)
559+
560+
**Features:**
561+
562+
- SQL autocomplete with table and column suggestions
563+
- Schema-aware query validation
564+
- Error detection for syntax and semantic issues
565+
- Hover information for database objects
566+
- Works with configured database integrations from extension storage
567+
568+
**Configuration:**
569+
570+
- No manual configuration required
571+
- Automatically uses database integrations configured in the extension
572+
- Credentials are passed securely from integration storage
573+
- Falls back gracefully if no integrations are configured
574+
575+
#### SQL LSP Startup Flow
576+
577+
1. **Notebook Opens**: User opens a `.deepnote` file
578+
2. **Kernel Selection**: DeepnoteKernelAutoSelector starts the kernel
579+
3. **Python LSP Start**: Python LSP client starts for Python cells
580+
4. **SQL LSP Start**: SQL LSP client starts for SQL cells
581+
- Gets path to `sql-language-server` binary from `node_modules`
582+
- Retrieves configured database integrations from storage
583+
- Converts integration configs to sql-language-server format
584+
- Starts server with `['up', '--method', 'stdio']` args
585+
- Passes database connections via initialization options
586+
5. **Ready**: Both Python and SQL intelligence available
587+
588+
#### Error Handling
589+
590+
The SQL LSP implementation handles errors gracefully:
591+
592+
- If `sql-language-server` binary is missing, logs warning and continues
593+
- If no database integrations are configured, SQL LSP still starts (but has no schemas)
594+
- If integration conversion fails, logs error and skips that integration
595+
- Python LSP is never affected by SQL LSP failures
596+
498597
### Differences from Toolkit LSP
499598

500599
The extension's LSP integration differs from the toolkit's in key ways:
501600

502-
| Aspect | Toolkit (Server-Side) | VS Code Extension (Client-Side) |
503-
|--------|----------------------|----------------------------------|
504-
| **Scope** | Multiple editors/clients | Single VS Code instance |
505-
| **Lifecycle** | Runs continuously with server | Per-notebook, on-demand |
506-
| **Transport** | WebSocket/TCP (multi-client) | stdio (process-based) |
507-
| **Management** | Toolkit manages all processes | Extension manages per-notebook |
508-
| **Use Case** | JupyterLab, web interfaces | VS Code editor integration |
601+
| Aspect | Toolkit (Server-Side) | VS Code Extension (Client-Side) |
602+
| -------------- | ----------------------------- | ------------------------------- |
603+
| **Scope** | Multiple editors/clients | Single VS Code instance |
604+
| **Lifecycle** | Runs continuously with server | Per-notebook, on-demand |
605+
| **Transport** | WebSocket/TCP (multi-client) | stdio (process-based) |
606+
| **Management** | Toolkit manages all processes | Extension manages per-notebook |
607+
| **Use Case** | JupyterLab, web interfaces | VS Code editor integration |
509608

510609
Both approaches are complementary—the toolkit provides server infrastructure while the extension provides client integration.
511610

@@ -518,8 +617,8 @@ The LSP integration opens up many possibilities:
518617
- **Team Features**: Shared language server configurations
519618
- **Custom Servers**: Domain-specific intelligence for specialized workflows
520619
- **Enhanced Debugging**: Inline variable inspection and breakpoints
521-
- **SQL LSP Integration**: Extend support to SQL cells in notebooks (planned)
522620
- **Multi-Language Support**: Add LSP clients for R, JavaScript, and other languages
621+
- **Enhanced SQL Support**: Add support for more database types beyond current MySQL, PostgreSQL, BigQuery
523622

524623
## Resources
525624

cspell.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
"language": "en",
2323
"words": [
2424
"alloydb",
25+
"altdb",
26+
"altuser",
2527
"blockgroup",
2628
"channeldef",
2729
"clickhouse",
2830
"dataframe",
2931
"datascience",
32+
"dbname",
3033
"deepnote",
3134
"deepnoteserver",
3235
"dntk",
@@ -50,6 +53,7 @@
5053
"matplotlib",
5154
"millis",
5255
"mindsdb",
56+
"mydb",
5357
"nbformat",
5458
"nbinsx",
5559
"numpy",
@@ -60,9 +64,11 @@
6064
"pylsp",
6165
"PYTHONHOME",
6266
"Reselecting",
67+
"rootpass",
6368
"scipy",
6469
"sklearn",
6570
"taskkill",
71+
"testdb",
6672
"toolsai",
6773
"trino",
6874
"Trino",

0 commit comments

Comments
 (0)