Skip to content

Commit f55f66e

Browse files
committed
minimal lsp
1 parent 13d9044 commit f55f66e

File tree

4 files changed

+82
-4
lines changed

4 files changed

+82
-4
lines changed

package.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
"main": "index.js",
55
"author": "andrewgryan <[email protected]>",
66
"license": "MIT",
7+
"scripts": {
8+
"test": "vitest"
9+
},
710
"devDependencies": {
8-
"@types/node": "^20.14.2"
11+
"@types/node": "^20.14.2",
12+
"vitest": "^1.6.0"
913
}
1014
}

src/main.test.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { expect, test } from 'vitest'
2+
import { encode, decode } from "./main"
3+
4+
test('encode', () => {
5+
const expected = "Content-Length: 2\r\n\r\n{}"
6+
expect(encode({})).toEqual(expected)
7+
})
8+
9+
test('decode request', () => {
10+
const payload = {id: 1, jsonrpc: "2.0", method: "textDocument/completion", params: {}}
11+
const request = encode(payload)
12+
const actual = decode(request)
13+
expect(actual).toEqual(payload)
14+
})

src/main.ts

+61-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,68 @@ const log = (msg: string) => {
99
})
1010
}
1111

12+
// Encode message
13+
export const encode = (payload: Object) => {
14+
const content = JSON.stringify(payload)
15+
return `Content-Length: ${content.length}\r\n\r\n${content}`
16+
}
17+
18+
interface Message {
19+
jsonrpc: string;
20+
}
21+
22+
interface RequestMessage extends Message {
23+
id: number;
24+
method: string;
25+
}
26+
27+
// Decode message
28+
export const decode = (content: string): RequestMessage | null => {
29+
const [header, body] = content.split("\r\n\r\n")
30+
if (header.startsWith("Content-Length")) {
31+
const length = parseInt(header.split(": ")[1])
32+
return JSON.parse(body.substring(0, length))
33+
} else {
34+
log(`Could not parse: ${content}\n`)
35+
return null
36+
}
37+
}
38+
39+
const respond = (id: number, result: Object | null) => {
40+
const response = encode({ jsonrpc: "2.0", id, result })
41+
process.stdout.write(response)
42+
log(response + "\n")
43+
}
44+
45+
const initializeResult = () => {
46+
return {
47+
capabilities: {
48+
textDocumentSync: 1
49+
},
50+
serverInfo: {
51+
name: "leaflet-html",
52+
version: "0.0.0"
53+
}
54+
}
55+
}
56+
57+
const shutdownResult = () => null
58+
1259
// Listen on stdin/stdout
1360
process.stdin.on("data", (buf) => {
1461
const message = buf.toString()
15-
log(message)
16-
process.stdout.write(message)
62+
log(message + "\n")
63+
const payload = decode(message)
64+
if (payload !== null) {
65+
switch (payload.method) {
66+
case("initialize"):
67+
respond(payload.id, initializeResult());
68+
break;
69+
case("shutdown"):
70+
respond(payload.id, shutdownResult());
71+
break;
72+
case("exit"):
73+
process.exit(0)
74+
}
75+
}
1776
})

tsconfig.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
"lib": ["es2022"],
77
"target": "es5"
88
},
9-
"include": ["./src/**/*.ts"]
9+
"include": ["./src/**/*.ts"],
10+
"exclude": ["./src/**/*.test.ts"]
1011
}

0 commit comments

Comments
 (0)