From 082b6793297d9cf485744e884f8d58195f5fc650 Mon Sep 17 00:00:00 2001 From: Stefan Wehr Date: Fri, 21 Nov 2025 14:29:21 +0100 Subject: [PATCH 1/4] logging in visualization --- .../frontend/frontend.ts | 4 +++- .../frontend/visualization_panel.ts | 18 ++++++++++++++---- src/programflow-visualization/main.ts | 2 +- src/programflow-visualization/types.ts | 2 +- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/programflow-visualization/frontend/frontend.ts b/src/programflow-visualization/frontend/frontend.ts index 08b50037..e362f852 100644 --- a/src/programflow-visualization/frontend/frontend.ts +++ b/src/programflow-visualization/frontend/frontend.ts @@ -1,4 +1,5 @@ import { ExtensionContext } from 'vscode'; +import * as vscode from 'vscode'; import { VisualizationPanel } from './visualization_panel'; import { MessagePort } from 'worker_threads'; import * as TraceCache from '../trace_cache'; @@ -7,6 +8,7 @@ let panel: VisualizationPanel | undefined = undefined; export async function startFrontend( context: ExtensionContext, + outChannel: vscode.OutputChannel, filePath: string, fileHash: string, tracePort: MessagePort | null): Promise { @@ -16,7 +18,7 @@ export async function startFrontend( } panel?.dispose(); - panel = await VisualizationPanel.getVisualizationPanel(context, filePath, fileHash, trace, tracePort); + panel = await VisualizationPanel.getVisualizationPanel(context, outChannel, filePath, fileHash, trace, tracePort); if (!panel) { return failure("Frontend couldn't be initialized!"); } diff --git a/src/programflow-visualization/frontend/visualization_panel.ts b/src/programflow-visualization/frontend/visualization_panel.ts index 4b7a7906..2d8a030d 100644 --- a/src/programflow-visualization/frontend/visualization_panel.ts +++ b/src/programflow-visualization/frontend/visualization_panel.ts @@ -18,8 +18,17 @@ export class VisualizationPanel { private _trace: FrontendTrace; private _traceIndex: number; private _tracePortSelfClose: boolean; + private _outChannel: vscode.OutputChannel; - private constructor(context: vscode.ExtensionContext, filePath: string, fileHash: string, trace: BackendTrace, tracePort: MessagePort | null) { + private constructor( + context: vscode.ExtensionContext, + outChannel: vscode.OutputChannel, + filePath: string, + fileHash: string, + trace: BackendTrace, + tracePort: MessagePort | null + ) { + this._outChannel = outChannel; this._fileHash = fileHash; this._tracePort = tracePort; this._backendTrace = { trace: trace, complete: trace.length > 0 }; @@ -74,8 +83,8 @@ export class VisualizationPanel { if (this._panel?.active) { this.updateLineHighlight(); } - }, undefined, context.subscriptions); - + }, undefined, context.subscriptions); + // Message Receivers this._panel.webview.onDidReceiveMessage( @@ -118,12 +127,13 @@ export class VisualizationPanel { public static async getVisualizationPanel( context: vscode.ExtensionContext, + outChannel: vscode.OutputChannel, filePath: string, fileHash: string, trace: BackendTrace, tracePort: MessagePort | null ): Promise { - return new VisualizationPanel(context, filePath, fileHash, trace, tracePort); + return new VisualizationPanel(context, outChannel, filePath, fileHash, trace, tracePort); } // TODO: Look if Typescript is possible OR do better documentation in all files diff --git a/src/programflow-visualization/main.ts b/src/programflow-visualization/main.ts index 87fbac1e..a56e0ab8 100644 --- a/src/programflow-visualization/main.ts +++ b/src/programflow-visualization/main.ts @@ -29,7 +29,7 @@ export function getProgFlowVizCallback(context: vscode.ExtensionContext, outChan tracePort = startBackend(context, file, outChannel); } - const result = await startFrontend(context, file.fsPath, fileHash, tracePort); + const result = await startFrontend(context, outChannel, file.fsPath, fileHash, tracePort); if (result) { await vscode.window.showErrorMessage("Error ProgramFlow-Visualization: " + result.errorMessage); return; diff --git a/src/programflow-visualization/types.ts b/src/programflow-visualization/types.ts index 7e4c4dc9..ca267080 100644 --- a/src/programflow-visualization/types.ts +++ b/src/programflow-visualization/types.ts @@ -1,4 +1,4 @@ -/** +/** * For better readable code */ type Try = Success | Failure; From dcba988767d8ad25ab1cb48c39dd978cd2b6ca34 Mon Sep 17 00:00:00 2001 From: Stefan Wehr Date: Fri, 21 Nov 2025 14:29:59 +0100 Subject: [PATCH 2/4] some questions as comments --- .../frontend/visualization_panel.ts | 41 ++++++++++++------- src/programflow-visualization/types.ts | 1 + 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/programflow-visualization/frontend/visualization_panel.ts b/src/programflow-visualization/frontend/visualization_panel.ts index 2d8a030d..2aea7519 100644 --- a/src/programflow-visualization/frontend/visualization_panel.ts +++ b/src/programflow-visualization/frontend/visualization_panel.ts @@ -196,34 +196,45 @@ export class VisualizationPanel { private async updateLineHighlight(remove: boolean = false) { if (this._trace.length === 0) { + this._outChannel.appendLine("updateLineHighlight: no trace available, aborting"); return; } + const traceFile = this._trace[this._traceIndex][3]!; let editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.filter( - editor => path.basename(editor.document.uri.path) === path.basename(this._trace[this._traceIndex][3]!) + editor => path.basename(editor.document.uri.path) === path.basename(traceFile) )[0]; - const openPath = vscode.Uri.parse(this._trace[this._traceIndex][3]!); + const openPath = vscode.Uri.parse(traceFile); if (!editor || editor.document.uri.path !== openPath.path) { + // How can it be that editor is not null/undefined, but the path does not match? await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup'); const document = await vscode.workspace.openTextDocument(openPath); editor = await vscode.window.showTextDocument(document); } - if (remove || editor.document.lineCount < this._trace[this._traceIndex][0]) { + const traceLine = this._trace[this._traceIndex][0]; + if (remove) { + this._outChannel.appendLine( + "updateLineHighlight: removing highlighting in " + editor.document.fileName); + editor.setDecorations(nextLineExecuteHighlightType, []); + } else if (editor.document.lineCount < traceLine) { + // How can it be that traceLine is out of range? + this._outChannel.appendLine( + "updateLineHighlight: removing highlighting in " + editor.document.fileName + + "(out of range)"); editor.setDecorations(nextLineExecuteHighlightType, []); } else { - this.setNextLineHighlighting(editor); - } - } - - private setNextLineHighlighting(editor: vscode.TextEditor) { - if (this._trace.length === 0) { - return; - } - const nextLine = this._trace[this._traceIndex][0] - 1; - - if (nextLine > -1) { - this.setEditorDecorations(editor, nextLineExecuteHighlightType, nextLine); + const hlLine = traceLine - 1; // why - 1 + if (hlLine > -1) { + this._outChannel.appendLine( + "updateLineHighlight: highlighting line " + hlLine + " in " + editor.document.fileName); + this.setEditorDecorations(editor, nextLineExecuteHighlightType, hlLine); + } else { + // how can this happen? + this._outChannel.appendLine( + "updateLineHighlight: cannot highlight line " + hlLine + " in " + + editor.document.fileName + "(out of range)"); + } } } diff --git a/src/programflow-visualization/types.ts b/src/programflow-visualization/types.ts index ca267080..b6f28a69 100644 --- a/src/programflow-visualization/types.ts +++ b/src/programflow-visualization/types.ts @@ -7,6 +7,7 @@ type Failure = { errorMessage: string }; // State Types for the Frontend type FrontendTrace = Array; +// FIXME: what are this array elements? Why isn't there a type with named fields? type FrontendTraceElem = [number, string, string, string, string]; // ############################################################################################ // State Types for the Backend From 44017fe18ece2906c5c03dbb90676fe9154162fc Mon Sep 17 00:00:00 2001 From: Stefan Wehr Date: Wed, 26 Nov 2025 18:25:33 +0100 Subject: [PATCH 3/4] try to fix some issues with visualization --- media/programflow-visualization/webview.js | 20 ++--- .../frontend/HTMLGenerator.ts | 8 +- .../frontend/visualization_panel.ts | 83 +++++++++++-------- src/programflow-visualization/types.ts | 11 ++- vscode-test/type-test.py | 3 +- 5 files changed, 76 insertions(+), 49 deletions(-) diff --git a/media/programflow-visualization/webview.js b/media/programflow-visualization/webview.js index 6e621e50..56f326f3 100644 --- a/media/programflow-visualization/webview.js +++ b/media/programflow-visualization/webview.js @@ -36,7 +36,7 @@ window.addEventListener("message", (event) => { /** * Updates the Visualization in the Webview, with the given BackendTraceElem. * - * @param traceElem A BackendTraceElem with 3 fields (line, stack, heap) + * @param traceElem A FrontendTraceElem */ function updateVisualization(traceElem) { const data = ` @@ -56,10 +56,10 @@ function updateVisualization(traceElem) {
- ${traceElem[1]} + ${traceElem.stackHTML}
- ${traceElem[2]} + ${traceElem.heapHTML}
`; @@ -78,17 +78,17 @@ function updateVisualization(traceElem) { } const stdoutLog = document.getElementById("stdout-log"); - stdoutLog.innerHTML = traceElem[4]; + stdoutLog.innerHTML = traceElem.outputState; stdoutLog.scrollTo(0, stdoutLog.scrollHeight); } /** * Updates the indendation for heap elements, if a other heap element references it. * - * @param traceElem A BackendTraceElem with 3 fields (line, stack, heap) + * @param traceElem A FrontendTraceElem */ function updateIntend(traceElem) { - const heapTags = traceElem[2].match(/(?<=startPointer)[0-9]+/g); + const heapTags = traceElem.heapHTML.match(/(?<=startPointer)[0-9]+/g); if (heapTags) { heapTags.forEach((tag) => { const element = document.getElementById("objectItem" + tag); @@ -117,7 +117,7 @@ function updateRefArrows(traceElem) { return new LinkerLine({ parent: document.getElementById("viz"), start: tag.elem1, - end: tag.elem2, + end: tag.elem2, size: 2, path: "magnet", startSocket: "right", @@ -139,9 +139,9 @@ function updateRefArrows(traceElem) { * @returns A list with all ids that have either a start or end pointer id in the html */ function getCurrentTags(traceElem) { - const stackTags = traceElem[1].match(/(?<=id=")(.+)Pointer[0-9]+/g); - const heapTags = traceElem[2].match(/(?<=startPointer)[0-9]+/g); - const uniqueId = traceElem[2].match(/(?<=)\d+(?=startPointer)/g); + const stackTags = traceElem.stackHTML.match(/(?<=id=")(.+)Pointer[0-9]+/g); + const heapTags = traceElem.heapHTML.match(/(?<=startPointer)[0-9]+/g); + const uniqueId = traceElem.heapHTML.match(/(?<=)\d+(?=startPointer)/g); if (!stackTags) { return; diff --git a/src/programflow-visualization/frontend/HTMLGenerator.ts b/src/programflow-visualization/frontend/HTMLGenerator.ts index 1da5e8f4..bab8730f 100644 --- a/src/programflow-visualization/frontend/HTMLGenerator.ts +++ b/src/programflow-visualization/frontend/HTMLGenerator.ts @@ -50,7 +50,13 @@ export class HTMLGenerator { if (traceElement.traceback !== undefined) { output += `${traceElement.traceback}`; } - return [traceElement.line, frameItems, objectItems, traceElement.filePath, output]; + return { + lineNumber: traceElement.line, + stackHTML: frameItems, + heapHTML: objectItems, + filename: traceElement.filePath, + outputState: output + }; } private objectItem(name: string, value: HeapValue): string { diff --git a/src/programflow-visualization/frontend/visualization_panel.ts b/src/programflow-visualization/frontend/visualization_panel.ts index 2aea7519..17a9fe8c 100644 --- a/src/programflow-visualization/frontend/visualization_panel.ts +++ b/src/programflow-visualization/frontend/visualization_panel.ts @@ -109,7 +109,7 @@ export class VisualizationPanel { this._trace.push((new HTMLGenerator()).generateHTML(backendTraceElem)); await this.postMessagesToWebview('updateButtons', 'updateContent'); if (firstElement) { - this.updateLineHighlight(); + await this.updateLineHighlight(); } }); this._tracePort?.on('close', async () => { @@ -195,45 +195,58 @@ export class VisualizationPanel { } private async updateLineHighlight(remove: boolean = false) { - if (this._trace.length === 0) { - this._outChannel.appendLine("updateLineHighlight: no trace available, aborting"); - return; - } - const traceFile = this._trace[this._traceIndex][3]!; - let editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.filter( - editor => path.basename(editor.document.uri.path) === path.basename(traceFile) - )[0]; + try { + if (this._trace.length === 0) { + this._outChannel.appendLine("updateLineHighlight: no trace available, aborting"); + return; + } + const traceFile = this._trace[this._traceIndex].filename; + this._outChannel.appendLine( + `updateLineHighlight: traceFile=${traceFile}, traceIndex=${this._traceIndex}, remove=${remove}`); - const openPath = vscode.Uri.parse(traceFile); - if (!editor || editor.document.uri.path !== openPath.path) { - // How can it be that editor is not null/undefined, but the path does not match? - await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup'); - const document = await vscode.workspace.openTextDocument(openPath); - editor = await vscode.window.showTextDocument(document); - } + // Use vscode.Uri.file() for proper file path to URI conversion + const openPath = vscode.Uri.file(traceFile); - const traceLine = this._trace[this._traceIndex][0]; - if (remove) { - this._outChannel.appendLine( - "updateLineHighlight: removing highlighting in " + editor.document.fileName); - editor.setDecorations(nextLineExecuteHighlightType, []); - } else if (editor.document.lineCount < traceLine) { - // How can it be that traceLine is out of range? - this._outChannel.appendLine( - "updateLineHighlight: removing highlighting in " + editor.document.fileName + - "(out of range)"); - editor.setDecorations(nextLineExecuteHighlightType, []); - } else { - const hlLine = traceLine - 1; // why - 1 - if (hlLine > -1) { + // Find editor by full normalized path, not just basename + let editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.find( + editor => editor.document.uri.fsPath === openPath.fsPath + ); + + if (!editor && remove) { + return; + } else if (!editor){ + this._outChannel.appendLine(`updateLineHighlight: editor not found, opening document: ${openPath.fsPath}`); + await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup'); + const document = await vscode.workspace.openTextDocument(openPath); + editor = await vscode.window.showTextDocument(document, { preserveFocus: false }); + // Give the editor time to fully initialize + await new Promise(resolve => setTimeout(resolve, 100)); + if (!editor) { + this._outChannel.appendLine(`updateLineHighlight: failed to get editor after opening document`); + return; + } + } + + const traceLine = this._trace[this._traceIndex].lineNumber; + const lineNo = traceLine - 1; // zero-based indexing in vscode + if (remove) { + this._outChannel.appendLine( + "updateLineHighlight: removing highlighting in " + editor.document.fileName); + editor.setDecorations(nextLineExecuteHighlightType, []); + } else if (lineNo < 0 || lineNo >= editor.document.lineCount) { this._outChannel.appendLine( - "updateLineHighlight: highlighting line " + hlLine + " in " + editor.document.fileName); - this.setEditorDecorations(editor, nextLineExecuteHighlightType, hlLine); + "updateLineHighlight: traceLine " + traceLine + " out of range (doc has " + + editor.document.lineCount + " lines) in " + editor.document.fileName); + editor.setDecorations(nextLineExecuteHighlightType, []); } else { - // how can this happen? this._outChannel.appendLine( - "updateLineHighlight: cannot highlight line " + hlLine + " in " + - editor.document.fileName + "(out of range)"); + "updateLineHighlight: highlighting line " + traceLine + " in " + editor.document.fileName); + this.setEditorDecorations(editor, nextLineExecuteHighlightType, lineNo); + } + } catch (error) { + this._outChannel.appendLine(`updateLineHighlight: ERROR - ${error}`); + if (error instanceof Error) { + this._outChannel.appendLine(`Stack: ${error.stack}`); } } } diff --git a/src/programflow-visualization/types.ts b/src/programflow-visualization/types.ts index b6f28a69..e2528ac4 100644 --- a/src/programflow-visualization/types.ts +++ b/src/programflow-visualization/types.ts @@ -7,8 +7,15 @@ type Failure = { errorMessage: string }; // State Types for the Frontend type FrontendTrace = Array; -// FIXME: what are this array elements? Why isn't there a type with named fields? -type FrontendTraceElem = [number, string, string, string, string]; + +type FrontendTraceElem = { + lineNumber: number, // 1-based + stackHTML: string, + heapHTML: string, + filename: string, + outputState: string, +}; + // ############################################################################################ // State Types for the Backend type PartialBackendTrace = { diff --git a/vscode-test/type-test.py b/vscode-test/type-test.py index 83800177..091a1c81 100644 --- a/vscode-test/type-test.py +++ b/vscode-test/type-test.py @@ -12,4 +12,5 @@ class Point: x: int y: int -p = Point() +p = Point(1, 2) +print(p) From 02ba3e022ea4ed12aea3b890b5be1f793bd42584 Mon Sep 17 00:00:00 2001 From: Stefan Wehr Date: Wed, 26 Nov 2025 18:35:19 +0100 Subject: [PATCH 4/4] version bump and changelog for 2.1.1 --- ChangeLog.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 1b448a98..0957de83 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,7 @@ # Write Your Python Program - CHANGELOG +* 2.1.1 (2025-11-26) + * Fix a couple of glitches with highlighting in the visualization * 2.1.0 (2025-11-07) * Fix bug with recursive types #192 * Improve startup performance and other minor improvements #190 diff --git a/package.json b/package.json index 8bfb738e..e9944d8e 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Write Your Python Program!", "description": "A user friendly python environment for beginners", "license": "See license in LICENSE", - "version": "2.1.0", + "version": "2.1.1", "publisher": "StefanWehr", "icon": "icon.png", "engines": {