diff --git a/src/extension.ts b/src/extension.ts index ed40993c..4c0f486b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -5,8 +5,7 @@ import { ManimCell } from './manimCell'; import { ManimCellRanges } from './manimCellRanges'; import { previewCode } from './previewCode'; import { startScene, exitScene } from './startStopScene'; -import { loggerName } from './logger'; -import Logger from './logger'; +import { Logger, Window, loggerName } from './logger'; export function activate(context: vscode.ExtensionContext) { @@ -15,35 +14,41 @@ export function activate(context: vscode.ExtensionContext) { const previewManimCellCommand = vscode.commands.registerCommand( 'manim-notebook.previewManimCell', (cellCode?: string, startLine?: number) => { + Logger.info(`๐Ÿ’  Command requested: Preview Manim Cell, startLine=${startLine}`); previewManimCell(cellCode, startLine); }); const previewSelectionCommand = vscode.commands.registerCommand( 'manim-notebook.previewSelection', () => { + Logger.info("๐Ÿ’  Command requested: Preview Selection"); previewSelection(); } ); const startSceneCommand = vscode.commands.registerCommand( 'manim-notebook.startScene', () => { + Logger.info("๐Ÿ’  Command requested: Start Scene"); startScene(); } ); const exitSceneCommand = vscode.commands.registerCommand( 'manim-notebook.exitScene', () => { + Logger.info("๐Ÿ’  Command requested: Exit Scene"); exitScene(); } ); const clearSceneCommand = vscode.commands.registerCommand( 'manim-notebook.clearScene', () => { + Logger.info("๐Ÿ’  Command requested: Clear Scene"); clearScene(); } ); const openLogFileCommand = vscode.commands.registerCommand( 'manim-notebook.openLogFile', async () => { + Logger.info("๐Ÿ’  Command requested: Open Log File"); openLogFile(context); }); @@ -60,7 +65,9 @@ export function activate(context: vscode.ExtensionContext) { Logger.info("Manim Notebook activated"); } -export function deactivate() { } +export function deactivate() { + Logger.info("๐Ÿ’  Manim Notebook extension deactivated"); +} /** * Previews the Manim code of the cell where the cursor is placed @@ -74,7 +81,7 @@ async function previewManimCell(cellCode?: string, startLine?: number) { if (cellCode === undefined) { const editor = window.activeTextEditor; if (!editor) { - window.showErrorMessage( + Window.showErrorMessage( 'No opened file found. Place your cursor in a Manim cell.'); return; } @@ -84,7 +91,7 @@ async function previewManimCell(cellCode?: string, startLine?: number) { const cursorLine = editor.selection.active.line; const range = ManimCellRanges.getCellRangeAtLine(document, cursorLine); if (!range) { - window.showErrorMessage('Place your cursor in a Manim cell.'); + Window.showErrorMessage('Place your cursor in a Manim cell.'); return; } cellCode = document.getText(range); @@ -92,7 +99,7 @@ async function previewManimCell(cellCode?: string, startLine?: number) { } if (startLineFinal === undefined) { - window.showErrorMessage('Internal error: Line number not found in `previewManimCell()`.'); + Window.showErrorMessage('Internal error: Line number not found in `previewManimCell()`.'); return; } @@ -105,7 +112,7 @@ async function previewManimCell(cellCode?: string, startLine?: number) { async function previewSelection() { const editor = window.activeTextEditor; if (!editor) { - window.showErrorMessage('Select some code to preview.'); + Window.showErrorMessage('Select some code to preview.'); return; } @@ -125,7 +132,7 @@ async function previewSelection() { } if (!selectedText) { - window.showErrorMessage('Select some code to preview.'); + Window.showErrorMessage('Select some code to preview.'); return; } @@ -140,7 +147,7 @@ async function clearScene() { try { await ManimShell.instance.executeCommandErrorOnNoActiveSession("clear()"); } catch (NoActiveSessionError) { - window.showErrorMessage('No active Manim session found to remove objects from.'); + Window.showErrorMessage('No active Manim session found to remove objects from.'); } } @@ -185,7 +192,7 @@ function registerManimCellProviders(context: vscode.ExtensionContext) { */ function openLogFile(context: vscode.ExtensionContext) { const logFilePath = vscode.Uri.joinPath(context.logUri, `${loggerName}.log`); - vscode.window.withProgress({ + window.withProgress({ location: vscode.ProgressLocation.Notification, title: "Opening Manim Notebook log file...", cancellable: false @@ -193,9 +200,9 @@ function openLogFile(context: vscode.ExtensionContext) { await new Promise(async (resolve) => { try { const doc = await vscode.workspace.openTextDocument(logFilePath); - await vscode.window.showTextDocument(doc); + await window.showTextDocument(doc); } catch { - vscode.window.showErrorMessage("Could not open Manim Notebook log file"); + Window.showErrorMessage("Could not open Manim Notebook log file"); } finally { resolve(); } @@ -207,4 +214,4 @@ function openLogFile(context: vscode.ExtensionContext) { // https://github.com/microsoft/vscode/blob/9de080f7cbcec77de4ef3e0d27fbf9fd335d3fba/extensions/typescript-language-features/src/typescriptServiceClient.ts#L580-L586 }); }); -} \ No newline at end of file +} diff --git a/src/logger.ts b/src/logger.ts index 3c4fe72f..24f77780 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -4,7 +4,7 @@ import * as path from 'path'; export const loggerName = 'Manim Notebook'; const logger = window.createOutputChannel(loggerName, { log: true }); -export default class Logger { +export class Logger { public static trace(message: string) { logger.trace(`${Logger.getFormattedCallerInformation()} ${message}`); @@ -87,3 +87,25 @@ export default class Logger { return `[${fileName}] [${methodName}]`; } } + +/** + * Class that wraps some VSCode window methods to log the messages before + * displaying them to the user as a notification. + */ +export class Window { + + public static showInformationMessage(message: string) { + Logger.info(`๐Ÿ’ก ${message}`); + window.showInformationMessage(message); + } + + public static showWarningMessage(message: string, ...items: string[]) { + Logger.warn(`๐Ÿ’ก ${message}`); + window.showWarningMessage(message, ...items); + } + + public static showErrorMessage(message: string) { + Logger.error(`๐Ÿ’ก ${message}`); + window.showErrorMessage(message); + } +} diff --git a/src/manimCell.ts b/src/manimCell.ts index a24174e0..1949e192 100644 --- a/src/manimCell.ts +++ b/src/manimCell.ts @@ -38,11 +38,11 @@ export class ManimCell implements vscode.CodeLensProvider, vscode.FoldingRangePr } public resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken): vscode.CodeLens { - if (!vscode.window.activeTextEditor) { + if (!window.activeTextEditor) { return codeLens; } - const document = vscode.window.activeTextEditor?.document; + const document = window.activeTextEditor?.document; const range = new vscode.Range(codeLens.range.start, codeLens.range.end); const cellCode = document.getText(range); diff --git a/src/manimShell.ts b/src/manimShell.ts index db35f886..889c929a 100644 --- a/src/manimShell.ts +++ b/src/manimShell.ts @@ -3,6 +3,7 @@ import { window } from 'vscode'; import { Terminal } from 'vscode'; import { startScene, exitScene } from './startStopScene'; import { EventEmitter } from 'events'; +import { Logger, Window } from './logger'; /** * Regular expression to match ANSI control sequences. Even though we might miss @@ -228,14 +229,17 @@ export class ManimShell { startLine?: number, handler?: CommandExecutionEventHandler ) { + Logger.debug(`๐Ÿš€ Exec command: ${command}, waitUntilFinished=${waitUntilFinished}` + + `, forceExecute=${forceExecute}, errorOnNoActiveShell=${errorOnNoActiveShell}`); + if (!errorOnNoActiveShell && startLine === undefined) { // should never happen if method is called correctly - window.showErrorMessage("Start line not set. Internal extension error."); + Window.showErrorMessage("Start line not set. Internal extension error."); return; } if (this.lockDuringStartup) { - window.showWarningMessage("Manim is currently starting. Please wait a moment."); + Window.showWarningMessage("Manim is currently starting. Please wait a moment."); return; } @@ -246,7 +250,7 @@ export class ManimShell { if (this.isExecutingCommand) { // MacOS specific behavior if (this.shouldLockDuringCommandExecution && !forceExecute) { - window.showWarningMessage( + Window.showWarningMessage( `Simultaneous Manim commands are not currently supported on MacOS. ` + `Please wait for the current operations to finish before initiating ` + `a new command.`); @@ -258,6 +262,7 @@ export class ManimShell { } this.isExecutingCommand = true; + Logger.debug("๐Ÿ”’ Command execution locked"); let shell: Terminal; if (errorOnNoActiveShell) { @@ -277,11 +282,14 @@ export class ManimShell { handler?.onCommandIssued?.(); this.waitUntilCommandFinished(currentExecutionCount, () => { + Logger.debug("๐Ÿ”“ Command execution unlocked"); this.isExecutingCommand = false; this.eventEmitter.off(ManimShellEvent.DATA, dataListener); }); if (waitUntilFinished) { + Logger.debug(`๐Ÿ•’ Waiting until command has finished: ${command}`); await this.waitUntilCommandFinished(currentExecutionCount); + Logger.debug(`๐Ÿ•’ Command has finished: ${command}`); } } @@ -304,6 +312,7 @@ export class ManimShell { */ public async executeStartCommand(command: string, isRequestedForAnotherCommand: boolean) { if (!isRequestedForAnotherCommand) { + Logger.debug("๐Ÿ”† Executing start command that is requested for its own"); if (this.hasActiveShell()) { const shouldAsk = await vscode.workspace.getConfiguration("manim-notebook") .get("confirmKillingActiveSceneToStartNewOne"); @@ -312,9 +321,12 @@ export class ManimShell { return; } } + Logger.debug("๐Ÿ”† User confirmed to kill active scene"); exitScene(); } this.activeShell = window.createTerminal(); + } else { + Logger.debug("๐Ÿ”† Executing start command that is requested for another command"); } await window.withProgress({ @@ -335,6 +347,8 @@ export class ManimShell { this.shellWeTryToSpawnIn = null; }); + + Logger.debug("๐Ÿ”† Execute Start command finished"); } /** @@ -342,6 +356,7 @@ export class ManimShell { * command execution. */ public resetActiveShell() { + Logger.debug("๐Ÿ’ซ Reset active shell"); this.iPythonCellCount = 0; this.activeShell = null; this.shellWeTryToSpawnIn = null; @@ -358,7 +373,7 @@ export class ManimShell { const CANCEL_OPTION = "Cancel"; const KILL_IT_ALWAYS_OPTION = "Kill it (don't ask the next time)"; - const selection = await window.showWarningMessage( + const selection = await Window.showWarningMessage( "We need to kill your Manim session to spawn a new one.", "Kill it", KILL_IT_ALWAYS_OPTION, CANCEL_OPTION); if (selection === undefined || selection === CANCEL_OPTION) { @@ -368,7 +383,7 @@ export class ManimShell { if (selection === KILL_IT_ALWAYS_OPTION) { await vscode.workspace.getConfiguration("manim-notebook") .update("confirmKillingActiveSceneToStartNewOne", false); - window.showInformationMessage( + Window.showInformationMessage( "You can re-enable the confirmation in the settings."); } return true; @@ -403,12 +418,18 @@ export class ManimShell { */ private exec(shell: Terminal, command: string) { this.detectShellExecutionEnd = false; + Logger.debug("๐Ÿ”’ Shell execution end detection disabled"); + if (shell.shellIntegration) { + Logger.debug(`๐Ÿ’จ Sending command to terminal (with shell integration): ${command}`); shell.shellIntegration.executeCommand(command); } else { + Logger.debug(`๐Ÿ’จ Sending command to terminal (without shell integration): ${command}`); shell.sendText(command); } + this.detectShellExecutionEnd = true; + Logger.debug("๐Ÿ”“ Shell execution end detection re-enabled"); } /** @@ -422,8 +443,12 @@ export class ManimShell { */ private async retrieveOrInitActiveShell(startLine: number): Promise { if (!this.hasActiveShell()) { + Logger.debug("๐Ÿ” No active shell found, requesting startScene"); this.activeShell = window.createTerminal(); await startScene(startLine); + Logger.debug("๐Ÿ” Started new scene to retrieve new shell"); + } else { + Logger.debug("๐Ÿ” Active shell already there"); } return this.activeShell as Terminal; } @@ -447,7 +472,11 @@ export class ManimShell { this.eventEmitter.once(ManimShellEvent.KEYBOARD_INTERRUPT, resolve); const listener = () => { + Logger.debug("๐Ÿ•’ While waiting for command to finish" + + `, iPythonCellCount=${this.iPythonCellCount}` + + `, currentExecutionCount=${currentExecutionCount}`); if (this.iPythonCellCount > currentExecutionCount) { + Logger.debug("๐Ÿ•’ Command has finished"); this.eventEmitter.off(ManimShellEvent.IPYTHON_CELL_FINISHED, listener); resolve(); } @@ -455,11 +484,13 @@ export class ManimShell { this.eventEmitter.on(ManimShellEvent.IPYTHON_CELL_FINISHED, listener); }); if (callback) { + Logger.debug("๐Ÿ•’ Calling callback after command has finished"); callback(); } } private async sendKeyboardInterrupt() { + Logger.debug("๐Ÿ’จ Sending keyboard interrupt to terminal"); this.activeShell?.sendText('\x03'); // send `Ctrl+C` } @@ -480,16 +511,21 @@ export class ManimShell { async (event: vscode.TerminalShellExecutionStartEvent) => { const stream = event.execution.read(); for await (const data of withoutAnsiCodes(stream)) { + Logger.trace(`๐Ÿงพ Terminal data:\n${data}`); + this.eventEmitter.emit(ManimShellEvent.DATA, data); if (data.match(MANIM_WELCOME_REGEX)) { if (this.activeShell && this.activeShell !== event.terminal) { - exitScene(); // Manim detected in new terminal + Logger.debug("๐Ÿ‘‹ Manim detected in new terminal, exiting old scene"); + exitScene(); } + Logger.debug("๐Ÿ‘‹ Manim welcome string detected"); this.activeShell = event.terminal; } if (data.match(KEYBOARD_INTERRUPT_REGEX)) { + Logger.debug("๐Ÿ›‘ Keyboard interrupt detected"); this.eventEmitter.emit(ManimShellEvent.KEYBOARD_INTERRUPT); } @@ -497,10 +533,12 @@ export class ManimShell { if (ipythonMatch) { const cellNumber = parseInt(ipythonMatch[0].match(/\d+/)![0]); this.iPythonCellCount = cellNumber; + Logger.debug(`๐Ÿ“ฆ IPython cell ${cellNumber} detected`); this.eventEmitter.emit(ManimShellEvent.IPYTHON_CELL_FINISHED); } if (data.match(ERROR_REGEX)) { + Logger.debug("๐Ÿšจ Error in IPython cell detected"); this.activeShell?.show(); } } @@ -509,9 +547,10 @@ export class ManimShell { window.onDidEndTerminalShellExecution( async (event: vscode.TerminalShellExecutionEndEvent) => { if (this.shellWeTryToSpawnIn === event.terminal) { + Logger.debug("โŒ Tried to spawn a new Manim session, but it failed"); this.eventEmitter.emit(ManimShellEvent.MANIM_NOT_STARTED); this.resetActiveShell(); - window.showErrorMessage( + Window.showErrorMessage( "Manim session could not be started." + " Have you verified that `manimgl` is installed?"); event.terminal.show(); @@ -519,10 +558,12 @@ export class ManimShell { } if (!this.detectShellExecutionEnd) { + Logger.debug("๐Ÿ”’ Shell execution end detection disabled while end event fired"); return; } if (event.terminal === this.activeShell) { + Logger.debug("๐Ÿ”š Active shell execution ended, will reset"); this.resetActiveShell(); } }); diff --git a/src/previewCode.ts b/src/previewCode.ts index 1b244679..ed38e029 100644 --- a/src/previewCode.ts +++ b/src/previewCode.ts @@ -1,7 +1,8 @@ import * as vscode from 'vscode'; -import { ManimShell } from './manimShell'; import { window } from 'vscode'; +import { ManimShell } from './manimShell'; import { EventEmitter } from 'events'; +import { Logger } from './logger'; const PREVIEW_COMMAND = `\x0C checkpoint_paste()\x1b`; // \x0C: is Ctrl + L @@ -34,6 +35,7 @@ export async function previewCode(code: string, startLine: number): Promise { + Logger.debug(`๐Ÿ“Š Command issued: ${PREVIEW_COMMAND}. Will restore clipboard`); restoreClipboard(clipboardBuffer); progress = new PreviewProgress(); }, @@ -77,7 +79,7 @@ class PreviewProgress { private animationName: string | undefined; constructor() { - vscode.window.withProgress({ + window.withProgress({ location: vscode.ProgressLocation.Notification, title: "Previewing Manim", cancellable: false @@ -122,6 +124,8 @@ class PreviewProgress { this.animationName = newAnimName; } + Logger.debug(`๐Ÿ“Š Progress: ${this.progress} -> ${newProgress} (${progressIncrement})`); + this.eventEmitter.emit(this.REPORT_EVENT, { increment: progressIncrement, message: newAnimName @@ -132,6 +136,7 @@ class PreviewProgress { * Finishes the progress notification, i.e. closes the progress bar. */ public finish() { + Logger.debug("๐Ÿ“Š Finishing progress notification"); this.eventEmitter.emit(this.FINISH_EVENT); } diff --git a/src/startStopScene.ts b/src/startStopScene.ts index 12d0326b..9cfbf5c3 100644 --- a/src/startStopScene.ts +++ b/src/startStopScene.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode'; import { ManimShell } from './manimShell'; import { window } from 'vscode'; +import { Logger, Window } from './logger'; /** * Runs the `manimgl` command in the terminal, with the current cursor's line number: @@ -24,7 +25,7 @@ import { window } from 'vscode'; export async function startScene(lineStart?: number) { const editor = window.activeTextEditor; if (!editor) { - window.showErrorMessage( + Window.showErrorMessage( 'No opened file found. Please place your cursor at a line of code.' ); return; @@ -35,7 +36,7 @@ export async function startScene(lineStart?: number) { const languageId = editor.document.languageId; if (languageId !== 'python') { - window.showErrorMessage("You don't have a Python file open."); + Window.showErrorMessage("You don't have a Python file open."); return; } @@ -55,7 +56,7 @@ export async function startScene(lineStart?: number) { .reverse() .find(({ index }) => index <= cursorLine); if (!matchingClass) { - window.showErrorMessage('Place your cursor in Manim code inside a class.'); + Window.showErrorMessage('Place your cursor in Manim code inside a class.'); return; } // E.g. here, sceneName = "SelectedScene" @@ -110,7 +111,7 @@ export async function startScene(lineStart?: number) { export async function exitScene() { try { await ManimShell.instance.executeCommandErrorOnNoActiveSession("exit()", false, true); - } catch(NoActiveSessionError) { - window.showErrorMessage('No active Manim session found to exit.'); + } catch (NoActiveSessionError) { + Window.showErrorMessage('No active Manim session found to exit.'); } }