From 85ccce5b0a832ba480c22acf4d9949e80486a7fc Mon Sep 17 00:00:00 2001 From: aeeeeeep Date: Tue, 11 Nov 2025 22:09:58 +0800 Subject: [PATCH] fea: enhance folding and sticky scroll for deep nested logs in vs extension --- tools/vscode_extension/README.md | 1 + tools/vscode_extension/README_zh.md | 1 + tools/vscode_extension/package.json | 18 ++- tools/vscode_extension/src/extension.js | 191 ++++++++++++++++++++++-- tools/vscode_extension/tsconfig.json | 24 +++ 5 files changed, 216 insertions(+), 19 deletions(-) create mode 100644 tools/vscode_extension/tsconfig.json diff --git a/tools/vscode_extension/README.md b/tools/vscode_extension/README.md index 3f02c18..5e4dcca 100644 --- a/tools/vscode_extension/README.md +++ b/tools/vscode_extension/README.md @@ -42,6 +42,7 @@ The extension provides the following configuration options: ]) - `objwatch-log-viewer.indentRainbowErrorColor`: Color used to highlight indentation errors (default: "rgba(128,32,32,0.3)") - `objwatch-log-viewer.indentRainbowIndicatorStyle`: Style of indentation indicators (default: "classic", options: "classic", "light") +- `objwatch-log-viewer.stickyScrollMaxLineCount`: Maximum number of lines to show in sticky scroll for deep nested structures (default: 32, range: 1-128) ## Log Format diff --git a/tools/vscode_extension/README_zh.md b/tools/vscode_extension/README_zh.md index 1248ffb..0d14b81 100644 --- a/tools/vscode_extension/README_zh.md +++ b/tools/vscode_extension/README_zh.md @@ -44,6 +44,7 @@ ]) - `objwatch-log-viewer.indentRainbowErrorColor`:用于高亮缩进错误的颜色(默认值:"rgba(128,32,32,0.3)") - `objwatch-log-viewer.indentRainbowIndicatorStyle`:缩进指示器的样式(默认值:"classic",选项:"classic"、"light") +- `objwatch-log-viewer.stickyScrollMaxLineCount`:粘滞滚动中显示的最大行数,用于深度嵌套结构(默认值:32,范围:1-128) ## 日志格式 diff --git a/tools/vscode_extension/package.json b/tools/vscode_extension/package.json index 1a89200..27a3a72 100644 --- a/tools/vscode_extension/package.json +++ b/tools/vscode_extension/package.json @@ -2,9 +2,9 @@ "name": "objwatch-log-viewer", "displayName": "ObjWatch Log Viewer", "description": "Enhanced viewer for ObjWatch debugging logs with syntax highlighting and folding support", - "version": "0.1.2", + "version": "0.1.3", "engines": { - "vscode": "^1.80.0" + "vscode": "^1.87.0" }, "categories": [ "Programming Languages", @@ -47,6 +47,11 @@ "default": true, "description": "Enable code folding for ObjWatch logs" }, + "objwatch-log-viewer.highlightEventTypes": { + "type": "boolean", + "default": true, + "description": "Highlight different event types with different colors" + }, "objwatch-log-viewer.enableIndentRainbow": { "type": "boolean", "default": true, @@ -75,6 +80,13 @@ ], "default": "classic", "description": "Style of indentation indicators" + }, + "objwatch-log-viewer.stickyScrollMaxLineCount": { + "type": "number", + "default": 32, + "minimum": 1, + "maximum": 128, + "description": "Maximum number of lines to show in sticky scroll for deep nested structures" } } } @@ -93,7 +105,7 @@ }, "devDependencies": { "@types/node": "^20.18.1", - "@types/vscode": "^1.80.0", + "@types/vscode": "^1.87.0", "@vscode/vsce": "^3.7.0", "typescript": "^5.3.0", "eslint": "^8.56.0" diff --git a/tools/vscode_extension/src/extension.js b/tools/vscode_extension/src/extension.js index 1bf7491..9383559 100644 --- a/tools/vscode_extension/src/extension.js +++ b/tools/vscode_extension/src/extension.js @@ -7,15 +7,26 @@ let indentDecorationTypes = []; let indentErrorDecorationType = null; let activeEditor = null; +// Global variables for event type highlighting +let eventTypeDecorationTypes = {}; +let eventTypeHighlightEnabled = true; + /** * @param {vscode.ExtensionContext} context */ function activate(context) { console.log('ObjWatch Log Viewer extension is now active!'); + // Apply sticky scroll settings for deep nested structures + applyStickyScrollSettings(); + // Initialize indent rainbow functionality initializeIndentRainbow(); + // Initialize event type highlighting + initializeEventTypeDecorations(); + applyHighlightEventTypes(); + // Listen for editor changes activeEditor = vscode.window.activeTextEditor; if (activeEditor && activeEditor.document.languageId === 'objwatch') { @@ -26,6 +37,7 @@ function activate(context) { activeEditor = editor; if (editor && editor.document.languageId === 'objwatch') { triggerUpdateIndentDecorations(); + triggerUpdateEventTypeDecorations(); } }, null, context.subscriptions); @@ -33,14 +45,18 @@ function activate(context) { if (activeEditor && event.document === activeEditor.document && activeEditor.document.languageId === 'objwatch') { triggerUpdateIndentDecorations(); + triggerUpdateEventTypeDecorations(); } }, null, context.subscriptions); vscode.workspace.onDidChangeConfiguration(configChangeEvent => { if (configChangeEvent.affectsConfiguration('objwatch-log-viewer')) { + applyStickyScrollSettings(); initializeIndentRainbow(); + applyHighlightEventTypes(); if (activeEditor && activeEditor.document.languageId === 'objwatch') { triggerUpdateIndentDecorations(); + triggerUpdateEventTypeDecorations(); } } }, null, context.subscriptions); @@ -59,36 +75,61 @@ function activate(context) { // Skip comment lines if (text.startsWith('#')) continue; - // Get indentation level (4 spaces per level) + // Get indentation level (4 spaces per level) and process ID const fullLine = document.lineAt(line).text; // Handle both regular and multi-process log formats - const match = fullLine.match(/^(?:\[#\d+\])?(\s*)(\d+\s+)(\s*)/); + // Improved regex to correctly capture process ID and indentation + const match = fullLine.match(/^(\s*)(?:\[(#\d+)\]\s*)?(\d+\s+)(.*)$/); let indentLevel = 0; - if (match && match[3]) { - indentLevel = match[3].length / 4; + let processId = null; + if (match) { + processId = match[2]; // Extract process ID if present + // Calculate indentation level based on leading spaces (4 spaces per level) + const leadingSpaces = match[1].length; + indentLevel = Math.floor(leadingSpaces / 4); } // Check for run events (start folding) if (text.includes('run ')) { - stack.push({ line, indentLevel }); + stack.push({ line, indentLevel, processId }); } // Check for end events (end folding) else if (text.includes('end ')) { - // Find matching start event + // Find matching start event with same process ID and indentation + // Use a temporary stack to preserve non-matching items + const tempStack = []; + let foundMatch = false; + while (stack.length > 0) { const top = stack.pop(); - if (top.indentLevel < indentLevel) { - // Push back if indentation doesn't match - stack.push(top); + // Check if process IDs match (both undefined/null or same value) + const processMatch = (!top.processId && !processId) || + (top.processId === processId); + + // Check if indentation levels match exactly and process IDs match + if (top.indentLevel === indentLevel && processMatch) { + // Create folding range for matching pair + ranges.push(new vscode.FoldingRange( + top.line, + line, + vscode.FoldingRangeKind.Region + )); + foundMatch = true; break; + } else { + // Keep non-matching items in temporary stack + tempStack.push(top); } - // Create folding range - ranges.push(new vscode.FoldingRange( - top.line, - line, - vscode.FoldingRangeKind.Region - )); - break; + } + + // Restore non-matching items back to stack + while (tempStack.length > 0) { + stack.push(tempStack.pop()); + } + + // If no match found, continue to next line + if (!foundMatch) { + continue; } } } @@ -165,6 +206,36 @@ function deactivate() { console.log('ObjWatch Log Viewer extension is now deactivated!'); } +// Apply sticky scroll settings for deep nested structures +function applyStickyScrollSettings() { + const config = vscode.workspace.getConfiguration('objwatch-log-viewer'); + const maxLineCount = config.get('stickyScrollMaxLineCount', 32); + + // Update editor sticky scroll settings + vscode.workspace.getConfiguration('editor').update('stickyScroll.maxLineCount', maxLineCount, vscode.ConfigurationTarget.Global); + + console.log(`Sticky scroll max line count set to: ${maxLineCount}`); +} + +// Apply highlight event types settings +function applyHighlightEventTypes() { + const config = vscode.workspace.getConfiguration('objwatch-log-viewer'); + const highlightEventTypes = config.get('highlightEventTypes', true); + + // Update global flag + eventTypeHighlightEnabled = highlightEventTypes; + + // Clear existing event type decorations if disabled + if (!highlightEventTypes) { + clearEventTypeDecorations(); + } else if (activeEditor && activeEditor.document.languageId === 'objwatch') { + // Re-apply decorations if enabled + triggerUpdateEventTypeDecorations(); + } + + console.log(`Event type highlighting ${highlightEventTypes ? 'enabled' : 'disabled'}`); +} + // Initialize indent rainbow functionality function initializeIndentRainbow() { // Clean up previous decorations @@ -225,6 +296,94 @@ function triggerUpdateIndentDecorations() { indentUpdateTimeout = setTimeout(updateIndentDecorations, 100); } +// Clear event type decorations +function clearEventTypeDecorations() { + Object.values(eventTypeDecorationTypes).forEach(decorationType => { + if (activeEditor) { + activeEditor.setDecorations(decorationType, []); + } + decorationType.dispose(); + }); + eventTypeDecorationTypes = {}; +} + +// Initialize event type decorations +function initializeEventTypeDecorations() { + // Clear existing decorations + clearEventTypeDecorations(); + + // Define font colors for different event types + const eventTypeColors = { + 'run': '#4CAF50', // Green - Start of execution (positive, beginning) + 'end': '#9E9E9E', // Gray - End of execution (neutral, completion) + 'upd': '#2196F3', // Blue - Variable creation (informative, new) + 'apd': '#FF9800', // Orange - Add to data structure (warning, modification) + 'pop': '#F44336' // Red - Remove from data structure (danger, deletion) + }; + + // Create decoration types for each event type + Object.entries(eventTypeColors).forEach(([eventType, color]) => { + eventTypeDecorationTypes[eventType] = vscode.window.createTextEditorDecorationType({ + color: color, + fontWeight: 'bold' + }); + }); +} + +// Trigger update of event type decorations +let eventTypeUpdateTimeout = null; +function triggerUpdateEventTypeDecorations() { + if (eventTypeUpdateTimeout) { + clearTimeout(eventTypeUpdateTimeout); + } + eventTypeUpdateTimeout = setTimeout(updateEventTypeDecorations, 100); +} + +// Update event type decorations +function updateEventTypeDecorations() { + if (!activeEditor || !eventTypeHighlightEnabled || Object.keys(eventTypeDecorationTypes).length === 0) { + return; + } + + const document = activeEditor.document; + const decorators = {}; + + // Initialize decorator arrays for each event type + Object.keys(eventTypeDecorationTypes).forEach(eventType => { + decorators[eventType] = []; + }); + + // Process each line + for (let lineNum = 0; lineNum < document.lineCount; lineNum++) { + const line = document.lineAt(lineNum); + const lineText = line.text; + + // Match event types in the line - handle both regular and multi-process log formats + // Updated regex to match event types that appear after line numbers (with or without spaces) + const eventMatch = lineText.match(/^(?:\s*(?:\[#\d+\]\s*)?\d+\s*)(run|end|upd|apd|pop)\b/); + if (eventMatch) { + const eventType = eventMatch[1]; + const eventStart = lineText.indexOf(eventType); + const eventEnd = eventStart + eventType.length; + + if (decorators[eventType]) { + const startPos = new vscode.Position(lineNum, eventStart); + const endPos = new vscode.Position(lineNum, eventEnd); + decorators[eventType].push({ + range: new vscode.Range(startPos, endPos) + }); + } + } + } + + // Apply decorations + Object.entries(eventTypeDecorationTypes).forEach(([eventType, decorationType]) => { + if (activeEditor && decorators[eventType]) { + activeEditor.setDecorations(decorationType, decorators[eventType]); + } + }); +} + // Update indent decorations function updateIndentDecorations() { if (!activeEditor || !indentDecorationTypes.length) { diff --git a/tools/vscode_extension/tsconfig.json b/tools/vscode_extension/tsconfig.json new file mode 100644 index 0000000..076c17d --- /dev/null +++ b/tools/vscode_extension/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2020", + "outDir": "out", + "lib": [ + "ES2020" + ], + "sourceMap": true, + "rootDir": "src", + "strict": true, + "allowJs": true, + "checkJs": false + }, + "include": [ + "src/**/*.js", + "src/**/*.ts" + ], + "exclude": [ + "node_modules", + ".vscode-test", + "out" + ] +} \ No newline at end of file