Skip to content

Commit f9423ec

Browse files
authored
feat(lsp): add completion method to LSPManager (#16)
* feat(lsp): add completion method to LSPManager Add code completion support with textDocument/completion: - completion(): Get code completion suggestions at a position - CompletionItemKind enum for completion item types - CompletionItem and CompletionList types Follows LSP 3.17 specification with support for: - CompletionList and CompletionItem[] response formats - Type guards for runtime checking Closes #15 * docs(lsp): update CLAUDE.md and create README.md - Add Vue and Dart servers to supported servers list - Document new methods: definition, references, completion - Add LSPManager methods reference table - Create comprehensive README with API reference * chore: update settings and gitignore - Add additional allowed bash commands to Claude settings - Add .claude/settings.local.json to gitignore - Fix import ordering in README.md (auto-formatted)
1 parent 45e82f3 commit f9423ec

5 files changed

Lines changed: 290 additions & 2 deletions

File tree

.claude/settings.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
"Bash(bun run test)",
66
"Bash(bun run lint)",
77
"Bash(bun run lint:fix:*)",
8-
"mcp__github__issue_write"
8+
"mcp__github__issue_write",
9+
"Bash(gh label list:*)",
10+
"Bash(gh issue:*)",
11+
"Bash(bun run tsc:*)",
12+
"Bash(bun test:*)",
13+
"Bash(bun install:*)"
914
],
1015
"deny": [],
1116
"ask": []

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ Thumbs.db
3030
# Coverage
3131
coverage/
3232

33+
/.claude/settings.local.json

packages/lsp/CLAUDE.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ LSP (Language Server Protocol) client implementation for AI coding tools.
44

55
## Overview
66

7-
This package provides a unified interface for interacting with multiple language servers, enabling real-time diagnostics, hover information, and symbol navigation.
7+
This package provides a unified interface for interacting with multiple language servers, enabling real-time diagnostics, code navigation, completions, and symbol navigation.
88

99
## Architecture
1010

@@ -22,11 +22,13 @@ src/
2222
|--------|-----|------------|----------------|
2323
| TypeScript | `typescript` | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | package-lock.json, bun.lockb, bun.lock, yarn.lock, pnpm-lock.yaml |
2424
| Deno | `deno` | .ts, .tsx, .js, .jsx, .mjs | deno.json, deno.jsonc |
25+
| Vue | `vue` | .vue | package.json, package-lock.json, bun.lockb, bun.lock, pnpm-lock.yaml, yarn.lock |
2526
| Oxlint | `oxlint` | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts, .vue, .astro, .svelte | .oxlintrc.json, package-lock.json, bun.lockb, bun.lock, pnpm-lock.yaml, yarn.lock, package.json |
2627
| Pyright | `pyright` | .py, .pyi | pyproject.toml, setup.py, requirements.txt, pyrightconfig.json |
2728
| Gopls | `gopls` | .go | go.work, go.mod, go.sum |
2829
| Rust Analyzer | `rust-analyzer` | .rs | Cargo.toml, Cargo.lock |
2930
| Kotlin | `kotlin` | .kt, .kts | build.gradle.kts, build.gradle, settings.gradle.kts, settings.gradle, pom.xml |
31+
| Dart | `dart` | .dart | pubspec.yaml, pubspec.lock |
3032

3133
## Adding a New Server
3234

@@ -89,13 +91,39 @@ const diags = await manager.diagnostics()
8991
// Get hover info
9092
const hover = await manager.hover({ file, line, character })
9193

94+
// Go to definition
95+
const defs = await manager.definition({ file, line, character })
96+
97+
// Find all references
98+
const refs = await manager.references({ file, line, character, includeDeclaration: true })
99+
100+
// Get code completions
101+
const completions = await manager.completion({ file, line, character })
102+
92103
// Search symbols
93104
const symbols = await manager.workspaceSymbol('query')
94105

106+
// Get document symbols
107+
const docSymbols = await manager.documentSymbol(uri)
108+
95109
// Cleanup
96110
await manager.shutdown()
97111
```
98112

113+
### LSPManager Methods
114+
115+
| Method | LSP Request | Description |
116+
|--------|-------------|-------------|
117+
| `touchFile()` | `textDocument/didOpen` | Open file in LSP servers |
118+
| `diagnostics()` | `textDocument/publishDiagnostics` | Get all diagnostics |
119+
| `hover()` | `textDocument/hover` | Get hover information |
120+
| `definition()` | `textDocument/definition` | Go to definition |
121+
| `references()` | `textDocument/references` | Find all references |
122+
| `completion()` | `textDocument/completion` | Get code completions |
123+
| `workspaceSymbol()` | `workspace/symbol` | Search workspace symbols |
124+
| `documentSymbol()` | `textDocument/documentSymbol` | Get document symbols |
125+
| `shutdown()` | `shutdown` | Close all clients |
126+
99127
### Server Utilities
100128

101129
```typescript

packages/lsp/README.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# @pleaseai/code-lsp
2+
3+
LSP (Language Server Protocol) client implementation for AI coding tools.
4+
5+
## Installation
6+
7+
```bash
8+
bun add @pleaseai/code-lsp
9+
```
10+
11+
## Features
12+
13+
- Multi-server support with automatic lifecycle management
14+
- Code navigation (definition, references)
15+
- Code completions
16+
- Diagnostics and hover information
17+
- Symbol search (workspace and document)
18+
- Auto-download for Kotlin, Dart, and Vue language servers
19+
20+
## Quick Start
21+
22+
```typescript
23+
import { LSPManager } from '@pleaseai/code-lsp'
24+
25+
const manager = new LSPManager('/path/to/project')
26+
27+
// Open a file to initialize LSP
28+
await manager.touchFile('src/index.ts', true)
29+
30+
// Get diagnostics
31+
const diagnostics = await manager.diagnostics()
32+
33+
// Go to definition
34+
const definitions = await manager.definition({
35+
file: 'src/index.ts',
36+
line: 10,
37+
character: 5,
38+
})
39+
40+
// Find all references
41+
const references = await manager.references({
42+
file: 'src/index.ts',
43+
line: 10,
44+
character: 5,
45+
includeDeclaration: true,
46+
})
47+
48+
// Get completions
49+
const completions = await manager.completion({
50+
file: 'src/index.ts',
51+
line: 10,
52+
character: 5,
53+
})
54+
55+
// Cleanup
56+
await manager.shutdown()
57+
```
58+
59+
## Supported Language Servers
60+
61+
| Language | Server | Auto-download |
62+
|----------|--------|---------------|
63+
| TypeScript/JavaScript | typescript-language-server | No |
64+
| Deno | deno lsp | No |
65+
| Vue | @vue/language-server | Yes |
66+
| Python | pyright-langserver | No |
67+
| Go | gopls | No |
68+
| Rust | rust-analyzer | No |
69+
| Kotlin | JetBrains Kotlin LSP | Yes |
70+
| Dart | dart language-server | Yes |
71+
| Linting | oxlint | No |
72+
73+
## API Reference
74+
75+
### LSPManager
76+
77+
| Method | Description |
78+
|--------|-------------|
79+
| `touchFile(file, waitForDiagnostics?)` | Open file in LSP servers |
80+
| `diagnostics()` | Get all diagnostics |
81+
| `hover({ file, line, character })` | Get hover information |
82+
| `definition({ file, line, character })` | Go to definition |
83+
| `references({ file, line, character, includeDeclaration? })` | Find all references |
84+
| `completion({ file, line, character })` | Get code completions |
85+
| `workspaceSymbol(query)` | Search workspace symbols |
86+
| `documentSymbol(uri)` | Get document symbols |
87+
| `status()` | Get connected server status |
88+
| `shutdown()` | Close all clients |
89+
90+
### Types
91+
92+
```typescript
93+
import {
94+
// Completions
95+
CompletionItem,
96+
CompletionItemKind,
97+
CompletionList,
98+
// Diagnostics
99+
Diagnostic,
100+
101+
DocumentSymbol,
102+
Location,
103+
LocationLink,
104+
105+
// Position and Range
106+
Position,
107+
Range,
108+
// Symbols
109+
Symbol,
110+
111+
SymbolKind,
112+
} from '@pleaseai/code-lsp'
113+
```
114+
115+
## Server Utilities
116+
117+
```typescript
118+
import { getServerById, getServersForExtension } from '@pleaseai/code-lsp'
119+
120+
// Get server by ID
121+
const tsServer = getServerById('typescript')
122+
123+
// Get servers for file extension
124+
const servers = getServersForExtension('.ts')
125+
```
126+
127+
## License
128+
129+
MIT

packages/lsp/src/index.ts

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,64 @@ export const LocationLinkSchema = z.object({
5959
})
6060
export type LocationLink = z.infer<typeof LocationLinkSchema>
6161

62+
/**
63+
* Completion item kind mapping
64+
* @see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItemKind
65+
*/
66+
export enum CompletionItemKind {
67+
Text = 1,
68+
Method = 2,
69+
Function = 3,
70+
Constructor = 4,
71+
Field = 5,
72+
Variable = 6,
73+
Class = 7,
74+
Interface = 8,
75+
Module = 9,
76+
Property = 10,
77+
Unit = 11,
78+
Value = 12,
79+
Enum = 13,
80+
Keyword = 14,
81+
Snippet = 15,
82+
Color = 16,
83+
File = 17,
84+
Reference = 18,
85+
Folder = 19,
86+
EnumMember = 20,
87+
Constant = 21,
88+
Struct = 22,
89+
Event = 23,
90+
Operator = 24,
91+
TypeParameter = 25,
92+
}
93+
94+
/**
95+
* LSP CompletionItem schema
96+
* A completion item represents a text snippet that is proposed to complete text being typed.
97+
*/
98+
export const CompletionItemSchema = z.object({
99+
label: z.string(),
100+
kind: z.number().optional(),
101+
detail: z.string().optional(),
102+
documentation: z.union([z.string(), z.object({ kind: z.string(), value: z.string() })]).optional(),
103+
sortText: z.string().optional(),
104+
filterText: z.string().optional(),
105+
insertText: z.string().optional(),
106+
insertTextFormat: z.number().optional(),
107+
})
108+
export type CompletionItem = z.infer<typeof CompletionItemSchema>
109+
110+
/**
111+
* LSP CompletionList schema
112+
* Represents a collection of completion items to be presented in the editor.
113+
*/
114+
export const CompletionListSchema = z.object({
115+
isIncomplete: z.boolean(),
116+
items: z.array(CompletionItemSchema),
117+
})
118+
export type CompletionList = z.infer<typeof CompletionListSchema>
119+
62120
/**
63121
* LSP Symbol schema
64122
*/
@@ -459,6 +517,73 @@ export class LSPManager {
459517
return results.flat()
460518
}
461519

520+
/**
521+
* Get code completion suggestions at the given position
522+
*
523+
* @see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion
524+
*/
525+
async completion(input: {
526+
file: string
527+
line: number
528+
character: number
529+
}): Promise<CompletionItem[]> {
530+
const clients = await this.getClients(input.file)
531+
532+
const results = await Promise.all(
533+
clients.map(client =>
534+
client.connection
535+
.sendRequest('textDocument/completion', {
536+
textDocument: {
537+
uri: pathToFileURL(input.file).href,
538+
},
539+
position: {
540+
line: input.line,
541+
character: input.character,
542+
},
543+
})
544+
.then((result: unknown) => this.normalizeCompletions(result))
545+
.catch(() => []),
546+
),
547+
)
548+
549+
return results.flat()
550+
}
551+
552+
/**
553+
* Normalize completion response to CompletionItem[]
554+
* Handles CompletionList and CompletionItem[] responses
555+
*/
556+
private normalizeCompletions(result: unknown): CompletionItem[] {
557+
if (!result)
558+
return []
559+
560+
// CompletionList format
561+
if (this.isCompletionList(result)) {
562+
return result.items
563+
}
564+
565+
// Direct CompletionItem[] format
566+
if (Array.isArray(result)) {
567+
return result.filter((item): item is CompletionItem =>
568+
typeof item === 'object' && item !== null && 'label' in item,
569+
)
570+
}
571+
572+
return []
573+
}
574+
575+
/**
576+
* Type guard for CompletionList
577+
*/
578+
private isCompletionList(obj: unknown): obj is CompletionList {
579+
return (
580+
typeof obj === 'object'
581+
&& obj !== null
582+
&& 'items' in obj
583+
&& Array.isArray((obj as CompletionList).items)
584+
)
585+
}
586+
462587
/**
463588
* Normalize definition response to Location[]
464589
* Handles Location, Location[], and LocationLink[] responses

0 commit comments

Comments
 (0)