Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,84 @@
|
||||
const vscode = require('vscode');
|
||||
const fs = require('node:fs/promises');
|
||||
const path = require('node:path');
|
||||
const { randomBytes } = require('node:crypto');
|
||||
const { pathToFileURL, fileURLToPath } = require('node:url');
|
||||
const markdown = require('markdown-it')({ html: true });
|
||||
const validateLink = markdown.validateLink;
|
||||
markdown.validateLink = href => href.startsWith('file:') || validateLink(href);
|
||||
|
||||
// 流程图与调用表共用阅读面板;链接打开真实源码或当前索引。
|
||||
async function openFlowPreview(context, target, root, calls) {
|
||||
const panel = vscode.window.createWebviewPanel('dever.links', 'Dever 调用流程', vscode.ViewColumn.Beside, {
|
||||
enableScripts: true,
|
||||
localResourceRoots: [vscode.Uri.joinPath(context.extensionUri, 'dist')]
|
||||
});
|
||||
let current = target.fsPath;
|
||||
async function render(filename, anchor = '') {
|
||||
const text = calls ? await calls.read(filename) : await fs.readFile(filename, 'utf8');
|
||||
if (!calls && !text.startsWith('<!-- Generated by dever inspect --links. Do not edit. -->')) {
|
||||
throw new Error('此文件不是生成的领域索引,请运行 dever inspect . --links。');
|
||||
}
|
||||
current = filename;
|
||||
panel.title = calls ? `Dever · ${filename}` : `Dever · ${path.basename(path.dirname(filename))}`;
|
||||
const script = panel.webview.asWebviewUri(vscode.Uri.joinPath(context.extensionUri, 'dist', 'flow-browser.js'));
|
||||
const mermaid = panel.webview.asWebviewUri(vscode.Uri.joinPath(context.extensionUri, 'dist', 'mermaid.js'));
|
||||
const nonce = randomBytes(16).toString('hex');
|
||||
panel.webview.html = `<!DOCTYPE html><html><head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-${nonce}'; style-src ${panel.webview.cspSource} 'unsafe-inline'; img-src ${panel.webview.cspSource} data:; font-src ${panel.webview.cspSource} data:;">
|
||||
<style>body{max-width:1100px;margin:24px auto;padding:0 24px;font:var(--vscode-font-size) var(--vscode-font-family);color:var(--vscode-editor-foreground);background:var(--vscode-editor-background)}a{color:var(--vscode-textLink-foreground)}table{border-collapse:collapse}td,th{padding:6px 12px;border:1px solid var(--vscode-panel-border)}pre{overflow:auto}details{margin:16px 0}.dever-flow{overflow:auto;margin:20px 0}.dever-flow svg{max-width:none!important}.dever-flow-error{color:var(--vscode-errorForeground)}</style>
|
||||
</head><body data-anchor="${markdown.utils.escapeHtml(anchor)}">${markdown.render(text)}
|
||||
<script nonce="${nonce}" src="${mermaid}"></script><script nonce="${nonce}" src="${script}"></script></body></html>`;
|
||||
}
|
||||
const messages = panel.webview.onDidReceiveMessage(async message => {
|
||||
if (message.type !== 'open' || typeof message.href !== 'string') return;
|
||||
try {
|
||||
if (calls && message.href.startsWith('command:dever.showCalls?')) {
|
||||
const [requestedRoot, name] = JSON.parse(decodeURIComponent(message.href.split('?')[1]));
|
||||
if (requestedRoot !== root) throw new Error('链接超出当前项目。');
|
||||
await render(name);
|
||||
return;
|
||||
}
|
||||
if (calls && message.href.startsWith('dever-source:')) {
|
||||
const target = vscode.Uri.parse(message.href);
|
||||
if (JSON.parse(target.query).root !== root) throw new Error('链接超出当前项目。');
|
||||
const document = await vscode.workspace.openTextDocument(target);
|
||||
const location = /^#L(\d+)(?:,(\d+))?$/.exec(new URL(message.href).hash);
|
||||
const line = location ? Number(location[1]) - 1 : 0;
|
||||
const column = location?.[2] ? Number(location[2]) - 1 : 0;
|
||||
await vscode.window.showTextDocument(document, { viewColumn: vscode.ViewColumn.One, selection: new vscode.Range(line, column, line, column) });
|
||||
return;
|
||||
}
|
||||
const destination = new URL(message.href, pathToFileURL(calls ? root + path.sep : current));
|
||||
if (destination.protocol !== 'file:') throw new Error('流程阅读页只打开项目源码和领域索引。');
|
||||
const filename = fileURLToPath(destination);
|
||||
const relative = path.relative(root, filename);
|
||||
if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
||||
throw new Error('链接超出当前项目。');
|
||||
}
|
||||
if (!calls && path.basename(filename) === 'link.md') {
|
||||
await render(filename, decodeURIComponent(destination.hash.slice(1)));
|
||||
} else {
|
||||
const location = /^#L(\d+)(?:,(\d+))?$/.exec(destination.hash);
|
||||
const line = location ? Number(location[1]) - 1 : 0;
|
||||
const column = location?.[2] ? Number(location[2]) - 1 : 0;
|
||||
const document = await vscode.workspace.openTextDocument(vscode.Uri.file(filename));
|
||||
await vscode.window.showTextDocument(document, { viewColumn: vscode.ViewColumn.One, selection: new vscode.Range(line, column, line, column) });
|
||||
}
|
||||
} catch (error) { vscode.window.showErrorMessage(error.message); }
|
||||
});
|
||||
panel.onDidDispose(() => messages.dispose());
|
||||
if (calls) {
|
||||
const changed = calls.changed.event(() => render(current).catch(error => {
|
||||
panel.dispose();
|
||||
vscode.window.showErrorMessage(error.message);
|
||||
}));
|
||||
panel.onDidDispose(() => changed.dispose());
|
||||
}
|
||||
context.subscriptions.push(panel);
|
||||
try { await render(calls ? calls.name : current); } catch (error) { panel.dispose(); throw error; }
|
||||
return panel;
|
||||
}
|
||||
|
||||
module.exports = { openFlowPreview };
|
||||
Reference in new issue
Block a user