diff --git a/package.json b/package.json index 9f910ba..798a03a 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,21 @@ "title": "Roadmap: Open Tab" } ], + "configuration": [ + { + "type": "string", + "default": "Development Checklist.md", + "description": "Filename of the markdown checklist to use for the roadmap view.", + "scope": "workspace", + "properties": { + "roadmapChecklist.filename": { + "type": "string", + "default": "Development Checklist.md", + "description": "The relative path to the markdown file used for the roadmap checklist." + } + } + } + ], "viewsContainers": { "activitybar": [ { diff --git a/src/extension.ts b/src/extension.ts index a661d86..adaccc0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,12 +1,48 @@ import * as vscode from 'vscode'; import { RoadmapTreeProvider, RoadmapItem } from './roadmapTree'; +import * as fs from 'fs'; import * as path from 'path'; -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); +export function activate(this: any, context: vscode.ExtensionContext) { + const output = vscode.window.createOutputChannel('Roadmap Checklist'); + const config = vscode.workspace.getConfiguration('roadmapChecklist'); + const checklistFilename = config.get('filename') || 'Development Checklist.md'; + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - const roadmapProvider = new RoadmapTreeProvider(checklistFile, context); + if (workspaceFolder) { + const filePath = path.join(workspaceFolder.uri.fsPath, checklistFilename); + this.checklistPath = filePath; + + console.log(`Using checklist file: ${this.checklistPath}`); + output.appendLine(`Using checklist file: ${this.checklistPath}`); + + if (!fs.existsSync(this.checklistPath)) { + vscode.window.showWarningMessage(`Checklist file not found: ${this.checklistPath}`); + } else { + output.appendLine('Checklist file found, refreshing...'); + setTimeout(() => this.refresh(), 0); + } + } + + const configSet = `Roadmap Checklist filename set to: ${checklistFilename}`; + output.appendLine(configSet); + + const checklistPath = vscode.workspace.workspaceFolders?.[0] + ? vscode.Uri.joinPath(vscode.workspace.workspaceFolders[0].uri, checklistFilename).fsPath + : ''; + + const message = `Using checklist file: ${this.checklistPath}`; + console.log(message); + output.appendLine(message); + output.show(); // Optional, only if you want it to be visible + + // if (fs.existsSync(checklistPath)) { + // console.log('[Extension] Roadmap checklist file found:', checklistPath); + // } else { + // vscode.window.showWarningMessage(`Roadmap checklist file not found: ${checklistPath}`); + // } + + const roadmapProvider = new RoadmapTreeProvider(checklistPath, context); vscode.window.registerTreeDataProvider('roadmapChecklist', roadmapProvider); const treeView = vscode.window.createTreeView('roadmapChecklist', { treeDataProvider: roadmapProvider @@ -14,11 +50,14 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push( vscode.commands.registerCommand('roadmap.toggleCheckbox', async (item: RoadmapItem) => { - const doc = vscode.workspace.textDocuments.find(d => d.uri.fsPath === checklistFile); + const doc = vscode.workspace.textDocuments.find(d => d.uri.fsPath === checklistPath); if (!doc) { return; } const edit = new vscode.WorkspaceEdit(); - const lines = doc.getText().split('\n'); + const rawText = doc.getText(); + + // Normalize line endings and split into lines + const lines = rawText.split(/\r?\n/); const index = lines.findIndex(line => line.trim().match(/^[-*]\s+\[[ xX]\]/) && line.includes(item.label) @@ -27,11 +66,20 @@ export function activate(context: vscode.ExtensionContext) { const line = lines[index]; const toggledLine = line.replace(/\[(x| )\]/i, item.checked ? '[ ]' : '[x]'); - const range = new vscode.Range(new vscode.Position(index, 0), new vscode.Position(index, line.length)); - edit.replace(doc.uri, range, toggledLine); + lines[index] = toggledLine; + + // Ensure exactly one trailing newline + const finalText = lines.join('\n').replace(/\n+$/, '') + '\n'; + + const fullRange = new vscode.Range( + doc.positionAt(0), + doc.positionAt(rawText.length) + ); + + edit.replace(doc.uri, fullRange, finalText); await vscode.workspace.applyEdit(edit); - await doc.save(); // ✅ Triggers the existing onDidSaveTextDocument → refresh() + await doc.save(); // ✅ Triggers refresh via onDidSave }), vscode.commands.registerCommand('roadmapChecklist.reveal', async (item: RoadmapItem) => { treeView.reveal(item, { expand: true }); diff --git a/src/roadmapTree.ts b/src/roadmapTree.ts index 957476d..2a3b59a 100644 --- a/src/roadmapTree.ts +++ b/src/roadmapTree.ts @@ -1,5 +1,6 @@ // roadmapTree.ts import * as vscode from 'vscode'; +import * as fs from 'fs'; export class RoadmapItem extends vscode.TreeItem { public children: RoadmapItem[] = []; @@ -60,6 +61,8 @@ export class RoadmapItem extends vscode.TreeItem { } export class RoadmapTreeProvider implements vscode.TreeDataProvider { + private output = vscode.window.createOutputChannel('Roadmap Checklist'); + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; @@ -83,8 +86,20 @@ export class RoadmapTreeProvider implements vscode.TreeDataProvider } refresh(): void { + let content = ''; const doc = vscode.workspace.textDocuments.find(d => d.uri.fsPath === this.checklistPath); - const content = doc?.getText() || ''; + + if (doc) { + content = doc.getText(); + } else { + try { + content = fs.readFileSync(this.checklistPath, 'utf8'); + } catch (err) { + vscode.window.showErrorMessage(`Failed to read checklist file: ${err}`); + return; + } + } + const expanded = this.context.workspaceState.get('expandedPhases') || []; const { items, firstOpenPhase } = this.parseMarkdown(content, expanded); this.items = items;