@birdcc/lsp 0.0.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.oxfmtrc.json +16 -0
- package/LICENSE +674 -0
- package/README.md +343 -0
- package/dist/completion.d.ts +8 -0
- package/dist/completion.d.ts.map +1 -0
- package/dist/completion.js +137 -0
- package/dist/completion.js.map +1 -0
- package/dist/definition.d.ts +5 -0
- package/dist/definition.d.ts.map +1 -0
- package/dist/definition.js +21 -0
- package/dist/definition.js.map +1 -0
- package/dist/diagnostic.d.ts +6 -0
- package/dist/diagnostic.d.ts.map +1 -0
- package/dist/diagnostic.js +38 -0
- package/dist/diagnostic.js.map +1 -0
- package/dist/document-symbol.d.ts +4 -0
- package/dist/document-symbol.d.ts.map +1 -0
- package/dist/document-symbol.js +20 -0
- package/dist/document-symbol.js.map +1 -0
- package/dist/hover-docs.d.ts +5 -0
- package/dist/hover-docs.d.ts.map +1 -0
- package/dist/hover-docs.js +141 -0
- package/dist/hover-docs.js.map +1 -0
- package/dist/hover-docs.yaml +600 -0
- package/dist/hover.d.ts +5 -0
- package/dist/hover.d.ts.map +1 -0
- package/dist/hover.js +81 -0
- package/dist/hover.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/lsp-server.d.ts +3 -0
- package/dist/lsp-server.d.ts.map +1 -0
- package/dist/lsp-server.js +250 -0
- package/dist/lsp-server.js.map +1 -0
- package/dist/references.d.ts +5 -0
- package/dist/references.d.ts.map +1 -0
- package/dist/references.js +48 -0
- package/dist/references.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +4 -0
- package/dist/server.js.map +1 -0
- package/dist/shared.d.ts +17 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +150 -0
- package/dist/shared.js.map +1 -0
- package/dist/symbol-utils.d.ts +17 -0
- package/dist/symbol-utils.d.ts.map +1 -0
- package/dist/symbol-utils.js +84 -0
- package/dist/symbol-utils.js.map +1 -0
- package/dist/validation.d.ts +21 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +47 -0
- package/dist/validation.js.map +1 -0
- package/package.json +45 -0
- package/scripts/copy-hover-yaml.mjs +14 -0
- package/src/completion.ts +223 -0
- package/src/definition.ts +50 -0
- package/src/diagnostic.ts +48 -0
- package/src/document-symbol.ts +27 -0
- package/src/hover-docs.ts +223 -0
- package/src/hover-docs.yaml +600 -0
- package/src/hover.ts +122 -0
- package/src/index.ts +8 -0
- package/src/lsp-server.ts +350 -0
- package/src/references.ts +107 -0
- package/src/server.ts +4 -0
- package/src/shared.ts +182 -0
- package/src/symbol-utils.ts +126 -0
- package/src/validation.ts +85 -0
- package/test/hover-docs.test.ts +18 -0
- package/test/lsp.test.ts +304 -0
- package/test/perf-baseline.test.ts +96 -0
- package/test/validation.test.ts +212 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import { parse } from "yaml";
|
|
6
|
+
|
|
7
|
+
export type HoverDocDiffType = "same" | "added" | "modified" | "removed";
|
|
8
|
+
export type HoverDocVersionTag = "v2+" | "v3+" | "v2" | "v2-v3";
|
|
9
|
+
|
|
10
|
+
interface HoverDocYamlEntry {
|
|
11
|
+
readonly keyword: string;
|
|
12
|
+
readonly description: string;
|
|
13
|
+
readonly detail: string;
|
|
14
|
+
readonly diff: HoverDocDiffType;
|
|
15
|
+
readonly version: HoverDocVersionTag;
|
|
16
|
+
readonly anchor?: string;
|
|
17
|
+
readonly anchors?: {
|
|
18
|
+
readonly v2?: string;
|
|
19
|
+
readonly v3?: string;
|
|
20
|
+
};
|
|
21
|
+
readonly notes?: {
|
|
22
|
+
readonly v2?: string;
|
|
23
|
+
readonly v3?: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface HoverDocYamlSource {
|
|
28
|
+
readonly version: number;
|
|
29
|
+
readonly baseUrls: {
|
|
30
|
+
readonly v2: string;
|
|
31
|
+
readonly v3: string;
|
|
32
|
+
};
|
|
33
|
+
readonly entries: readonly HoverDocYamlEntry[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const normalizeAnchor = (value: string | undefined): string | undefined => {
|
|
37
|
+
if (typeof value !== "string") {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const normalized = value.trim().replace(/^#/, "");
|
|
42
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const loadHoverDocYaml = (): HoverDocYamlSource => {
|
|
46
|
+
const hoverDocsPath = path.join(
|
|
47
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
48
|
+
"hover-docs.yaml",
|
|
49
|
+
);
|
|
50
|
+
const raw = readFileSync(hoverDocsPath, "utf8");
|
|
51
|
+
const parsed = parse(raw) as HoverDocYamlSource;
|
|
52
|
+
|
|
53
|
+
if (!parsed || !Array.isArray(parsed.entries) || !parsed.baseUrls) {
|
|
54
|
+
throw new Error("Invalid hover docs yaml structure");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return parsed;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const isDiffType = (value: string): value is HoverDocDiffType =>
|
|
61
|
+
value === "same" ||
|
|
62
|
+
value === "added" ||
|
|
63
|
+
value === "modified" ||
|
|
64
|
+
value === "removed";
|
|
65
|
+
|
|
66
|
+
const isVersionTag = (value: string): value is HoverDocVersionTag =>
|
|
67
|
+
value === "v2+" || value === "v3+" || value === "v2" || value === "v2-v3";
|
|
68
|
+
|
|
69
|
+
const hoverDocSource = loadHoverDocYaml();
|
|
70
|
+
|
|
71
|
+
const VERSION_BASE_URLS = {
|
|
72
|
+
v2: hoverDocSource.baseUrls.v2,
|
|
73
|
+
v3: hoverDocSource.baseUrls.v3,
|
|
74
|
+
} as const;
|
|
75
|
+
|
|
76
|
+
const toCanonicalEntry = (entry: HoverDocYamlEntry): HoverDocYamlEntry => {
|
|
77
|
+
const keyword = entry.keyword.trim().toLowerCase();
|
|
78
|
+
if (keyword.length === 0) {
|
|
79
|
+
throw new Error("Hover keyword must not be empty");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!isDiffType(entry.diff)) {
|
|
83
|
+
throw new Error(`Invalid diff type for keyword '${keyword}'`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!isVersionTag(entry.version)) {
|
|
87
|
+
throw new Error(`Invalid version tag for keyword '${keyword}'`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const anchor = normalizeAnchor(entry.anchor);
|
|
91
|
+
const anchors = entry.anchors
|
|
92
|
+
? {
|
|
93
|
+
...(normalizeAnchor(entry.anchors.v2)
|
|
94
|
+
? { v2: normalizeAnchor(entry.anchors.v2) }
|
|
95
|
+
: {}),
|
|
96
|
+
...(normalizeAnchor(entry.anchors.v3)
|
|
97
|
+
? { v3: normalizeAnchor(entry.anchors.v3) }
|
|
98
|
+
: {}),
|
|
99
|
+
}
|
|
100
|
+
: undefined;
|
|
101
|
+
|
|
102
|
+
if (
|
|
103
|
+
(entry.version === "v2+" ||
|
|
104
|
+
entry.version === "v3+" ||
|
|
105
|
+
entry.version === "v2") &&
|
|
106
|
+
!anchor
|
|
107
|
+
) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Keyword '${keyword}' requires a single anchor for version '${entry.version}'`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (
|
|
114
|
+
entry.version === "v2-v3" &&
|
|
115
|
+
!anchor &&
|
|
116
|
+
(!anchors || Object.keys(anchors).length === 0)
|
|
117
|
+
) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Keyword '${keyword}' requires anchor or anchors for version 'v2-v3'`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
...entry,
|
|
125
|
+
keyword,
|
|
126
|
+
anchor,
|
|
127
|
+
anchors,
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const normalizedEntries = hoverDocSource.entries.map(toCanonicalEntry);
|
|
132
|
+
|
|
133
|
+
const dedupedEntries: HoverDocYamlEntry[] = [];
|
|
134
|
+
const seenKeywords = new Set<string>();
|
|
135
|
+
for (const entry of normalizedEntries) {
|
|
136
|
+
if (seenKeywords.has(entry.keyword)) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
seenKeywords.add(entry.keyword);
|
|
141
|
+
dedupedEntries.push(entry);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const buildDocUrl = (
|
|
145
|
+
version: keyof typeof VERSION_BASE_URLS,
|
|
146
|
+
anchor?: string,
|
|
147
|
+
): string => {
|
|
148
|
+
const baseUrl = VERSION_BASE_URLS[version];
|
|
149
|
+
if (!anchor || anchor.length === 0) {
|
|
150
|
+
return baseUrl;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return `${baseUrl}#${anchor}`;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const buildDocsSection = (entry: HoverDocYamlEntry): string => {
|
|
157
|
+
const lines: string[] = [];
|
|
158
|
+
|
|
159
|
+
if (entry.version === "v2+") {
|
|
160
|
+
lines.push(`- [BIRD v2.18 / v3.2.0](${buildDocUrl("v2", entry.anchor)})`);
|
|
161
|
+
} else if (entry.version === "v3+") {
|
|
162
|
+
lines.push(`- [BIRD v3.2.0](${buildDocUrl("v3", entry.anchor)})`);
|
|
163
|
+
} else if (entry.version === "v2") {
|
|
164
|
+
lines.push(`- [BIRD v2.18](${buildDocUrl("v2", entry.anchor)})`);
|
|
165
|
+
} else {
|
|
166
|
+
const v2Anchor = entry.anchors?.v2 ?? entry.anchor;
|
|
167
|
+
const v3Anchor = entry.anchors?.v3 ?? entry.anchor;
|
|
168
|
+
|
|
169
|
+
if (v2Anchor) {
|
|
170
|
+
lines.push(`- [BIRD v2.18](${buildDocUrl("v2", v2Anchor)})`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (v3Anchor) {
|
|
174
|
+
lines.push(`- [BIRD v3.2.0](${buildDocUrl("v3", v3Anchor)})`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return lines.join("\n");
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const buildNotesSection = (entry: HoverDocYamlEntry): string => {
|
|
182
|
+
if (!entry.notes) {
|
|
183
|
+
return "";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const lines: string[] = [];
|
|
187
|
+
if (entry.notes.v2) {
|
|
188
|
+
lines.push(`- v2: ${entry.notes.v2}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (entry.notes.v3) {
|
|
192
|
+
lines.push(`- v3: ${entry.notes.v3}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (lines.length === 0) {
|
|
196
|
+
return "";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return `\n\nNotes:\n${lines.join("\n")}`;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const toHoverMarkdown = (entry: HoverDocYamlEntry): string => {
|
|
203
|
+
return (
|
|
204
|
+
[
|
|
205
|
+
`### ${entry.description}`,
|
|
206
|
+
"",
|
|
207
|
+
entry.detail,
|
|
208
|
+
"",
|
|
209
|
+
`Diff: \`${entry.diff}\``,
|
|
210
|
+
`Version: \`${entry.version}\``,
|
|
211
|
+
"Docs:",
|
|
212
|
+
buildDocsSection(entry),
|
|
213
|
+
].join("\n") + buildNotesSection(entry)
|
|
214
|
+
);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export const HOVER_KEYWORD_DOCS: Record<string, string> = Object.fromEntries(
|
|
218
|
+
dedupedEntries.map((entry) => [entry.keyword, toHoverMarkdown(entry)]),
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
export const HOVER_KEYWORDS: readonly string[] = Object.freeze(
|
|
222
|
+
dedupedEntries.map((entry) => entry.keyword),
|
|
223
|
+
);
|