Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,103 @@
|
||||
const vscode = require('vscode');
|
||||
const { createInspection, range, uri } = require('./inspection.cjs');
|
||||
const { at, relations } = require('./graph.cjs');
|
||||
const { registerReading } = require('./reading.cjs');
|
||||
const { registerHover } = require('./hover.cjs');
|
||||
|
||||
function registerNavigation(context) {
|
||||
const inspection = createInspection(context);
|
||||
const selector = [
|
||||
{ language: 'dever', scheme: 'file' },
|
||||
{ language: 'dever', scheme: 'dever-source' }
|
||||
];
|
||||
|
||||
async function item(index, symbol, root) {
|
||||
const first = symbol.declarations[0];
|
||||
if (!first) return undefined;
|
||||
await inspection.verify(index, first.file, root);
|
||||
const end = symbol.declarations.at(-1).end;
|
||||
const span = range({ start: first.start, end });
|
||||
const selected = new vscode.Range(span.start, span.start);
|
||||
const value = new vscode.CallHierarchyItem(vscode.SymbolKind.Function, symbol.name, symbol.kind,
|
||||
uri(index, first.file, root), span, selected);
|
||||
return value;
|
||||
}
|
||||
|
||||
async function lookup(document, position) {
|
||||
const state = await inspection.current(document);
|
||||
return state ? { ...state, symbols: at(state.index, state.file, position) } : undefined;
|
||||
}
|
||||
|
||||
context.subscriptions.push(vscode.languages.registerDefinitionProvider(selector, {
|
||||
async provideDefinition(document, position) {
|
||||
const state = await lookup(document, position);
|
||||
if (!state) return [];
|
||||
return Promise.all(state.symbols.flatMap(symbol => symbol.declarations)
|
||||
.map(site => inspection.location(state.index, site, state.root)));
|
||||
}
|
||||
}));
|
||||
context.subscriptions.push(vscode.languages.registerReferenceProvider(selector, {
|
||||
async provideReferences(document, position, options) {
|
||||
const state = await lookup(document, position);
|
||||
if (!state) return [];
|
||||
const ids = new Set(state.symbols.map(symbol => symbol.id));
|
||||
const sites = state.index.calls.filter(call => ids.has(call.callee)).map(call => call.site);
|
||||
if (options.includeDeclaration) sites.push(...state.symbols.flatMap(symbol => symbol.declarations));
|
||||
const unique = [...new Map(sites.map(site => [JSON.stringify(site), site])).values()];
|
||||
return Promise.all(unique.map(site => inspection.location(state.index, site, state.root)));
|
||||
}
|
||||
}));
|
||||
|
||||
async function hierarchy(value, incoming) {
|
||||
const document = await vscode.workspace.openTextDocument(value.uri);
|
||||
const state = await inspection.current(document);
|
||||
if (!state) throw new Error('请保存源码后重新打开调用层级。');
|
||||
const { root, index, file } = state;
|
||||
const start = value.selectionRange.start;
|
||||
const symbol = index.symbols.find(symbol => symbol.name === value.name && symbol.declarations.some(site =>
|
||||
site.file === file && site.start.line === start.line && site.start.character === start.character));
|
||||
if (!symbol) throw new Error('函数位置已变化,请重新打开调用层级。');
|
||||
await inspection.verify(index, symbol.declarations[0].file, root);
|
||||
const result = [];
|
||||
for (const relation of relations(index, symbol.id, incoming)) {
|
||||
const target = await item(index, relation.symbol, root);
|
||||
if (!target) continue;
|
||||
for (const call of relation.calls) await inspection.verify(index, call.site.file, root);
|
||||
const ranges = relation.calls.map(call => range(call.site));
|
||||
result.push(incoming ? new vscode.CallHierarchyIncomingCall(target, ranges)
|
||||
: new vscode.CallHierarchyOutgoingCall(target, ranges));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
context.subscriptions.push(vscode.languages.registerCallHierarchyProvider(selector, {
|
||||
async prepareCallHierarchy(document, position) {
|
||||
const state = await lookup(document, position);
|
||||
if (!state) return [];
|
||||
return (await Promise.all(state.symbols.map(symbol => item(state.index, symbol, state.root)))).filter(Boolean);
|
||||
},
|
||||
provideCallHierarchyIncomingCalls(value) { return hierarchy(value, true); },
|
||||
provideCallHierarchyOutgoingCalls(value) { return hierarchy(value, false); }
|
||||
}));
|
||||
context.subscriptions.push(vscode.languages.registerCodeLensProvider(selector, {
|
||||
onDidChangeCodeLenses: inspection.changed.event,
|
||||
async provideCodeLenses(document) {
|
||||
const state = await inspection.current(document);
|
||||
if (!state) return [];
|
||||
return state.index.symbols.filter(symbol => symbol.kind !== 'generated'
|
||||
&& symbol.declarations[0]?.file === state.file).map(symbol => {
|
||||
const incoming = relations(state.index, symbol.id, true);
|
||||
const count = incoming.reduce((total, relation) => total + relation.calls.length, 0);
|
||||
const start = range(symbol.declarations[0]).start;
|
||||
return new vscode.CodeLens(new vscode.Range(start, start), {
|
||||
title: `${incoming.length} 个调用方 · ${count} 处调用 · 查看调用关系`,
|
||||
command: 'dever.showCalls', arguments: [state.root, symbol.name]
|
||||
});
|
||||
});
|
||||
}
|
||||
}));
|
||||
registerReading(context, inspection, lookup);
|
||||
registerHover(context, selector, inspection, lookup);
|
||||
}
|
||||
|
||||
module.exports = { registerNavigation };
|
||||
Reference in new issue
Block a user