feature: First working version

This commit is contained in:
Keith Solomon
2025-06-22 10:51:16 -05:00
parent be8a2649f8
commit 21a7e972f2
6 changed files with 200 additions and 21 deletions

View File

@@ -1,26 +1,17 @@
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import { RoadmapTreeProvider } from './roadmapTree';
import * as path from 'path';
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
const checklistFile = path.join(vscode.workspace.workspaceFolders?.[0].uri.fsPath || '', 'Development Checklist.md');
console.log('[Extension] Using checklist path:', checklistFile);
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "vscode-project-roadmap" is now active!');
const roadmapProvider = new RoadmapTreeProvider(checklistFile);
vscode.window.registerTreeDataProvider('roadmapChecklist', roadmapProvider);
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
const disposable = vscode.commands.registerCommand('vscode-project-roadmap.helloWorld', () => {
// The code you place here will be executed every time your command is executed
// Display a message box to the user
vscode.window.showInformationMessage('Hello World from VSCode Project Roadmap!');
});
context.subscriptions.push(disposable);
context.subscriptions.push(
vscode.commands.registerCommand('roadmapView.refresh', () => roadmapProvider.refresh())
);
}
// This method is called when your extension is deactivated
export function deactivate() {}

73
src/roadmapTree.ts Normal file
View File

@@ -0,0 +1,73 @@
import * as vscode from 'vscode';
export class RoadmapItem extends vscode.TreeItem {
constructor(
public readonly label: string,
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
public readonly checked: boolean = false,
public readonly children: RoadmapItem[] = []
) {
super(label, collapsibleState);
this.description = checked ? '✅ Done' : '';
this.iconPath = new vscode.ThemeIcon(checked ? 'check' : 'circle-large-outline');
}
}
export class RoadmapTreeProvider implements vscode.TreeDataProvider<RoadmapItem> {
private _onDidChangeTreeData: vscode.EventEmitter<RoadmapItem | undefined | void> = new vscode.EventEmitter();
readonly onDidChangeTreeData: vscode.Event<RoadmapItem | undefined | void> = this._onDidChangeTreeData.event;
private items: RoadmapItem[] = [];
constructor(private readonly checklistPath: string) {
this.refresh(); // Load initial data
vscode.workspace.onDidSaveTextDocument(doc => {
if (doc.uri.fsPath === checklistPath) {
this.refresh();
}
});
}
refresh(): void {
console.log('[Roadmap] Refresh called');
const doc = vscode.workspace.textDocuments.find(d => d.uri.fsPath === this.checklistPath);
if (!doc) {
console.warn(`[Roadmap] Document not open: ${this.checklistPath}`);
}
const content = doc?.getText() || '';
console.log('[Roadmap] Loaded content:', content.slice(0, 200)); // preview first 200 chars
this.items = this.parseMarkdown(content);
this._onDidChangeTreeData.fire();
}
getTreeItem(element: RoadmapItem): vscode.TreeItem {
return element;
}
getChildren(element?: RoadmapItem): vscode.ProviderResult<RoadmapItem[]> {
return element ? element.children : this.items;
}
private parseMarkdown(content: string): RoadmapItem[] {
const lines = content.split('\n');
const items: RoadmapItem[] = [];
let currentPhase: RoadmapItem | null = null;
for (const line of lines) {
const phaseMatch = line.match(/^##\s+(.+)/);
const taskMatch = line.match(/^[-*]\s+\[( |x)\]\s+(.+)/);
if (phaseMatch) {
currentPhase = new RoadmapItem(phaseMatch[1].trim(), vscode.TreeItemCollapsibleState.Collapsed);
items.push(currentPhase);
} else if (taskMatch && currentPhase) {
const checked = taskMatch[1] === 'x';
const task = new RoadmapItem(taskMatch[2].trim(), vscode.TreeItemCollapsibleState.None, checked);
currentPhase.children.push(task);
}
}
return items;
}
}