Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+119
View File
@@ -0,0 +1,119 @@
const vscode = require('vscode');
const path = require('node:path');
const { relations } = require('./graph.cjs');
const { uri, projectRoot } = require('./inspection.cjs');
const { openFlowPreview } = require('./dist/flow-preview.cjs');
const { description, command } = require('./hover.cjs');
function registerReading(context, inspection, lookup) {
const changed = new vscode.EventEmitter();
const previews = new Map();
context.subscriptions.push(changed, inspection.changed.event(() => {
for (const uri of previews.values()) changed.fire(uri);
}));
context.subscriptions.push(vscode.workspace.onDidCloseTextDocument(document => {
previews.delete(document.uri.toString());
}));
const provider = {
onDidChange: changed.event,
async provideTextDocumentContent(documentUri) {
previews.set(documentUri.toString(), documentUri);
const { root, name } = JSON.parse(documentUri.query);
const index = await inspection.load(root);
const symbols = index.symbols.filter(symbol => symbol.name === name);
if (!symbols.length) throw new Error(`函数已不存在:${name}`);
const lines = [`# ${name}`, '', '静态源码关系;分支可能只执行一部分,任务派发不表示同步执行。', '', index.coverage, ''];
if (index.documentation_errors.length) lines.push('**说明校验未通过。** 详见“Dever”输出。', '');
function link(site) {
const file = index.files[site.file];
const target = uri(index, site.file, root).with({ fragment: `L${site.start.line + 1},${site.start.character + 1}` });
return `[${file.path}:${site.start.line + 1}](${target.toString()})`;
}
for (const symbol of symbols) {
for (const signature of symbol.signatures) lines.push('```dever', signature, '```', '');
const detail = await description(inspection, index, symbol, root);
if (detail) lines.push(detail.markdown, '', `[主题说明](${detail.uri.toString()})`, '');
for (const site of symbol.declarations) {
await inspection.verify(index, site.file, root);
lines.push('## 定义', '', link(site), '');
}
for (const incoming of [true, false]) {
lines.push(incoming ? '## 谁调用它' : '## 它调用谁', '');
const related = relations(index, symbol.id, incoming);
if (!related.length) lines.push('当前已检查源码中没有此类关系。', '');
else lines.push('| 函数 | 领域 | 关系 | 调用位置 | 定义 / 上下游 |', '| --- | --- | --- | --- | --- |');
for (const relation of related) {
for (const call of relation.calls) {
await inspection.verify(index, call.site.file, root);
const definitions = relation.symbol.declarations.map(link).join('、') || '内建操作';
const chain = command('dever.showCalls', [root, relation.symbol.name]);
lines.push(`| ${relation.symbol.name} | ${relation.symbol.name.split('.').slice(0, 2).join('.')} | ${call.kind} | ${link(call.site)} | ${definitions} · [上下游](${chain}) |`);
}
}
lines.push('');
}
}
return lines.join('\n');
}
};
context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('dever-calls', provider));
context.subscriptions.push(vscode.commands.registerCommand('dever.showCalls', async (root, name) => {
try {
if (!root || !name) {
const editor = vscode.window.activeTextEditor;
if (!editor) return;
const state = await lookup(editor.document, editor.selection.active);
if (!state) throw new Error('请先保存源码,再查看调用关系。');
if (!state.symbols.length) throw new Error('请把光标放在函数或调用位置。');
root = state.root;
name = state.symbols[0].name;
}
const target = vscode.Uri.from({ scheme: 'dever-calls', path: '/calls.md', query: JSON.stringify({ root, name }) });
await openFlowPreview(context, target, root, {
name,
changed: inspection.changed,
read: name => provider.provideTextDocumentContent(target.with({ query: JSON.stringify({ root, name }) }))
});
} catch (error) { vscode.window.showErrorMessage(error.message); }
}));
context.subscriptions.push(vscode.commands.registerCommand('dever.previewDocumentation', async (root, name) => {
try {
const document = vscode.window.activeTextEditor?.document;
if (!root || !name) {
if (!document) return;
const state = await lookup(document, vscode.window.activeTextEditor.selection.active);
root = state?.root || await projectRoot(document);
name = state?.symbols[0]?.name;
}
const index = await inspection.load(root);
const symbol = index.symbols.find(value => value.name === name);
const detail = symbol && await description(inspection, index, symbol, root);
if (!detail) throw new Error('该符号尚未提供 doc/ 说明。');
await vscode.commands.executeCommand('markdown.showPreviewToSide', detail.uri.with({ fragment: `L${detail.line + 1}` }));
} catch (error) { vscode.window.showErrorMessage(error.message); }
}));
context.subscriptions.push(vscode.commands.registerCommand('dever.openDomainLinks', async (root, name) => {
try {
const document = vscode.window.activeTextEditor?.document;
if (!root && !document) return;
root ||= await projectRoot(document);
const index = await inspection.load(root);
const symbol = index.symbols.find(value => value.name === name);
const source = symbol?.declarations[0];
const filename = source ? index.files[source.file].path : document.uri.fsPath;
const relative = path.relative(path.join(root, 'module'), filename).split(path.sep);
if (relative.length < 3 || relative[0] === '..') {
throw new Error('请在 module/<组件>/<领域>/ 内的源码中执行此命令。');
}
const target = vscode.Uri.file(path.join(root, 'module', ...relative.slice(0, 2), 'link.md'));
await inspection.checkLinks(root);
const panel = await openFlowPreview(context, target, root);
const changed = inspection.changed.event(() => panel.dispose());
panel.onDidDispose(() => changed.dispose());
} catch (error) {
vscode.window.showErrorMessage(`${error.message} 可在项目根运行 dever inspect . --links 生成索引。`);
}
}));
}
module.exports = { registerReading };