diff --git a/README.md b/README.md
index 1b092bf9..95bd5ccb 100644
--- a/README.md
+++ b/README.md
@@ -71,3 +71,16 @@ The resulting workflow can look like Grant's 🥳
- [contributing](https://github.com/bhoov/manim-notebook/blob/main/CONTRIBUTING.md)
- [wiki](https://github.com/bhoov/manim-notebook/wiki)
+
+
+
+## Troubleshooting
+
+If you encounter an issue, search for some related keywords first in the [issues](https://github.com/bhoov/manim-notebook/issues). If you can't find anything, feel free to open a new issue. To analyze the problem, we need a **log file** from you:
+
+- Reload the VSCode window. This is important for us such that only important log messages are included in the log file and not unrelated ones.
+- Open the command palette `Ctrl+Shift+P` (or `Cmd+Shift+P`). Use the command `Developer: Set Log Level...`, click on `Manim Notebook` and set the log level to `Trace`.
+- Now reproduce the issue, e.g. by running a command that causes the problem.
+- Open the command palette again and use the command `Manim Notebook: Open Log File`.
+- Attach the log file to your GitHub issue. To do so, right-click on the opened log file header (the tab pane that shows the filename at the top of the editor) and select `Reveal In File Explorer` (or `Reveal in Finder`). Then drag and drop the file into the GitHub issue text field.
+- Last, but not least, don't forget to set the log level back to `Info` to avoid performance issues. `Developer: Set Log Level...` -> `Manim Notebook` -> `Info`.
diff --git a/package.json b/package.json
index e25bdc45..2fc66046 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,11 @@
"command": "manim-notebook.clearScene",
"title": "Remove all objects from scene",
"category": "Manim Notebook"
+ },
+ {
+ "command": "manim-notebook.openLogFile",
+ "title": "Open Log File",
+ "category": "Manim Notebook"
}
],
"keybindings": [
diff --git a/src/extension.ts b/src/extension.ts
index 35bd6567..ed40993c 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -5,6 +5,8 @@ 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';
export function activate(context: vscode.ExtensionContext) {
@@ -40,14 +42,22 @@ export function activate(context: vscode.ExtensionContext) {
}
);
+ const openLogFileCommand = vscode.commands.registerCommand(
+ 'manim-notebook.openLogFile', async () => {
+ openLogFile(context);
+ });
+
context.subscriptions.push(
previewManimCellCommand,
previewSelectionCommand,
startSceneCommand,
exitSceneCommand,
- clearSceneCommand
+ clearSceneCommand,
+ openLogFileCommand
);
registerManimCellProviders(context);
+
+ Logger.info("Manim Notebook activated");
}
export function deactivate() { }
@@ -167,3 +177,34 @@ function registerManimCellProviders(context: vscode.ExtensionContext) {
manimCell.applyCellDecorations(window.activeTextEditor);
}
}
+
+/**
+ * Opens the Manim Notebook log file in a new editor.
+ *
+ * @param context The extension context.
+ */
+function openLogFile(context: vscode.ExtensionContext) {
+ const logFilePath = vscode.Uri.joinPath(context.logUri, `${loggerName}.log`);
+ vscode.window.withProgress({
+ location: vscode.ProgressLocation.Notification,
+ title: "Opening Manim Notebook log file...",
+ cancellable: false
+ }, async (progressIndicator, token) => {
+ await new Promise(async (resolve) => {
+ try {
+ const doc = await vscode.workspace.openTextDocument(logFilePath);
+ await vscode.window.showTextDocument(doc);
+ } catch {
+ vscode.window.showErrorMessage("Could not open Manim Notebook log file");
+ } finally {
+ resolve();
+ }
+
+ // I've also tried to open the log file in the OS browser,
+ // but didn't get it to work via:
+ // commands.executeCommand("revealFileInOS", logFilePath);
+ // For a sample usage, see this:
+ // 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
new file mode 100644
index 00000000..3c4fe72f
--- /dev/null
+++ b/src/logger.ts
@@ -0,0 +1,89 @@
+import { window } from 'vscode';
+import * as path from 'path';
+
+export const loggerName = 'Manim Notebook';
+const logger = window.createOutputChannel(loggerName, { log: true });
+
+export default class Logger {
+
+ public static trace(message: string) {
+ logger.trace(`${Logger.getFormattedCallerInformation()} ${message}`);
+ }
+
+ public static debug(message: string) {
+ logger.debug(`${Logger.getFormattedCallerInformation()} ${message}`);
+ }
+
+ public static info(message: string) {
+ logger.info(`${Logger.getFormattedCallerInformation()} ${message}`);
+ }
+
+ public static warn(message: string) {
+ logger.warn(`${Logger.getFormattedCallerInformation()} ${message}`);
+ }
+
+ public static error(message: string) {
+ logger.error(`${Logger.getFormattedCallerInformation()} ${message}`);
+ }
+
+ /**
+ * Returns formatted caller information in the form of
+ * "[filename] [methodname]".
+ *
+ * It works by creating a stack trace and extracting the file name from the
+ * third line of the stack trace, e.g.
+ *
+ * Error:
+ * at Logger.getCurrentFileName (manim-notebook/out/logger.js:32:19)
+ * at Logger.info (manim-notebook/out/logger.js:46:39)
+ * at activate (manim-notebook/out/extension.js:37:21)
+ * ...
+ *
+ * where "extension.js:37:21" is the file that called the logger method
+ * and "activate" is the respective method.
+ *
+ * Another example where the Logger is called in a Promise might be:
+ *
+ * Error:
+ * at Function.getFormattedCallerInformation (manim-notebook/src/logger.ts:46:23)
+ * at Function.info (manim-notebook/src/logger.ts:18:31)
+ * at manim-notebook/src/extension.ts:199:12
+ *
+ * where "extension.ts:199:12" is the file that called the logger method
+ * and the method is unknown.
+ */
+ private static getFormattedCallerInformation(): string {
+ const error = new Error();
+ const stack = error.stack;
+
+ const unknownString = "[unknown] [unknown]";
+
+ if (!stack) {
+ return unknownString;
+ }
+
+ const stackLines = stack.split('\n');
+ if (stackLines.length < 4) {
+ return unknownString;
+ }
+
+ const callerLine = stackLines[3];
+ if (!callerLine) {
+ return unknownString;
+ }
+
+ const fileMatch = callerLine.match(/(?:[^\(\s])*?:\d+:\d+/);
+ let fileName = 'unknown';
+ if (fileMatch && fileMatch[0]) {
+ fileName = path.basename(fileMatch[0]);
+ }
+
+ const methodMatch = callerLine.match(/at (\w+) \(/);
+ let methodName = 'unknown';
+ if (methodMatch && methodMatch[1]) {
+ methodName = methodMatch[1];
+ }
+
+ return `[${fileName}] [${methodName}]`;
+ }
+}