Architecture decisions: files, diagram references, views and panels
- *.archify-decision files: id (unique, words), title, status, date, elements, diagrams, supersedes, and prose; parsed, merged and link-checked in the model index; diagnostics on the file - diagrams point at decisions with decisions: [...] on the diagram, nodes and edges; stripped before Archify, unknown ids reported, completion, hover and go-to-definition for the ids and for elements/diagrams/ supersedes in decision files - Decisions panel on the main.archify page; Decisions rail panel under a diagram (hover lights up the node or connection); decisions on element cards and in the element dialog; decision view for the file, with a lost-model note outside a model - Archify | New Decision… command and template; editor schemas; the three New Model examples carry decisions; docs, changelog, 0.8.0
This commit is contained in:
1 parent
33ebdd6328
commit
ebf320a023
49 files changed
+1637
-55
No files matched your search
+27
-3
@@ -2,6 +2,7 @@ import path from 'node:path';
|
||||
import * as vscode from 'vscode';
|
||||
import { log } from '../util/log';
|
||||
import {
|
||||
DecisionAttachment,
|
||||
DiagramSummary,
|
||||
MAIN_FILE,
|
||||
ModelMeta,
|
||||
@@ -17,6 +18,7 @@ import {
|
||||
summarizeDiagram,
|
||||
} from './format';
|
||||
import { parse as parseYaml } from 'yaml';
|
||||
import { DECISION_EXT, DecisionSet, isDecisionFile, mergeDecisions, parseDecision, validateDecisions } from './decisions';
|
||||
|
||||
/**
|
||||
* Finds models in the workspace and keeps what they contain. A model is the
|
||||
@@ -36,6 +38,10 @@ export interface ModelIndex {
|
||||
groups: { group: string; diagrams: DiagramSummary[] }[];
|
||||
/** Element id → diagrams (relative paths) that refer to it. */
|
||||
usage: Map<string, string[]>;
|
||||
/** The model's decisions, with link problems already checked. */
|
||||
decisions: DecisionSet;
|
||||
/** Decision id → diagrams that point at it, with where. */
|
||||
decisionUsage: Map<string, { path: string; attachedTo: DecisionAttachment }[]>;
|
||||
/** Nested main.archify files found below the root; not supported. */
|
||||
nested: string[];
|
||||
}
|
||||
@@ -71,7 +77,7 @@ export class ModelService implements vscode.Disposable {
|
||||
private readonly disposables: vscode.Disposable[];
|
||||
|
||||
constructor() {
|
||||
const watcher = vscode.workspace.createFileSystemWatcher(`**/*{.archify,${REGISTRY_EXT}}`);
|
||||
const watcher = vscode.workspace.createFileSystemWatcher(`**/*{.archify,${REGISTRY_EXT},${DECISION_EXT}}`);
|
||||
const changed = (uri: vscode.Uri) => void this.onFileChanged(uri);
|
||||
this.disposables = [
|
||||
watcher,
|
||||
@@ -80,7 +86,7 @@ export class ModelService implements vscode.Disposable {
|
||||
watcher.onDidChange(changed),
|
||||
vscode.workspace.onDidChangeTextDocument((event) => {
|
||||
// Registry edits in the editor should re-render open diagrams before a save.
|
||||
if (isRegistryFile(event.document.fileName) || isMainFile(event.document.fileName)) changed(event.document.uri);
|
||||
if (isRegistryFile(event.document.fileName) || isMainFile(event.document.fileName) || isDecisionFile(event.document.fileName)) changed(event.document.uri);
|
||||
}),
|
||||
this.changeEmitter,
|
||||
];
|
||||
@@ -118,6 +124,12 @@ export class ModelService implements vscode.Disposable {
|
||||
return found ?? undefined;
|
||||
}
|
||||
|
||||
/** The model index of the model a file belongs to, or undefined outside a model. */
|
||||
async indexFor(uri: vscode.Uri): Promise<ModelIndex | undefined> {
|
||||
const root = await this.rootOf(uri);
|
||||
return root ? this.index(root) : undefined;
|
||||
}
|
||||
|
||||
/** The registry of the model a file belongs to, or undefined outside a model. */
|
||||
async registryFor(uri: vscode.Uri): Promise<{ root: vscode.Uri; registry: Registry } | undefined> {
|
||||
const root = await this.rootOf(uri);
|
||||
@@ -175,7 +187,19 @@ export class ModelService implements vscode.Disposable {
|
||||
}
|
||||
const usage = new Map<string, string[]>();
|
||||
for (const d of diagrams) for (const id of d.used) usage.set(id, [...(usage.get(id) ?? []), d.relativePath]);
|
||||
return { root, main, meta, mainHasDiagram, registry, diagrams, groups: sortedGroups(diagrams), usage, nested };
|
||||
|
||||
const decisionFiles = await vscode.workspace.findFiles(new vscode.RelativePattern(root, `**/*${DECISION_EXT}`), SKIP);
|
||||
const decisionParts = await Promise.all(
|
||||
decisionFiles.sort((a, b) => a.fsPath.localeCompare(b.fsPath)).map(async (file) => {
|
||||
const rel = path.relative(root.fsPath, file.fsPath).split(path.sep).join('/');
|
||||
return { file: rel, ...parseDecision(await readText(file), rel) };
|
||||
}),
|
||||
);
|
||||
const decisions = mergeDecisions(decisionParts);
|
||||
decisions.problems.push(...validateDecisions(decisions, registry, new Set(diagrams.map((d) => d.relativePath))));
|
||||
const decisionUsage = new Map<string, { path: string; attachedTo: DecisionAttachment }[]>();
|
||||
for (const d of diagrams) for (const ref of d.decisions) decisionUsage.set(ref.id, [...(decisionUsage.get(ref.id) ?? []), { path: d.relativePath, attachedTo: ref.attachedTo }]);
|
||||
return { root, main, meta, mainHasDiagram, registry, diagrams, groups: sortedGroups(diagrams), usage, decisions, decisionUsage, nested };
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
Reference in new issue
Block a user