From 190a00edc291aca706802341696ef4e8864c9ee7 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Tue, 21 Jul 2026 00:33:44 +0100 Subject: [PATCH 01/14] refactor: json parsing into it's own `parseJsonContent` util function - Refactored `readJsonFile` util function to extract the json parsing into it's own `parseJsonContent` util function. This keeps the parsing separated from the reading of the file (DOT principle). - Added new `parseJsonContent` util function, and added the function call to the `readJsonFile` util. --- src/utils.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index de2752b..3e5f2d6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -30,17 +30,31 @@ export function readJsonFile(filepath: string, return null; } - const jsonErrors: jsonc.ParseError[] = []; - // Read the contents of the JSON file. const fileContent = fs .readFileSync(filepath, {encoding: "utf8"}) .toString() .replace(/^\uFEFF/, ""); // Remove BOM if present. + return parseJsonContent(filepath, fileContent); +} + +/** + * Parse the JSON content and handle any parse errors. + * + * @template T The expected type of the parsed JSON content. + * @param {string} filepath The path of the file. + * @param {string} fileContent The content of the file. + * + * @returns {T} The parsed JSON content as the passed T type. + */ +function parseJsonContent(filepath: string, fileContent: string): T { + const jsonErrors: jsonc.ParseError[] = []; + // Parse the JSON content using jsonc-parser, allowing empty content and trailing commas. const jsonContents = jsonc.parse(fileContent, jsonErrors, {allowEmptyContent: true, allowTrailingComma: true}) ?? {}; + // If there are any parse errors, construct a detailed error message and throw an error. if (jsonErrors.length > 0) { const errorMessages = constructJsonParseErrorMsg(filepath, fileContent, jsonErrors); const errorMsg = "Failed to parse a required JSON file"; @@ -63,6 +77,7 @@ export function readJsonFile(filepath: string, throw error; } + // Otherwise, return the parsed JSON content. return jsonContents as T; } From 375b555a094c7cbc4776f630608fbabc39743676 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Tue, 21 Jul 2026 00:35:26 +0100 Subject: [PATCH 02/14] docs: add commit message generation Copilot instructions for vscode --- .../commit-message-generation.instructions.md | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 .github/instructions/commit-message-generation.instructions.md diff --git a/.github/instructions/commit-message-generation.instructions.md b/.github/instructions/commit-message-generation.instructions.md new file mode 100644 index 0000000..48aa659 --- /dev/null +++ b/.github/instructions/commit-message-generation.instructions.md @@ -0,0 +1,151 @@ +--- +description: Apply only when generating a suggested git commit message from the VS Code Source Control "Generate Commit Message" action. Do not use for staging files or running git commits. +--- + +# Commit Message Generation + +Use this instruction only to generate a commit message suggestion from the current git changes shown in Source Control. It must not stage files, create a commit, or perform any git action that changes repository state. + +## Workflow + +- Base the message on the currently staged changes when staged changes exist. +- If nothing is staged, base the message on the current unstaged working tree changes. +- Summarise the actual change, not the user's intent or ticket title. +- Only use a scope on `docs`, `chore`, `build`, and `ci` commits, and only when it meaningfully narrows the audience or area. Omit the scope on all other types. +- Return only the proposed commit message. + +## Output Quality Examples + +The following shows the same diff handled correctly and incorrectly. + +**Bad** — raw diff tokens leaked into the message: + +``` +refactor(U pouvoirs Ret coins): update config methods to use helper traits 文 আ obстоятельsspaq Tritur disposto மற்றும்... +``` + +**Good** — high-level summary inferred from file paths and clear hunks only: + +``` +refactor: extract repeated config resolution logic into helper methods +``` + +If the diff is too noisy to summarise accurately, use a safe generic fallback: + +``` +chore: update project files +``` + +## Format + +``` +[optional scope]: + +[optional body] + +[optional footer(s)] +``` + +- **Present tense, imperative mood**: "add feature" not "added feature" +- **Scope**: lowercase, in parentheses — only permitted on `docs`, `chore`, `build`, and `ci` types; omit on all others +- **Description**: under 72 characters, concise, and in sentence case; preserve original casing for code/class references in backticks + +## Commit Types + +This repository extends the standard Conventional Commits types with additional project-specific types. + +| Type | When to use | +| ----------- | ----------------------------------------------------------- | +| `feat` | New feature | +| `fix` | Bug fix | +| `docs` | Documentation and docblocks only | +| `style` | Formatting/whitespace, missing semi-colons, no logic change | +| `refactor` | Code restructuring, no behaviour change | +| `perf` | Performance improvement | +| `test` | Add or update tests | +| `build` | Build system or dependency changes | +| `chore` | Maintenance, tooling, config, version bumps | +| `ci` | CI/CD pipeline | +| `revert` | Revert a previous commit | +| `remove` | Remove code or files | +| `security` | Security-related changes | +| `deprecate` | Deprecation-related changes | + +## Custom Type Examples + +Prefer these extended types when they describe the change more accurately than `refactor`, `fix`, or `chore`. + +- Use `remove` only when code or files are actually deleted, not when they are merely moved or refactored. +- Use `security` when security risk reduction is the primary intent and outcome, not for unrelated fixes. +- Use `deprecate` only when introducing or documenting a deprecation path, not when fully removing the deprecated code. + +``` +remove(drivers): delete legacy driver compatibility shim + +security(nginx): harden fastcgi param handling for site isolation + +deprecate(config): mark `php_port` as deprecated in favour of `php81_port` +``` + +## Breaking Changes + +Use `!` after type/scope and add a `BREAKING CHANGE:` footer: + +``` +feat!: rename PHP port config key + +BREAKING CHANGE: `php_port` renamed to `php_port_override` +``` + +## Body & Footers + +Add a body when the _why_ is not obvious from the subject line. + +Body guidance: + +- Explain why the change was needed when it is not already obvious. +- Include relevant technical context only when it improves clarity. +- Use sentence case and proper punctuation. +- Use bullet points only when listing multiple distinct changes. +- Always separate each bullet point with a blank line. +- When referring to methods across multiple classes, prefix them with the class name, for example `ClassName::methodName`. + +Footer guidance: + +- Put issue references in the footer, for example `Closes #36` or `Refs #36`. + +**Bad** — wrong case, missing backticks, footer buried in body, no blank line before footer: + +``` +fix - Fixed the field variable + +fixed $field to $correctField in Config getValue method. also updated tests. closes #36 +``` + +**Good**: + +``` +fix: correct variable replacement + +Variable was missed when replacement happened, causing errors. + +- Fixed incorrect `$field` variable name to `$correctField` in `Config::getValue` method. + +- Updated tests to cover this case. + +Closes #36 +``` + +## Best Practices + +- Review the current Source Control changes before generating the message. +- Ensure the entire message is professional and clearly communicates the purpose of the commit. +- Important: Use `docs` for JSDoc additions/changes, not `refactor`. +- Use **British English** spelling for commit messages (e.g. "optimise" not "optimize", "colour", not "color") to maintain consistency with existing messages. +- Always enclose code references in backticks (e.g. `php_port`). +- Always separate each bullet point with a blank line. + +## Safety Rules + +- Never stage files, commit changes, amend commits, or push. +- Never suggest including secrets, credentials, or private keys in a commit. From 5571f65b31ffb18c8dd8b5dae26fd2de51515213 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 31 Jul 2026 01:05:02 +0100 Subject: [PATCH 03/14] feat: add new `logLevel` setting to determine the level of logs to output - Introduced `auto-comment-blocks.logLevel` setting to configure log verbosity with the options of `debug` (default), `info`, `error`, and `off` levels. Also added the new `logLevel` to the settings interface. - Added new `LogLevel` utils type to define the string union of the levels. - Changed the old unused `debugMode` property in Logger to the new `logLevel` property that accepts strings with the default set as "debug". - Changed the old unused `setDebugMode` method in Logger to the new `setLogLevel` method to set the new `logLevel` property to the desired log level. - Implemented the new `shouldLog` method with level weights to ensure the logger outputs logs at the correct level equated by the weighting system. The higher the level weight, the more verbose the logs get. Added the method call into the `info`, `debug`, and `error` methods. - Implemented dynamic log level adjustment on change of the `logLevel` setting, without extension reload. --- package.json | 18 ++++++++++++++ src/extension.ts | 12 ++++++++++ src/interfaces/settings.ts | 3 +++ src/interfaces/utils.ts | 5 ++++ src/logger.ts | 48 +++++++++++++++++++++++++++----------- 5 files changed, 73 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 3bfd8c2..cfaa6a7 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,24 @@ "type": "boolean", "default": false, "markdownDescription": "When enabled, Blade style block comments will be used in Blade contexts. Ie. `{{-- --}}` comments will be used instead of the HTML `` comments. Keybinding to enable/disable, default `ctrl + shift + m` (macOS: `cmd + shift + m`). If `blade` language ID is set in the disabledLanguages, then the HTML `` comments will be used." + }, + "auto-comment-blocks.logLevel": { + "scope": "resource", + "type": "string", + "enum": [ + "debug", + "info", + "error", + "off" + ], + "markdownEnumDescriptions": [ + "Log debug, info, and errors", + "Log info and errors", + "Log errors only", + "Disable logging" + ], + "default": "debug", + "markdownDescription": "Set the logging level. `debug` is the most verbose, and `off` disables all logging." } } }, diff --git a/src/extension.ts b/src/extension.ts index c76915f..f270022 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,11 +6,15 @@ import {Configuration} from "./configuration"; import {logger} from "./logger"; import {ExtensionData} from "./extensionData"; import {addDevEnvVariables} from "./utils"; +import {LogLevel} from "./interfaces/utils"; export function activate(context: vscode.ExtensionContext) { // Setup logger first logger.setupOutputChannel(); + const initialLogLevel = vscode.workspace.getConfiguration("auto-comment-blocks").get("logLevel", "debug"); + logger.setLogLevel(initialLogLevel); + // Only load dev environment variables when not in production if (context.extensionMode !== vscode.ExtensionMode.Production) { addDevEnvVariables(); @@ -57,6 +61,14 @@ export function activate(context: vscode.ExtensionContext) { } } + /** + * Logging Level + */ + if (event.affectsConfiguration(`${extensionName}.logLevel`)) { + const logLevel = configuration.getConfigurationValue("logLevel"); + logger.setLogLevel(logLevel); + } + // Settings that require an extension host reload when changed. const reloadRequiredSettings = [ "disabledLanguages", diff --git a/src/interfaces/settings.ts b/src/interfaces/settings.ts index da080b7..577269c 100644 --- a/src/interfaces/settings.ts +++ b/src/interfaces/settings.ts @@ -1,3 +1,5 @@ +import {LogLevel} from "./utils"; + export interface Settings { singleLineBlockOnEnter: boolean; disabledLanguages: string[]; @@ -7,4 +9,5 @@ export interface Settings { multiLineStyleBlocks: string[]; overrideDefaultLanguageMultiLineComments: Record; bladeOverrideComments: boolean; + logLevel: LogLevel; } diff --git a/src/interfaces/utils.ts b/src/interfaces/utils.ts index 404880f..103d7e9 100644 --- a/src/interfaces/utils.ts +++ b/src/interfaces/utils.ts @@ -37,3 +37,8 @@ export interface MultiLineLanguageDefinitions extends JsonObject { * Language ID */ export type LanguageId = string; + +/** + * Log level + */ +export type LogLevel = "debug" | "info" | "error" | "off"; diff --git a/src/logger.ts b/src/logger.ts index 91ef9fb..8c26d3a 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,4 +1,5 @@ import {OutputChannel, window} from "vscode"; +import {LogLevel} from "./interfaces/utils"; /** * Logger class for the Auto Comment Blocks extension. @@ -19,12 +20,11 @@ class Logger { private outputChannel: OutputChannel; /** - * Whether to log `debug` level messages or not. - * Set to `true` by default. + * Current log level. * - * @type {boolean} + * @type {LogLevel} */ - private debugMode = true; + private logLevel: LogLevel = "debug"; /*********** * Methods * @@ -44,14 +44,12 @@ class Logger { } /** - * Turn debug mode on or off. Off will disable debug messages. + * Set the log level. * - * TODO: Possibly add a toggle setting in the extension user settings. - * - * @param {boolean} debug Whether to enable or disable debug mode. + * @param {LogLevel} level Desired log level. */ - public setDebugMode(debug: boolean): void { - this.debugMode = debug; + public setLogLevel(level: LogLevel): void { + this.logLevel = level; } /** @@ -76,7 +74,9 @@ class Logger { * @param {string} message The message to be logged. */ public info(message: string): void { - this.logMessage("INFO", message); + if (this.shouldLog("info")) { + this.logMessage("INFO", message); + } } /** @@ -87,7 +87,7 @@ class Logger { * @param {unknown} data [Optional] Extra data that is useful for debugging, like an object or array. */ public debug(message: string, data?: unknown): void { - if (this.debugMode) { + if (this.shouldLog("debug")) { this.logMessage("DEBUG", message, data); } } @@ -99,7 +99,29 @@ class Logger { * @param {Error} error An Error object. */ public error(message: string, error?: Error): void { - this.logMessage("ERROR", message, error); + if (this.shouldLog("error")) { + this.logMessage("ERROR", message, error); + } + } + + /** + * Determine whether a log should be emitted for the current level. + * + * @param {LogLevel} requiredLevel The minimum level required to emit the log. + * + * @returns {boolean} Whether the log should be emitted. + */ + private shouldLog(requiredLevel: LogLevel): boolean { + // Numeric weights used for level comparison. + const levelWeight: Record = { + debug: 3, // Emits debug, info, and error logs - the most verbose level. + info: 2, // Emits info and error logs. + error: 1, // Emits error logs only. + off: 0, // Disables all logs. + }; + + // Emit when the configured level is at least as verbose as the requested level. + return levelWeight[this.logLevel] >= levelWeight[requiredLevel]; } /** From 611b268892519a9f21e586259a64b08e342b206c Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 31 Jul 2026 02:32:09 +0100 Subject: [PATCH 04/14] refactor: move logging of extension details into the `activate` function - Move the logging of extension details from the Configuration `constructor` into the extension `activate` function. - Added new `important` method to Logger to output important logs that bypasses the log level and always be logged. To be used sparingly. - Added important output to the extension `activate` function to log the id and version of the extension. --- src/configuration.ts | 4 ---- src/extension.ts | 6 ++++++ src/logger.ts | 11 +++++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 3acb96d..fdceaf1 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -81,10 +81,6 @@ export class Configuration { ***********/ public constructor() { - // Always output extension information to channel on activate. - logger.debug(`Extension details:`, this.extensionData.getAll()); - logger.debug(`Extension Discovery Paths:`, this.extensionData.getAllExtensionDiscoveryPaths()); - this.findAllLanguageConfigFilePaths(); this.setLanguageConfigDefinitions(); diff --git a/src/extension.ts b/src/extension.ts index f270022..44896a1 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -22,6 +22,12 @@ export function activate(context: vscode.ExtensionContext) { // Initialize extension data and configuration const extensionData = new ExtensionData(null, true); + + // Always output extension information to channel on activate. + logger.important(`Activating ${extensionData.get("id")} v${extensionData.get("version")}`); + logger.debug(`Extension details:`, extensionData.getAll()); + logger.debug(`Extension Discovery Paths:`, extensionData.getAllExtensionDiscoveryPaths()); + const configuration = new Configuration(); const extensionName = extensionData.get("namespace"); const extensionDisplayName = extensionData.get("displayName"); diff --git a/src/logger.ts b/src/logger.ts index 8c26d3a..cf14cb4 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -104,6 +104,17 @@ class Logger { } } + /** + * Send an important message to the output channel. + * This is a special log level that is always emitted regardless of the log level, + * and should be used sparingly. + * + * @param {string} message The message to be logged. + */ + public important(message: string): void { + this.logMessage("IMPORTANT", message); + } + /** * Determine whether a log should be emitted for the current level. * From 36b03d0bf9b8aa21943fb43b765a9307ee49e7ba Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 31 Jul 2026 02:36:06 +0100 Subject: [PATCH 05/14] fix: on document open info log to include the language ID. - Fixed the info log in the `onDidOpenTextDocument` event to include the language ID, so we can see clearly in the output what language ID triggered the event. --- src/extension.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 44896a1..ab1aeda 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -103,8 +103,8 @@ export function activate(context: vscode.ExtensionContext) { * * Called when active editor language is changed, so re-configure the comment blocks. */ - const documentOpenDisposable = vscode.workspace.onDidOpenTextDocument(() => { - logger.info("Active editor language changed, re-configuring comment blocks."); + const documentOpenDisposable = vscode.workspace.onDidOpenTextDocument((e) => { + logger.info(`Active editor language changed to "${e.languageId}", re-configuring comment blocks.`); // Dispose of old comment block configurations to prevent memory leaks commentBlocksDisposables.forEach((disposable) => disposable.dispose()); From ee5ed7905b5e97e597c99c062156de1298142129 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 31 Jul 2026 02:56:10 +0100 Subject: [PATCH 06/14] fix: `logLevel` setting description to mention about important logs. --- package.json | 2 +- src/logger.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index cfaa6a7..ade9ab5 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "Disable logging" ], "default": "debug", - "markdownDescription": "Set the logging level. `debug` is the most verbose, and `off` disables all logging." + "markdownDescription": "Set the logging level. `debug` is the most verbose, and `off` disables all logging, except for those special few labelled as 'important'." } } }, diff --git a/src/logger.ts b/src/logger.ts index cf14cb4..d289349 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -128,7 +128,7 @@ class Logger { debug: 3, // Emits debug, info, and error logs - the most verbose level. info: 2, // Emits info and error logs. error: 1, // Emits error logs only. - off: 0, // Disables all logs. + off: 0, // Disables all logs, except for the special "important" logs that are always emitted. }; // Emit when the configured level is at least as verbose as the requested level. From 3d8c8ffe2e5e9be356f79142d8c4be18502cccdf Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sat, 1 Aug 2026 01:29:13 +0100 Subject: [PATCH 07/14] refactor: `getAll` method to prepare extension data for logging. - Added `prepareForLogging` ExtensionData method to remove `packageJSON` entry from the `extensionData` Map clone because it doesn't add anything useful to the debugging logs, so it just adds clutter. - Updated `getAll` ExtensionData method signature to allow a `prepareForLogging` boolean param. Also updated the method to clone the `extensionData` Map to avoid mutating the original data, and added the `prepareForLogging` method call. --- src/extension.ts | 2 +- src/extensionData.ts | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index ab1aeda..eb06708 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -25,7 +25,7 @@ export function activate(context: vscode.ExtensionContext) { // Always output extension information to channel on activate. logger.important(`Activating ${extensionData.get("id")} v${extensionData.get("version")}`); - logger.debug(`Extension details:`, extensionData.getAll()); + logger.debug(`Extension details:`, extensionData.getAll(true)); logger.debug(`Extension Discovery Paths:`, extensionData.getAllExtensionDiscoveryPaths()); const configuration = new Configuration(); diff --git a/src/extensionData.ts b/src/extensionData.ts index 221693e..2c33ee3 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -310,13 +310,32 @@ export class ExtensionData { * * @returns {ExtensionMetaData} A plain object containing all extension details. */ - public getAll(): ExtensionMetaData | null { + public getAll(prepareForLogging: boolean = false): ExtensionMetaData | null { // If no data, return null if (this.extensionData.size === 0) { return null; } - return Object.fromEntries(this.extensionData) as unknown as ExtensionMetaData; + // Clone the Map to avoid mutating the original data. + const extensionDataClone = new Map(this.extensionData); + + // Prepare the data for logging. + if (prepareForLogging) { + this.prepareForLogging(extensionDataClone); + } + + return Object.fromEntries(extensionDataClone) as unknown as ExtensionMetaData; + } + + /** + * Prepare the extension data for logging by removing sensitive or irrelevant information. + * + * @param {Map} extensionDataClone + * The extension data Map clone. + */ + private prepareForLogging(extensionDataClone: Map) { + // Remove the packageJSON entry to avoid logging irrelevant information. + extensionDataClone.delete("packageJSON"); } /** From b0db087ef603066ba95dd1a80d22eeaf39f41eb3 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sat, 1 Aug 2026 02:09:15 +0100 Subject: [PATCH 08/14] fix: adjust log output formatting for clarity - Changed log message output to use `append` instead of `appendLine` for better formatting. This is so that any meta data can start on the same line as the message, especially for objects. - Added leading space for meta data output. - Added a new line to the output with `appendLine` when no meta data is present, so that the next log can start on a new line. --- src/logger.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/logger.ts b/src/logger.ts index d289349..b4bd4ab 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -149,13 +149,16 @@ class Logger { const time = new Date().toLocaleTimeString(); // Output the log message to the output channel. - this.outputChannel.appendLine(`["${level}" - ${time}] ${message}`); + this.outputChannel.append(`["${level}" - ${time}] ${message}`); if (meta) { const data: string = this.formatMeta(message, meta); - // Output the meta data to the output channel. - this.outputChannel.appendLine(data); + // Output the meta data to the output channel with a leading space. + this.outputChannel.appendLine(` ${data}`); + } else { + // Output a new line to the output channel. + this.outputChannel.appendLine(""); } } From 1719160cef45529052494990bd23c2b1ecb0689a Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sat, 1 Aug 2026 03:18:33 +0100 Subject: [PATCH 09/14] feat: log levels validation and fallback to the `debug` default. - Added `logLevels` object to define log levels. - Changed the `LogLevel` type to create it's union type from the keys of the new `logLevels` object. - Updated `setLogLevel` method to validate log levels and default to "debug" if invalid. - Added `isValidLogLevel` method to check for valid log levels. This creates an array of the new `logLevel` object values and checks if the specified level is included to validate it. --- src/interfaces/utils.ts | 14 ++++++++++++-- src/logger.ts | 21 +++++++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/interfaces/utils.ts b/src/interfaces/utils.ts index 103d7e9..6f174fb 100644 --- a/src/interfaces/utils.ts +++ b/src/interfaces/utils.ts @@ -39,6 +39,16 @@ export interface MultiLineLanguageDefinitions extends JsonObject { export type LanguageId = string; /** - * Log level + * Log levels */ -export type LogLevel = "debug" | "info" | "error" | "off"; +export const logLevels = { + debug: "debug", + info: "info", + error: "error", + off: "off", +} as const; + +/** + * Log level union type, derived from the keys of the logLevels object. + */ +export type LogLevel = (typeof logLevels)[keyof typeof logLevels]; diff --git a/src/logger.ts b/src/logger.ts index b4bd4ab..85eaadb 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,5 +1,5 @@ import {OutputChannel, window} from "vscode"; -import {LogLevel} from "./interfaces/utils"; +import {LogLevel, logLevels} from "./interfaces/utils"; /** * Logger class for the Auto Comment Blocks extension. @@ -48,10 +48,27 @@ class Logger { * * @param {LogLevel} level Desired log level. */ - public setLogLevel(level: LogLevel): void { + public setLogLevel(level: LogLevel | string): void { + // If the provided log level is not valid, default to "debug" and log an error message. + if (!this.isValidLogLevel(level)) { + this.logLevel = "debug"; + logger.error(`Invalid log level: "${level}". Defaulting to "debug".`); + return; + } + this.logLevel = level; } + /** + * Check if the provided log level is valid. + * @param level The log level to check. + * + * @returns `true` if the log level is valid, `false` otherwise. + */ + private isValidLogLevel(level: string): level is LogLevel { + return (Object.values(logLevels) as string[]).includes(level); + } + /** * Allow the extension to cleanup output channel */ From e67716baeff6813e94d69bd342d3cd7f3622d8d3 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sat, 1 Aug 2026 03:53:47 +0100 Subject: [PATCH 10/14] perf: avoid cloning extension data map when not preparing for logging. The `getAll` ExtensionData method cloned the `extensionData` Map on every call, even when `prepareForLogging` was false, adding unnecessary processing when iterating over many extensions, which would have been a potential performance issue. - Moved cloning into `prepareForLogging` method, which now returns the redacted clone itself. - Refactored `getAll` method to only call `prepareForLogging` when needed, otherwise it reads directly from the original map. --- src/extensionData.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/extensionData.ts b/src/extensionData.ts index 2c33ee3..3d9305e 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -316,26 +316,25 @@ export class ExtensionData { return null; } - // Clone the Map to avoid mutating the original data. - const extensionDataClone = new Map(this.extensionData); - - // Prepare the data for logging. - if (prepareForLogging) { - this.prepareForLogging(extensionDataClone); - } + const data = prepareForLogging ? this.prepareForLogging() : this.extensionData; - return Object.fromEntries(extensionDataClone) as unknown as ExtensionMetaData; + return Object.fromEntries(data) as unknown as ExtensionMetaData; } /** - * Prepare the extension data for logging by removing sensitive or irrelevant information. + * Prepare the extension data for logging by cloning it and removing irrelevant + * information, without mutating the original data. * - * @param {Map} extensionDataClone - * The extension data Map clone. + * @returns {Map} + * The cloned, redacted extension data Map. */ - private prepareForLogging(extensionDataClone: Map) { + private prepareForLogging(): Map { + const extensionDataClone = new Map(this.extensionData); + // Remove the packageJSON entry to avoid logging irrelevant information. extensionDataClone.delete("packageJSON"); + + return extensionDataClone; } /** From 077d9489392e5bf2898e82d8f2a6bd8d44454fc6 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 3 Aug 2026 00:21:09 +0100 Subject: [PATCH 11/14] fix: info log message for on document open event for clarity. - Updated log message to indicate both document opening and language change events for better clarity and removed the "active editor" wording as it doesn't necessarily fire for the active editor. --- src/extension.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index eb06708..2f134bf 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -104,7 +104,7 @@ export function activate(context: vscode.ExtensionContext) { * Called when active editor language is changed, so re-configure the comment blocks. */ const documentOpenDisposable = vscode.workspace.onDidOpenTextDocument((e) => { - logger.info(`Active editor language changed to "${e.languageId}", re-configuring comment blocks.`); + logger.info(`Document opened or language changed to "${e.languageId}", re-configuring comment blocks.`); // Dispose of old comment block configurations to prevent memory leaks commentBlocksDisposables.forEach((disposable) => disposable.dispose()); From 2951029ee6ef3c1957e1f40115ee3776be76b78a Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 3 Aug 2026 00:30:37 +0100 Subject: [PATCH 12/14] perf: `onDidOpenTextDocument` to return early for non-file documents. The `onDidOpenTextDocument` event also fires for non-file documents, like the git and output panels, so re-configuring the comment blocks for these are redundant. - Fixed by returning early if the uri scheme is not file or untitled, preventing unnecessary processing and improving performance. --- src/extension.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/extension.ts b/src/extension.ts index 2f134bf..2253235 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -104,6 +104,13 @@ export function activate(context: vscode.ExtensionContext) { * Called when active editor language is changed, so re-configure the comment blocks. */ const documentOpenDisposable = vscode.workspace.onDidOpenTextDocument((e) => { + // If the document is not a file or untitled scheme, then return early for + // virtual documents (e.g. git, output, etc. panels), as we only need to + // re-configure comment blocks for normal files. + if (e.uri.scheme !== "file" && e.uri.scheme !== "untitled") { + return; + } + logger.info(`Document opened or language changed to "${e.languageId}", re-configuring comment blocks.`); // Dispose of old comment block configurations to prevent memory leaks From 5f62c76b4ff4b9c6d5fa9ab164e9b9e7f49ca9aa Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 3 Aug 2026 01:00:47 +0100 Subject: [PATCH 13/14] perf: prevent unnecessary processing when logger isn't in `debug` mode. - Added `isDebugEnabled` Logger method to check if debug is enabled. - Added `isDebugEnabled` method call in a conditional in `logDebugInfo` Configuration method to check and exit early when debug logging is not enabled, to avoid unnecessary processing and disk reading. --- src/configuration.ts | 5 +++++ src/logger.ts | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/configuration.ts b/src/configuration.ts index fdceaf1..1704b6b 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -981,6 +981,11 @@ export class Configuration { * Logs the environment, configuration settings, and language configs for debugging purposes. */ private logDebugInfo() { + // If debug logging is not enabled, exit early. + if (!logger.isDebugEnabled()) { + return; + } + // The path to the built-in extensions. The env variable changes when on WSL. // So we can use it for both Windows and WSL. const builtInExtensionsPath = this.extensionData.getExtensionDiscoveryPath("builtInExtensionsPath"); diff --git a/src/logger.ts b/src/logger.ts index 85eaadb..2a86931 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -132,6 +132,14 @@ class Logger { this.logMessage("IMPORTANT", message); } + /** + * Determine whether debug logging is enabled. + * @returns `true` if debug logging is enabled, `false` otherwise. + */ + public isDebugEnabled(): boolean { + return this.shouldLog("debug"); + } + /** * Determine whether a log should be emitted for the current level. * From d35599e277c0ea7b164c7d012fceab0d11608b54 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Tue, 4 Aug 2026 02:18:36 +0100 Subject: [PATCH 14/14] docs: update code comment for the `onDidOpenTextDocument` event. Update the code comment to document the exact reason why we use the `onDidOpenTextDocument` event. --- src/extension.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index 2253235..31b9a69 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -101,7 +101,9 @@ export function activate(context: vscode.ExtensionContext) { * language id of a text document has been changed. As described in * https://github.com/microsoft/vscode/blob/4e8fbaef741afebd24684b88cac47c2f44dfb8eb/src/vscode-dts/vscode.d.ts#L13716-L13728 * - * Called when active editor language is changed, so re-configure the comment blocks. + * Re-configuring the comment blocks here protects against other extensions activating + * after this extension and overriding our language configuration, which would cause our + * comment blocks to not work properly (e.g `/*!`). */ const documentOpenDisposable = vscode.workspace.onDidOpenTextDocument((e) => { // If the document is not a file or untitled scheme, then return early for