@markdy/language-server 0.7.29 → 0.8.1
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/dist/index.js +72 -351
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
BEAT_CUE_KEYWORDS,
|
|
6
|
+
NODE_KINDS,
|
|
6
7
|
ParseError,
|
|
7
|
-
|
|
8
|
-
parse,
|
|
9
|
-
registerActorPack
|
|
8
|
+
parse
|
|
10
9
|
} from "@markdy/core";
|
|
11
|
-
import { SYSTEM_ACTOR_TYPES, SYSTEM_FLOW_ACTIONS, systemsPack } from "@markdy/stdlib-systems";
|
|
12
10
|
import {
|
|
13
11
|
CompletionItemKind,
|
|
14
12
|
createConnection,
|
|
@@ -21,387 +19,110 @@ import {
|
|
|
21
19
|
import { TextDocument } from "vscode-languageserver-textdocument";
|
|
22
20
|
var connection = createConnection(ProposedFeatures.all);
|
|
23
21
|
var documents = new TextDocuments(TextDocument);
|
|
24
|
-
registerActorPack(systemsPack);
|
|
25
|
-
var UNIVERSAL_ACTIONS = [...UNIVERSAL_ACTION_NAMES];
|
|
26
|
-
var FIGURE_ACTIONS = [...FIGURE_ONLY_ACTION_NAMES];
|
|
27
|
-
var SYSTEM_ACTIONS = [...SYSTEM_FLOW_ACTIONS];
|
|
28
|
-
var SYSTEM_TYPES = new Set(SYSTEM_ACTOR_TYPES);
|
|
29
|
-
var ARCHITECTURE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
30
|
-
"service",
|
|
31
|
-
"database",
|
|
32
|
-
"db",
|
|
33
|
-
"queue",
|
|
34
|
-
"cache",
|
|
35
|
-
"api",
|
|
36
|
-
"user",
|
|
37
|
-
"client",
|
|
38
|
-
"cloud",
|
|
39
|
-
"region",
|
|
40
|
-
"container",
|
|
41
|
-
"microservice",
|
|
42
|
-
"cluster"
|
|
43
|
-
]);
|
|
44
22
|
var KEYWORDS = [
|
|
45
23
|
"scene",
|
|
46
|
-
"
|
|
47
|
-
"asset",
|
|
48
|
-
"var",
|
|
49
|
-
"def",
|
|
50
|
-
"seq",
|
|
24
|
+
"layout",
|
|
51
25
|
"group",
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
|
|
26
|
+
"beat",
|
|
27
|
+
"style",
|
|
28
|
+
"pattern",
|
|
29
|
+
"use",
|
|
30
|
+
"theme",
|
|
31
|
+
"edge",
|
|
32
|
+
"LR",
|
|
33
|
+
"RL",
|
|
34
|
+
"TB",
|
|
35
|
+
"BT",
|
|
36
|
+
"show",
|
|
37
|
+
"hide",
|
|
38
|
+
"glow",
|
|
39
|
+
"focus",
|
|
40
|
+
...BEAT_CUE_KEYWORDS,
|
|
41
|
+
...NODE_KINDS
|
|
56
42
|
];
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
SEMANTIC_TOKEN_TYPES.map((type, i) => [type, i])
|
|
60
|
-
);
|
|
61
|
-
var ACTION_DOCS = {
|
|
62
|
-
enter: "Slide actor in from edge. Params: `from`, `dur`, `ease`.",
|
|
63
|
-
exit: "Slide actor out to edge. Params: `to`, `dur`, `ease`.",
|
|
64
|
-
move: "Move actor to coordinate. Params: `to=(x,y)`, `dur`, `ease`.",
|
|
65
|
-
fade_in: "Fade actor opacity to 1. Params: `dur`, `ease`.",
|
|
66
|
-
fade_out: "Fade actor opacity to 0. Params: `dur`, `ease`.",
|
|
67
|
-
say: "Show a speech bubble. Params: `text`, `dur`.",
|
|
68
|
-
play: "Expand sequence on actor. Params: `seqName, ...args`.",
|
|
69
|
-
request: "Draw an outbound flow edge. Params: `to`, `label`, `dur`, `style`.",
|
|
70
|
-
response: "Draw a return flow edge. Params: `to`, `label`, `dur`, `style`.",
|
|
71
|
-
emit: "Draw async fire-and-forget flow edge. Params: `to`, `label`, `dur`, `style=fire_and_forget`.",
|
|
72
|
-
pan: "Move camera center. Params: `to=(x,y)`, `dur`, `ease`.",
|
|
73
|
-
zoom: "Change camera zoom. Params: `to`, `dur`, `ease`.",
|
|
74
|
-
shake: "Shake actor/camera. Params: `intensity`, `dur`.",
|
|
75
|
-
spring: "Spring to a coordinate with overshoot. Params: `to=(x,y)`, `stiffness`, `dur`.",
|
|
76
|
-
follow_path: "Move along an SVG path using CSS motion paths. Params: `path`, `dur`, `rotate`.",
|
|
77
|
-
pulse: "Scale up and settle back for emphasis. Params: `amount`, `dur`.",
|
|
78
|
-
glow: "Animate drop-shadow/box-shadow emphasis. Params: `color`, `strength`, `dur`.",
|
|
79
|
-
ripple: "Emit a transient expanding ring from the actor center. Params: `color`, `size`, `dur`.",
|
|
80
|
-
blur: "Animate CSS blur. Params: `from`, `to`, `dur`.",
|
|
81
|
-
line_reveal: "Reveal an actor with a directional clip-path wipe. Params: `from`, `dur`.",
|
|
82
|
-
mask: "Animate a circular clip-path mask. Params: `from`, `to`, `dur`.",
|
|
83
|
-
parallax: "Offset an actor by depth for layered movement. Params: `depth`, `by=(x,y)`, `dur`."
|
|
84
|
-
};
|
|
85
|
-
function extractActors(text) {
|
|
86
|
-
const actors = [];
|
|
43
|
+
function extractNodes(text) {
|
|
44
|
+
const nodes = [];
|
|
87
45
|
const lines = text.split(/\r?\n/);
|
|
88
46
|
for (let i = 0; i < lines.length; i++) {
|
|
89
47
|
const raw = lines[i].trim();
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
48
|
+
const m = /^(\w[\w.-]*)\s+(\w[\w.-]*)/.exec(raw);
|
|
49
|
+
if (!m) continue;
|
|
50
|
+
if (["scene", "layout", "group", "beat", "style", "pattern", "edge", "use"].includes(m[1])) continue;
|
|
51
|
+
if (NODE_KINDS.has(m[1].toLowerCase())) {
|
|
52
|
+
nodes.push({ kind: m[1], name: m[2], line: i });
|
|
94
53
|
}
|
|
95
|
-
const shorthand = /^(service|database|db|queue|cache|api|user|client|cloud|region|container|microservice|cluster)\s+(\w+)\b/.exec(raw);
|
|
96
|
-
if (shorthand) {
|
|
97
|
-
const type = shorthand[1] === "api" ? "service" : shorthand[1] === "user" ? "client" : shorthand[1];
|
|
98
|
-
actors.push({ name: shorthand[2], type, line: i });
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return actors;
|
|
102
|
-
}
|
|
103
|
-
function getActionsForActorType(actorType) {
|
|
104
|
-
const out = [...UNIVERSAL_ACTIONS];
|
|
105
|
-
if (actorType === "figure") out.push(...FIGURE_ACTIONS);
|
|
106
|
-
if (SYSTEM_TYPES.has(actorType)) {
|
|
107
|
-
out.push(...SYSTEM_ACTIONS);
|
|
108
54
|
}
|
|
109
|
-
return
|
|
110
|
-
}
|
|
111
|
-
function lineRange(doc, lineNumber) {
|
|
112
|
-
const line = Math.max(0, Math.min(lineNumber, doc.lineCount - 1));
|
|
113
|
-
const text = doc.getText({
|
|
114
|
-
start: { line, character: 0 },
|
|
115
|
-
end: { line, character: 1e4 }
|
|
116
|
-
});
|
|
117
|
-
return {
|
|
118
|
-
start: { line, character: 0 },
|
|
119
|
-
end: { line, character: Math.max(1, text.length) }
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
function parseWarningsToDiagnostics(warnings, doc) {
|
|
123
|
-
return warnings.map((w) => ({
|
|
124
|
-
severity: DiagnosticSeverity.Warning,
|
|
125
|
-
range: lineRange(doc, w.line - 1),
|
|
126
|
-
message: w.message,
|
|
127
|
-
source: "markdy"
|
|
128
|
-
}));
|
|
55
|
+
return nodes;
|
|
129
56
|
}
|
|
130
|
-
function
|
|
131
|
-
const text = document.getText();
|
|
132
|
-
const diagnostics = [];
|
|
57
|
+
function parseDiagnostics(text) {
|
|
133
58
|
try {
|
|
134
59
|
const ast = parse(text);
|
|
135
|
-
|
|
136
|
-
} catch (
|
|
137
|
-
if (
|
|
138
|
-
|
|
139
|
-
severity: DiagnosticSeverity.Error,
|
|
140
|
-
range: lineRange(document, err.line - 1),
|
|
141
|
-
message: err.message.replace(/^Line \d+:\s*/, ""),
|
|
142
|
-
source: "markdy"
|
|
143
|
-
});
|
|
144
|
-
} else if (err instanceof Error) {
|
|
145
|
-
diagnostics.push({
|
|
146
|
-
severity: DiagnosticSeverity.Error,
|
|
147
|
-
range: lineRange(document, 0),
|
|
148
|
-
message: err.message,
|
|
149
|
-
source: "markdy"
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
connection.sendDiagnostics({ uri: document.uri, diagnostics });
|
|
154
|
-
}
|
|
155
|
-
function getLinePrefix(doc, position) {
|
|
156
|
-
const lineStart = doc.offsetAt({ line: position.line, character: 0 });
|
|
157
|
-
const at = doc.offsetAt(position);
|
|
158
|
-
return doc.getText().slice(lineStart, at);
|
|
159
|
-
}
|
|
160
|
-
function toMethodCompletion(action) {
|
|
161
|
-
return {
|
|
162
|
-
label: action,
|
|
163
|
-
kind: CompletionItemKind.Method,
|
|
164
|
-
detail: "Markdy action",
|
|
165
|
-
documentation: ACTION_DOCS[action] ?? void 0,
|
|
166
|
-
insertText: `${action}()`
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
function getTokenAtPosition(doc, position) {
|
|
170
|
-
const lineText = doc.getText({
|
|
171
|
-
start: { line: position.line, character: 0 },
|
|
172
|
-
end: { line: position.line, character: 1e4 }
|
|
173
|
-
});
|
|
174
|
-
const idx = Math.min(position.character, lineText.length);
|
|
175
|
-
const left = lineText.slice(0, idx).match(/[A-Za-z_][\w.]*$/)?.[0] ?? "";
|
|
176
|
-
const rightMatch = lineText.slice(idx).match(/^[\w.]*/);
|
|
177
|
-
const right = rightMatch ? rightMatch[0] : "";
|
|
178
|
-
const token = `${left}${right}`;
|
|
179
|
-
return token || null;
|
|
180
|
-
}
|
|
181
|
-
function pushSemanticToken(data, state, line, char, length, type) {
|
|
182
|
-
const tokenType = SEMANTIC_TOKEN_TYPE_INDEX.get(type);
|
|
183
|
-
if (tokenType === void 0) return;
|
|
184
|
-
const deltaLine = line - state.line;
|
|
185
|
-
const deltaStart = deltaLine === 0 ? char - state.char : char;
|
|
186
|
-
data.push(deltaLine, deltaStart, length, tokenType, 0);
|
|
187
|
-
state.line = line;
|
|
188
|
-
state.char = char;
|
|
189
|
-
}
|
|
190
|
-
function buildSemanticTokenData(document) {
|
|
191
|
-
const data = [];
|
|
192
|
-
const cursor = { line: 0, char: 0 };
|
|
193
|
-
const text = document.getText();
|
|
194
|
-
const actors = new Set(extractActors(text).map((a) => a.name));
|
|
195
|
-
const lines = text.split(/\r?\n/);
|
|
196
|
-
for (let line = 0; line < lines.length; line++) {
|
|
197
|
-
const content = lines[line];
|
|
198
|
-
const head = content.trim();
|
|
199
|
-
const tokens = [];
|
|
200
|
-
for (const keyword of KEYWORDS) {
|
|
201
|
-
if (head.startsWith(keyword) && !/\w/.test(head.charAt(keyword.length))) {
|
|
202
|
-
const start = content.indexOf(keyword);
|
|
203
|
-
if (start >= 0) tokens.push({ char: start, length: keyword.length, type: "keyword" });
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
for (const name of actors) {
|
|
207
|
-
const match = new RegExp(`\\b${name}\\b`).exec(content);
|
|
208
|
-
if (match?.index !== void 0) {
|
|
209
|
-
tokens.push({ char: match.index, length: name.length, type: "variable" });
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
const actionMatch = /\.\s*([A-Za-z_]\w*)\s*\(/.exec(content);
|
|
213
|
-
if (actionMatch?.index !== void 0) {
|
|
214
|
-
const actionName = actionMatch[1];
|
|
215
|
-
const actionStart = actionMatch.index + actionMatch[0].indexOf(actionName);
|
|
216
|
-
tokens.push({ char: actionStart, length: actionName.length, type: "method" });
|
|
217
|
-
}
|
|
218
|
-
const defMatch = /^def\s+([A-Za-z_]\w*)/.exec(head);
|
|
219
|
-
if (defMatch) {
|
|
220
|
-
const start = content.indexOf(defMatch[1]);
|
|
221
|
-
if (start >= 0) tokens.push({ char: start, length: defMatch[1].length, type: "class" });
|
|
222
|
-
}
|
|
223
|
-
tokens.sort((a, b) => a.char - b.char);
|
|
224
|
-
for (const token of tokens) {
|
|
225
|
-
pushSemanticToken(data, cursor, line, token.char, token.length, token.type);
|
|
60
|
+
return ast.diagnostics;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (error instanceof ParseError) {
|
|
63
|
+
return [{ severity: "error", message: error.message, line: error.line }];
|
|
226
64
|
}
|
|
65
|
+
return [{ severity: "error", message: String(error), line: 1 }];
|
|
227
66
|
}
|
|
228
|
-
return data;
|
|
229
67
|
}
|
|
230
|
-
function buildDocumentSymbols(
|
|
68
|
+
function buildDocumentSymbols(text) {
|
|
231
69
|
const symbols = [];
|
|
232
|
-
const
|
|
233
|
-
for (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
kind: SymbolKind.Namespace,
|
|
241
|
-
range: lineRange(document, i),
|
|
242
|
-
selectionRange: lineRange(document, i),
|
|
243
|
-
children: []
|
|
244
|
-
});
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
const actor = /^actor\s+(\w+)\s*=/.exec(raw);
|
|
248
|
-
if (actor) {
|
|
249
|
-
symbols.push({
|
|
250
|
-
name: actor[1],
|
|
251
|
-
detail: "actor",
|
|
252
|
-
kind: SymbolKind.Variable,
|
|
253
|
-
range: lineRange(document, i),
|
|
254
|
-
selectionRange: lineRange(document, i),
|
|
255
|
-
children: []
|
|
256
|
-
});
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
const shorthand = /^(service|database|db|queue|cache|api|user|client|cloud|region|container|microservice|cluster)\s+(\w+)\b/.exec(raw);
|
|
260
|
-
if (shorthand) {
|
|
261
|
-
symbols.push({
|
|
262
|
-
name: shorthand[2],
|
|
263
|
-
detail: shorthand[1],
|
|
264
|
-
kind: SymbolKind.Variable,
|
|
265
|
-
range: lineRange(document, i),
|
|
266
|
-
selectionRange: lineRange(document, i),
|
|
267
|
-
children: []
|
|
268
|
-
});
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
const seq = /^seq\s+(\w+)/.exec(raw);
|
|
272
|
-
if (seq) {
|
|
273
|
-
symbols.push({
|
|
274
|
-
name: seq[1],
|
|
275
|
-
detail: "sequence",
|
|
276
|
-
kind: SymbolKind.Function,
|
|
277
|
-
range: lineRange(document, i),
|
|
278
|
-
selectionRange: lineRange(document, i),
|
|
279
|
-
children: []
|
|
280
|
-
});
|
|
281
|
-
continue;
|
|
282
|
-
}
|
|
283
|
-
const def = /^def\s+(\w+)/.exec(raw);
|
|
284
|
-
if (def) {
|
|
285
|
-
symbols.push({
|
|
286
|
-
name: def[1],
|
|
287
|
-
detail: "template",
|
|
288
|
-
kind: SymbolKind.Class,
|
|
289
|
-
range: lineRange(document, i),
|
|
290
|
-
selectionRange: lineRange(document, i),
|
|
291
|
-
children: []
|
|
292
|
-
});
|
|
293
|
-
}
|
|
70
|
+
const nodes = extractNodes(text);
|
|
71
|
+
for (const node of nodes) {
|
|
72
|
+
symbols.push({
|
|
73
|
+
name: node.name,
|
|
74
|
+
kind: SymbolKind.Class,
|
|
75
|
+
range: { start: { line: node.line, character: 0 }, end: { line: node.line, character: 80 } },
|
|
76
|
+
selectionRange: { start: { line: node.line, character: 0 }, end: { line: node.line, character: 80 } }
|
|
77
|
+
});
|
|
294
78
|
}
|
|
295
79
|
return symbols;
|
|
296
80
|
}
|
|
297
|
-
connection.onInitialize((
|
|
81
|
+
connection.onInitialize(() => ({
|
|
298
82
|
capabilities: {
|
|
299
83
|
textDocumentSync: TextDocumentSyncKind.Incremental,
|
|
300
|
-
completionProvider: {
|
|
301
|
-
resolveProvider: false,
|
|
302
|
-
triggerCharacters: [".", "@"]
|
|
303
|
-
},
|
|
304
|
-
hoverProvider: true,
|
|
84
|
+
completionProvider: { triggerCharacters: [" ", ".", ":", "-", ">"] },
|
|
305
85
|
documentSymbolProvider: true,
|
|
306
|
-
|
|
307
|
-
full: true,
|
|
308
|
-
legend: {
|
|
309
|
-
tokenTypes: [...SEMANTIC_TOKEN_TYPES],
|
|
310
|
-
tokenModifiers: []
|
|
311
|
-
}
|
|
312
|
-
}
|
|
86
|
+
hoverProvider: true
|
|
313
87
|
}
|
|
314
88
|
}));
|
|
315
|
-
documents.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
89
|
+
documents.onDidChangeContent((change) => {
|
|
90
|
+
const text = change.document.getText();
|
|
91
|
+
const diagnostics = parseDiagnostics(text).map((d) => ({
|
|
92
|
+
severity: d.severity === "error" ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
|
|
93
|
+
range: {
|
|
94
|
+
start: { line: Math.max(0, d.line - 1), character: 0 },
|
|
95
|
+
end: { line: Math.max(0, d.line - 1), character: 120 }
|
|
96
|
+
},
|
|
97
|
+
message: d.message,
|
|
98
|
+
source: "markdy"
|
|
99
|
+
}));
|
|
100
|
+
connection.sendDiagnostics({ uri: change.document.uri, diagnostics });
|
|
319
101
|
});
|
|
320
102
|
connection.onCompletion((params) => {
|
|
321
103
|
const doc = documents.get(params.textDocument.uri);
|
|
322
104
|
if (!doc) return [];
|
|
323
|
-
const
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const actorType = actorByName.get(actorName);
|
|
331
|
-
if (!actorType) return [];
|
|
332
|
-
return getActionsForActorType(actorType).filter((a) => a.startsWith(partial)).map(toMethodCompletion);
|
|
333
|
-
}
|
|
334
|
-
const lineEventPrefix = /@[\d.+]*:\s*$/.test(prefix);
|
|
335
|
-
if (lineEventPrefix) {
|
|
336
|
-
return actors.map((a) => ({
|
|
337
|
-
label: a.name,
|
|
338
|
-
kind: CompletionItemKind.Variable,
|
|
339
|
-
detail: `${a.type} actor`,
|
|
340
|
-
insertText: `${a.name}.`
|
|
341
|
-
}));
|
|
342
|
-
}
|
|
343
|
-
const items = [];
|
|
344
|
-
for (const keyword of KEYWORDS) {
|
|
345
|
-
items.push({
|
|
346
|
-
label: keyword,
|
|
347
|
-
kind: CompletionItemKind.Keyword,
|
|
348
|
-
detail: "Markdy keyword"
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
for (const actor of actors) {
|
|
352
|
-
items.push({
|
|
353
|
-
label: actor.name,
|
|
354
|
-
kind: CompletionItemKind.Variable,
|
|
355
|
-
detail: `${actor.type} actor`
|
|
356
|
-
});
|
|
357
|
-
}
|
|
105
|
+
const nodes = extractNodes(doc.getText());
|
|
106
|
+
const items = [
|
|
107
|
+
...KEYWORDS.map((label) => ({ label, kind: CompletionItemKind.Keyword })),
|
|
108
|
+
...nodes.map((n) => ({ label: n.name, kind: CompletionItemKind.Variable })),
|
|
109
|
+
{ label: "$nodes", kind: CompletionItemKind.Variable },
|
|
110
|
+
{ label: "$title", kind: CompletionItemKind.Variable }
|
|
111
|
+
];
|
|
358
112
|
return items;
|
|
359
113
|
});
|
|
360
|
-
connection.onHover((params) => {
|
|
361
|
-
const doc = documents.get(params.textDocument.uri);
|
|
362
|
-
if (!doc) return null;
|
|
363
|
-
const token = getTokenAtPosition(doc, params.position);
|
|
364
|
-
if (!token) return null;
|
|
365
|
-
const actor = extractActors(doc.getText()).find((a) => a.name === token);
|
|
366
|
-
if (actor) {
|
|
367
|
-
return {
|
|
368
|
-
contents: {
|
|
369
|
-
kind: "markdown",
|
|
370
|
-
value: `**${actor.name}**
|
|
371
|
-
|
|
372
|
-
Actor type: \`${actor.type}\``
|
|
373
|
-
}
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
if (ACTION_DOCS[token]) {
|
|
377
|
-
return {
|
|
378
|
-
contents: {
|
|
379
|
-
kind: "markdown",
|
|
380
|
-
value: `**${token}**
|
|
381
|
-
|
|
382
|
-
${ACTION_DOCS[token]}`
|
|
383
|
-
}
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
if (KEYWORDS.includes(token)) {
|
|
387
|
-
return {
|
|
388
|
-
contents: {
|
|
389
|
-
kind: "markdown",
|
|
390
|
-
value: `Markdy keyword: \`${token}\``
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
return null;
|
|
395
|
-
});
|
|
396
114
|
connection.onDocumentSymbol((params) => {
|
|
397
115
|
const doc = documents.get(params.textDocument.uri);
|
|
398
116
|
if (!doc) return [];
|
|
399
|
-
return buildDocumentSymbols(doc);
|
|
117
|
+
return buildDocumentSymbols(doc.getText());
|
|
400
118
|
});
|
|
401
|
-
connection.
|
|
119
|
+
connection.onHover((params) => {
|
|
402
120
|
const doc = documents.get(params.textDocument.uri);
|
|
403
|
-
if (!doc) return
|
|
404
|
-
|
|
121
|
+
if (!doc) return null;
|
|
122
|
+
const line = doc.getText().split(/\r?\n/)[params.position.line]?.trim() ?? "";
|
|
123
|
+
if (line.startsWith("beat ")) return { contents: { kind: "markdown", value: "Named narrative beat with indented cues." } };
|
|
124
|
+
if (line.includes("->")) return { contents: { kind: "markdown", value: "Request/call flow edge." } };
|
|
125
|
+
return null;
|
|
405
126
|
});
|
|
406
127
|
documents.listen(connection);
|
|
407
128
|
connection.listen();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markdy/language-server",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Language Server Protocol implementation for MarkdyScript.",
|
|
3
|
+
"version": "0.8.1",
|
|
4
|
+
"description": "Language Server Protocol implementation for MarkdyScript diagrams.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"lsp",
|
|
27
27
|
"language-server",
|
|
28
28
|
"markdyscript",
|
|
29
|
+
"diagram-as-code",
|
|
29
30
|
"editor"
|
|
30
31
|
],
|
|
31
32
|
"author": "Hoang Yell <hoangyell@gmail.com> (https://hoangyell.com)",
|
|
@@ -44,8 +45,8 @@
|
|
|
44
45
|
"dependencies": {
|
|
45
46
|
"vscode-languageserver": "^9.0.1",
|
|
46
47
|
"vscode-languageserver-textdocument": "^1.0.12",
|
|
47
|
-
"@markdy/core": "0.
|
|
48
|
-
"@markdy/stdlib-systems": "0.
|
|
48
|
+
"@markdy/core": "0.8.1",
|
|
49
|
+
"@markdy/stdlib-systems": "0.8.1"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"tsup": "^8.5.1",
|