From 1b3b17047f162d55cd1a616221dbfab44173a877 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 21 Aug 2026 06:27:12 +0100 Subject: [PATCH 1/5] feat: enable the language definitions to be auto-updated on settings change - Added new `updateLanguageDefinitions` function in Configuration class to update the language definitions. - Refactored the `onDidChangeConfiguration` event in the `activate` function of the extension to auto-update the language definitions and reconfigure the comment blocks when a user changes the settings. It uses the new `updateLanguageDefinitions` function to update the definitions before reconfiguring the comment blocks. - Removed the old `reloadRequiredSettings` array and the `showReloadMessage` function. --- src/configuration.ts | 14 +++++++++++++ src/extension.ts | 47 ++++++++++++++++++++------------------------ 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 1ce5802..abac5f8 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -682,6 +682,20 @@ export class Configuration { utils.writeJsonFile(this.multiLineLangDefinitionFilePath, multiLineData); } + /** + * Update language definitions. + */ + public updateLanguageDefinitions() { + // Remove all elements from the current Map, so we can update + // the definitions with an empty Map. + this.singleLineBlocksMap.clear(); + this.multiLineBlocksMap.clear(); + // Update the definitions. + this.setSingleLineCommentLanguageDefinitions(); + this.setMultiLineCommentLanguageDefinitions(); + this.writeCommentLanguageDefinitionsToJsonFile(); + } + /** * Sets the language configuration for a given language ID. * diff --git a/src/extension.ts b/src/extension.ts index 31b9a69..828c1c0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -75,21 +75,34 @@ export function activate(context: vscode.ExtensionContext) { logger.setLogLevel(logLevel); } - // Settings that require an extension host reload when changed. - const reloadRequiredSettings = [ - "disabledLanguages", - "overrideDefaultLanguageMultiLineComments", + /** + * Automatically update (without extension host reload) language definitions and + * reconfigure the comment blocks when any of the following settings are changed. + */ + const languageSettings = [ "multiLineStyleBlocks", "slashStyleBlocks", "hashStyleBlocks", "semicolonStyleBlocks", + "disabledLanguages", + "overrideDefaultLanguageMultiLineComments", ]; - // Settings that require extension host reload - for (const setting of reloadRequiredSettings) { + for (const setting of languageSettings) { if (event.affectsConfiguration(`${extensionName}.${setting}`)) { - showReloadMessage(extensionName, setting); - break; // Only show one reload message at a time + logger.info(`Configuration setting ${extensionName}.${setting} has changed.`); + // Dispose of old comment block configurations to prevent memory leaks + commentBlocksDisposables.forEach((disposable) => disposable.dispose()); + commentBlocksDisposables = []; + + configuration.updateLanguageDefinitions(); + + commentBlocksDisposables = configuration.configureCommentBlocks(); + disposables.push(...commentBlocksDisposables); + + logger.info("Comment block configurations have been updated."); + + break; // Only update once per change } } }); @@ -132,21 +145,3 @@ export function activate(context: vscode.ExtensionContext) { export function deactivate() { logger.disposeLogger(); } - -/** - * Shows a message prompting the user to reload the extension host. - * @param extensionName The namespace of the extension - * @param settingName The name of the setting that was changed - */ -function showReloadMessage(extensionName: string, settingName: string): void { - vscode.window - .showInformationMessage( - `The ${extensionName}.${settingName} setting has been changed. Please reload the Extension Host to take effect.`, - "Reload" - ) - .then((selection) => { - if (selection === "Reload") { - vscode.commands.executeCommand("workbench.action.restartExtensionHost"); - } - }); -} From 737f9bd9aeae2f72836e9dc0342bc3ebcb22278d Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sat, 22 Aug 2026 05:49:42 +0100 Subject: [PATCH 2/5] perf: cache default configs to reduce redundant repeated disk reads. - Added new Configuration class properties: - `defaultMultiLineConfig` to store the default multi-line configuration object. - `languagesToSkip` to store the languages to skip object. - Refactored `getLanguagesToSkip` method to get the languages from the new `languagesToSkip` class property instead of repeatedly reading the `skip-languages.jsonc` file from disk on every loop iteration of the `findAllLanguageConfigFilePaths` method. - Refactored `setLanguageConfiguration` method to get the default config from the new `defaultMultiLineConfig` class property instead of repeatedly reading the `default-multi-line-config.json` file from disk on every loop iteration of the `configureCommentBlocks` method. - Added a once-per-activation disk read of the `default-multi-line-config.json` and `skip-languages.jsonc` files in the Configuration `constructor` method, and add their contents to the respective new properties. This caches the JSON objects in memory ready for later use, enhancing performance by not reading them from disk on every loop iteration. --- src/configuration.ts | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index abac5f8..551267d 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -76,11 +76,27 @@ export class Configuration { */ private readonly multiLineLangDefinitionFilePath: string = `${this.autoGeneratedDir}/multi-line-languages.json`; + /** + * The default multi-line configuration. + */ + private readonly defaultMultiLineConfig: vscode.LanguageConfiguration; + + /** + * The languages to skip, like plaintext, that don't have comment syntax. + */ + private readonly languagesToSkip: JsonObject; + /*********** * Methods * ***********/ public constructor() { + const configPath = `${__dirname}/../../config`; + // Read the default multi-line config from the JSON file and cache it for later use. + this.defaultMultiLineConfig = utils.readJsonFile(`${configPath}/default-multi-line-config.json`) as vscode.LanguageConfiguration; + // Read the languages to skip from the JSON file and cache it for later use. + this.languagesToSkip = utils.readJsonFile(`${configPath}/skip-languages.jsonc`); + this.findAllLanguageConfigFilePaths(); this.setLanguageConfigDefinitions(); @@ -300,8 +316,7 @@ export class Configuration { * @returns {JsonArray} */ private getLanguagesToSkip(): JsonArray { - const json = utils.readJsonFile(`${__dirname}/../../config/skip-languages.jsonc`); - return json.languages as JsonArray; + return this.languagesToSkip.languages as JsonArray; } /** @@ -720,13 +735,12 @@ export class Configuration { */ private setLanguageConfiguration(langId: LanguageId, multiLine?: boolean, singleLineStyle?: SingleLineCommentStyle): vscode.Disposable { const internalLangConfig: vscode.LanguageConfiguration = this.getLanguageConfig(langId); - const defaultMultiLineConfig = utils.readJsonFile(`${__dirname}/../../config/default-multi-line-config.json`) as vscode.LanguageConfiguration; let langConfig = {...internalLangConfig}; if (multiLine) { langConfig.autoClosingPairs = utils.mergeArraysBy( - defaultMultiLineConfig.autoClosingPairs, + this.defaultMultiLineConfig.autoClosingPairs, internalLangConfig?.autoClosingPairs, "open" ); @@ -740,7 +754,7 @@ export class Configuration { // Only assign the default config comments if it doesn't already exist. // (nullish assignment operator ??=) - langConfig.comments ??= defaultMultiLineConfig.comments; + langConfig.comments ??= this.defaultMultiLineConfig.comments; // If the default multi-line comments has been overridden for the langId, // add the overridden multi-line comments to the langConfig. From 818547d1510cee8e4bf3e0e811d40539e2011af7 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sat, 22 Aug 2026 06:54:35 +0100 Subject: [PATCH 3/5] perf: only write language definitions to file in development mode. Writing to the auto generated language definition files is not very useful in production and are only helpful in development. In production, they are only ever read from disk to log their data for debugging. So for performance, they should only be written when in development/testing mode, and the updated definitions should only be cached in memory in production mode. - Removed the `writeCommentLanguageDefinitionsToJsonFile` method calls from the `constructor` and `updateLanguageDefinitions` methods. - Refactored `writeCommentLanguageDefinitionsToJsonFile` method: - Changed the visibility of the method from `private` to `public`, so it can be called from outside of the class. - Extracted the call to the `convertMapToReversedObject` utils function into a new method: `getSingleLineLanguageDefinitions`. This method returns the formatted and reversed single-line definitions object ready for logging or writing to JSON file. - Extracted the `Object.fromEntries` call into a new method: `getMultiLineLanguageDefinitions`. This method returns the formatted multi-line definitions object ready for logging or writing to JSON file. - Changed the `writeJsonFile` method calls to get the data from the 2 new methods instead of the old removed variables. - Changed the logging of the language definitions in `logDebugInfo` method to get the data from the new `getMultiLineLanguageDefinitions` and `getSingleLineLanguageDefinitions` methods, instead of reading directly from disk. - Added a conditional in the `activate` function to only run the `writeCommentLanguageDefinitionsToJsonFile` Configuration method when the context of the extension is not running in production (ie. it's running in development or testing mode). The same conditional is added into the configuration change event so when the definitions auto update they are written to the files in development/testing mode. --- src/configuration.ts | 44 ++++++++++++++++++++++++++------------------ src/extension.ts | 10 ++++++++++ 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 551267d..cc80dbb 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -102,7 +102,6 @@ export class Configuration { this.setMultiLineCommentLanguageDefinitions(); this.setSingleLineCommentLanguageDefinitions(); - this.writeCommentLanguageDefinitionsToJsonFile(); this.logDebugInfo(); } @@ -677,24 +676,40 @@ export class Configuration { this.singleLineBlocksMap.set("customSupportedLanguages", new Map([...tempMap].sort())); } + /** + * Get the single-line language definitions object + * formatted and reversed by comment style for + * logging and development debug file output. + * + * @returns {SingleLineLanguageDefinitions} + */ + public getSingleLineLanguageDefinitions(): SingleLineLanguageDefinitions { + return utils.convertMapToReversedObject(this.singleLineBlocksMap); + } + + /** + * Get the multi-line language definitions object + * formatted for logging and development debug file output. + * + * @returns {MultiLineLanguageDefinitions} + */ + public getMultiLineLanguageDefinitions(): MultiLineLanguageDefinitions { + return Object.fromEntries(this.multiLineBlocksMap) as unknown as MultiLineLanguageDefinitions; + } + /** * Write Comment Language Definitions to the respective JSON file: * either multi-line-languages.json, or single-line-languages.json. */ - private writeCommentLanguageDefinitionsToJsonFile() { + public writeCommentLanguageDefinitionsToJsonFile() { // Ensure the auto-generated directory exists. utils.ensureDirExists(this.autoGeneratedDir); - // Convert the singleLineBlocksMap to an object. - const singleLineData = utils.convertMapToReversedObject(this.singleLineBlocksMap); - - const multiLineData = Object.fromEntries(this.multiLineBlocksMap) as unknown as MultiLineLanguageDefinitions; - // Write into the single-line-languages.json file. - utils.writeJsonFile(this.singleLineLangDefinitionFilePath, singleLineData); + utils.writeJsonFile(this.singleLineLangDefinitionFilePath, this.getSingleLineLanguageDefinitions()); // Write into the multi-line-languages.json file. - utils.writeJsonFile(this.multiLineLangDefinitionFilePath, multiLineData); + utils.writeJsonFile(this.multiLineLangDefinitionFilePath, this.getMultiLineLanguageDefinitions()); } /** @@ -708,7 +723,6 @@ export class Configuration { // Update the definitions. this.setSingleLineCommentLanguageDefinitions(); this.setMultiLineCommentLanguageDefinitions(); - this.writeCommentLanguageDefinitionsToJsonFile(); } /** @@ -1045,16 +1059,10 @@ export class Configuration { logger.debug("The language configs found are:", this.languageConfigs); // Multi-line language definitions. - logger.debug( - "The supported languages for multi-line blocks:", - utils.readJsonFile(this.multiLineLangDefinitionFilePath) - ); + logger.debug("The supported languages for multi-line blocks:", this.getMultiLineLanguageDefinitions()); // Single-line language definitions. - logger.debug( - "The supported languages for single-line blocks:", - utils.readJsonFile(this.singleLineLangDefinitionFilePath) - ); + logger.debug("The supported languages for single-line blocks:", this.getSingleLineLanguageDefinitions()); } /** diff --git a/src/extension.ts b/src/extension.ts index 828c1c0..9de99ed 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -32,6 +32,11 @@ export function activate(context: vscode.ExtensionContext) { const extensionName = extensionData.get("namespace"); const extensionDisplayName = extensionData.get("displayName"); + // In development mode, write language definitions to JSON files for debugging/reference. + if (context.extensionMode !== vscode.ExtensionMode.Production) { + configuration.writeCommentLanguageDefinitionsToJsonFile(); + } + // Store disposables for cleanup const disposables: vscode.Disposable[] = []; let commentBlocksDisposables: vscode.Disposable[] = []; @@ -97,6 +102,11 @@ export function activate(context: vscode.ExtensionContext) { configuration.updateLanguageDefinitions(); + // In development mode, write updated language definitions to JSON files. + if (context.extensionMode !== vscode.ExtensionMode.Production) { + configuration.writeCommentLanguageDefinitionsToJsonFile(); + } + commentBlocksDisposables = configuration.configureCommentBlocks(); disposables.push(...commentBlocksDisposables); From 95990ce367b2203c616b7bc16dc9ef03809283a3 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sat, 22 Aug 2026 06:58:28 +0100 Subject: [PATCH 4/5] chore(tsconfig): enable auto type acquisition This helps vscode to show intellisense on native JS functions. --- tsconfig.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tsconfig.json b/tsconfig.json index 345fb18..27594a4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,5 +8,8 @@ "rootDir": ".", "typeRoots": ["./node_modules/@types"] }, + "typeAcquisition": { + "enable": true, + }, "exclude": ["node_modules", ".vscode-test"] } From 4d245158d9648d6c2e6d93b8061394e217a74dbd Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Tue, 25 Aug 2026 04:10:49 +0100 Subject: [PATCH 5/5] fix: auto-update definitions on change of the multi-line comments override. While adding a multi-line override in the `overrideDefaultLanguageMultiLineComments` setting auto-updated the language definitions correctly and used the override style, removing the override didn't work and it was still in place internally in vscode, and the extension's auto-complete no-longer worked. This happened because the override was inadvertently changing the multi-line style in the cached internal language config whilst also changing it on the shallow-copy for the immediate setting into vscode. So without ever changing it back as it was never supposed to be changed, the override was still apart of the extension's cached language configs making it permanent and breaking functionality. - Fixed by deep-cloning the `internalLangConfig` using JavaScript's `structuredClone` function in `setLanguageConfiguration` method so mutations (like comment overrides) never write back into the cached `languageConfigs` Map, which prevents pollution during definition auto-updates. - Added deep-cloning of the comments object in the cached `defaultMultiLineConfig` using `structuredClone` to prevent shared references and accidental mutations down the line during the fallback assignment. - Update comment override assignment to construct a new block comment tuple while preserving the ending, instead of mutating the existing array in place. --- src/configuration.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index cc80dbb..8c52848 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -736,7 +736,7 @@ export class Configuration { * * This method performs the following tasks: * - Retrieves the internal language configuration for the specified language ID. - * - Reads the default multi-line configuration from a JSON file. + * - Uses the cached default multi-line configuration. * - Merges the default multi-line configuration with the internal language configuration if * multiLine is `true`. * - Sets the appropriate comment styles and onEnter rules. @@ -748,9 +748,13 @@ export class Configuration { * with rogue characters being inserted on new lines. */ private setLanguageConfiguration(langId: LanguageId, multiLine?: boolean, singleLineStyle?: SingleLineCommentStyle): vscode.Disposable { - const internalLangConfig: vscode.LanguageConfiguration = this.getLanguageConfig(langId); + const internalLangConfig: vscode.LanguageConfiguration | undefined = this.getLanguageConfig(langId); - let langConfig = {...internalLangConfig}; + // Deep-clone the internalLangConfig so modifications never write back + // into the cached `languageConfigs` Map by accident. + let langConfig: vscode.LanguageConfiguration = internalLangConfig + ? structuredClone(internalLangConfig) + : {}; if (multiLine) { langConfig.autoClosingPairs = utils.mergeArraysBy( @@ -767,13 +771,17 @@ export class Configuration { ); // Only assign the default config comments if it doesn't already exist. - // (nullish assignment operator ??=) - langConfig.comments ??= this.defaultMultiLineConfig.comments; + // Clone the comments to avoid modifying the original object down the line. + langConfig.comments ??= structuredClone(this.defaultMultiLineConfig.comments); // If the default multi-line comments has been overridden for the langId, - // add the overridden multi-line comments to the langConfig. - if (this.isLangIdMultiLineCommentOverridden(langId)) { - langConfig.comments.blockComment[0] = this.getOverriddenMultiLineComment(langId); + // AND the langConfig has a comments key with a blockComment key, then + // update the opening comment style while preserving the ending. + if (this.isLangIdMultiLineCommentOverridden(langId) && langConfig.comments?.blockComment) { + langConfig.comments.blockComment = [ + this.getOverriddenMultiLineComment(langId), + langConfig.comments.blockComment[1] + ]; } /**