@treedy/vue-lsp-mcp 0.2.1 → 0.2.2
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 +913 -1
- package/dist/vue-service.d.ts +103 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -19666,6 +19666,17 @@ function getFileContent(filePath) {
|
|
|
19666
19666
|
}
|
|
19667
19667
|
return "";
|
|
19668
19668
|
}
|
|
19669
|
+
function positionToOffset(content, line, character) {
|
|
19670
|
+
const lines = content.split(`
|
|
19671
|
+
`);
|
|
19672
|
+
const safeLine = Math.max(0, Math.min(line, lines.length - 1));
|
|
19673
|
+
let offset = 0;
|
|
19674
|
+
for (let i = 0;i < safeLine; i++) {
|
|
19675
|
+
offset += lines[i].length + 1;
|
|
19676
|
+
}
|
|
19677
|
+
const safeChar = Math.max(0, Math.min(character, lines[safeLine].length));
|
|
19678
|
+
return offset + safeChar;
|
|
19679
|
+
}
|
|
19669
19680
|
function toUri(filePath) {
|
|
19670
19681
|
return `file://${path.resolve(filePath)}`;
|
|
19671
19682
|
}
|
|
@@ -19875,7 +19886,7 @@ async function getConnection(projectRoot) {
|
|
|
19875
19886
|
}
|
|
19876
19887
|
}
|
|
19877
19888
|
}
|
|
19878
|
-
await sendMessage(conn, "initialize", {
|
|
19889
|
+
const initResult = await sendMessage(conn, "initialize", {
|
|
19879
19890
|
processId: process.pid,
|
|
19880
19891
|
rootUri: toUri(projectRoot),
|
|
19881
19892
|
rootPath: projectRoot,
|
|
@@ -19885,7 +19896,23 @@ async function getConnection(projectRoot) {
|
|
|
19885
19896
|
completion: { completionItem: { snippetSupport: true } },
|
|
19886
19897
|
signatureHelp: {},
|
|
19887
19898
|
definition: {},
|
|
19899
|
+
implementation: {},
|
|
19900
|
+
typeDefinition: {},
|
|
19888
19901
|
references: {},
|
|
19902
|
+
documentHighlight: {},
|
|
19903
|
+
selectionRange: {},
|
|
19904
|
+
foldingRange: {},
|
|
19905
|
+
documentLink: {},
|
|
19906
|
+
callHierarchy: {},
|
|
19907
|
+
codeAction: {
|
|
19908
|
+
dynamicRegistration: false,
|
|
19909
|
+
codeActionLiteralSupport: {
|
|
19910
|
+
codeActionKind: {
|
|
19911
|
+
valueSet: ["quickfix", "refactor", "source", "source.organizeImports"]
|
|
19912
|
+
}
|
|
19913
|
+
}
|
|
19914
|
+
},
|
|
19915
|
+
rename: { prepareSupport: true },
|
|
19889
19916
|
publishDiagnostics: {},
|
|
19890
19917
|
synchronization: {
|
|
19891
19918
|
didOpen: true,
|
|
@@ -19914,6 +19941,7 @@ async function getConnection(projectRoot) {
|
|
|
19914
19941
|
}
|
|
19915
19942
|
]
|
|
19916
19943
|
});
|
|
19944
|
+
conn.serverCapabilities = initResult?.capabilities || {};
|
|
19917
19945
|
sendNotification(conn, "initialized", {});
|
|
19918
19946
|
conn.initialized = true;
|
|
19919
19947
|
return conn;
|
|
@@ -20011,6 +20039,52 @@ async function getDefinition(filePath, line, column) {
|
|
|
20011
20039
|
return [];
|
|
20012
20040
|
}
|
|
20013
20041
|
}
|
|
20042
|
+
async function getImplementation(filePath, line, column) {
|
|
20043
|
+
const projectRoot = findProjectRoot(filePath);
|
|
20044
|
+
const conn = await getConnection(projectRoot);
|
|
20045
|
+
await ensureDocumentOpen(conn, filePath);
|
|
20046
|
+
const result = await sendMessage(conn, "textDocument/implementation", {
|
|
20047
|
+
textDocument: { uri: toUri(filePath) },
|
|
20048
|
+
position: { line: line - 1, character: column - 1 }
|
|
20049
|
+
});
|
|
20050
|
+
if (!result)
|
|
20051
|
+
return [];
|
|
20052
|
+
const locations = Array.isArray(result) ? result : [result];
|
|
20053
|
+
return locations.map((loc) => {
|
|
20054
|
+
const uri = loc.targetUri || loc.uri;
|
|
20055
|
+
const range = loc.targetRange || loc.range;
|
|
20056
|
+
return {
|
|
20057
|
+
file: fromUri(uri),
|
|
20058
|
+
line: range.start.line + 1,
|
|
20059
|
+
column: range.start.character + 1,
|
|
20060
|
+
endLine: range.end.line + 1,
|
|
20061
|
+
endColumn: range.end.character + 1
|
|
20062
|
+
};
|
|
20063
|
+
});
|
|
20064
|
+
}
|
|
20065
|
+
async function getTypeDefinition(filePath, line, column) {
|
|
20066
|
+
const projectRoot = findProjectRoot(filePath);
|
|
20067
|
+
const conn = await getConnection(projectRoot);
|
|
20068
|
+
await ensureDocumentOpen(conn, filePath);
|
|
20069
|
+
const result = await sendMessage(conn, "textDocument/typeDefinition", {
|
|
20070
|
+
textDocument: { uri: toUri(filePath) },
|
|
20071
|
+
position: { line: line - 1, character: column - 1 }
|
|
20072
|
+
});
|
|
20073
|
+
if (!result)
|
|
20074
|
+
return [];
|
|
20075
|
+
const locations = Array.isArray(result) ? result : [result];
|
|
20076
|
+
return locations.map((loc) => {
|
|
20077
|
+
const uri = loc.targetUri || loc.uri;
|
|
20078
|
+
const range = loc.targetRange || loc.range;
|
|
20079
|
+
return {
|
|
20080
|
+
file: fromUri(uri),
|
|
20081
|
+
line: range.start.line + 1,
|
|
20082
|
+
column: range.start.character + 1,
|
|
20083
|
+
endLine: range.end.line + 1,
|
|
20084
|
+
endColumn: range.end.character + 1
|
|
20085
|
+
};
|
|
20086
|
+
});
|
|
20087
|
+
}
|
|
20014
20088
|
async function getReferences(filePath, line, column) {
|
|
20015
20089
|
const projectRoot = findProjectRoot(filePath);
|
|
20016
20090
|
const conn = await getConnection(projectRoot);
|
|
@@ -20034,6 +20108,25 @@ async function getReferences(filePath, line, column) {
|
|
|
20034
20108
|
return [];
|
|
20035
20109
|
}
|
|
20036
20110
|
}
|
|
20111
|
+
async function getDocumentHighlights(filePath, line, column) {
|
|
20112
|
+
const projectRoot = findProjectRoot(filePath);
|
|
20113
|
+
const conn = await getConnection(projectRoot);
|
|
20114
|
+
await ensureDocumentOpen(conn, filePath);
|
|
20115
|
+
const result = await sendMessage(conn, "textDocument/documentHighlight", {
|
|
20116
|
+
textDocument: { uri: toUri(filePath) },
|
|
20117
|
+
position: { line: line - 1, character: column - 1 }
|
|
20118
|
+
});
|
|
20119
|
+
if (!result || !Array.isArray(result))
|
|
20120
|
+
return [];
|
|
20121
|
+
return result.map((item) => ({
|
|
20122
|
+
file: path.resolve(filePath),
|
|
20123
|
+
line: item.range.start.line + 1,
|
|
20124
|
+
column: item.range.start.character + 1,
|
|
20125
|
+
endLine: item.range.end.line + 1,
|
|
20126
|
+
endColumn: item.range.end.character + 1,
|
|
20127
|
+
kind: item.kind ?? 1
|
|
20128
|
+
}));
|
|
20129
|
+
}
|
|
20037
20130
|
async function getCompletions(filePath, line, column, limit = 20) {
|
|
20038
20131
|
const projectRoot = findProjectRoot(filePath);
|
|
20039
20132
|
const conn = await getConnection(projectRoot);
|
|
@@ -20114,6 +20207,29 @@ async function getSignatureHelp(filePath, line, column) {
|
|
|
20114
20207
|
return null;
|
|
20115
20208
|
}
|
|
20116
20209
|
}
|
|
20210
|
+
async function getPrepareRename(filePath, line, column) {
|
|
20211
|
+
const projectRoot = findProjectRoot(filePath);
|
|
20212
|
+
const conn = await getConnection(projectRoot);
|
|
20213
|
+
await ensureDocumentOpen(conn, filePath);
|
|
20214
|
+
const result = await sendMessage(conn, "textDocument/prepareRename", {
|
|
20215
|
+
textDocument: { uri: toUri(filePath) },
|
|
20216
|
+
position: { line: line - 1, character: column - 1 }
|
|
20217
|
+
});
|
|
20218
|
+
if (!result)
|
|
20219
|
+
return { canRename: false };
|
|
20220
|
+
const range = "range" in result ? result.range : result;
|
|
20221
|
+
const placeholder = "placeholder" in result ? result.placeholder : undefined;
|
|
20222
|
+
return {
|
|
20223
|
+
canRename: true,
|
|
20224
|
+
placeholder,
|
|
20225
|
+
range: {
|
|
20226
|
+
line: range.start.line + 1,
|
|
20227
|
+
column: range.start.character + 1,
|
|
20228
|
+
endLine: range.end.line + 1,
|
|
20229
|
+
endColumn: range.end.character + 1
|
|
20230
|
+
}
|
|
20231
|
+
};
|
|
20232
|
+
}
|
|
20117
20233
|
async function getInlayHints(filePath) {
|
|
20118
20234
|
const projectRoot = findProjectRoot(filePath);
|
|
20119
20235
|
const conn = await getConnection(projectRoot);
|
|
@@ -20138,6 +20254,228 @@ async function getInlayHints(filePath) {
|
|
|
20138
20254
|
return [];
|
|
20139
20255
|
}
|
|
20140
20256
|
}
|
|
20257
|
+
async function getSemanticTokens(filePath) {
|
|
20258
|
+
const projectRoot = findProjectRoot(filePath);
|
|
20259
|
+
const conn = await getConnection(projectRoot);
|
|
20260
|
+
await ensureDocumentOpen(conn, filePath);
|
|
20261
|
+
const result = await sendMessage(conn, "textDocument/semanticTokens/full", {
|
|
20262
|
+
textDocument: { uri: toUri(filePath) }
|
|
20263
|
+
});
|
|
20264
|
+
if (!result || !Array.isArray(result.data)) {
|
|
20265
|
+
return { tokens: [], count: 0 };
|
|
20266
|
+
}
|
|
20267
|
+
const legend = conn.serverCapabilities?.semanticTokensProvider?.legend || {};
|
|
20268
|
+
const tokenTypes = Array.isArray(legend.tokenTypes) ? legend.tokenTypes : [
|
|
20269
|
+
"namespace",
|
|
20270
|
+
"type",
|
|
20271
|
+
"class",
|
|
20272
|
+
"enum",
|
|
20273
|
+
"interface",
|
|
20274
|
+
"struct",
|
|
20275
|
+
"typeParameter",
|
|
20276
|
+
"parameter",
|
|
20277
|
+
"variable",
|
|
20278
|
+
"property",
|
|
20279
|
+
"enumMember",
|
|
20280
|
+
"event",
|
|
20281
|
+
"function",
|
|
20282
|
+
"method",
|
|
20283
|
+
"macro",
|
|
20284
|
+
"keyword",
|
|
20285
|
+
"modifier",
|
|
20286
|
+
"comment",
|
|
20287
|
+
"string",
|
|
20288
|
+
"number",
|
|
20289
|
+
"regexp",
|
|
20290
|
+
"operator",
|
|
20291
|
+
"decorator"
|
|
20292
|
+
];
|
|
20293
|
+
const tokenModifiers = Array.isArray(legend.tokenModifiers) ? legend.tokenModifiers : [];
|
|
20294
|
+
let line = 0;
|
|
20295
|
+
let character = 0;
|
|
20296
|
+
const decoded = [];
|
|
20297
|
+
for (let i = 0;i < result.data.length; i += 5) {
|
|
20298
|
+
const deltaLine = result.data[i];
|
|
20299
|
+
const deltaStart = result.data[i + 1];
|
|
20300
|
+
const length = result.data[i + 2];
|
|
20301
|
+
const tokenType = result.data[i + 3];
|
|
20302
|
+
const modifierBits = result.data[i + 4];
|
|
20303
|
+
line = line + deltaLine;
|
|
20304
|
+
character = deltaLine > 0 ? deltaStart : character + deltaStart;
|
|
20305
|
+
const modifiers = tokenModifiers.filter((_, idx) => (modifierBits & 1 << idx) !== 0);
|
|
20306
|
+
decoded.push({
|
|
20307
|
+
line: line + 1,
|
|
20308
|
+
column: character + 1,
|
|
20309
|
+
endLine: line + 1,
|
|
20310
|
+
endColumn: character + length + 1,
|
|
20311
|
+
token_type: tokenTypes[tokenType] || String(tokenType),
|
|
20312
|
+
token_modifiers: modifiers
|
|
20313
|
+
});
|
|
20314
|
+
}
|
|
20315
|
+
return { tokens: decoded, count: decoded.length };
|
|
20316
|
+
}
|
|
20317
|
+
async function getSelectionRanges(filePath, line, column) {
|
|
20318
|
+
const projectRoot = findProjectRoot(filePath);
|
|
20319
|
+
const conn = await getConnection(projectRoot);
|
|
20320
|
+
await ensureDocumentOpen(conn, filePath);
|
|
20321
|
+
const result = await sendMessage(conn, "textDocument/selectionRange", {
|
|
20322
|
+
textDocument: { uri: toUri(filePath) },
|
|
20323
|
+
positions: [{ line: line - 1, character: column - 1 }]
|
|
20324
|
+
});
|
|
20325
|
+
if (!Array.isArray(result) || result.length === 0)
|
|
20326
|
+
return [];
|
|
20327
|
+
const ranges = [];
|
|
20328
|
+
let current = result[0];
|
|
20329
|
+
while (current) {
|
|
20330
|
+
const itemRange = current.range;
|
|
20331
|
+
ranges.push({
|
|
20332
|
+
line: itemRange.start.line + 1,
|
|
20333
|
+
column: itemRange.start.character + 1,
|
|
20334
|
+
endLine: itemRange.end.line + 1,
|
|
20335
|
+
endColumn: itemRange.end.character + 1
|
|
20336
|
+
});
|
|
20337
|
+
current = current.parent;
|
|
20338
|
+
}
|
|
20339
|
+
return ranges;
|
|
20340
|
+
}
|
|
20341
|
+
async function getFoldingRanges(filePath) {
|
|
20342
|
+
const projectRoot = findProjectRoot(filePath);
|
|
20343
|
+
const conn = await getConnection(projectRoot);
|
|
20344
|
+
await ensureDocumentOpen(conn, filePath);
|
|
20345
|
+
const result = await sendMessage(conn, "textDocument/foldingRange", {
|
|
20346
|
+
textDocument: { uri: toUri(filePath) }
|
|
20347
|
+
});
|
|
20348
|
+
if (!Array.isArray(result))
|
|
20349
|
+
return [];
|
|
20350
|
+
return result.map((item) => ({
|
|
20351
|
+
kind: item.kind,
|
|
20352
|
+
line: (item.startLine ?? 0) + 1,
|
|
20353
|
+
column: (item.startCharacter ?? 0) + 1,
|
|
20354
|
+
endLine: (item.endLine ?? 0) + 1,
|
|
20355
|
+
endColumn: (item.endCharacter ?? 0) + 1
|
|
20356
|
+
}));
|
|
20357
|
+
}
|
|
20358
|
+
async function getDocumentLinks(filePath) {
|
|
20359
|
+
const projectRoot = findProjectRoot(filePath);
|
|
20360
|
+
const conn = await getConnection(projectRoot);
|
|
20361
|
+
await ensureDocumentOpen(conn, filePath);
|
|
20362
|
+
const result = await sendMessage(conn, "textDocument/documentLink", {
|
|
20363
|
+
textDocument: { uri: toUri(filePath) }
|
|
20364
|
+
});
|
|
20365
|
+
if (!Array.isArray(result))
|
|
20366
|
+
return [];
|
|
20367
|
+
return result.map((item) => ({
|
|
20368
|
+
target: item.target,
|
|
20369
|
+
line: item.range.start.line + 1,
|
|
20370
|
+
column: item.range.start.character + 1,
|
|
20371
|
+
endLine: item.range.end.line + 1,
|
|
20372
|
+
endColumn: item.range.end.character + 1
|
|
20373
|
+
}));
|
|
20374
|
+
}
|
|
20375
|
+
async function getCallHierarchy(filePath, line, column, direction = "both") {
|
|
20376
|
+
const projectRoot = findProjectRoot(filePath);
|
|
20377
|
+
const conn = await getConnection(projectRoot);
|
|
20378
|
+
await ensureDocumentOpen(conn, filePath);
|
|
20379
|
+
const prepared = await sendMessage(conn, "textDocument/prepareCallHierarchy", {
|
|
20380
|
+
textDocument: { uri: toUri(filePath) },
|
|
20381
|
+
position: { line: line - 1, character: column - 1 }
|
|
20382
|
+
});
|
|
20383
|
+
if (!prepared || Array.isArray(prepared) && prepared.length === 0) {
|
|
20384
|
+
return { items: [], count: 0, direction };
|
|
20385
|
+
}
|
|
20386
|
+
const baseItems = Array.isArray(prepared) ? prepared : [prepared];
|
|
20387
|
+
const includeIncoming = direction === "both" || direction === "incoming";
|
|
20388
|
+
const includeOutgoing = direction === "both" || direction === "outgoing";
|
|
20389
|
+
const items = await Promise.all(baseItems.map(async (item) => {
|
|
20390
|
+
const normalizeItem = (node) => ({
|
|
20391
|
+
name: node.name,
|
|
20392
|
+
kind: node.kind,
|
|
20393
|
+
containerName: node.containerName,
|
|
20394
|
+
file: fromUri(node.uri),
|
|
20395
|
+
line: (node.selectionRange?.start?.line ?? node.range.start.line) + 1,
|
|
20396
|
+
column: (node.selectionRange?.start?.character ?? node.range.start.character) + 1
|
|
20397
|
+
});
|
|
20398
|
+
const incoming = includeIncoming ? await sendMessage(conn, "callHierarchy/incomingCalls", { item }) || [] : [];
|
|
20399
|
+
const outgoing = includeOutgoing ? await sendMessage(conn, "callHierarchy/outgoingCalls", { item }) || [] : [];
|
|
20400
|
+
return {
|
|
20401
|
+
symbol: normalizeItem(item),
|
|
20402
|
+
incoming: incoming.map((call) => ({
|
|
20403
|
+
from: normalizeItem(call.from),
|
|
20404
|
+
call_count: Array.isArray(call.fromRanges) ? call.fromRanges.length : 0
|
|
20405
|
+
})),
|
|
20406
|
+
outgoing: outgoing.map((call) => ({
|
|
20407
|
+
to: normalizeItem(call.to),
|
|
20408
|
+
call_count: Array.isArray(call.fromRanges) ? call.fromRanges.length : 0
|
|
20409
|
+
}))
|
|
20410
|
+
};
|
|
20411
|
+
}));
|
|
20412
|
+
return { items, count: items.length, direction };
|
|
20413
|
+
}
|
|
20414
|
+
async function getCodeActions(filePath, line, column) {
|
|
20415
|
+
const projectRoot = findProjectRoot(filePath);
|
|
20416
|
+
const conn = await getConnection(projectRoot);
|
|
20417
|
+
await ensureDocumentOpen(conn, filePath);
|
|
20418
|
+
const result = await sendMessage(conn, "textDocument/codeAction", {
|
|
20419
|
+
textDocument: { uri: toUri(filePath) },
|
|
20420
|
+
range: {
|
|
20421
|
+
start: { line: line - 1, character: column - 1 },
|
|
20422
|
+
end: { line: line - 1, character: column - 1 }
|
|
20423
|
+
},
|
|
20424
|
+
context: { diagnostics: [] }
|
|
20425
|
+
});
|
|
20426
|
+
return Array.isArray(result) ? result : [];
|
|
20427
|
+
}
|
|
20428
|
+
async function executeCommand(filePath, command, args = []) {
|
|
20429
|
+
const projectRoot = findProjectRoot(filePath);
|
|
20430
|
+
const conn = await getConnection(projectRoot);
|
|
20431
|
+
return sendMessage(conn, "workspace/executeCommand", { command, arguments: args });
|
|
20432
|
+
}
|
|
20433
|
+
async function applyWorkspaceEdit(edit) {
|
|
20434
|
+
const allEdits = new Map;
|
|
20435
|
+
if (edit?.changes && typeof edit.changes === "object") {
|
|
20436
|
+
for (const [uri, edits] of Object.entries(edit.changes)) {
|
|
20437
|
+
allEdits.set(uri, Array.isArray(edits) ? edits : []);
|
|
20438
|
+
}
|
|
20439
|
+
}
|
|
20440
|
+
if (Array.isArray(edit?.documentChanges)) {
|
|
20441
|
+
for (const change of edit.documentChanges) {
|
|
20442
|
+
if (change?.textDocument?.uri && Array.isArray(change.edits)) {
|
|
20443
|
+
const uri = change.textDocument.uri;
|
|
20444
|
+
const existing = allEdits.get(uri) || [];
|
|
20445
|
+
existing.push(...change.edits);
|
|
20446
|
+
allEdits.set(uri, existing);
|
|
20447
|
+
}
|
|
20448
|
+
}
|
|
20449
|
+
}
|
|
20450
|
+
let filesChanged = 0;
|
|
20451
|
+
let editsApplied = 0;
|
|
20452
|
+
for (const [uri, edits] of allEdits) {
|
|
20453
|
+
const filePath = fromUri(uri);
|
|
20454
|
+
if (!fs.existsSync(filePath))
|
|
20455
|
+
continue;
|
|
20456
|
+
const original = getFileContent(filePath);
|
|
20457
|
+
let content = original;
|
|
20458
|
+
const sorted = [...edits].sort((a, b) => {
|
|
20459
|
+
if (a.range.start.line !== b.range.start.line)
|
|
20460
|
+
return b.range.start.line - a.range.start.line;
|
|
20461
|
+
return b.range.start.character - a.range.start.character;
|
|
20462
|
+
});
|
|
20463
|
+
for (const textEdit of sorted) {
|
|
20464
|
+
const start = textEdit.range.start;
|
|
20465
|
+
const end = textEdit.range.end;
|
|
20466
|
+
const startOffset = positionToOffset(content, start.line, start.character);
|
|
20467
|
+
const endOffset = positionToOffset(content, end.line, end.character);
|
|
20468
|
+
content = content.slice(0, startOffset) + (textEdit.newText || "") + content.slice(endOffset);
|
|
20469
|
+
editsApplied += 1;
|
|
20470
|
+
}
|
|
20471
|
+
if (content !== original) {
|
|
20472
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
20473
|
+
await updateDocument(filePath, content);
|
|
20474
|
+
filesChanged += 1;
|
|
20475
|
+
}
|
|
20476
|
+
}
|
|
20477
|
+
return { filesChanged, editsApplied };
|
|
20478
|
+
}
|
|
20141
20479
|
async function getDiagnostics(filePath) {
|
|
20142
20480
|
const projectRoot = findProjectRoot(filePath);
|
|
20143
20481
|
const absPath = path.resolve(filePath);
|
|
@@ -20687,6 +21025,18 @@ async function getDefinition3(file, line, column) {
|
|
|
20687
21025
|
}
|
|
20688
21026
|
return getDefinition2(absPath, line, column);
|
|
20689
21027
|
}
|
|
21028
|
+
async function getImplementation2(file, line, column) {
|
|
21029
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21030
|
+
if (error2 || !absPath)
|
|
21031
|
+
throw new Error(error2 || "Invalid path");
|
|
21032
|
+
return getImplementation(absPath, line, column);
|
|
21033
|
+
}
|
|
21034
|
+
async function getTypeDefinition2(file, line, column) {
|
|
21035
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21036
|
+
if (error2 || !absPath)
|
|
21037
|
+
throw new Error(error2 || "Invalid path");
|
|
21038
|
+
return getTypeDefinition(absPath, line, column);
|
|
21039
|
+
}
|
|
20690
21040
|
async function getReferences3(file, line, column) {
|
|
20691
21041
|
const { absPath, error: error2 } = resolveFilePath(file);
|
|
20692
21042
|
if (error2 || !absPath)
|
|
@@ -20697,6 +21047,12 @@ async function getReferences3(file, line, column) {
|
|
|
20697
21047
|
}
|
|
20698
21048
|
return getReferences2(absPath, line, column);
|
|
20699
21049
|
}
|
|
21050
|
+
async function getDocumentHighlights2(file, line, column) {
|
|
21051
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21052
|
+
if (error2 || !absPath)
|
|
21053
|
+
throw new Error(error2 || "Invalid path");
|
|
21054
|
+
return getDocumentHighlights(absPath, line, column);
|
|
21055
|
+
}
|
|
20700
21056
|
async function getCompletions3(file, line, column, limit = 20) {
|
|
20701
21057
|
const { absPath, error: error2 } = resolveFilePath(file);
|
|
20702
21058
|
if (error2 || !absPath)
|
|
@@ -20717,6 +21073,39 @@ async function getSignatureHelp3(file, line, column) {
|
|
|
20717
21073
|
}
|
|
20718
21074
|
return getSignatureHelp2(absPath, line, column);
|
|
20719
21075
|
}
|
|
21076
|
+
function symbolAtPosition(content, line, column) {
|
|
21077
|
+
const lines = content.split(`
|
|
21078
|
+
`);
|
|
21079
|
+
if (line < 1 || line > lines.length)
|
|
21080
|
+
return "";
|
|
21081
|
+
const text = lines[line - 1] || "";
|
|
21082
|
+
if (!text.length)
|
|
21083
|
+
return "";
|
|
21084
|
+
const idx = Math.max(0, Math.min(text.length - 1, column - 1));
|
|
21085
|
+
const re = /[A-Za-z_][A-Za-z0-9_]*/g;
|
|
21086
|
+
let match;
|
|
21087
|
+
while ((match = re.exec(text)) !== null) {
|
|
21088
|
+
if (match.index <= idx && idx < match.index + match[0].length) {
|
|
21089
|
+
return match[0];
|
|
21090
|
+
}
|
|
21091
|
+
}
|
|
21092
|
+
return "";
|
|
21093
|
+
}
|
|
21094
|
+
function packageNameFromPath(filePath) {
|
|
21095
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
21096
|
+
const marker = "/node_modules/";
|
|
21097
|
+
const idx = normalized.lastIndexOf(marker);
|
|
21098
|
+
if (idx < 0)
|
|
21099
|
+
return null;
|
|
21100
|
+
const rest = normalized.slice(idx + marker.length);
|
|
21101
|
+
const parts = rest.split("/");
|
|
21102
|
+
if (!parts.length)
|
|
21103
|
+
return null;
|
|
21104
|
+
if (parts[0].startsWith("@") && parts.length > 1) {
|
|
21105
|
+
return `${parts[0]}/${parts[1]}`;
|
|
21106
|
+
}
|
|
21107
|
+
return parts[0] || null;
|
|
21108
|
+
}
|
|
20720
21109
|
var require2 = createRequire3(import.meta.url);
|
|
20721
21110
|
var packageJson = require2("../package.json");
|
|
20722
21111
|
var server = new McpServer({
|
|
@@ -20800,6 +21189,88 @@ server.tool("definition", "Go to definition of a symbol at a specific position i
|
|
|
20800
21189
|
};
|
|
20801
21190
|
}
|
|
20802
21191
|
});
|
|
21192
|
+
server.tool("implementation", "Go to implementation of a symbol at a specific position in a Vue SFC file", {
|
|
21193
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)"),
|
|
21194
|
+
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
21195
|
+
column: exports_external.number().int().positive().describe("Column number (1-based)")
|
|
21196
|
+
}, async ({ file, line, column }) => {
|
|
21197
|
+
try {
|
|
21198
|
+
const implementations = await getImplementation2(file, line, column);
|
|
21199
|
+
if (!implementations || implementations.length === 0) {
|
|
21200
|
+
return {
|
|
21201
|
+
content: [{ type: "text", text: JSON.stringify({ error: "No implementation found" }) }]
|
|
21202
|
+
};
|
|
21203
|
+
}
|
|
21204
|
+
return {
|
|
21205
|
+
content: [{
|
|
21206
|
+
type: "text",
|
|
21207
|
+
text: JSON.stringify(implementations.length === 1 ? implementations[0] : implementations)
|
|
21208
|
+
}]
|
|
21209
|
+
};
|
|
21210
|
+
} catch (error2) {
|
|
21211
|
+
const message = String(error2);
|
|
21212
|
+
if (message.includes("Method not found") || message.includes("implementation")) {
|
|
21213
|
+
return {
|
|
21214
|
+
content: [{
|
|
21215
|
+
type: "text",
|
|
21216
|
+
text: JSON.stringify({
|
|
21217
|
+
error: "NOT_IMPLEMENTED",
|
|
21218
|
+
error_code: "NOT_IMPLEMENTED",
|
|
21219
|
+
message: "Vue backend does not expose implementation in this environment.",
|
|
21220
|
+
next_step: "Use definition/references or switch to TypeScript backend for this feature.",
|
|
21221
|
+
install_commands: [],
|
|
21222
|
+
missing_packages: [],
|
|
21223
|
+
strict_mode: true
|
|
21224
|
+
})
|
|
21225
|
+
}]
|
|
21226
|
+
};
|
|
21227
|
+
}
|
|
21228
|
+
return {
|
|
21229
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }]
|
|
21230
|
+
};
|
|
21231
|
+
}
|
|
21232
|
+
});
|
|
21233
|
+
server.tool("type_definition", "Go to type definition of a symbol at a specific position in a Vue SFC file", {
|
|
21234
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)"),
|
|
21235
|
+
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
21236
|
+
column: exports_external.number().int().positive().describe("Column number (1-based)")
|
|
21237
|
+
}, async ({ file, line, column }) => {
|
|
21238
|
+
try {
|
|
21239
|
+
const typeDefinitions = await getTypeDefinition2(file, line, column);
|
|
21240
|
+
if (!typeDefinitions || typeDefinitions.length === 0) {
|
|
21241
|
+
return {
|
|
21242
|
+
content: [{ type: "text", text: JSON.stringify({ error: "No type definition found" }) }]
|
|
21243
|
+
};
|
|
21244
|
+
}
|
|
21245
|
+
return {
|
|
21246
|
+
content: [{
|
|
21247
|
+
type: "text",
|
|
21248
|
+
text: JSON.stringify(typeDefinitions.length === 1 ? typeDefinitions[0] : typeDefinitions)
|
|
21249
|
+
}]
|
|
21250
|
+
};
|
|
21251
|
+
} catch (error2) {
|
|
21252
|
+
const message = String(error2);
|
|
21253
|
+
if (message.includes("Method not found") || message.includes("typeDefinition")) {
|
|
21254
|
+
return {
|
|
21255
|
+
content: [{
|
|
21256
|
+
type: "text",
|
|
21257
|
+
text: JSON.stringify({
|
|
21258
|
+
error: "NOT_IMPLEMENTED",
|
|
21259
|
+
error_code: "NOT_IMPLEMENTED",
|
|
21260
|
+
message: "Vue backend does not expose type_definition in this environment.",
|
|
21261
|
+
next_step: "Use hover/definition or switch to TypeScript backend for this feature.",
|
|
21262
|
+
install_commands: [],
|
|
21263
|
+
missing_packages: [],
|
|
21264
|
+
strict_mode: true
|
|
21265
|
+
})
|
|
21266
|
+
}]
|
|
21267
|
+
};
|
|
21268
|
+
}
|
|
21269
|
+
return {
|
|
21270
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }]
|
|
21271
|
+
};
|
|
21272
|
+
}
|
|
21273
|
+
});
|
|
20803
21274
|
server.tool("references", "Find all references to a symbol at a specific position in a Vue SFC file", {
|
|
20804
21275
|
file: exports_external.string().describe("Absolute path to the .vue file"),
|
|
20805
21276
|
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
@@ -20819,6 +21290,85 @@ server.tool("references", "Find all references to a symbol at a specific positio
|
|
|
20819
21290
|
};
|
|
20820
21291
|
}
|
|
20821
21292
|
});
|
|
21293
|
+
server.tool("call_hierarchy", "Get incoming/outgoing call hierarchy for symbol at a specific position in a Vue SFC file", {
|
|
21294
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)"),
|
|
21295
|
+
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
21296
|
+
column: exports_external.number().int().positive().describe("Column number (1-based)"),
|
|
21297
|
+
direction: exports_external.enum(["incoming", "outgoing", "both"]).default("both").optional().describe("Call hierarchy direction to include")
|
|
21298
|
+
}, async ({ file, line, column, direction }) => {
|
|
21299
|
+
try {
|
|
21300
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21301
|
+
if (error2 || !absPath) {
|
|
21302
|
+
return { content: [{ type: "text", text: error2 || "Invalid path" }] };
|
|
21303
|
+
}
|
|
21304
|
+
const result = await getCallHierarchy(absPath, line, column, direction || "both");
|
|
21305
|
+
if (!result.count) {
|
|
21306
|
+
return {
|
|
21307
|
+
content: [{ type: "text", text: JSON.stringify({ error: "No call hierarchy found", ...result }) }]
|
|
21308
|
+
};
|
|
21309
|
+
}
|
|
21310
|
+
return {
|
|
21311
|
+
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
21312
|
+
};
|
|
21313
|
+
} catch (error2) {
|
|
21314
|
+
const message = String(error2);
|
|
21315
|
+
if (message.includes("Method not found") || message.includes("callHierarchy")) {
|
|
21316
|
+
return {
|
|
21317
|
+
content: [{
|
|
21318
|
+
type: "text",
|
|
21319
|
+
text: JSON.stringify({
|
|
21320
|
+
error: "NOT_IMPLEMENTED",
|
|
21321
|
+
error_code: "NOT_IMPLEMENTED",
|
|
21322
|
+
message: "Vue backend does not expose call_hierarchy in this environment.",
|
|
21323
|
+
next_step: "Use references/definition or switch to TypeScript backend for call_hierarchy.",
|
|
21324
|
+
install_commands: [],
|
|
21325
|
+
missing_packages: [],
|
|
21326
|
+
strict_mode: true
|
|
21327
|
+
})
|
|
21328
|
+
}]
|
|
21329
|
+
};
|
|
21330
|
+
}
|
|
21331
|
+
return {
|
|
21332
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }]
|
|
21333
|
+
};
|
|
21334
|
+
}
|
|
21335
|
+
});
|
|
21336
|
+
server.tool("document_highlight", "Find symbol highlights in current Vue document at a specific position", {
|
|
21337
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)"),
|
|
21338
|
+
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
21339
|
+
column: exports_external.number().int().positive().describe("Column number (1-based)")
|
|
21340
|
+
}, async ({ file, line, column }) => {
|
|
21341
|
+
try {
|
|
21342
|
+
const highlights = await getDocumentHighlights2(file, line, column);
|
|
21343
|
+
return {
|
|
21344
|
+
content: [{
|
|
21345
|
+
type: "text",
|
|
21346
|
+
text: JSON.stringify({ highlights, count: highlights.length })
|
|
21347
|
+
}]
|
|
21348
|
+
};
|
|
21349
|
+
} catch (error2) {
|
|
21350
|
+
const message = String(error2);
|
|
21351
|
+
if (message.includes("Method not found") || message.includes("documentHighlight")) {
|
|
21352
|
+
return {
|
|
21353
|
+
content: [{
|
|
21354
|
+
type: "text",
|
|
21355
|
+
text: JSON.stringify({
|
|
21356
|
+
error: "NOT_IMPLEMENTED",
|
|
21357
|
+
error_code: "NOT_IMPLEMENTED",
|
|
21358
|
+
message: "Vue backend does not expose document_highlight in this environment.",
|
|
21359
|
+
next_step: "Use references as fallback for symbol occurrences.",
|
|
21360
|
+
install_commands: [],
|
|
21361
|
+
missing_packages: [],
|
|
21362
|
+
strict_mode: true
|
|
21363
|
+
})
|
|
21364
|
+
}]
|
|
21365
|
+
};
|
|
21366
|
+
}
|
|
21367
|
+
return {
|
|
21368
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }]
|
|
21369
|
+
};
|
|
21370
|
+
}
|
|
21371
|
+
});
|
|
20822
21372
|
server.tool("completions", "Get code completion suggestions at a specific position in a Vue SFC file", {
|
|
20823
21373
|
file: exports_external.string().describe("Absolute path to the .vue file"),
|
|
20824
21374
|
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
@@ -20888,6 +21438,77 @@ server.tool("inlay_hints", "Get inlay hints (type annotations, parameter names)
|
|
|
20888
21438
|
};
|
|
20889
21439
|
}
|
|
20890
21440
|
});
|
|
21441
|
+
server.tool("semantic_tokens", "Get semantic tokens for a Vue SFC file", {
|
|
21442
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)")
|
|
21443
|
+
}, async ({ file }) => {
|
|
21444
|
+
try {
|
|
21445
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21446
|
+
if (error2 || !absPath) {
|
|
21447
|
+
return { content: [{ type: "text", text: error2 || "Invalid path" }] };
|
|
21448
|
+
}
|
|
21449
|
+
const result = await getSemanticTokens(absPath);
|
|
21450
|
+
return {
|
|
21451
|
+
content: [{
|
|
21452
|
+
type: "text",
|
|
21453
|
+
text: JSON.stringify(result)
|
|
21454
|
+
}]
|
|
21455
|
+
};
|
|
21456
|
+
} catch (error2) {
|
|
21457
|
+
const message = String(error2);
|
|
21458
|
+
if (message.includes("Method not found") || message.includes("semanticTokens")) {
|
|
21459
|
+
return {
|
|
21460
|
+
content: [{
|
|
21461
|
+
type: "text",
|
|
21462
|
+
text: JSON.stringify({
|
|
21463
|
+
error: "NOT_IMPLEMENTED",
|
|
21464
|
+
error_code: "NOT_IMPLEMENTED",
|
|
21465
|
+
message: "Vue backend does not expose semantic tokens in this environment.",
|
|
21466
|
+
next_step: "Use hover/definition/references or switch to TypeScript/Python where semantic_tokens is available.",
|
|
21467
|
+
install_commands: [],
|
|
21468
|
+
missing_packages: [],
|
|
21469
|
+
strict_mode: true
|
|
21470
|
+
})
|
|
21471
|
+
}]
|
|
21472
|
+
};
|
|
21473
|
+
}
|
|
21474
|
+
return {
|
|
21475
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }]
|
|
21476
|
+
};
|
|
21477
|
+
}
|
|
21478
|
+
});
|
|
21479
|
+
server.tool("moniker", "Get moniker-like symbol identity for cross-package tracking in Vue files", {
|
|
21480
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)"),
|
|
21481
|
+
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
21482
|
+
column: exports_external.number().int().positive().describe("Column number (1-based)")
|
|
21483
|
+
}, async ({ file, line, column }) => {
|
|
21484
|
+
try {
|
|
21485
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21486
|
+
if (error2 || !absPath) {
|
|
21487
|
+
return { content: [{ type: "text", text: error2 || "Invalid path" }] };
|
|
21488
|
+
}
|
|
21489
|
+
const definitions = await getDefinition3(absPath, line, column);
|
|
21490
|
+
const sourceFile = definitions.length > 0 ? definitions[0].file : absPath;
|
|
21491
|
+
const content = getFileContent(absPath);
|
|
21492
|
+
const symbol = symbolAtPosition(content, line, column);
|
|
21493
|
+
const packageName = packageNameFromPath(sourceFile);
|
|
21494
|
+
const identifier = packageName ? `npm:${packageName}:${symbol || `${line}:${column}`}` : `workspace:${sourceFile}:${symbol || `${line}:${column}`}`;
|
|
21495
|
+
return {
|
|
21496
|
+
content: [{
|
|
21497
|
+
type: "text",
|
|
21498
|
+
text: JSON.stringify({
|
|
21499
|
+
symbol,
|
|
21500
|
+
identifier,
|
|
21501
|
+
package_name: packageName,
|
|
21502
|
+
source_file: sourceFile
|
|
21503
|
+
})
|
|
21504
|
+
}]
|
|
21505
|
+
};
|
|
21506
|
+
} catch (error2) {
|
|
21507
|
+
return {
|
|
21508
|
+
content: [{ type: "text", text: JSON.stringify({ error: String(error2) }) }]
|
|
21509
|
+
};
|
|
21510
|
+
}
|
|
21511
|
+
});
|
|
20891
21512
|
server.tool("diagnostics", "Get type errors and warnings for Vue SFC files", {
|
|
20892
21513
|
path: exports_external.string().describe("Path to a .vue file or directory to check (absolute or relative to active workspace)")
|
|
20893
21514
|
}, async ({ path: inputPath }) => {
|
|
@@ -21025,6 +21646,150 @@ server.tool("symbols", "Extract symbols (variables, functions, components) from
|
|
|
21025
21646
|
};
|
|
21026
21647
|
}
|
|
21027
21648
|
});
|
|
21649
|
+
server.tool("code_action", "Get available code actions (quick fixes/refactors) at a specific position in a Vue SFC file", {
|
|
21650
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)"),
|
|
21651
|
+
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
21652
|
+
column: exports_external.number().int().positive().describe("Column number (1-based)")
|
|
21653
|
+
}, async ({ file, line, column }) => {
|
|
21654
|
+
try {
|
|
21655
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21656
|
+
if (error2 || !absPath) {
|
|
21657
|
+
return { content: [{ type: "text", text: error2 || "Invalid path" }] };
|
|
21658
|
+
}
|
|
21659
|
+
const actions = await getCodeActions(absPath, line, column);
|
|
21660
|
+
const formatted = actions.map((action) => ({
|
|
21661
|
+
title: action.title,
|
|
21662
|
+
kind: action.kind || "quickfix",
|
|
21663
|
+
hasEdit: Boolean(action.edit),
|
|
21664
|
+
hasCommand: Boolean(action.command),
|
|
21665
|
+
data: action.data
|
|
21666
|
+
}));
|
|
21667
|
+
return {
|
|
21668
|
+
content: [{ type: "text", text: JSON.stringify({ actions: formatted, count: formatted.length }) }]
|
|
21669
|
+
};
|
|
21670
|
+
} catch (error2) {
|
|
21671
|
+
const message = String(error2);
|
|
21672
|
+
if (message.includes("Method not found") || message.includes("codeAction")) {
|
|
21673
|
+
return {
|
|
21674
|
+
content: [{
|
|
21675
|
+
type: "text",
|
|
21676
|
+
text: JSON.stringify({
|
|
21677
|
+
error: "NOT_IMPLEMENTED",
|
|
21678
|
+
error_code: "NOT_IMPLEMENTED",
|
|
21679
|
+
message: "Vue backend does not expose code_action in this environment.",
|
|
21680
|
+
next_step: "Use diagnostics + manual edits as fallback.",
|
|
21681
|
+
install_commands: [],
|
|
21682
|
+
missing_packages: [],
|
|
21683
|
+
strict_mode: true
|
|
21684
|
+
})
|
|
21685
|
+
}]
|
|
21686
|
+
};
|
|
21687
|
+
}
|
|
21688
|
+
return {
|
|
21689
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }]
|
|
21690
|
+
};
|
|
21691
|
+
}
|
|
21692
|
+
});
|
|
21693
|
+
server.tool("run_code_action", "Run a specific code action at a position in a Vue SFC file", {
|
|
21694
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)"),
|
|
21695
|
+
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
21696
|
+
column: exports_external.number().int().positive().describe("Column number (1-based)"),
|
|
21697
|
+
title: exports_external.string().describe("Code action title (exact match from code_action output)")
|
|
21698
|
+
}, async ({ file, line, column, title }) => {
|
|
21699
|
+
try {
|
|
21700
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21701
|
+
if (error2 || !absPath) {
|
|
21702
|
+
return { content: [{ type: "text", text: error2 || "Invalid path" }] };
|
|
21703
|
+
}
|
|
21704
|
+
const actions = await getCodeActions(absPath, line, column);
|
|
21705
|
+
const action = actions.find((a) => a.title === title);
|
|
21706
|
+
if (!action) {
|
|
21707
|
+
return {
|
|
21708
|
+
content: [{ type: "text", text: JSON.stringify({ error: `Action '${title}' not found` }) }]
|
|
21709
|
+
};
|
|
21710
|
+
}
|
|
21711
|
+
let editResult = null;
|
|
21712
|
+
if (action.edit) {
|
|
21713
|
+
editResult = await applyWorkspaceEdit(action.edit);
|
|
21714
|
+
}
|
|
21715
|
+
if (action.command) {
|
|
21716
|
+
const commandName = typeof action.command === "string" ? action.command : action.command.command;
|
|
21717
|
+
const args = typeof action.command === "string" ? [] : action.command.arguments || [];
|
|
21718
|
+
await executeCommand(absPath, commandName, args);
|
|
21719
|
+
}
|
|
21720
|
+
return {
|
|
21721
|
+
content: [{
|
|
21722
|
+
type: "text",
|
|
21723
|
+
text: JSON.stringify({
|
|
21724
|
+
success: true,
|
|
21725
|
+
title: action.title,
|
|
21726
|
+
kind: action.kind || "quickfix",
|
|
21727
|
+
appliedEdit: Boolean(action.edit),
|
|
21728
|
+
executedCommand: Boolean(action.command),
|
|
21729
|
+
editResult
|
|
21730
|
+
})
|
|
21731
|
+
}]
|
|
21732
|
+
};
|
|
21733
|
+
} catch (error2) {
|
|
21734
|
+
const message = String(error2);
|
|
21735
|
+
if (message.includes("Method not found") || message.includes("codeAction") || message.includes("executeCommand")) {
|
|
21736
|
+
return {
|
|
21737
|
+
content: [{
|
|
21738
|
+
type: "text",
|
|
21739
|
+
text: JSON.stringify({
|
|
21740
|
+
error: "NOT_IMPLEMENTED",
|
|
21741
|
+
error_code: "NOT_IMPLEMENTED",
|
|
21742
|
+
message: "Vue backend cannot run code actions in this environment.",
|
|
21743
|
+
next_step: "Use code_action for hints and apply edits manually.",
|
|
21744
|
+
install_commands: [],
|
|
21745
|
+
missing_packages: [],
|
|
21746
|
+
strict_mode: true
|
|
21747
|
+
})
|
|
21748
|
+
}]
|
|
21749
|
+
};
|
|
21750
|
+
}
|
|
21751
|
+
return {
|
|
21752
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }]
|
|
21753
|
+
};
|
|
21754
|
+
}
|
|
21755
|
+
});
|
|
21756
|
+
server.tool("prepare_rename", "Check whether a symbol can be safely renamed at a specific position in a Vue SFC file", {
|
|
21757
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)"),
|
|
21758
|
+
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
21759
|
+
column: exports_external.number().int().positive().describe("Column number (1-based)")
|
|
21760
|
+
}, async ({ file, line, column }) => {
|
|
21761
|
+
try {
|
|
21762
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21763
|
+
if (error2 || !absPath) {
|
|
21764
|
+
return { content: [{ type: "text", text: error2 || "Invalid path" }] };
|
|
21765
|
+
}
|
|
21766
|
+
const result = await getPrepareRename(absPath, line, column);
|
|
21767
|
+
return {
|
|
21768
|
+
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
21769
|
+
};
|
|
21770
|
+
} catch (error2) {
|
|
21771
|
+
const message = String(error2);
|
|
21772
|
+
if (message.includes("Method not found") || message.includes("prepareRename")) {
|
|
21773
|
+
return {
|
|
21774
|
+
content: [{
|
|
21775
|
+
type: "text",
|
|
21776
|
+
text: JSON.stringify({
|
|
21777
|
+
error: "NOT_IMPLEMENTED",
|
|
21778
|
+
error_code: "NOT_IMPLEMENTED",
|
|
21779
|
+
message: "Vue backend does not expose prepare_rename in this environment.",
|
|
21780
|
+
next_step: "Use rename preview and verify affected ranges manually.",
|
|
21781
|
+
install_commands: [],
|
|
21782
|
+
missing_packages: [],
|
|
21783
|
+
strict_mode: true
|
|
21784
|
+
})
|
|
21785
|
+
}]
|
|
21786
|
+
};
|
|
21787
|
+
}
|
|
21788
|
+
return {
|
|
21789
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }]
|
|
21790
|
+
};
|
|
21791
|
+
}
|
|
21792
|
+
});
|
|
21028
21793
|
server.tool("rename", "Preview renaming a symbol at a specific position (shows all locations that would be renamed)", {
|
|
21029
21794
|
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)"),
|
|
21030
21795
|
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
@@ -21071,6 +21836,153 @@ server.tool("rename", "Preview renaming a symbol at a specific position (shows a
|
|
|
21071
21836
|
};
|
|
21072
21837
|
}
|
|
21073
21838
|
});
|
|
21839
|
+
server.tool("linked_editing_range", "Get linked editing ranges at a specific position in a Vue SFC file", {
|
|
21840
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)"),
|
|
21841
|
+
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
21842
|
+
column: exports_external.number().int().positive().describe("Column number (1-based)")
|
|
21843
|
+
}, async ({ file, line, column }) => {
|
|
21844
|
+
try {
|
|
21845
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21846
|
+
if (error2 || !absPath) {
|
|
21847
|
+
return { content: [{ type: "text", text: error2 || "Invalid path" }] };
|
|
21848
|
+
}
|
|
21849
|
+
const locations = await getRenameLocations(absPath, line, column);
|
|
21850
|
+
if (!locations || locations.length === 0) {
|
|
21851
|
+
return {
|
|
21852
|
+
content: [{ type: "text", text: JSON.stringify({ ranges: [], count: 0 }) }]
|
|
21853
|
+
};
|
|
21854
|
+
}
|
|
21855
|
+
const current = path3.resolve(absPath);
|
|
21856
|
+
const ranges = locations.filter((loc) => path3.resolve(loc.file) === current).map((loc) => ({
|
|
21857
|
+
line: loc.line,
|
|
21858
|
+
column: loc.column,
|
|
21859
|
+
endLine: loc.line,
|
|
21860
|
+
endColumn: loc.column + loc.length
|
|
21861
|
+
}));
|
|
21862
|
+
return {
|
|
21863
|
+
content: [{
|
|
21864
|
+
type: "text",
|
|
21865
|
+
text: JSON.stringify({ ranges, count: ranges.length })
|
|
21866
|
+
}]
|
|
21867
|
+
};
|
|
21868
|
+
} catch (error2) {
|
|
21869
|
+
return {
|
|
21870
|
+
content: [{ type: "text", text: JSON.stringify({ error: String(error2) }) }]
|
|
21871
|
+
};
|
|
21872
|
+
}
|
|
21873
|
+
});
|
|
21874
|
+
server.tool("selection_range", "Get nested smart selection ranges at a specific position in a Vue SFC file", {
|
|
21875
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)"),
|
|
21876
|
+
line: exports_external.number().int().positive().describe("Line number (1-based)"),
|
|
21877
|
+
column: exports_external.number().int().positive().describe("Column number (1-based)")
|
|
21878
|
+
}, async ({ file, line, column }) => {
|
|
21879
|
+
try {
|
|
21880
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21881
|
+
if (error2 || !absPath) {
|
|
21882
|
+
return { content: [{ type: "text", text: error2 || "Invalid path" }] };
|
|
21883
|
+
}
|
|
21884
|
+
const ranges = await getSelectionRanges(absPath, line, column);
|
|
21885
|
+
if (!ranges.length) {
|
|
21886
|
+
return {
|
|
21887
|
+
content: [{ type: "text", text: JSON.stringify({ error: "No selection range found" }) }]
|
|
21888
|
+
};
|
|
21889
|
+
}
|
|
21890
|
+
return {
|
|
21891
|
+
content: [{ type: "text", text: JSON.stringify({ ranges, count: ranges.length }) }]
|
|
21892
|
+
};
|
|
21893
|
+
} catch (error2) {
|
|
21894
|
+
const message = String(error2);
|
|
21895
|
+
if (message.includes("Method not found") || message.includes("selectionRange")) {
|
|
21896
|
+
return {
|
|
21897
|
+
content: [{
|
|
21898
|
+
type: "text",
|
|
21899
|
+
text: JSON.stringify({
|
|
21900
|
+
error: "NOT_IMPLEMENTED",
|
|
21901
|
+
error_code: "NOT_IMPLEMENTED",
|
|
21902
|
+
message: "Vue backend does not expose selection_range in this environment.",
|
|
21903
|
+
next_step: "Use read_file_with_hints/symbols and explicit ranges as fallback.",
|
|
21904
|
+
install_commands: [],
|
|
21905
|
+
missing_packages: [],
|
|
21906
|
+
strict_mode: true
|
|
21907
|
+
})
|
|
21908
|
+
}]
|
|
21909
|
+
};
|
|
21910
|
+
}
|
|
21911
|
+
return {
|
|
21912
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }]
|
|
21913
|
+
};
|
|
21914
|
+
}
|
|
21915
|
+
});
|
|
21916
|
+
server.tool("folding_range", "Get foldable ranges in a Vue SFC file", {
|
|
21917
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)")
|
|
21918
|
+
}, async ({ file }) => {
|
|
21919
|
+
try {
|
|
21920
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21921
|
+
if (error2 || !absPath) {
|
|
21922
|
+
return { content: [{ type: "text", text: error2 || "Invalid path" }] };
|
|
21923
|
+
}
|
|
21924
|
+
const ranges = await getFoldingRanges(absPath);
|
|
21925
|
+
return {
|
|
21926
|
+
content: [{ type: "text", text: JSON.stringify({ ranges, count: ranges.length }) }]
|
|
21927
|
+
};
|
|
21928
|
+
} catch (error2) {
|
|
21929
|
+
const message = String(error2);
|
|
21930
|
+
if (message.includes("Method not found") || message.includes("foldingRange")) {
|
|
21931
|
+
return {
|
|
21932
|
+
content: [{
|
|
21933
|
+
type: "text",
|
|
21934
|
+
text: JSON.stringify({
|
|
21935
|
+
error: "NOT_IMPLEMENTED",
|
|
21936
|
+
error_code: "NOT_IMPLEMENTED",
|
|
21937
|
+
message: "Vue backend does not expose folding_range in this environment.",
|
|
21938
|
+
next_step: "Use symbols-based sections for manual folding.",
|
|
21939
|
+
install_commands: [],
|
|
21940
|
+
missing_packages: [],
|
|
21941
|
+
strict_mode: true
|
|
21942
|
+
})
|
|
21943
|
+
}]
|
|
21944
|
+
};
|
|
21945
|
+
}
|
|
21946
|
+
return {
|
|
21947
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }]
|
|
21948
|
+
};
|
|
21949
|
+
}
|
|
21950
|
+
});
|
|
21951
|
+
server.tool("document_link", "Extract links (imports/URLs) from a Vue SFC file", {
|
|
21952
|
+
file: exports_external.string().describe("Path to the .vue file (absolute or relative to active workspace)")
|
|
21953
|
+
}, async ({ file }) => {
|
|
21954
|
+
try {
|
|
21955
|
+
const { absPath, error: error2 } = resolveFilePath(file);
|
|
21956
|
+
if (error2 || !absPath) {
|
|
21957
|
+
return { content: [{ type: "text", text: error2 || "Invalid path" }] };
|
|
21958
|
+
}
|
|
21959
|
+
const links = await getDocumentLinks(absPath);
|
|
21960
|
+
return {
|
|
21961
|
+
content: [{ type: "text", text: JSON.stringify({ links, count: links.length }) }]
|
|
21962
|
+
};
|
|
21963
|
+
} catch (error2) {
|
|
21964
|
+
const message = String(error2);
|
|
21965
|
+
if (message.includes("Method not found") || message.includes("documentLink")) {
|
|
21966
|
+
return {
|
|
21967
|
+
content: [{
|
|
21968
|
+
type: "text",
|
|
21969
|
+
text: JSON.stringify({
|
|
21970
|
+
error: "NOT_IMPLEMENTED",
|
|
21971
|
+
error_code: "NOT_IMPLEMENTED",
|
|
21972
|
+
message: "Vue backend does not expose document_link in this environment.",
|
|
21973
|
+
next_step: "Use search over import/url patterns as fallback.",
|
|
21974
|
+
install_commands: [],
|
|
21975
|
+
missing_packages: [],
|
|
21976
|
+
strict_mode: true
|
|
21977
|
+
})
|
|
21978
|
+
}]
|
|
21979
|
+
};
|
|
21980
|
+
}
|
|
21981
|
+
return {
|
|
21982
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }]
|
|
21983
|
+
};
|
|
21984
|
+
}
|
|
21985
|
+
});
|
|
21074
21986
|
server.tool("search", "Search for a pattern in Vue files using ripgrep", {
|
|
21075
21987
|
pattern: exports_external.string().describe("The regex pattern to search for"),
|
|
21076
21988
|
path: exports_external.string().optional().describe("Directory or file to search in (absolute or relative to active workspace)"),
|
package/dist/vue-service.d.ts
CHANGED
|
@@ -75,6 +75,26 @@ export declare function getDefinition(filePath: string, line: number, column: nu
|
|
|
75
75
|
line: number;
|
|
76
76
|
column: number;
|
|
77
77
|
}>>;
|
|
78
|
+
/**
|
|
79
|
+
* Get implementation locations
|
|
80
|
+
*/
|
|
81
|
+
export declare function getImplementation(filePath: string, line: number, column: number): Promise<Array<{
|
|
82
|
+
file: string;
|
|
83
|
+
line: number;
|
|
84
|
+
column: number;
|
|
85
|
+
endLine: number;
|
|
86
|
+
endColumn: number;
|
|
87
|
+
}>>;
|
|
88
|
+
/**
|
|
89
|
+
* Get type definition locations
|
|
90
|
+
*/
|
|
91
|
+
export declare function getTypeDefinition(filePath: string, line: number, column: number): Promise<Array<{
|
|
92
|
+
file: string;
|
|
93
|
+
line: number;
|
|
94
|
+
column: number;
|
|
95
|
+
endLine: number;
|
|
96
|
+
endColumn: number;
|
|
97
|
+
}>>;
|
|
78
98
|
/**
|
|
79
99
|
* Get all references to a symbol
|
|
80
100
|
*/
|
|
@@ -83,6 +103,17 @@ export declare function getReferences(filePath: string, line: number, column: nu
|
|
|
83
103
|
line: number;
|
|
84
104
|
column: number;
|
|
85
105
|
}>>;
|
|
106
|
+
/**
|
|
107
|
+
* Get document highlights for a symbol
|
|
108
|
+
*/
|
|
109
|
+
export declare function getDocumentHighlights(filePath: string, line: number, column: number): Promise<Array<{
|
|
110
|
+
file: string;
|
|
111
|
+
line: number;
|
|
112
|
+
column: number;
|
|
113
|
+
endLine: number;
|
|
114
|
+
endColumn: number;
|
|
115
|
+
kind: number;
|
|
116
|
+
}>>;
|
|
86
117
|
/**
|
|
87
118
|
* Get completions at a position
|
|
88
119
|
*/
|
|
@@ -104,10 +135,82 @@ export declare function getSignatureHelp(filePath: string, line: number, column:
|
|
|
104
135
|
activeSignature: number;
|
|
105
136
|
activeParameter: number;
|
|
106
137
|
} | null>;
|
|
138
|
+
/**
|
|
139
|
+
* Check whether rename is valid at this position
|
|
140
|
+
*/
|
|
141
|
+
export declare function getPrepareRename(filePath: string, line: number, column: number): Promise<{
|
|
142
|
+
canRename: boolean;
|
|
143
|
+
placeholder?: string;
|
|
144
|
+
range?: {
|
|
145
|
+
line: number;
|
|
146
|
+
column: number;
|
|
147
|
+
endLine: number;
|
|
148
|
+
endColumn: number;
|
|
149
|
+
};
|
|
150
|
+
}>;
|
|
107
151
|
/**
|
|
108
152
|
* Get inlay hints for a file
|
|
109
153
|
*/
|
|
110
154
|
export declare function getInlayHints(filePath: string): Promise<any[]>;
|
|
155
|
+
/**
|
|
156
|
+
* Get semantic tokens for a file and decode to absolute positions.
|
|
157
|
+
*/
|
|
158
|
+
export declare function getSemanticTokens(filePath: string): Promise<{
|
|
159
|
+
tokens: any[];
|
|
160
|
+
count: number;
|
|
161
|
+
}>;
|
|
162
|
+
/**
|
|
163
|
+
* Get nested smart selection ranges for a position.
|
|
164
|
+
*/
|
|
165
|
+
export declare function getSelectionRanges(filePath: string, line: number, column: number): Promise<Array<{
|
|
166
|
+
line: number;
|
|
167
|
+
column: number;
|
|
168
|
+
endLine: number;
|
|
169
|
+
endColumn: number;
|
|
170
|
+
}>>;
|
|
171
|
+
/**
|
|
172
|
+
* Get foldable ranges in a file.
|
|
173
|
+
*/
|
|
174
|
+
export declare function getFoldingRanges(filePath: string): Promise<Array<{
|
|
175
|
+
kind?: string;
|
|
176
|
+
line: number;
|
|
177
|
+
column: number;
|
|
178
|
+
endLine: number;
|
|
179
|
+
endColumn: number;
|
|
180
|
+
}>>;
|
|
181
|
+
/**
|
|
182
|
+
* Get document links (imports/URLs).
|
|
183
|
+
*/
|
|
184
|
+
export declare function getDocumentLinks(filePath: string): Promise<Array<{
|
|
185
|
+
target?: string;
|
|
186
|
+
line: number;
|
|
187
|
+
column: number;
|
|
188
|
+
endLine: number;
|
|
189
|
+
endColumn: number;
|
|
190
|
+
}>>;
|
|
191
|
+
/**
|
|
192
|
+
* Get call hierarchy around a symbol.
|
|
193
|
+
*/
|
|
194
|
+
export declare function getCallHierarchy(filePath: string, line: number, column: number, direction?: "incoming" | "outgoing" | "both"): Promise<{
|
|
195
|
+
items: any[];
|
|
196
|
+
count: number;
|
|
197
|
+
direction: "incoming" | "outgoing" | "both";
|
|
198
|
+
}>;
|
|
199
|
+
/**
|
|
200
|
+
* Get code actions at a position.
|
|
201
|
+
*/
|
|
202
|
+
export declare function getCodeActions(filePath: string, line: number, column: number): Promise<any[]>;
|
|
203
|
+
/**
|
|
204
|
+
* Execute command from code action.
|
|
205
|
+
*/
|
|
206
|
+
export declare function executeCommand(filePath: string, command: string, args?: any[]): Promise<any>;
|
|
207
|
+
/**
|
|
208
|
+
* Apply WorkspaceEdit to disk and sync open docs.
|
|
209
|
+
*/
|
|
210
|
+
export declare function applyWorkspaceEdit(edit: any): Promise<{
|
|
211
|
+
filesChanged: number;
|
|
212
|
+
editsApplied: number;
|
|
213
|
+
}>;
|
|
111
214
|
/**
|
|
112
215
|
* Diagnostic type for internal use
|
|
113
216
|
*/
|