From dd0141856d1990096f0efa514710bf02f3f93ee4 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 11 Jan 2026 04:08:29 +0000 Subject: [PATCH 01/38] feat: add `Settings` interface to allow intellisense & full type safety. - Added new `Settings` interface to define all the configuration settings and their typings. - Refactored the `Configuration::getConfigurationValue` method to use the new `Settings` interface and automatically get the correct typing of the specified key from the interface. This also enables intellisense support for the settings keys. It also removes the requirement of hardcoding the type via generic typing when using the method. ie. `getConfigurationValue()`. - Removed all generic typings from the `getConfigurationValue` method calls. --- src/configuration.ts | 38 ++++++++++++++++++-------------------- src/interfaces/settings.ts | 10 ++++++++++ 2 files changed, 28 insertions(+), 20 deletions(-) create mode 100644 src/interfaces/settings.ts diff --git a/src/configuration.ts b/src/configuration.ts index b4182f5..86ff0cf 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -11,6 +11,7 @@ import {Rules} from "./rules"; import {logger} from "./logger"; import * as utils from "./utils"; import {ExtensionData} from "./extensionData"; +import {Settings, SupportUnsupportedLanguages} from "./interfaces/settings"; export class Configuration { /************** @@ -218,18 +219,16 @@ export class Configuration { /** * Get value of the specified key from the extension's user configuration settings. * - * @param {string} key The key of the specific setting. - * - * @returns {T} Returns the value of the `key`. + * @param {K} key The key of the specific setting. * - * NOTE: Return is typed as `T`, which is a generic type that represents the type that is declared when called (as explained in this StackOverflow answer: https://stackoverflow.com/a/49622066/2358222) + * @returns {Settings[K]} Returns the value of the `key` with proper typing. * * @example ```ts - * this.getConfigurationValue("disabledLanguages"); + * this.getConfigurationValue("disabledLanguages"); // Returns string[] with full type safety * ``` */ - public getConfigurationValue(key: string): T { - return this.getConfiguration().get(key); + public getConfigurationValue(key: K): Settings[K] { + return this.getConfiguration().get(key); } /** @@ -254,7 +253,7 @@ export class Configuration { * @returns {boolean} */ public isLangIdDisabled(langId: string): boolean { - return this.getConfigurationValue("disabledLanguages").includes(langId); + return this.getConfigurationValue("disabledLanguages").includes(langId); } /** @@ -264,7 +263,7 @@ export class Configuration { * @returns {boolean} */ private isLangIdMultiLineCommentOverridden(langId: string): boolean { - const overriddenList = this.getConfigurationValue("overrideDefaultLanguageMultiLineComments"); + const overriddenList = this.getConfigurationValue("overrideDefaultLanguageMultiLineComments"); return overriddenList.hasOwnProperty(langId); } @@ -276,7 +275,7 @@ export class Configuration { * @returns {string} */ private getOverriddenMultiLineComment(langId: string) { - const overriddenList = this.getConfigurationValue("overrideDefaultLanguageMultiLineComments"); + const overriddenList = this.getConfigurationValue("overrideDefaultLanguageMultiLineComments"); return overriddenList[langId]; } @@ -552,7 +551,7 @@ export class Configuration { // for sanity reasons. this.multiLineBlocksMap.set("supportedLanguages", langArray.sort()); - const multiLineStyleBlocksLangs = this.getConfigurationValue("multiLineStyleBlocks"); + const multiLineStyleBlocksLangs = this.getConfigurationValue("multiLineStyleBlocks"); // Empty the langArray to reuse it. langArray = []; @@ -622,7 +621,7 @@ export class Configuration { tempMap.clear(); // Get user-customized langIds for the //-style and add to the map. - let customSlashLangs = this.getConfigurationValue("slashStyleBlocks"); + let customSlashLangs = this.getConfigurationValue("slashStyleBlocks"); for (let langId of customSlashLangs) { // If langId is exists (ie. not NULL or empty string) AND // the langId is longer than 0, AND @@ -633,7 +632,7 @@ export class Configuration { } // Get user-customized langIds for the #-style and add to the map. - let customHashLangs = this.getConfigurationValue("hashStyleBlocks"); + let customHashLangs = this.getConfigurationValue("hashStyleBlocks"); for (let langId of customHashLangs) { // If langId is exists (ie. not NULL or empty string) AND // the langId is longer than 0, AND @@ -644,7 +643,7 @@ export class Configuration { } // Get user-customized langIds for the ;-style and add to the map. - let customSemicolonLangs = this.getConfigurationValue("semicolonStyleBlocks"); + let customSemicolonLangs = this.getConfigurationValue("semicolonStyleBlocks"); for (let langId of customSemicolonLangs) { // If langId is exists (ie. not NULL or empty string) AND // the langId is longer than 0, AND @@ -721,12 +720,11 @@ export class Configuration { * Get the user settings/configuration and set the blade or html comments accordingly. */ if (langId === "blade") { - langConfig.comments.blockComment = this.setBladeComments(this.getConfigurationValue("bladeOverrideComments"), true); + langConfig.comments.blockComment = this.setBladeComments(this.getConfigurationValue("bladeOverrideComments"), true); } } - let isOnEnter = this.getConfigurationValue("singleLineBlockOnEnter"); - + let isOnEnter = this.getConfigurationValue("singleLineBlockOnEnter"); // Add the single-line onEnter rules to the langConfig. // // If isOnEnter is true AND singleLineStyle isn't false, i.e. is a string, @@ -906,7 +904,7 @@ export class Configuration { } var indentedNewLine = "\n" + line.text.substring(0, line.text.search(indentRegex)); - let isOnEnter = this.getConfigurationValue("singleLineBlockOnEnter"); + let isOnEnter = this.getConfigurationValue("singleLineBlockOnEnter"); if (!isOnEnter) { indentedNewLine += style + " "; } @@ -928,7 +926,7 @@ export class Configuration { // Only carry out function if languageId is blade. if (langId === "blade" && !this.isLangIdDisabled(langId)) { // Read current value - let isOverridden = this.getConfigurationValue("bladeOverrideComments"); + let isOverridden = this.getConfigurationValue("bladeOverrideComments"); if (isOverridden === false) { // Update to true @@ -938,7 +936,7 @@ export class Configuration { this.updateConfigurationValue("bladeOverrideComments", false); } // Read new value - let bladeOverrideComments = this.getConfigurationValue("bladeOverrideComments"); + let bladeOverrideComments = this.getConfigurationValue("bladeOverrideComments"); // Set the comments for blade language. this.setBladeComments(bladeOverrideComments); diff --git a/src/interfaces/settings.ts b/src/interfaces/settings.ts new file mode 100644 index 0000000..8b0cb53 --- /dev/null +++ b/src/interfaces/settings.ts @@ -0,0 +1,10 @@ +export interface Settings { + singleLineBlockOnEnter: boolean; + disabledLanguages: string[]; + slashStyleBlocks: string[]; + hashStyleBlocks: string[]; + semicolonStyleBlocks: string[]; + multiLineStyleBlocks: string[]; + overrideDefaultLanguageMultiLineComments: string[]; + bladeOverrideComments: boolean; +} From d3e9bac51708e11e692a8495d3172f953ab85fbf Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 11 Jan 2026 04:16:33 +0000 Subject: [PATCH 02/38] fix: `overrideDefaultLanguageMultiLineComments` setting type. - Fixed the typing of `overrideDefaultLanguageMultiLineComments` setting to use the `Record` utility type. The setting is defined in the package.json as an object, but it has always been wrongly typed as an array (`string[]`). The utility type `Record` defines the types for the keys and values, assuming all keys and all values will be of the same typing, ie stringed keys and numeric values, in our case though it's stringed values. It's an easier way of typing it as `{ [ key: string ]: string }`. --- src/interfaces/settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/settings.ts b/src/interfaces/settings.ts index 8b0cb53..da080b7 100644 --- a/src/interfaces/settings.ts +++ b/src/interfaces/settings.ts @@ -5,6 +5,6 @@ export interface Settings { hashStyleBlocks: string[]; semicolonStyleBlocks: string[]; multiLineStyleBlocks: string[]; - overrideDefaultLanguageMultiLineComments: string[]; + overrideDefaultLanguageMultiLineComments: Record; bladeOverrideComments: boolean; } From dce0661aa0b3f6a668fed1ef08dc1cc363ba412e Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 11 Jan 2026 04:27:03 +0000 Subject: [PATCH 03/38] fix: remove `SupportUnsupportedLanguages` interface import. - Removed the import for the `SupportUnsupportedLanguages` interface, as this isn't available until 1.2.0. This was wrongly added to this PR while adding it for the 1.2.0 version. --- src/configuration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configuration.ts b/src/configuration.ts index 86ff0cf..5f19be0 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -11,7 +11,7 @@ import {Rules} from "./rules"; import {logger} from "./logger"; import * as utils from "./utils"; import {ExtensionData} from "./extensionData"; -import {Settings, SupportUnsupportedLanguages} from "./interfaces/settings"; +import {Settings} from "./interfaces/settings"; export class Configuration { /************** From 00db701e9fc0191170849ee217aa70608e26ead2 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 11 Jan 2026 04:30:51 +0000 Subject: [PATCH 04/38] fix: removed the generic typing in extension.ts - Removed the generic typing from the `Configuration::getConfigurationValue` method call in extension.ts file. --- src/extension.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 4d24398..9909b02 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -22,7 +22,7 @@ export function activate(context: vscode.ExtensionContext) { const extensionDisplayName = extensionData.get("displayName"); - let disabledLangConfig: string[] = configuration.getConfigurationValue("disabledLanguages"); + let disabledLangConfig: string[] = configuration.getConfigurationValue("disabledLanguages"); if (disabledLangConfig.length > 0) { vscode.window.showInformationMessage(`${disabledLangConfig.join(", ")} languages are disabled for ${extensionDisplayName}.`); @@ -41,7 +41,7 @@ export function activate(context: vscode.ExtensionContext) { // If the affected setting is bladeOverrideComments... if (event.affectsConfiguration(`${extensionName}.bladeOverrideComments`)) { // Get the setting. - let bladeOverrideComments: boolean = configuration.getConfigurationValue("bladeOverrideComments"); + let bladeOverrideComments: boolean = configuration.getConfigurationValue("bladeOverrideComments"); configuration.setBladeComments(bladeOverrideComments); From 16e481d6372454e4897c219439e279b20b697264 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 11 Jan 2026 04:35:34 +0000 Subject: [PATCH 05/38] ci: change job name from `deploy` to `check-ts` - Changed the job name in check-ts.yml from `deploy` to `check-ts`, as it's confusing seeing `deploy` on GitHub PR's and actions when it actually isn't the deploy workflow. This workflow only checks ts errors. --- .github/workflows/check-ts.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-ts.yml b/.github/workflows/check-ts.yml index 5ce44be..e35c0f8 100644 --- a/.github/workflows/check-ts.yml +++ b/.github/workflows/check-ts.yml @@ -3,7 +3,7 @@ on: name: Compile TypeScript to check for errors jobs: - deploy: + check-ts: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From b5d1bcbbcfffc1404348e95ac09c7bac6985c89b Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sat, 17 Jan 2026 04:07:54 +0000 Subject: [PATCH 06/38] refactor: merge `createExtensionData` into `setExtensionData` method - Merge `ExtensionData::createExtensionData` method into `ExtensionData::setExtensionData` method for simplicity. We once had a unified method before until I split them into the 2 aforementioned methods in PR #12 (commit b0a40aa). Not sure why they were split, maybe because I thought too many `this.extensionData.set` was duplication, but it actually isn't. So we now merge them back. --- src/extensionData.ts | 44 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/src/extensionData.ts b/src/extensionData.ts index 473513a..2d6ed4d 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -52,42 +52,26 @@ export class ExtensionData { * Set the extension data into the extensionData Map. */ private setExtensionData() { - // Set all entries in the extensionData Map. - Object.entries(this.createExtensionData()).forEach(([key, value]) => { - this.extensionData.set(key, value); - }); - } - - /** - * Create the extension data object for the extensionData Map. - * It also helps for type inference intellisense in the get method. - * - * @returns The extension data object with keys and values. - */ - private createExtensionData() { // The path to the user extensions. const userExtensionsPath = isWsl ? path.join(vscode.env.appRoot, "../../", "extensions") : path.join(this.packageJsonData.extensionPath, "../"); - // Set the keys and values for the Map. - // The keys will also be used for type inference in VSCode intellisense. - return { - id: this.packageJsonData.id, - name: this.packageJsonData.contributes.configuration.namespace, - displayName: this.packageJsonData.displayName, - version: this.packageJsonData.version, - userExtensionsPath: userExtensionsPath, - // The path to the built-in extensions. - // This env variable changes when on WSL to it's WSL-built-in extensions path. - builtInExtensionsPath: path.join(vscode.env.appRoot, "extensions"), + // Set each key-value pair directly into the Map + this.extensionData.set("id", this.packageJsonData.id); + this.extensionData.set("name", this.packageJsonData.contributes.configuration.namespace); + this.extensionData.set("displayName", this.packageJsonData.displayName); + this.extensionData.set("version", this.packageJsonData.version); + this.extensionData.set("userExtensionsPath", userExtensionsPath); + // The path to the built-in extensions. + // This env variable changes when on WSL to it's WSL-built-in extensions path. + this.extensionData.set("builtInExtensionsPath", path.join(vscode.env.appRoot, "extensions")); - // Only set these if running in WSL. - ...(isWsl && { - WindowsUserExtensionsPathFromWsl: path.dirname(process.env.VSCODE_WSL_EXT_LOCATION!), - WindowsBuiltInExtensionsPathFromWsl: path.join(process.env.VSCODE_CWD!, "resources/app/extensions"), - }), - } as const; + // Only set these if running in WSL + if (isWsl) { + this.extensionData.set("WindowsUserExtensionsPathFromWsl", path.dirname(process.env.VSCODE_WSL_EXT_LOCATION!)); + this.extensionData.set("WindowsBuiltInExtensionsPathFromWsl", path.join(process.env.VSCODE_CWD!, "resources/app/extensions")); + } } /** From 83c51edbbbaefedc534c9ba4b3118e9bdd4550cc Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sat, 17 Jan 2026 04:23:49 +0000 Subject: [PATCH 07/38] feat: add new `ExtensionMetaData` interface for better typings - Added new `ExtensionMetaData` interface for better typings and vscode intellisense. - Changed the `extensionData` property map to use typing of the new `ExtensionMetaData` interface. - Changed `get` and `getAll` methods to use the typing of `ExtensionMetaData` interface. This is instead of using return type of the old `createExtensionData` method, and still allows vscode intellisense of the data properties. It also is a better way to type the properties. --- src/extensionData.ts | 15 ++++++++------- src/interfaces/extensionMetaData.ts | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 src/interfaces/extensionMetaData.ts diff --git a/src/extensionData.ts b/src/extensionData.ts index 2d6ed4d..1f8863f 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -4,14 +4,15 @@ import isWsl from "is-wsl"; import {IPackageJson} from "package-json-type"; import {readJsonFile} from "./utils"; +import {ExtensionMetaData} from "./interfaces/extensionMetaData"; export class ExtensionData { /** * This extension details in the form of a key:value Map object. * - * @type {Map} + * @type {Map} */ - private extensionData = new Map(); + private extensionData = new Map(); /** * The package.json data for this extension. @@ -79,18 +80,18 @@ export class ExtensionData { * * @param {K} key The key of the extension detail to get. * - * @returns {ReturnType[K] | undefined} The value of the extension detail, or undefined if the key does not exist. + * @returns {ExtensionMetaData[K] | undefined} The value of the extension detail, or undefined if the key does not exist. */ - public get>(key: K): ReturnType[K] | undefined { - return this.extensionData.get(key) as ReturnType[K] | undefined; + public get(key: K): ExtensionMetaData[K] | undefined { + return this.extensionData.get(key) as ExtensionMetaData[K] | undefined; } /** * Get all extension data. * - * @returns {ReadonlyMap} A read-only Map containing all extension details. + * @returns {ReadonlyMap} A read-only Map containing all extension details. */ - public getAll(): ReadonlyMap { + public getAll(): ReadonlyMap { return this.extensionData; } } diff --git a/src/interfaces/extensionMetaData.ts b/src/interfaces/extensionMetaData.ts new file mode 100644 index 0000000..94c51b2 --- /dev/null +++ b/src/interfaces/extensionMetaData.ts @@ -0,0 +1,15 @@ +export interface ExtensionMetaData { + id: string; + name: string; + displayName: string; + version: string; + userExtensionsPath: string; + builtInExtensionsPath: string; + + /** + * Only set when running in WSL. + */ + + WindowsUserExtensionsPathFromWsl?: string; + WindowsBuiltInExtensionsPathFromWsl?: string; +} From 81f797b3baa0066ede35b90a4fafaaaeb44c8985 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Wed, 21 Jan 2026 05:30:23 +0000 Subject: [PATCH 08/38] refactor: `setBladeComments` method and fix typings. - Refactor `Configuration::setBladeComments` method to remove the duplicated blade and html comment arrays. We now only define the arrays once as a variable and use it in the return statement. - Fix `setBladeComments` method return type to vscode's `CharacterPair` type. Also typed the blade and html comments variables as `CharacterPair`. --- src/configuration.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 5f19be0..4b16e20 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -179,28 +179,34 @@ export class Configuration { * If `true`, it returns the comments, if `false` (default), it sets the comments to * the language directly. * + * @returns {vscode.CharacterPair | void} Returns the blade comments if `onStart` is `true`, otherwise nothing. + * */ - public setBladeComments(bladeOverrideComments: boolean, onStart: boolean = false): any { + public setBladeComments(bladeOverrideComments: boolean, onStart: boolean = false): vscode.CharacterPair | void { // Is enabled AND blade langId is NOT set as disabled... if (bladeOverrideComments === true && !this.isLangIdDisabled("blade")) { + const bladeComments: vscode.CharacterPair = ["{{--", "--}}"]; + if (onStart) { - return ["{{--", "--}}"]; + return bladeComments; } else { vscode.languages.setLanguageConfiguration("blade", { comments: { - blockComment: ["{{--", "--}}"], + blockComment: bladeComments, }, }); } } // Is disabled OR blade langId is set as disabled... else if (!bladeOverrideComments || this.isLangIdDisabled("blade")) { + const htmlComments: vscode.CharacterPair = [""]; + if (onStart) { - return [""]; + return htmlComments; } else { vscode.languages.setLanguageConfiguration("blade", { comments: { - blockComment: [""], + blockComment: htmlComments, }, }); } From 8af89da5550bdc7c19b6b5275f75d3c0707e55c2 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Wed, 21 Jan 2026 05:38:36 +0000 Subject: [PATCH 09/38] fix: parameter type `any` to `unknown` in `updateConfigurationValue` - Fixed the type of the `value` parameter of `Configuration::updateConfigurationValue` method to use `unknown` instead of `any`. This is because `any` type just disables TypeScript's error reporting, while `unknown` is a type-safe usage for "anything". Using `unknown` type is generally preferred over `any`. --- src/configuration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 4b16e20..d83e9ee 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -242,13 +242,13 @@ export class Configuration { * * @param {string} key The key of the specific setting. - * @param {any} value The value to update the setting with. + * @param {unknown} value The value to update the setting with. * * @example ```ts * this.updateConfigurationValue("bladeOverrideComments", true); * ``` */ - public updateConfigurationValue(key: string, value: any) { + public updateConfigurationValue(key: string, value: unknown) { // .update(config key, new value, global) this.getConfiguration().update(key, value, true); } From 21bfecbd9a3ccf5c827294d734efdd0da37eaf69 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Wed, 21 Jan 2026 05:47:05 +0000 Subject: [PATCH 10/38] fix: language config types to vscode's `LanguageConfiguration` - Fixed the type for the `config` param to vscode's `LanguageConfiguration` instead of `any` in both `setMultiLineCommentLanguageDefinitions` and `setSingleLineCommentLanguageDefinitions` methods. - Typed the `langArray` variable in `setMultiLineCommentLanguageDefinitions` method as `string[]`. - Fixed `defaultMultiLineConfig` variable's type to vscode's `LanguageConfiguration` instead of `any` in `setLanguageConfiguration`. --- src/configuration.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index d83e9ee..405a398 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -533,9 +533,9 @@ export class Configuration { * Set the multi-line comments language definitions. */ private setMultiLineCommentLanguageDefinitions() { - let langArray = []; + let langArray: string[] = []; - this.languageConfigs.forEach((config: any, langId: string) => { + this.languageConfigs.forEach((config: vscode.LanguageConfiguration, langId: string) => { // If the config object has own property of comments AND the comments key has // own property of blockComment... if (Object.hasOwn(config, "comments") && Object.hasOwn(config.comments, "blockComment")) { @@ -581,7 +581,7 @@ export class Configuration { private setSingleLineCommentLanguageDefinitions() { let style: string; const tempMap: Map = new Map(); - this.languageConfigs.forEach((config: any, langId: string) => { + this.languageConfigs.forEach((config: vscode.LanguageConfiguration, langId: string) => { // console.log(langId, config.comments.lineComment); let style: string = ""; @@ -702,7 +702,7 @@ export class Configuration { */ private setLanguageConfiguration(langId: string, multiLine?: boolean, singleLineStyle?: string): vscode.Disposable { const internalLangConfig: vscode.LanguageConfiguration = this.getLanguageConfig(langId); - const defaultMultiLineConfig: any = utils.readJsonFile(`${__dirname}/../../config/default-multi-line-config.json`); + const defaultMultiLineConfig: vscode.LanguageConfiguration = utils.readJsonFile(`${__dirname}/../../config/default-multi-line-config.json`); let langConfig = {...internalLangConfig}; From 2821c589406698e6ff0ab57cd6e50cc04282d7d1 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Wed, 21 Jan 2026 05:57:23 +0000 Subject: [PATCH 11/38] refactor: the `lineComment` style check to use strict equality checks Previously, the `lineComment` style check in `setSingleLineCommentLanguageDefinitions` used the string `includes` method to determine if the style "included" a `;` because they could also be `;;`. But since those are the only 2 strings that it can be, it makes more sense to just do a strict equality check for both instead. - Refactored `lineComment` style conditional to remove the `includes` method and use strict equality checks for `;` OR `;;`. --- src/configuration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 405a398..c792577 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -604,8 +604,8 @@ export class Configuration { else if (lineComment === "#") { style = "#"; } - // If the lineComment includes a ";" (; or ;;)... - else if (lineComment.includes(";")) { + // If the lineComment is ";" or ";;"... + else if (lineComment === ";" || lineComment === ";;") { style = ";"; } From f2504bc546c04ae3a1cb355d0d87029187d0a6d3 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Wed, 21 Jan 2026 07:31:57 +0000 Subject: [PATCH 12/38] fix: types for line comments in `setSingleLineCommentLanguageDefinitions` - Added a new custom type, `SingleLineCommentStyle`, in the new `commentStyles` file to define 3 union strings for the single line styles. - Fix `style` variable type in `setSingleLineCommentLanguageDefinitions` to the union type of the new `SingleLineCommentStyle` type and `null`. This gives more info to ts for what `style` should be, either 1 of the 3 strings or `null`. By typing the 3 style options ("//", "#", ";"), we are also enabling vscode's intellisense to auto complete the value. This helps for future development. - Changed the default value for `style` variable to be `null` instead of an empty string. This is so that we don't need to type the new `SingleLineCommentStyle` as `""` for empty string which looks weird as a union type. It also provides a better understanding of why the variable might be null, ie. an unsupported style. Also changed the style empty string checks to null checks. - Fixed typing for the language config `lineComment` property. The property used to be solely a string, but as of June 2025, VScode changed it to be a string, object or null, in the PR https://github.com/microsoft/vscode/pull/243283, specifically commit microsoft/vscode@d9145a2. However, this has not been updated in the npm package `@types/vscode` by DefinitelyTyped, so ts still thinks it should be a string only. So when we try to access the `comment` property in the object, then ts spits out errors. While I created an issue for it (https://github.com/DefinitelyTyped/DefinitelyTyped/issues/74372), we need need to manually add the changes until @types/vscode is changed accordingly. - Added new `LineComment` custom type in the new commentStyles file to be a union of `string`, `LineCommentConfig`, and `null`. This typing is inline with vscode's change, but as a custom type instead of inside an interface. Also typed the `lineComment` variable as this new type and cast the `config.comments.lineComment` as this type too, to avoid any ts errors. - Added new `LineCommentConfig` interface in the new commentStyles file, to provide info for the object options. This is inline with vscode's change. - Changed the conditional for the `lineComment` as an object to first check it is of type object and not null before checking if `comment` property exists in the object. This prevents ts erroring. --- src/configuration.ts | 10 +++++----- src/interfaces/commentStyles.ts | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 src/interfaces/commentStyles.ts diff --git a/src/configuration.ts b/src/configuration.ts index c792577..60ffbf8 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -583,16 +583,16 @@ export class Configuration { const tempMap: Map = new Map(); this.languageConfigs.forEach((config: vscode.LanguageConfiguration, langId: string) => { // console.log(langId, config.comments.lineComment); - let style: string = ""; + let style: SingleLineCommentStyle | null = null; // If the config object has own property of comments AND the comments key has // own property of lineComment... if (Object.hasOwn(config, "comments") && Object.hasOwn(config.comments, "lineComment")) { - let lineComment = config.comments.lineComment; + let lineComment: LineComment = config.comments.lineComment as LineComment; // Line comments can be a string or an object with a "comment" key. // If the lineComment is an object, get the "comment" key value. - if (Object.hasOwn(lineComment, "comment")) { + if (typeof lineComment === "object" && lineComment !== null && Object.hasOwn(lineComment, "comment")) { lineComment = lineComment.comment; } @@ -609,10 +609,10 @@ export class Configuration { style = ";"; } - // If style is NOT an empty string, (i.e. not an unsupported single-line + // If style is NOT null, (i.e. not an unsupported single-line // comment like bat's @rem), AND // the langId isn't set as disabled... - if (style != "" && !this.isLangIdDisabled(langId)) { + if (style !== null && !this.isLangIdDisabled(langId)) { // Set the langId and it's style into the Map. tempMap.set(langId, style); } diff --git a/src/interfaces/commentStyles.ts b/src/interfaces/commentStyles.ts new file mode 100644 index 0000000..eb9f0fd --- /dev/null +++ b/src/interfaces/commentStyles.ts @@ -0,0 +1,33 @@ +export type SingleLineCommentStyle = "//" | "#" | ";"; + +/** + * Line Comments + * + * Taken directly from VScode's commit in June 2025 that changed the line comment config. + * https://github.com/microsoft/vscode/commit/d9145a291dcef0bad3ace81a3d55727ca294c122#diff-0dfa7db579eface8250affb76bc88717725a121401d4d8598bc36b92b0b6ef62 + * + * The @types/vscode package does not yet have these changes. + * So until they're added, we define them manually. + */ + +/** + * The line comment token, like `// this is a comment`. + * Can be a string, an object with comment and optional noIndent properties, or null. + */ +export type LineComment = string | LineCommentConfig | null; + +/** + * Configuration for line comments. + */ +export interface LineCommentConfig { + /** + * The line comment token, like `//` + */ + comment: string; + + /** + * Whether the comment token should not be indented and placed at the first column. + * Defaults to false. + */ + noIndent?: boolean; +} From 5965287b41e2c2f413f376b93022df1e75190117 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Wed, 21 Jan 2026 08:42:58 +0000 Subject: [PATCH 13/38] fix: ts type error while trying to set blade comments. - Fixed ts type error `Type 'void | CharacterPair' is not assignable to type 'CharacterPair'` while trying to set blade comments in `setLanguageConfiguration` method. This probably happens because the `setBladeComments` method is returning void and being set directly to the lang config `blockComment` property which doesn't work. So to avoid this, we set the blade comments into a new variable instead and then only set the `blockComment` property if the conditional check of `bladeComments` variable is truthy, ie. has any value except null,undefined, etc. --- src/configuration.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/configuration.ts b/src/configuration.ts index 60ffbf8..20769d0 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -726,7 +726,12 @@ export class Configuration { * Get the user settings/configuration and set the blade or html comments accordingly. */ if (langId === "blade") { - langConfig.comments.blockComment = this.setBladeComments(this.getConfigurationValue("bladeOverrideComments"), true); + const bladeComments = this.setBladeComments(this.getConfigurationValue("bladeOverrideComments"), true); + + // If bladeComments is has a value... + if (bladeComments) { + langConfig.comments.blockComment = bladeComments; + } } } From 0b776fcfaf3b5f04112cc609721114f56f286050 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Wed, 21 Jan 2026 08:51:19 +0000 Subject: [PATCH 14/38] remove: the unused duplicate `style` variable. - Removed the unused duplicate `style` variable in `setSingleLineCommentLanguageDefinitions` method. --- src/configuration.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/configuration.ts b/src/configuration.ts index 20769d0..b3bb484 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -579,7 +579,6 @@ export class Configuration { * Set the single-line comments language definitions. */ private setSingleLineCommentLanguageDefinitions() { - let style: string; const tempMap: Map = new Map(); this.languageConfigs.forEach((config: vscode.LanguageConfiguration, langId: string) => { // console.log(langId, config.comments.lineComment); From 1365c469c4b557ca17a68a5db970b3726483d693 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Wed, 21 Jan 2026 08:54:25 +0000 Subject: [PATCH 15/38] fix: the missing imports for `LineComment` & `SingleLineCommentStyle` --- src/configuration.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/configuration.ts b/src/configuration.ts index b3bb484..f43eca1 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -12,6 +12,7 @@ import {logger} from "./logger"; import * as utils from "./utils"; import {ExtensionData} from "./extensionData"; import {Settings} from "./interfaces/settings"; +import {LineComment, SingleLineCommentStyle} from "./interfaces/commentStyles"; export class Configuration { /************** From 824ce9b084926d5eaa3d41928f047c6b82958373 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 23 Jan 2026 16:19:01 +0000 Subject: [PATCH 16/38] fix: `data` param in logger's `debug` method to be optional - Fixed the `data` param in logger's `debug` method to be an optional param, so it no longer is required if we just want the `DEBUG` message in the logs. --- src/logger.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/logger.ts b/src/logger.ts index 3d9c58e..6d91cbe 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -84,9 +84,9 @@ class Logger { * This is helpful for logging objects and arrays. * * @param {string} message The message to be logged. - * @param {unknown} data Extra data that is useful for debugging, like an object or array. + * @param {unknown} data [Optional] Extra data that is useful for debugging, like an object or array. */ - public debug(message: string, data: unknown): void { + public debug(message: string, data?: unknown): void { if (this.debugMode) { this.logMessage("DEBUG", message, data); } From 6534893ff64984cf5a601a6c504da91c099c5396 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 23 Jan 2026 17:38:01 +0000 Subject: [PATCH 17/38] feat: add a dedicated `namespace` property for the `extensionData`. - Added an optional `namespace` property in the `ExtensionMetaData` interface. - Move the configuration namespace value of the `name` property in `setExtensionData` to the new `namespace` property as it's value. - Set the `name` property n `setExtensionData` to the name in the packageJson instead of the configuration namespace. - Change all occurrences of getting the `name` property to the new `namespace` property since each occurrence is for the namespace and not the actual name. --- src/configuration.ts | 4 ++-- src/extension.ts | 2 +- src/extensionData.ts | 3 ++- src/interfaces/extensionMetaData.ts | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index f43eca1..36ceddd 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -220,7 +220,7 @@ export class Configuration { * @returns {vscode.WorkspaceConfiguration} */ public getConfiguration(): vscode.WorkspaceConfiguration { - return vscode.workspace.getConfiguration(this.extensionData.get("name"), null); + return vscode.workspace.getConfiguration(this.extensionData.get("namespace"), null); } /** @@ -932,7 +932,7 @@ export class Configuration { */ private handleChangeBladeMultiLineBlock(textEditor: vscode.TextEditor) { let langId = textEditor.document.languageId; - const extensionName = this.extensionData.get("name"); + const extensionName = this.extensionData.get("namespace"); // Only carry out function if languageId is blade. if (langId === "blade" && !this.isLangIdDisabled(langId)) { diff --git a/src/extension.ts b/src/extension.ts index 9909b02..9f95d44 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -18,7 +18,7 @@ export function activate(context: vscode.ExtensionContext) { disposables.push(...configureCommentBlocksDisposable, ...registerCommandsDisposable); - const extensionName = extensionData.get("name"); + const extensionName = extensionData.get("namespace"); const extensionDisplayName = extensionData.get("displayName"); diff --git a/src/extensionData.ts b/src/extensionData.ts index 1f8863f..4739816 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -60,7 +60,8 @@ export class ExtensionData { // Set each key-value pair directly into the Map this.extensionData.set("id", this.packageJsonData.id); - this.extensionData.set("name", this.packageJsonData.contributes.configuration.namespace); + this.extensionData.set("name", this.packageJsonData.name); + this.extensionData.set("namespace", this.packageJsonData.contributes.configuration.namespace); this.extensionData.set("displayName", this.packageJsonData.displayName); this.extensionData.set("version", this.packageJsonData.version); this.extensionData.set("userExtensionsPath", userExtensionsPath); diff --git a/src/interfaces/extensionMetaData.ts b/src/interfaces/extensionMetaData.ts index 94c51b2..c1cadbd 100644 --- a/src/interfaces/extensionMetaData.ts +++ b/src/interfaces/extensionMetaData.ts @@ -1,6 +1,7 @@ export interface ExtensionMetaData { id: string; name: string; + namespace?: string; displayName: string; version: string; userExtensionsPath: string; From 07def7e07e6b0508261834d37264ad13ec268c5b Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 23 Jan 2026 17:43:44 +0000 Subject: [PATCH 18/38] docs: add docblocks to the `ExtensionMetaData` interface. --- src/interfaces/extensionMetaData.ts | 42 ++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/interfaces/extensionMetaData.ts b/src/interfaces/extensionMetaData.ts index c1cadbd..090bc75 100644 --- a/src/interfaces/extensionMetaData.ts +++ b/src/interfaces/extensionMetaData.ts @@ -1,16 +1,56 @@ +/** + * Extension metadata for a VSCode extension + */ export interface ExtensionMetaData { + /** + * The unique ID in the form of `publisher.name`. + */ id: string; + + /** + * The name. + * Directly from package.json "name" key. + */ name: string; + + /** + * The namespace for this extension's configuration settings, + * which is a slightly shorter version of the name. + */ namespace?: string; + + /** + * The display name. + * Directly from package.json "displayName" key. + */ displayName: string; + + /** + * The version. + * Directly from package.json "version" key. + */ version: string; + /** + * The path to the user extensions. + */ userExtensionsPath: string; + + /** + * The path to the built-in extensions. + */ builtInExtensionsPath: string; /** + * The Windows path to the user extensions when running in WSL. + * * Only set when running in WSL. */ - WindowsUserExtensionsPathFromWsl?: string; + + /** + * The Windows path to the built-in extensions when running in WSL. + * + * Only set when running in WSL. + */ WindowsBuiltInExtensionsPathFromWsl?: string; } From be9d9726df2b7157a61ea8078d845798fd232e3b Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sat, 24 Jan 2026 23:45:59 +0000 Subject: [PATCH 19/38] refactor: paths to installed extensions into a separate method - Added new `ExtensionData::setExtensionDiscoveryPaths` method to set the paths to installed extensions, and added it's method call to the constructor. - Moved the setting of the extension paths from `setExtensionData` into the new `setExtensionDiscoveryPaths` method. - Added new `ExtensionPaths` interface and moved all the related properties from the `ExtensionMetaData` interface into the new interface. - Added new `ExtensionData::extensionDiscoveryPaths` property to hold the Map of all the paths, and uses the `ExtensionPaths` interface. - Changed references to the `extensionData` property in `setExtensionDiscoveryPaths` method to use the new `extensionDiscoveryPaths` property. - Added `getExtensionDiscoveryPath` and `getAllExtensionDiscoveryPaths` methods to get a specific extension path by key or get all paths, respectively. - Changed all method calls to `get` extension paths in `Configuration` class to use the `getExtensionDiscoveryPath` method; and added the `getAllExtensionDiscoveryPaths` method call in the constructor. --- src/configuration.ts | 19 ++++++----- src/extensionData.ts | 52 ++++++++++++++++++++++++----- src/interfaces/extensionMetaData.ts | 7 ++++ 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 36ceddd..a77b83e 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -76,6 +76,7 @@ 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(); @@ -309,8 +310,8 @@ export class Configuration { // If running in WSL... if (isWsl) { // Get the Windows user and built-in extensions paths. - const windowsUserExtensionsPath = this.extensionData.get("WindowsUserExtensionsPathFromWsl"); - const windowsBuiltInExtensionsPath = this.extensionData.get("WindowsBuiltInExtensionsPathFromWsl"); + const windowsUserExtensionsPath = this.extensionData.getExtensionDiscoveryPath("WindowsUserExtensionsPathFromWsl"); + const windowsBuiltInExtensionsPath = this.extensionData.getExtensionDiscoveryPath("WindowsBuiltInExtensionsPathFromWsl"); // Read the paths and create arrays of the extensions. const windowsBuiltInExtensions = this.readExtensionsFromDirectory(windowsBuiltInExtensionsPath); @@ -320,8 +321,8 @@ export class Configuration { extensions.push(...windowsBuiltInExtensions, ...windowsUserExtensions); } - const userExtensionsPath = this.extensionData.get("userExtensionsPath"); - const builtInExtensionsPath = this.extensionData.get("builtInExtensionsPath"); + const userExtensionsPath = this.extensionData.getExtensionDiscoveryPath("userExtensionsPath"); + const builtInExtensionsPath = this.extensionData.getExtensionDiscoveryPath("builtInExtensionsPath"); // Read the paths and create arrays of the extensions. const userExtensions = this.readExtensionsFromDirectory(userExtensionsPath); @@ -971,25 +972,25 @@ export class Configuration { private logDebugInfo() { // 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.get("builtInExtensionsPath"); + const builtInExtensionsPath = this.extensionData.getExtensionDiscoveryPath("builtInExtensionsPath"); let extensionsPaths = {}; if (isWsl) { // Get the Windows user and built-in extensions paths. - const windowsUserExtensionsPath = this.extensionData.get("WindowsUserExtensionsPathFromWsl"); - const windowsBuiltInExtensionsPath = this.extensionData.get("WindowsBuiltInExtensionsPathFromWsl"); + const windowsUserExtensionsPath = this.extensionData.getExtensionDiscoveryPath("WindowsUserExtensionsPathFromWsl"); + const windowsBuiltInExtensionsPath = this.extensionData.getExtensionDiscoveryPath("WindowsBuiltInExtensionsPathFromWsl"); extensionsPaths = { "Windows-installed Built-in Extensions Path": windowsBuiltInExtensionsPath, "Windows-installed User Extensions Path": windowsUserExtensionsPath, "WSL-installed Built-in Extensions Path": builtInExtensionsPath, - "WSL-installed User Extensions Path": this.extensionData.get("userExtensionsPath"), + "WSL-installed User Extensions Path": this.extensionData.getExtensionDiscoveryPath("userExtensionsPath"), }; } else { extensionsPaths = { "Built-in Extensions Path": builtInExtensionsPath, - "User Extensions Path": this.extensionData.get("userExtensionsPath"), + "User Extensions Path": this.extensionData.getExtensionDiscoveryPath("userExtensionsPath"), }; } diff --git a/src/extensionData.ts b/src/extensionData.ts index 4739816..ad26b6d 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -4,7 +4,7 @@ import isWsl from "is-wsl"; import {IPackageJson} from "package-json-type"; import {readJsonFile} from "./utils"; -import {ExtensionMetaData} from "./interfaces/extensionMetaData"; +import {ExtensionMetaData, ExtensionPaths} from "./interfaces/extensionMetaData"; export class ExtensionData { /** @@ -14,6 +14,14 @@ export class ExtensionData { */ private extensionData = new Map(); + /** + * Extension discovery paths in the form of a key:value Map object. + * + * @type {Map} + */ + private extensionDiscoveryPaths = new Map(); + + /** * The package.json data for this extension. * @@ -24,6 +32,7 @@ export class ExtensionData { public constructor() { this.packageJsonData = this.getExtensionPackageJsonData(); this.setExtensionData(); + this.setExtensionDiscoveryPaths(); } /** @@ -53,10 +62,6 @@ export class ExtensionData { * Set the extension data into the extensionData Map. */ private setExtensionData() { - // The path to the user extensions. - const userExtensionsPath = isWsl - ? path.join(vscode.env.appRoot, "../../", "extensions") - : path.join(this.packageJsonData.extensionPath, "../"); // Set each key-value pair directly into the Map this.extensionData.set("id", this.packageJsonData.id); @@ -64,15 +69,24 @@ export class ExtensionData { this.extensionData.set("namespace", this.packageJsonData.contributes.configuration.namespace); this.extensionData.set("displayName", this.packageJsonData.displayName); this.extensionData.set("version", this.packageJsonData.version); - this.extensionData.set("userExtensionsPath", userExtensionsPath); + } + + private setExtensionDiscoveryPaths() { + // The path to the user extensions. + // const userExtensionsPath = isWsl ? path.join(vscode.env.appRoot, "../../", "extensions") : path.join(this.extensionPath, "../"); + + //TODO: REMOVE THIS BEFORE RELEASE. FOR TESTING ONLY. + const userExtensionsPath = isWsl ? path.join(vscode.env.appRoot, "../../", "extensions") : "C:\\Users\\Stuart\\.vscode\\extensions"; + + this.extensionDiscoveryPaths.set("userExtensionsPath", userExtensionsPath); // The path to the built-in extensions. // This env variable changes when on WSL to it's WSL-built-in extensions path. - this.extensionData.set("builtInExtensionsPath", path.join(vscode.env.appRoot, "extensions")); + this.extensionDiscoveryPaths.set("builtInExtensionsPath", path.join(vscode.env.appRoot, "extensions")); // Only set these if running in WSL if (isWsl) { - this.extensionData.set("WindowsUserExtensionsPathFromWsl", path.dirname(process.env.VSCODE_WSL_EXT_LOCATION!)); - this.extensionData.set("WindowsBuiltInExtensionsPathFromWsl", path.join(process.env.VSCODE_CWD!, "resources/app/extensions")); + this.extensionDiscoveryPaths.set("WindowsUserExtensionsPathFromWsl", path.dirname(process.env.VSCODE_WSL_EXT_LOCATION!)); + this.extensionDiscoveryPaths.set("WindowsBuiltInExtensionsPathFromWsl", path.join(process.env.VSCODE_CWD!, "resources/app/extensions")); } } @@ -95,4 +109,24 @@ export class ExtensionData { public getAll(): ReadonlyMap { return this.extensionData; } + + /** + * Get the extension discovery paths by a specified key. + * + * @param {K} key The key of the specific path to get. + * + * @returns {ExtensionPaths[K] | undefined} The value of the extension detail, or undefined if the key does not exist. + */ + public getExtensionDiscoveryPath(key: K): ExtensionPaths[K] | undefined { + return this.extensionDiscoveryPaths.get(key) as ExtensionPaths[K] | undefined; + } + + /** + * Get all extension discovery paths. + * + * @returns {ReadonlyMap} A read-only Map containing all extension discovery paths. + */ + public getAllExtensionDiscoveryPaths(): ReadonlyMap { + return this.extensionDiscoveryPaths; + } } diff --git a/src/interfaces/extensionMetaData.ts b/src/interfaces/extensionMetaData.ts index 090bc75..a4fc8de 100644 --- a/src/interfaces/extensionMetaData.ts +++ b/src/interfaces/extensionMetaData.ts @@ -30,6 +30,13 @@ export interface ExtensionMetaData { * Directly from package.json "version" key. */ version: string; + +} + +/** + * Extension discovery paths configuration for this extension + */ +export interface ExtensionPaths { /** * The path to the user extensions. */ From 2b0ce9a276474c849bf1989b6cf2524accaae805 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 25 Jan 2026 22:53:53 +0000 Subject: [PATCH 20/38] refactor: move creation of the extension ID into `setExtensionData`. - Move the creation of the extension ID from the `getExtensionPackageJsonData` method into the `setExtensionData` method. And set the id property of the `extensionData` Map directly as the id variable. This is instead of using the `packageJSON` object as the middleman, and using a custom property which is then used to set the `extensionData` Map. The ID doesn't need to be in the packageJSON object, so we can just create and set the id directly in the `setExtensionData` method. --- src/extensionData.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/extensionData.ts b/src/extensionData.ts index ad26b6d..45bc5e8 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -45,8 +45,6 @@ export class ExtensionData { const packageJSON: IPackageJson = readJsonFile(path.join(extensionPath, "package.json")); - // Set the id (publisher.name) into the packageJSON object as a new `id` key. - packageJSON.id = `${packageJSON.publisher}.${packageJSON.name}`; packageJSON.extensionPath = extensionPath; // The configuration settings namespace is a shortened version of the extension name. @@ -62,9 +60,11 @@ export class ExtensionData { * Set the extension data into the extensionData Map. */ private setExtensionData() { + // Create the extension ID (publisher.name). + const id = `${this.packageJsonData.publisher}.${this.packageJsonData.name}`; // Set each key-value pair directly into the Map - this.extensionData.set("id", this.packageJsonData.id); + this.extensionData.set("id", id); this.extensionData.set("name", this.packageJsonData.name); this.extensionData.set("namespace", this.packageJsonData.contributes.configuration.namespace); this.extensionData.set("displayName", this.packageJsonData.displayName); From 6f77cc51f56776eba0d0f82073990d58c54ac629 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 25 Jan 2026 23:15:40 +0000 Subject: [PATCH 21/38] refactor: move `settingsNamespace` creation into `setExtensionData`. - Move `settingsNamespace` creation from `getExtensionPackageJsonData` method into the `setExtensionData` method. So that the `namespace` property of the `extensionData` Map can be set directly using the variable instead of using the `packageJSON` object as a middleman again. We only add namespace to `packageJSON.contributes.configuration` as a way to eventually add it to the `extensionData` Map. This is redundant code. --- src/extensionData.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/extensionData.ts b/src/extensionData.ts index 45bc5e8..7d6c454 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -47,11 +47,6 @@ export class ExtensionData { packageJSON.extensionPath = extensionPath; - // The configuration settings namespace is a shortened version of the extension name. - // We just need to replace "automatic" with "auto" in the name. - const settingsNamespace: string = packageJSON.name.replace("automatic", "auto"); - // Set the namespace to the packageJSON `configuration` object as a new `namespace` key. - packageJSON.contributes.configuration.namespace = settingsNamespace; return packageJSON; } @@ -66,7 +61,11 @@ export class ExtensionData { // Set each key-value pair directly into the Map this.extensionData.set("id", id); this.extensionData.set("name", this.packageJsonData.name); - this.extensionData.set("namespace", this.packageJsonData.contributes.configuration.namespace); + // The configuration settings namespace is a shortened version of the extension name. + // We just need to replace "automatic" with "auto" in the name. + const settingsNamespace: string = this.packageJsonData.name.replace("automatic", "auto"); + + this.extensionData.set("namespace", settingsNamespace); this.extensionData.set("displayName", this.packageJsonData.displayName); this.extensionData.set("version", this.packageJsonData.version); } From c2711eb53a9354f671519d98832e46231e11985c Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 26 Jan 2026 01:54:25 +0000 Subject: [PATCH 22/38] fix: JSON file reading with optional `null` return for missing files. - Moved the check for package.json file existing (`fs.existsSync`) in `Configruation::readExtensionsFromDirectory` method into the `readJsonFile` utility function. This is so that the util function can check if a JSON file exists before trying to read it. If file doesn't exist then it just returns `null`. - Removed the `fs.existsSync` check and replaced it with a `null` check in the `readExtensionsFromDirectory` method. - Refactored `readJsonFile` util function to accept a new `throwOnFileMissing` param with a default value of `true`. This param, along with it's accompanying logic, will throw an error if a file is missing (doesn't exist) and when the param is `true, instead of returning `null`. This allows only specific usages of the util function to return `null` instead of error throwing when the param is set to `false`. Thus being backward compatibile with other usages. --- src/configuration.ts | 15 ++++++++------- src/utils.ts | 23 +++++++++++++++++++---- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index a77b83e..e75fe38 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -495,16 +495,17 @@ export class Configuration { // Get the package.json file path. const packageJSONPath = path.join(extensionPath, "package.json"); + const packageJSON: IPackageJson = utils.readJsonFile(packageJSONPath, false); - // If the package.json file exists... - if (fs.existsSync(packageJSONPath)) { - const packageJSON: IPackageJson = utils.readJsonFile(packageJSONPath); + if (packageJSON === null) { + return; + } - const id = `${packageJSON.publisher}.${packageJSON.name}`; + const id = `${packageJSON.publisher}.${packageJSON.name}`; - // Push the extension data object into the array. - foundExtensions.push({id, extensionPath, packageJSON}); - } + // Push the extension data object into the array. + foundExtensions.push({id, extensionPath, packageJSON}); + } }); diff --git a/src/utils.ts b/src/utils.ts index ea4faba..fc296ae 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -7,11 +7,26 @@ import {window} from "vscode"; * Read the file and parse the JSON. * * @param {string} filepath The path of the file. + * @param {boolean} [throwOnFileMissing=true] Whether to throw an error if the file doesn't exist. + * If `false`, returns `null`. Default is `true`. * - * @returns {any} The JSON file content as an object. - * @throws Will throw an error if the JSON file cannot be parsed. + * @returns {any | null} The JSON file content as an object, or `null` if file doesn't exist and `throwOnFileMissing` is `false`. + * @throws Will throw an error if the JSON file cannot be parsed or if file doesn't exist and `throwOnFileMissing` is `true`. */ -export function readJsonFile(filepath: string): any { +export function readJsonFile(filepath: string, throwOnFileMissing: boolean = true): any | null { + // Check if file exists first. + // If file doesn't exist... + if (!fs.existsSync(filepath)) { + // If throwOnFileMissing param is true, throw an error. + if (throwOnFileMissing) { + const error = new Error(`JSON file not found: "${filepath}"`); + logger.error(error.stack); + throw error; + } + // Otherwise just return null. + return null; + } + const jsonErrors: jsonc.ParseError[] = []; const fileContent = fs @@ -31,7 +46,7 @@ export function readJsonFile(filepath: string): any { .showErrorMessage( `${errorMsg}. The extension cannot continue. Please check the "Auto Comment Blocks" Output Channel for errors.`, "OK", - "Open Output Channel" + "Open Output Channel", ) .then((selection) => { if (selection === "Open Output Channel") { From ea401c8797a185d5d252c654122ca2f31bf9daf6 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 26 Jan 2026 02:14:37 +0000 Subject: [PATCH 23/38] feat: add `extensionPath` property to the `ExtensionData` class. - Added new `extensionPath` private property to the `ExtensionData` class to hold the absolute path of the extension. - Move the `extensionPath` creation from `getExtensionPackageJsonData` method into the `constructor`, and set it to the new class property of the same name. - Split the extension package.json path creation from the `readJsonFile` method call param into a new separate `packageJSONPath` variable in the `getExtensionPackageJsonData` method, and use this new variable as the param of `readJsonFile`. - Added new `extensionPath` property to the `ExtensionMetaData` interface to allow the `extensionData` Map to hold this new property, and added setting it's value into the `setExtensionData` method. - Removed the setting of the `extensionPath` into the `packageJson` object in `getExtensionPackageJsonData`, in favour of the new `extensionData` Map property of the same name. --- src/extensionData.ts | 21 +++++++++++++-------- src/interfaces/extensionMetaData.ts | 4 ++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/extensionData.ts b/src/extensionData.ts index 7d6c454..8f376c4 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -21,6 +21,12 @@ export class ExtensionData { */ private extensionDiscoveryPaths = new Map(); + /** + * The absolute path of the extension. + * + * @type {string} + */ + private readonly extensionPath: string; /** * The package.json data for this extension. @@ -30,6 +36,9 @@ export class ExtensionData { private packageJsonData: IPackageJson; public constructor() { + // Set the path to this extension's path. + this.extensionPath = path.join(__dirname, "../../"); + this.packageJsonData = this.getExtensionPackageJsonData(); this.setExtensionData(); this.setExtensionDiscoveryPaths(); @@ -41,14 +50,9 @@ export class ExtensionData { * @returns {IPackageJson} The package.json data for this extension, with extra custom keys. */ private getExtensionPackageJsonData(): IPackageJson { - const extensionPath = path.join(__dirname, "../../"); - - const packageJSON: IPackageJson = readJsonFile(path.join(extensionPath, "package.json")); - - packageJSON.extensionPath = extensionPath; - - - return packageJSON; + // Get the package.json file path. + const packageJSONPath = path.join(this.extensionPath, "package.json"); + return readJsonFile(packageJSONPath); } /** @@ -68,6 +72,7 @@ export class ExtensionData { this.extensionData.set("namespace", settingsNamespace); this.extensionData.set("displayName", this.packageJsonData.displayName); this.extensionData.set("version", this.packageJsonData.version); + this.extensionData.set("extensionPath", this.extensionPath); } private setExtensionDiscoveryPaths() { diff --git a/src/interfaces/extensionMetaData.ts b/src/interfaces/extensionMetaData.ts index a4fc8de..7ee8c8e 100644 --- a/src/interfaces/extensionMetaData.ts +++ b/src/interfaces/extensionMetaData.ts @@ -31,6 +31,10 @@ export interface ExtensionMetaData { */ version: string; + /** + * The absolute path to the extension. + */ + extensionPath: string; } /** From b693795098cd6bceb6879b131abf69038e30b17b Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 26 Jan 2026 02:33:40 +0000 Subject: [PATCH 24/38] fix: add `null` check for `packageJsonData` before setting extensionData - Added the `throwOnFileMissing` param of the readJsonFile util function call to as `false` in the `getExtensionPackageJsonData` method to allow the util function to return `null`. - Changed return types of the `getExtensionPackageJsonData` function to allow `null`. - Added a `packageJson` null check in the constructor to only allow running the `setExtensionData` method when `packageJsonData` is not `null`. --- src/extensionData.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/extensionData.ts b/src/extensionData.ts index 8f376c4..9bbdbd1 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -40,19 +40,24 @@ export class ExtensionData { this.extensionPath = path.join(__dirname, "../../"); this.packageJsonData = this.getExtensionPackageJsonData(); - this.setExtensionData(); + + // Only proceed with extension data setup if packageJsonData is NOT null. + if (this.packageJsonData !== null) { + this.setExtensionData(); + } + this.setExtensionDiscoveryPaths(); } /** * Get the names, id, and version of this extension from package.json. * - * @returns {IPackageJson} The package.json data for this extension, with extra custom keys. + * @returns {IPackageJson | null} The package.json data for this extension, with extra custom keys. */ - private getExtensionPackageJsonData(): IPackageJson { + private getExtensionPackageJsonData(): IPackageJson | null { // Get the package.json file path. const packageJSONPath = path.join(this.extensionPath, "package.json"); - return readJsonFile(packageJSONPath); + return readJsonFile(packageJSONPath, false); } /** From 279d3b4d9361336e88c715ed49abfb43839c6de3 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 26 Jan 2026 04:30:52 +0000 Subject: [PATCH 25/38] refactor: allow the `extensionData` Map for any extension's data. - Added `packageJson` property to the `ExtensionMetaData` interface with the type of `IPackageJson`. - Added a new `ExtensionMetaDataValue` utility type to automatically get all types of the `ExtensionMetaData` and construct them as a union type. This is needed because typing the Map value as `string` will no longer work with the new `packageJson` property since it's not a string. - Changed the typing of the `extensionData` Map value to use the new `ExtensionMetaDataValue` type. - Added new `extensionPath` param to the constructor so that the class can be initialised with an optional extension path. The param is `null` be default. - Added the new `extensionPath` param variable to set the class's `extensionPath` property. Using a nullish coalescing operator (`??`) on the variable, we set the property to the variable if it's a string, otherwise set it to our extension's path. - Changed setting of the extension namespace property to only be set if the extension is ours - wrapped the code in an if statement in `setExtensionData` method. This is because we only need to know our extension's config namespace. - Set the new `packageJson` property into the `extensionData` Map with the class's `packageJsonData` property as the value in `setExtensionData`. - Changed all typings of any variables relating to an array of extensions in `Configuration` class to use the `ExtensionMetaData` interface. Because we have the required properties in place now we don't need to type them as `any`, we can now use the interface for a better developing experience. In addition, we can also now remove all occurrences of the array object typing of `Array<{ id: string; extensionPath: string; packageJSON: IPackageJson }>` and replace it with the `ExtensionMetaData` interface as the type `ExtensionMetaData[]`. This makes it easier to handle, and removes all duplication. - Changed `Configuration::readExtensionsFromDirectory` method to use the `ExtensionData` class to `getAll` data of each extension in the directory. This removes all now redundant code dealing with the package.json within the method, since it's handled in the `ExtensionData` class. - Changed `ExtensionData::getAll` method to return a plain object instead of a readonly Map, this is so that we can use it in the `Configuration::readExtensionsFromDirectory` method without ts errors. Also added a conditional that will return null if the `extensionData` map has a size of 0, ie. is empty. --- src/configuration.ts | 25 +++++++---------- src/extensionData.ts | 43 ++++++++++++++++++----------- src/interfaces/extensionMetaData.ts | 10 +++++++ 3 files changed, 47 insertions(+), 31 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index e75fe38..815d073 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -13,6 +13,7 @@ import * as utils from "./utils"; import {ExtensionData} from "./extensionData"; import {Settings} from "./interfaces/settings"; import {LineComment, SingleLineCommentStyle} from "./interfaces/commentStyles"; +import {ExtensionMetaData} from "./interfaces/extensionMetaData"; export class Configuration { /************** @@ -166,7 +167,7 @@ export class Configuration { "auto-comment-blocks.changeBladeMultiLineBlock", (textEditor, edit, args) => { this.handleChangeBladeMultiLineBlock(textEditor); - } + }, ); return [singleLineBlockCommand, changeBladeMultiLineBlockCommand]; } @@ -305,7 +306,7 @@ export class Configuration { * (built-in and 3rd party). */ private findAllLanguageConfigFilePaths() { - const extensions: any[] = []; + const extensions: ExtensionMetaData[] = []; // If running in WSL... if (isWsl) { @@ -477,12 +478,11 @@ export class Configuration { * * @param {string} extensionsPath The path where extensions are stored. * - * @returns {Array<{ id: string; extensionPath: string; packageJSON: IPackageJson }>} + * @returns {ExtensionMetaData[]} */ - private readExtensionsFromDirectory(extensionsPath: string): Array<{id: string; extensionPath: string; packageJSON: IPackageJson}> { + private readExtensionsFromDirectory(extensionsPath: string): ExtensionMetaData[] { // Create an array to hold the found extensions. - const foundExtensions: Array<{id: string; extensionPath: string; packageJSON: IPackageJson}> = []; - + const foundExtensions: ExtensionMetaData[] = []; fs.readdirSync(extensionsPath).forEach((extensionName) => { const extensionPath = path.join(extensionsPath, extensionName); @@ -493,19 +493,14 @@ export class Configuration { return; } - // Get the package.json file path. - const packageJSONPath = path.join(extensionPath, "package.json"); - const packageJSON: IPackageJson = utils.readJsonFile(packageJSONPath, false); + const extensionData = new ExtensionData(extensionPath).getAll(); - if (packageJSON === null) { + if (extensionData === null) { return; } - const id = `${packageJSON.publisher}.${packageJSON.name}`; - // Push the extension data object into the array. - foundExtensions.push({id, extensionPath, packageJSON}); - + foundExtensions.push(extensionData); } }); @@ -959,7 +954,7 @@ export class Configuration { else if (langId == "blade" && this.isLangIdDisabled(langId)) { vscode.window.showInformationMessage( `Blade is set as disabled in the "${extensionName}.disabledLanguages" setting. The "${extensionName}.bladeOverrideComments" setting will have no affect.`, - "OK" + "OK", ); // Set the comments for blade language. diff --git a/src/extensionData.ts b/src/extensionData.ts index 9bbdbd1..08582b5 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -4,15 +4,15 @@ import isWsl from "is-wsl"; import {IPackageJson} from "package-json-type"; import {readJsonFile} from "./utils"; -import {ExtensionMetaData, ExtensionPaths} from "./interfaces/extensionMetaData"; +import {ExtensionMetaData, ExtensionPaths, ExtensionMetaDataValue} from "./interfaces/extensionMetaData"; export class ExtensionData { /** - * This extension details in the form of a key:value Map object. + * Extension data in the form of a key:value Map object. * - * @type {Map} + * @type {Map} */ - private extensionData = new Map(); + private extensionData = new Map(); /** * Extension discovery paths in the form of a key:value Map object. @@ -22,7 +22,7 @@ export class ExtensionData { private extensionDiscoveryPaths = new Map(); /** - * The absolute path of the extension. + * The absolute path of the requested extension. * * @type {string} */ @@ -35,9 +35,9 @@ export class ExtensionData { */ private packageJsonData: IPackageJson; - public constructor() { - // Set the path to this extension's path. - this.extensionPath = path.join(__dirname, "../../"); + public constructor(extensionPath: string | null = null) { + // Set the path if provided, otherwise default to this extension's path. + this.extensionPath = extensionPath ?? path.join(__dirname, "../../"); this.packageJsonData = this.getExtensionPackageJsonData(); @@ -70,14 +70,20 @@ export class ExtensionData { // Set each key-value pair directly into the Map this.extensionData.set("id", id); this.extensionData.set("name", this.packageJsonData.name); - // The configuration settings namespace is a shortened version of the extension name. - // We just need to replace "automatic" with "auto" in the name. - const settingsNamespace: string = this.packageJsonData.name.replace("automatic", "auto"); - this.extensionData.set("namespace", settingsNamespace); + // Only set the namespace if it dealing with this extension. + if (this.packageJsonData.name === "automatic-comment-blocks") { + // The configuration settings namespace is a shortened version of the extension name. + // We just need to replace "automatic" with "auto" in the name. + const settingsNamespace: string = this.packageJsonData.name.replace("automatic", "auto"); + + this.extensionData.set("namespace", settingsNamespace); + } + this.extensionData.set("displayName", this.packageJsonData.displayName); this.extensionData.set("version", this.packageJsonData.version); this.extensionData.set("extensionPath", this.extensionPath); + this.extensionData.set("packageJSON", this.packageJsonData); } private setExtensionDiscoveryPaths() { @@ -111,12 +117,17 @@ export class ExtensionData { } /** - * Get all extension data. + * Get all extension data as a plain object. * - * @returns {ReadonlyMap} A read-only Map containing all extension details. + * @returns {ExtensionMetaData} A plain object containing all extension details. */ - public getAll(): ReadonlyMap { - return this.extensionData; + public getAll(): ExtensionMetaData | null { + // If no data, return null + if (this.extensionData.size === 0) { + return null; + } + + return Object.fromEntries(this.extensionData) as unknown as ExtensionMetaData; } /** diff --git a/src/interfaces/extensionMetaData.ts b/src/interfaces/extensionMetaData.ts index 7ee8c8e..646ec81 100644 --- a/src/interfaces/extensionMetaData.ts +++ b/src/interfaces/extensionMetaData.ts @@ -1,3 +1,8 @@ +import {IPackageJson} from "package-json-type"; + +// Utility types for cleaner Map typing +export type ExtensionMetaDataValue = ExtensionMetaData[keyof ExtensionMetaData]; + /** * Extension metadata for a VSCode extension */ @@ -35,6 +40,11 @@ export interface ExtensionMetaData { * The absolute path to the extension. */ extensionPath: string; + + /** + * The full package.json data + */ + packageJSON: IPackageJson; } /** From 9ff08579b9626f620b6f50a893802ff5284c39c2 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 26 Jan 2026 04:41:26 +0000 Subject: [PATCH 26/38] fix: remove the testing only code mistakenly added in commit be9d972 The `//TODO: REMOVE THIS BEFORE RELEASE. FOR TESTING ONLY.` comment along with it's testing `userExtensionsPath` variable code was mistakenly added in commit be9d972. This was never supposed have seen the lights of git, and is only used in development to ensure it gets the right extensions path otherwise the development environment would get the wrong path. Fixed by removing the testing code, and uncommenting the real code. --- src/extensionData.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/extensionData.ts b/src/extensionData.ts index 08582b5..8d7fd63 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -88,10 +88,7 @@ export class ExtensionData { private setExtensionDiscoveryPaths() { // The path to the user extensions. - // const userExtensionsPath = isWsl ? path.join(vscode.env.appRoot, "../../", "extensions") : path.join(this.extensionPath, "../"); - - //TODO: REMOVE THIS BEFORE RELEASE. FOR TESTING ONLY. - const userExtensionsPath = isWsl ? path.join(vscode.env.appRoot, "../../", "extensions") : "C:\\Users\\Stuart\\.vscode\\extensions"; + const userExtensionsPath = isWsl ? path.join(vscode.env.appRoot, "../../", "extensions") : path.join(this.extensionPath, "../"); this.extensionDiscoveryPaths.set("userExtensionsPath", userExtensionsPath); // The path to the built-in extensions. From a3a2b9031b2754f1634655409b8584adcf4132b2 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 30 Jan 2026 04:06:17 +0000 Subject: [PATCH 27/38] docs: add some code comments --- src/extensionData.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/extensionData.ts b/src/extensionData.ts index 8d7fd63..46df8d1 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -37,6 +37,10 @@ export class ExtensionData { public constructor(extensionPath: string | null = null) { // Set the path if provided, otherwise default to this extension's path. + // + // For this extension's path, we use `__dirname` and go up two levels + // (from "out/src" to the extension root). This path is also used to locate all other + // user-installed extensions later for the `userExtensionsPath` discovery path. this.extensionPath = extensionPath ?? path.join(__dirname, "../../"); this.packageJsonData = this.getExtensionPackageJsonData(); @@ -88,6 +92,9 @@ export class ExtensionData { private setExtensionDiscoveryPaths() { // The path to the user extensions. + // + // On Windows/Linux/Mac: ~/.vscode[-server|remote]/extensions + // On WSL: ~/.vscode-[server|remote]/extensions const userExtensionsPath = isWsl ? path.join(vscode.env.appRoot, "../../", "extensions") : path.join(this.extensionPath, "../"); this.extensionDiscoveryPaths.set("userExtensionsPath", userExtensionsPath); From c246fe22da9ccda9c577ca92ff3ef523ce3381a9 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 30 Jan 2026 07:33:15 +0000 Subject: [PATCH 28/38] fix: improve type safety and clarity - Added some code comments for clarity. - Improved type safety on a few variables, including the `config` in the `Configuration::setLanguageConfigDefinitions` method and the `event` of the `onDidChangeConfiguration` API in the `activate` method in extension.ts. --- src/configuration.ts | 9 ++++++--- src/extension.ts | 2 +- src/utils.ts | 3 +-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 815d073..259543e 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -379,7 +379,7 @@ export class Configuration { this.languageConfigFilePaths.forEach((paths, langId) => { // Loop through the paths array... paths.forEach((filepath) => { - let config = utils.readJsonFile(filepath); + let config = utils.readJsonFile(filepath) as vscode.LanguageConfiguration; // If the config JSON has more than 0 keys (ie. not empty) if (Object.keys(config).length > 0) { @@ -514,7 +514,8 @@ export class Configuration { * @returns {string[]} An array of language ID strings. */ private getMultiLineLanguages(key: "supportedLanguages" | "customSupportedLanguages"): string[] { - return this.multiLineBlocksMap.get(key); + // The non-null assertion operator (!) ensures that the key is never undefined. + return this.multiLineBlocksMap.get(key)!; } /** @@ -524,7 +525,8 @@ export class Configuration { * @returns {Map} The Map of the languages and styles. */ private getSingleLineLanguages(key: "supportedLanguages" | "customSupportedLanguages"): Map { - return this.singleLineBlocksMap.get(key); + // The non-null assertion operator (!) ensures that the key is never undefined. + return this.singleLineBlocksMap.get(key)!; } /** @@ -1028,6 +1030,7 @@ export class Configuration { rules.forEach((rule) => { if (rule.action && "indent" in rule.action) { // Convert JSON format to API format + // Usage of the `any` type is necessary here because `rule.action` const indentValue = (rule.action as any).indent; delete (rule.action as any).indent; diff --git a/src/extension.ts b/src/extension.ts index 9f95d44..700dc39 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -32,7 +32,7 @@ export function activate(context: vscode.ExtensionContext) { * When the configuration/user settings are changed, set the extension * to reflect the settings and output a message to the user. */ - vscode.workspace.onDidChangeConfiguration((event: any) => { + vscode.workspace.onDidChangeConfiguration((event: vscode.ConfigurationChangeEvent) => { // TODO: Work on automatically updating the languages instead of making the user reload the extension. /** diff --git a/src/utils.ts b/src/utils.ts index fc296ae..d8b49ab 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -219,8 +219,7 @@ export function mergeArraysBy(primaryArray: T[], secondaryArray: T[], key: ke // Start with primary array (avoids side effects) const merged = [...primary]; - // Add items from secondary array that don't exist in primary, - // removing any duplicates. + // Add items from secondary array that don't exist in primary, removing any duplicates. secondary.forEach((item) => { // Test all items in the merged array to check if the value of the key // already exists in the merged array. From 4c4e36116892f2e16ee77c9b57f757562d76e87e Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 30 Jan 2026 07:41:54 +0000 Subject: [PATCH 29/38] chore(prettier): add `trailingComma` option and set to `es5`. By default `trailingComma` option is set to `all` when not defined, and this made changes to extension.ts file by adding unnecessary trailing commas to function and method params. This could be seen in all the `showInformationMessage` method calls. By defining the option with the value `es5`, trailing commas will only be affective on arrays and objects, not functions and methods. --- .prettierrc.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.prettierrc.json b/.prettierrc.json index 39a51ec..be0684a 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -30,7 +30,8 @@ "*.ts" ], "options": { - "quoteProps": "consistent" + "quoteProps": "consistent", + "trailingComma": "es5" } } ] From a832c7e878b91417370f98563f2e151f03e46f38 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Fri, 30 Jan 2026 21:19:34 +0000 Subject: [PATCH 30/38] fix: introduce proper typing for json objects. - Added proper typings for json objects. - `JsonObject` interface to define a generic json object. - `JsonValue` type to define the possible types of the json values. - `JsonArray` type to define an array in json. - Added interfaces to define the single-line and multi-line language definitions, which extend the `JsonObject`: `SingleLineLanguageDefinitions` and `MultiLineLanguageDefinitions`. - Refactored `readJsonFile` util function to use the new `JsonObject` interface and `JsonValue` type. The function now passes a generic type `T` as it's return value, if the generic type isn't defined when calling the function, then it defaults to `JsonObject`. This ensures that specific types or interfaces can be defined as the return type that naturally is or extends a `JsonObject`. Eg. `readJsonFile()`. - Refactored `writeJsonFile` util function to pass a generic type `T` as it's return value and it's `data` param type as `T`. - Changed `obj` param type in `reconstructRegex` util function to `unknown. - Changed `convertMapToReversedObject` util function return type to `JsonObject`. - Changed `readJsonFile` function call in `ExtensionData::getExtensionPackageJsonData` method to define the generic return type as `IPackageJson`. - Changed `Configuration::getLanguagesToSkip` method return type to `JsonArray`. - Refactored `Configuration::writeCommentLanguageDefinitionsToJsonFile` method to properly type the code as the new `MultiLineLanguageDefinitions` and `SingleLineLanguageDefinitions` interfaces. - Changed `defaultMultiLineConfig` variable in `Configuration::setLanguageConfiguration` to be cast as `vscode.LanguageConfiguration` to ensure the `readJsonFile` return type is of that type. - Auto-formatting changes., like removal of commas. - Changed the `readJsonFile` function call return type in `Configuration::logDebugInfo` method to use the `MultiLineLanguageDefinitions` and `SingleLineLanguageDefinitions` interfaces. --- src/configuration.ts | 38 ++++++++++++++++++++++++++------------ src/extensionData.ts | 2 +- src/interfaces/utils.ts | 34 ++++++++++++++++++++++++++++++++++ src/utils.ts | 22 ++++++++++++---------- 4 files changed, 73 insertions(+), 23 deletions(-) create mode 100644 src/interfaces/utils.ts diff --git a/src/configuration.ts b/src/configuration.ts index 259543e..72efdc6 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -14,6 +14,8 @@ import {ExtensionData} from "./extensionData"; import {Settings} from "./interfaces/settings"; import {LineComment, SingleLineCommentStyle} from "./interfaces/commentStyles"; import {ExtensionMetaData} from "./interfaces/extensionMetaData"; +import {MultiLineLanguageDefinitions, SingleLineLanguageDefinitions} from "./interfaces/utils"; +import {JsonArray} from "./interfaces/utils"; export class Configuration { /************** @@ -167,7 +169,7 @@ export class Configuration { "auto-comment-blocks.changeBladeMultiLineBlock", (textEditor, edit, args) => { this.handleChangeBladeMultiLineBlock(textEditor); - }, + } ); return [singleLineBlockCommand, changeBladeMultiLineBlockCommand]; } @@ -294,11 +296,11 @@ export class Configuration { * * Idea from this StackOverflow answer https://stackoverflow.com/a/72988011/2358222 * - * @returns {string[]} + * @returns {JsonArray} */ - private getLanguagesToSkip(): string[] { + private getLanguagesToSkip(): JsonArray { const json = utils.readJsonFile(`${__dirname}/../../config/skip-languages.jsonc`); - return json.languages; + return json.languages as JsonArray; } /** @@ -671,10 +673,16 @@ export class Configuration { // Ensure the auto-generated directory exists. utils.ensureDirExists(this.autoGeneratedDir); - // Write the into the single-line-languages.json file. - utils.writeJsonFile(this.singleLineLangDefinitionFilePath, utils.convertMapToReversedObject(this.singleLineBlocksMap)); - // Write the into the multi-line-languages.json file. - utils.writeJsonFile(this.multiLineLangDefinitionFilePath, Object.fromEntries(this.multiLineBlocksMap)); + // Convert the singleLineBlocksMap to an object. + const singleLineData = utils.convertMapToReversedObject(this.singleLineBlocksMap) as SingleLineLanguageDefinitions; + + const multiLineData = Object.fromEntries(this.multiLineBlocksMap) as unknown as MultiLineLanguageDefinitions; + + // Write into the single-line-languages.json file. + utils.writeJsonFile(this.singleLineLangDefinitionFilePath, singleLineData); + + // Write into the multi-line-languages.json file. + utils.writeJsonFile(this.multiLineLangDefinitionFilePath, multiLineData); } /** @@ -701,7 +709,7 @@ export class Configuration { */ private setLanguageConfiguration(langId: string, multiLine?: boolean, singleLineStyle?: string): vscode.Disposable { const internalLangConfig: vscode.LanguageConfiguration = this.getLanguageConfig(langId); - const defaultMultiLineConfig: vscode.LanguageConfiguration = utils.readJsonFile(`${__dirname}/../../config/default-multi-line-config.json`); + const defaultMultiLineConfig = utils.readJsonFile(`${__dirname}/../../config/default-multi-line-config.json`) as vscode.LanguageConfiguration; let langConfig = {...internalLangConfig}; @@ -956,7 +964,7 @@ export class Configuration { else if (langId == "blade" && this.isLangIdDisabled(langId)) { vscode.window.showInformationMessage( `Blade is set as disabled in the "${extensionName}.disabledLanguages" setting. The "${extensionName}.bladeOverrideComments" setting will have no affect.`, - "OK", + "OK" ); // Set the comments for blade language. @@ -1011,8 +1019,14 @@ export class Configuration { // Log the objects for debugging purposes. logger.debug("The language config filepaths found are:", this.languageConfigFilePaths); logger.debug("The language configs found are:", this.languageConfigs); - logger.debug("The supported languages for multi-line blocks:", utils.readJsonFile(this.multiLineLangDefinitionFilePath)); - logger.debug("The supported languages for single-line blocks:", utils.readJsonFile(this.singleLineLangDefinitionFilePath)); + logger.debug( + "The supported languages for multi-line blocks:", + utils.readJsonFile(this.multiLineLangDefinitionFilePath) + ); + logger.debug( + "The supported languages for single-line blocks:", + utils.readJsonFile(this.singleLineLangDefinitionFilePath) + ); } /** diff --git a/src/extensionData.ts b/src/extensionData.ts index 46df8d1..900d26d 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -61,7 +61,7 @@ export class ExtensionData { private getExtensionPackageJsonData(): IPackageJson | null { // Get the package.json file path. const packageJSONPath = path.join(this.extensionPath, "package.json"); - return readJsonFile(packageJSONPath, false); + return readJsonFile(packageJSONPath, false); } /** diff --git a/src/interfaces/utils.ts b/src/interfaces/utils.ts new file mode 100644 index 0000000..d001118 --- /dev/null +++ b/src/interfaces/utils.ts @@ -0,0 +1,34 @@ +import {SingleLineCommentStyle} from "./commentStyles"; + +/** + * Represents a JSON object. + */ +export interface JsonObject { + [key: string]: JsonValue; +} + +/** + * Represents a valid JSON value. + */ +export type JsonValue = string | number | boolean | null | JsonObject | JsonArray; + +/** + * Represents a JSON array. + */ +export type JsonArray = JsonValue[] | readonly JsonValue[]; + +/** + * Structure for single-line language definitions JSON file + */ +export interface SingleLineLanguageDefinitions extends JsonObject { + supportedLanguages: Record; + customSupportedLanguages: Record; +} + +/** + * Structure for multi-line language definitions JSON file + */ +export interface MultiLineLanguageDefinitions extends JsonObject { + supportedLanguages: string[]; + customSupportedLanguages: string[]; +} diff --git a/src/utils.ts b/src/utils.ts index d8b49ab..b06a877 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,6 +2,7 @@ import * as fs from "node:fs"; import * as jsonc from "jsonc-parser"; import {logger} from "./logger"; import {window} from "vscode"; +import {JsonObject, JsonValue} from "./interfaces/utils"; /** * Read the file and parse the JSON. @@ -10,10 +11,11 @@ import {window} from "vscode"; * @param {boolean} [throwOnFileMissing=true] Whether to throw an error if the file doesn't exist. * If `false`, returns `null`. Default is `true`. * - * @returns {any | null} The JSON file content as an object, or `null` if file doesn't exist and `throwOnFileMissing` is `false`. + * @returns {T | null} The JSON file content as the passed T type (defaulting to a JSON object) + * or `null` if file doesn't exist and `throwOnFileMissing` is `false`. * @throws Will throw an error if the JSON file cannot be parsed or if file doesn't exist and `throwOnFileMissing` is `true`. */ -export function readJsonFile(filepath: string, throwOnFileMissing: boolean = true): any | null { +export function readJsonFile(filepath: string, throwOnFileMissing: boolean = true): T | null { // Check if file exists first. // If file doesn't exist... if (!fs.existsSync(filepath)) { @@ -46,7 +48,7 @@ export function readJsonFile(filepath: string, throwOnFileMissing: boolean = tru .showErrorMessage( `${errorMsg}. The extension cannot continue. Please check the "Auto Comment Blocks" Output Channel for errors.`, "OK", - "Open Output Channel", + "Open Output Channel" ) .then((selection) => { if (selection === "Open Output Channel") { @@ -57,7 +59,7 @@ export function readJsonFile(filepath: string, throwOnFileMissing: boolean = tru throw error; } - return jsonContents; + return jsonContents as T; } /** @@ -94,10 +96,10 @@ function constructJsonParseErrorMsg(filepath: string, fileContent: string, jsonE * Read the file and parse the JSON. * * @param {string} filepath The path of the file. - * @param {any} data The data to write into the file. + * @param {T} data The data to write into the file. * @returns The file content. */ -export function writeJsonFile(filepath: string, data: any): any { +export function writeJsonFile(filepath: string, data: T) { // Write the updated JSON back into the file and add tab indentation // to make it easier to read. fs.writeFileSync(filepath, JSON.stringify(data, null, "\t")); @@ -122,7 +124,7 @@ export function ensureDirExists(dir: string) { * @param key The key to check in the object * @returns {RegExp} The reconstructed regex pattern. */ -export function reconstructRegex(obj: any, key: string) { +export function reconstructRegex(obj: unknown, key: string) { // If key has a "pattern" key, then it's an object... if (Object.hasOwn(obj[key], "pattern")) { return new RegExp(obj[key].pattern); @@ -139,7 +141,7 @@ export function reconstructRegex(obj: any, key: string) { * Code based on this StackOverflow answer https://stackoverflow.com/a/45728850/2358222 * * @param {Map>} m The Map to convert to an object. - * @returns {object} The converted object. + * @returns {JsonObject} The converted object. * * @example * reverseMapping( @@ -165,8 +167,8 @@ export function reconstructRegex(obj: any, key: string) { * } * } */ -export function convertMapToReversedObject(m: Map>): object { - const result: any = {}; +export function convertMapToReversedObject(m: Map>): JsonObject { + const result: JsonObject = {}; // Convert a nested key:value Map from inside another Map into an key:array object, // while reversing/switching the keys and values. The Map's values are now the keys of From e6db6611c432d06689b10a52ff39c2763a665da3 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 1 Feb 2026 02:37:33 +0000 Subject: [PATCH 31/38] fix: types for `writeJsonFile` & `convertMapToReversedObject` functions. - Fixed returns for `writeJsonFile` util function. In the previous commit (a832c7e), the return type was changed from `any` to a passable generic T type. But despite always being typed as returning a value even before the commit, the function doesn't actually return a value. It only performs a side effect by writing to disk. Fixed by removing the return type altogether and the docblock return comment. Also changed it's function calls in `Configuration::writeCommentLanguageDefinitionsToJsonFile` method to remove the passing of the generic return types. - Changed `data` param of `writeJsonFile` util function to be of type `JsonValue` instead of `T`. - Fixed `convertMapToReversedObject` util function return type to a passable generic `T` type, and that defaults to return of `JsonObject` if the type is not defined when calling. This keeps it constraint to `JsonObject` type while also accepting a passable return type that is JSONable. And changed the function calls in `Configuration::writeCommentLanguageDefinitionsToJsonFile` method to pass the `SingleLineLanguageDefinitions` interface as it's generic return type instead of casting it as such. - Removed the type from the `result` variable declaration in `convertMapToReversedObject` util function and we now cast it `as T` at the end when returning to ensure it satisfies the new return typing. --- src/configuration.ts | 6 +++--- src/utils.ts | 13 ++++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 72efdc6..6b5b8c5 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -674,15 +674,15 @@ export class Configuration { utils.ensureDirExists(this.autoGeneratedDir); // Convert the singleLineBlocksMap to an object. - const singleLineData = utils.convertMapToReversedObject(this.singleLineBlocksMap) as SingleLineLanguageDefinitions; + 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, singleLineData); // Write into the multi-line-languages.json file. - utils.writeJsonFile(this.multiLineLangDefinitionFilePath, multiLineData); + utils.writeJsonFile(this.multiLineLangDefinitionFilePath, multiLineData); } /** diff --git a/src/utils.ts b/src/utils.ts index b06a877..64a09b6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -96,10 +96,9 @@ function constructJsonParseErrorMsg(filepath: string, fileContent: string, jsonE * Read the file and parse the JSON. * * @param {string} filepath The path of the file. - * @param {T} data The data to write into the file. - * @returns The file content. + * @param {JsonValue} data The data to write into the file. */ -export function writeJsonFile(filepath: string, data: T) { +export function writeJsonFile(filepath: string, data: JsonValue) { // Write the updated JSON back into the file and add tab indentation // to make it easier to read. fs.writeFileSync(filepath, JSON.stringify(data, null, "\t")); @@ -141,7 +140,7 @@ export function reconstructRegex(obj: unknown, key: string) { * Code based on this StackOverflow answer https://stackoverflow.com/a/45728850/2358222 * * @param {Map>} m The Map to convert to an object. - * @returns {JsonObject} The converted object. + * @returns {T} The converted object. * * @example * reverseMapping( @@ -167,8 +166,8 @@ export function reconstructRegex(obj: unknown, key: string) { * } * } */ -export function convertMapToReversedObject(m: Map>): JsonObject { - const result: JsonObject = {}; +export function convertMapToReversedObject(m: Map>): T { + const result = {}; // Convert a nested key:value Map from inside another Map into an key:array object, // while reversing/switching the keys and values. The Map's values are now the keys of @@ -192,7 +191,7 @@ export function convertMapToReversedObject(m: Map>): // as the key. result[key] = Object.keys(o).reduce((r, k) => Object.assign(r, {[o[k]]: (r[o[k]] || []).concat(k)}), {}); } - return result; + return result as T; } /** From c224f7b4529b89f278e3755f049d0cfdd335e3a5 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 1 Feb 2026 03:01:00 +0000 Subject: [PATCH 32/38] fix: `reconstructRegex` return type and docblock types. - Fixed `reconstructRegex` return type to explicitly be typed as `RegExp` and added docblock types. --- src/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 64a09b6..dff78eb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -119,11 +119,11 @@ export function ensureDirExists(dir: string) { * Reconstruct the regex pattern because vscode doesn't like the regex pattern as a string, * or some patterns are not working as expected. * - * @param obj The object - * @param key The key to check in the object + * @param {unknown} obj The object + * @param {string} key The key to check in the object * @returns {RegExp} The reconstructed regex pattern. */ -export function reconstructRegex(obj: unknown, key: string) { +export function reconstructRegex(obj: unknown, key: string): RegExp { // If key has a "pattern" key, then it's an object... if (Object.hasOwn(obj[key], "pattern")) { return new RegExp(obj[key].pattern); From dce869ce167ad5eae79a551423bb408e27d53ba4 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 1 Feb 2026 07:04:39 +0000 Subject: [PATCH 33/38] fix: `mergeArraysBy` generic return type. - Fixed `mergeArraysBy` util function call in `Configuration::setLanguageConfiguration` method to pass its generic return type as vscode's `AutoClosingPair` or `OnEnterRule`. The function is declared with a passable generic `T` type, but it wasn't being used, so we might as well use it and pass the return types. --- src/configuration.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 6b5b8c5..0433a13 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -714,10 +714,18 @@ export class Configuration { let langConfig = {...internalLangConfig}; if (multiLine) { - langConfig.autoClosingPairs = utils.mergeArraysBy(defaultMultiLineConfig.autoClosingPairs, internalLangConfig?.autoClosingPairs, "open"); + langConfig.autoClosingPairs = utils.mergeArraysBy( + defaultMultiLineConfig.autoClosingPairs, + internalLangConfig?.autoClosingPairs, + "open" + ); // Add the multi-line onEnter rules to the langConfig. - langConfig.onEnterRules = utils.mergeArraysBy(Rules.multilineEnterRules, internalLangConfig?.onEnterRules, "beforeText"); + langConfig.onEnterRules = utils.mergeArraysBy( + Rules.multilineEnterRules, + internalLangConfig?.onEnterRules, + "beforeText" + ); // Only assign the default config comments if it doesn't already exist. // (nullish assignment operator ??=) @@ -750,15 +758,15 @@ export class Configuration { if (isOnEnter && singleLineStyle) { // //-style comments if (singleLineStyle === "//") { - langConfig.onEnterRules = utils.mergeArraysBy(Rules.slashEnterRules, langConfig?.onEnterRules, "beforeText"); + langConfig.onEnterRules = utils.mergeArraysBy(Rules.slashEnterRules, langConfig?.onEnterRules, "beforeText"); } // #-style comments else if (singleLineStyle === "#") { - langConfig.onEnterRules = utils.mergeArraysBy(Rules.hashEnterRules, langConfig?.onEnterRules, "beforeText"); + langConfig.onEnterRules = utils.mergeArraysBy(Rules.hashEnterRules, langConfig?.onEnterRules, "beforeText"); } // ;-style comments else if (singleLineStyle === ";") { - langConfig.onEnterRules = utils.mergeArraysBy(Rules.semicolonEnterRules, langConfig?.onEnterRules, "beforeText"); + langConfig.onEnterRules = utils.mergeArraysBy(Rules.semicolonEnterRules, langConfig?.onEnterRules, "beforeText"); } } // If isOnEnter is false AND singleLineStyle isn't false, i.e. a string. From 3bebd6b22ea8426cc247cde00506b4cd12d41f1c Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Sun, 1 Feb 2026 07:09:04 +0000 Subject: [PATCH 34/38] docs: add docblock to `ExtensionData::setExtensionDiscoveryPaths` method --- src/extensionData.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/extensionData.ts b/src/extensionData.ts index 900d26d..c9db9c4 100644 --- a/src/extensionData.ts +++ b/src/extensionData.ts @@ -90,6 +90,9 @@ export class ExtensionData { this.extensionData.set("packageJSON", this.packageJsonData); } + /** + * Set the extension discovery paths into the extensionDiscoveryPaths Map. + */ private setExtensionDiscoveryPaths() { // The path to the user extensions. // From 90f93517712e1250b9b6a38c65cb7da8e6b9d59e Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 2 Feb 2026 04:29:33 +0000 Subject: [PATCH 35/38] feat: add types to various `Configuration` methods and variables. - Added `string` types to these class properties: - `autoGeneratedDir` - `singleLineLangDefinitionFilePath` - `multiLineLangDefinitionFilePath` - Added return types to these methods: - `configureCommentBlocks` - `registerCommands` - `getOverriddenMultiLineComment` - `getLanguageConfig` - Fixed some docblock formatting and comments. - Added `JsonObject` type to the variables `extensionsPaths` and `env` in `logDebugInfo` method. --- src/configuration.ts | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 0433a13..1dfd14b 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -14,8 +14,7 @@ import {ExtensionData} from "./extensionData"; import {Settings} from "./interfaces/settings"; import {LineComment, SingleLineCommentStyle} from "./interfaces/commentStyles"; import {ExtensionMetaData} from "./interfaces/extensionMetaData"; -import {MultiLineLanguageDefinitions, SingleLineLanguageDefinitions} from "./interfaces/utils"; -import {JsonArray} from "./interfaces/utils"; +import {JsonObject, JsonArray, MultiLineLanguageDefinitions, SingleLineLanguageDefinitions} from "./interfaces/utils"; export class Configuration { /************** @@ -58,19 +57,19 @@ export class Configuration { * The directory where the auto-generated language definitions are stored. * @type {string} */ - private readonly autoGeneratedDir = `${__dirname}/../../auto-generated-language-definitions`; + private readonly autoGeneratedDir: string = `${__dirname}/../../auto-generated-language-definitions`; /** * The file path for the single-line language definitions. * @type {string} */ - private readonly singleLineLangDefinitionFilePath = `${this.autoGeneratedDir}/single-line-languages.json`; + private readonly singleLineLangDefinitionFilePath: string = `${this.autoGeneratedDir}/single-line-languages.json`; /** * The file path for the multi-line language definitions. * @type {string} */ - private readonly multiLineLangDefinitionFilePath = `${this.autoGeneratedDir}/multi-line-languages.json`; + private readonly multiLineLangDefinitionFilePath: string = `${this.autoGeneratedDir}/multi-line-languages.json`; /*********** * Methods * @@ -96,7 +95,7 @@ export class Configuration { * * @returns {vscode.Disposable[]} */ - public configureCommentBlocks() { + public configureCommentBlocks(): vscode.Disposable[] { const disposables: vscode.Disposable[] = []; /** @@ -161,16 +160,18 @@ export class Configuration { * * @returns {vscode.Disposable[]} */ - public registerCommands() { + public registerCommands(): vscode.Disposable[] { const singleLineBlockCommand = vscode.commands.registerTextEditorCommand("auto-comment-blocks.singleLineBlock", (textEditor, edit, args) => { this.handleSingleLineBlock(textEditor, edit); }); + const changeBladeMultiLineBlockCommand = vscode.commands.registerTextEditorCommand( "auto-comment-blocks.changeBladeMultiLineBlock", (textEditor, edit, args) => { this.handleChangeBladeMultiLineBlock(textEditor); } ); + return [singleLineBlockCommand, changeBladeMultiLineBlockCommand]; } @@ -283,9 +284,9 @@ export class Configuration { * Get the overridden multi-line comment for the specified language ID. * * @param {string} langId Language ID - * @returns {string} + * @returns {string} The overridden multi-line comment style. */ - private getOverriddenMultiLineComment(langId: string) { + private getOverriddenMultiLineComment(langId: string): string { const overriddenList = this.getConfigurationValue("overrideDefaultLanguageMultiLineComments"); return overriddenList[langId]; @@ -464,7 +465,7 @@ export class Configuration { * @param langId Language ID * @returns {vscode.LanguageConfiguration | undefined} */ - private getLanguageConfig(langId: string) { + private getLanguageConfig(langId: string): vscode.LanguageConfiguration | undefined { if (this.languageConfigs.has(langId)) { return this.languageConfigs.get(langId); } @@ -689,8 +690,8 @@ export class Configuration { * Sets the language configuration for a given language ID. * * @param {string} langId - The language ID for which the configuration is being set. - * @param {boolean} multiLine - Optional. If true, sets multi-line comment configuration. - * @param {string} singleLineStyle - Optional. Specifies the style of single-line comments (e.g., "//", "#", ";"). + * @param {boolean} multiLine - Optional. If `true`, sets multi-line comment configuration. + * @param {string} singleLineStyle - Optional. Specifies the style of single-line comments (e.g., `"//"`, `"#"`, `";"`). * * @returns {vscode.Disposable} * @@ -988,7 +989,7 @@ export class Configuration { // So we can use it for both Windows and WSL. const builtInExtensionsPath = this.extensionData.getExtensionDiscoveryPath("builtInExtensionsPath"); - let extensionsPaths = {}; + let extensionsPaths: JsonObject = {}; if (isWsl) { // Get the Windows user and built-in extensions paths. @@ -1008,7 +1009,7 @@ export class Configuration { }; } - const env = { + const env: JsonObject = { "OS": process.platform, "Platform": process.platform, "VS Code Details": { From d611ee7b81d0670e5cbff5161c600138e5155191 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 2 Feb 2026 04:58:25 +0000 Subject: [PATCH 36/38] feat: add `SingleLineCommentStyle` type to variables and params. - Changed various docblock comments. - Changed Map key typings to the union type of `"customSupportedLanguages" | "supportedLanguages"` instead of just `string` in the class properties `singleLineBlocksMap` and `multiLineBlocksMap`. This is to be more verbose for what keys the Map can have. Since it's only these 2 keys, it makes sense to just use them as a union type. It also aids in vscode intellisense. - Changed typings to use `SingleLineCommentStyle` type instead of just `string` for: - `singleLineBlocksMap` class property inner Map value. - `getSingleLineLanguages` method return type. - `tempMap` variable in `setSingleLineCommentLanguageDefinitions` method. - Docblock types. - `singleLineStyle` param of `setLanguageConfiguration` method. - `style` variable in `handleSingleLineBlock` method. - Added new `ExtraSingleLineCommentStyles` type to define all the extra single-line comment styles like `///` and `##`. This is needed to prevent ts errors like: "error TS2322: Type '";;"' is not assignable to type 'SingleLineCommentStyle'." This was because changing the style types to use `SingleLineCommentStyle`, it affected the `handleSingleLineBlock` method usage of setting the extra styles directly on the `style` variable. Thus ts would rightly error that we're not allowed to set the style to anything other than specified in the union type. This new type is the union of all the extra styles, and we just use it on the `style` variable as a union of both `SingleLineCommentStyle` and `ExtraSingleLineCommentStyles` to ensure the extra styles are allowed. --- src/configuration.ts | 33 +++++++++++++++++++-------------- src/interfaces/commentStyles.ts | 8 ++++++++ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 1dfd14b..6c87846 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -12,7 +12,7 @@ import {logger} from "./logger"; import * as utils from "./utils"; import {ExtensionData} from "./extensionData"; import {Settings} from "./interfaces/settings"; -import {LineComment, SingleLineCommentStyle} from "./interfaces/commentStyles"; +import {ExtraSingleLineCommentStyles, LineComment, SingleLineCommentStyle} from "./interfaces/commentStyles"; import {ExtensionMetaData} from "./interfaces/extensionMetaData"; import {JsonObject, JsonArray, MultiLineLanguageDefinitions, SingleLineLanguageDefinitions} from "./interfaces/utils"; @@ -38,20 +38,24 @@ export class Configuration { private readonly languageConfigs = new Map(); /** - * A key:value Map object of supported language IDs and their single-line style comments. + * A key:value Map object of supported and custom supported language IDs + * and their single-line style comments. * - * @property {string} key Language ID. - * @property {string} value Style of line comment. + * @property {string} - Map key can be either "customSupportedLanguages" + * or "supportedLanguages". + * @property {Map} - Map value is an inner Map object of + * language IDs and their single-line comment styles. */ - private singleLineBlocksMap: Map> = new Map(); + private singleLineBlocksMap: Map<"customSupportedLanguages" | "supportedLanguages", Map> = new Map(); /** * A Map object of an array of supported language IDs for multi-line block comments. * - * @property {string} key - "languages" - * @property {string[]} value - Array of language IDs. + * @property {string} - Map key can be either "customSupportedLanguages" + * or "supportedLanguages". + * @property {string[]} - Map value is an array of language IDs. */ - private multiLineBlocksMap: Map = new Map(); + private multiLineBlocksMap: Map<"customSupportedLanguages" | "supportedLanguages", string[]> = new Map(); /** * The directory where the auto-generated language definitions are stored. @@ -525,9 +529,9 @@ export class Configuration { * Get the single-line languages and styles. * * @param {"supportedLanguages" | "customSupportedLanguages"} key A stringed key, either `"supportedLanguages"` or `"customSupportedLanguages"` - * @returns {Map} The Map of the languages and styles. + * @returns {Map} The Map of the languages and styles. */ - private getSingleLineLanguages(key: "supportedLanguages" | "customSupportedLanguages"): Map { + private getSingleLineLanguages(key: "supportedLanguages" | "customSupportedLanguages"): Map { // The non-null assertion operator (!) ensures that the key is never undefined. return this.singleLineBlocksMap.get(key)!; } @@ -582,7 +586,8 @@ export class Configuration { * Set the single-line comments language definitions. */ private setSingleLineCommentLanguageDefinitions() { - const tempMap: Map = new Map(); + const tempMap: Map = new Map(); + this.languageConfigs.forEach((config: vscode.LanguageConfiguration, langId: string) => { // console.log(langId, config.comments.lineComment); let style: SingleLineCommentStyle | null = null; @@ -691,7 +696,7 @@ export class Configuration { * * @param {string} langId - The language ID for which the configuration is being set. * @param {boolean} multiLine - Optional. If `true`, sets multi-line comment configuration. - * @param {string} singleLineStyle - Optional. Specifies the style of single-line comments (e.g., `"//"`, `"#"`, `";"`). + * @param {SingleLineCommentStyle} singleLineStyle - Optional. Specifies the style of single-line comments (e.g., `"//"`, `"#"`, `";"`). * * @returns {vscode.Disposable} * @@ -708,7 +713,7 @@ export class Configuration { * Note: This method ensures that the language configuration is correctly set and avoids issues * with rogue characters being inserted on new lines. */ - private setLanguageConfiguration(langId: string, multiLine?: boolean, singleLineStyle?: string): vscode.Disposable { + private setLanguageConfiguration(langId: string, 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; @@ -895,7 +900,7 @@ export class Configuration { // Get the langId from the auto-supported langs. If it doesn't exist, try getting it from // the custom-supported langs instead. - var style = singleLineLangs.get(langId) ?? customSingleLineLangs.get(langId); + var style: SingleLineCommentStyle | ExtraSingleLineCommentStyles = singleLineLangs.get(langId) ?? customSingleLineLangs.get(langId); if (style && textEditor.selection.isEmpty) { let line = textEditor.document.lineAt(textEditor.selection.active); diff --git a/src/interfaces/commentStyles.ts b/src/interfaces/commentStyles.ts index eb9f0fd..0206fb2 100644 --- a/src/interfaces/commentStyles.ts +++ b/src/interfaces/commentStyles.ts @@ -1,5 +1,13 @@ +/** + * Define the single-line comment styles. + */ export type SingleLineCommentStyle = "//" | "#" | ";"; +/** + * Define the extra single-line comment styles, like `///`, etc. + */ +export type ExtraSingleLineCommentStyles = "##" | ";;" | "///" | "//!"; + /** * Line Comments * From 3e9d9a475f7279526b0cdcdbbd6230eb7c13d5b7 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 2 Feb 2026 05:01:49 +0000 Subject: [PATCH 37/38] fix: docblock param name in `Logger::setupOutputChannel` method. - Fixed the docblock param `channel` to the actual name of the param `channelOverride`. --- src/logger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logger.ts b/src/logger.ts index 6d91cbe..91ef9fb 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -33,7 +33,7 @@ class Logger { /** * Override the output channel * - * @param {OutputChannel} channel A vscode output channel. + * @param {OutputChannel} channelOverride A vscode output channel. */ public setupOutputChannel(channelOverride?: OutputChannel): void { if (channelOverride) { From 73b3fbb757e393eda3542ce4d46c1fd4cdc7cb82 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 2 Feb 2026 05:29:13 +0000 Subject: [PATCH 38/38] feat: add new `LanguageId` type to be more self documenting. - Added new type `LanguageId` which is just an alias for `string`. By typing relevant strings as `LanguageId`, it becomes more self documenting. - Changed `languageConfigFilePaths` and `languageConfigs` class properties to define it's types on the property instead of defining the generic type of the `new Map` instance. This is just to keep things looking clean and inline with other code. - Changed `string` types to use the new `LanguageId` in `Configuration` class for: - `languageConfigFilePaths`, `languageConfigs`, `singleLineBlocksMap`, and `multiLineBlocksMap` class property Maps. - Docblock types. - `isLangIdDisabled`, `isLangIdMultiLineCommentOverridden`, `getOverriddenMultiLineComment` and `getLanguageConfig` method params. - Various variables in many methods. - `getMultiLineLanguages` and `getSingleLineLanguages` method returns. --- src/configuration.ts | 56 ++++++++++++++++++++--------------------- src/interfaces/utils.ts | 5 ++++ 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/configuration.ts b/src/configuration.ts index 6c87846..d46b8c6 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -14,7 +14,7 @@ import {ExtensionData} from "./extensionData"; import {Settings} from "./interfaces/settings"; import {ExtraSingleLineCommentStyles, LineComment, SingleLineCommentStyle} from "./interfaces/commentStyles"; import {ExtensionMetaData} from "./interfaces/extensionMetaData"; -import {JsonObject, JsonArray, MultiLineLanguageDefinitions, SingleLineLanguageDefinitions} from "./interfaces/utils"; +import {JsonObject, JsonArray, LanguageId, MultiLineLanguageDefinitions, SingleLineLanguageDefinitions} from "./interfaces/utils"; export class Configuration { /************** @@ -30,12 +30,12 @@ export class Configuration { /** * A key:value Map object of language IDs and their config file paths. */ - private languageConfigFilePaths = new Map(); + private languageConfigFilePaths: Map = new Map(); /** * A key:value Map object of language IDs and their configs. */ - private readonly languageConfigs = new Map(); + private readonly languageConfigs: Map = new Map(); /** * A key:value Map object of supported and custom supported language IDs @@ -43,19 +43,19 @@ export class Configuration { * * @property {string} - Map key can be either "customSupportedLanguages" * or "supportedLanguages". - * @property {Map} - Map value is an inner Map object of + * @property {Map} - Map value is an inner Map object of * language IDs and their single-line comment styles. */ - private singleLineBlocksMap: Map<"customSupportedLanguages" | "supportedLanguages", Map> = new Map(); + private singleLineBlocksMap: Map<"customSupportedLanguages" | "supportedLanguages", Map> = new Map(); /** * A Map object of an array of supported language IDs for multi-line block comments. * * @property {string} - Map key can be either "customSupportedLanguages" * or "supportedLanguages". - * @property {string[]} - Map value is an array of language IDs. + * @property {LanguageId[]} - Map value is an array of language IDs. */ - private multiLineBlocksMap: Map<"customSupportedLanguages" | "supportedLanguages", string[]> = new Map(); + private multiLineBlocksMap: Map<"customSupportedLanguages" | "supportedLanguages", LanguageId[]> = new Map(); /** * The directory where the auto-generated language definitions are stored. @@ -265,20 +265,20 @@ export class Configuration { /** * Is the language ID disabled? - * @param {string} langId Language ID + * @param {LanguageId} langId Language ID * @returns {boolean} */ - public isLangIdDisabled(langId: string): boolean { + public isLangIdDisabled(langId: LanguageId): boolean { return this.getConfigurationValue("disabledLanguages").includes(langId); } /** * Is the multi-line comment overridden for the specified language ID? * - * @param {string} langId Language ID + * @param {LanguageId} langId Language ID * @returns {boolean} */ - private isLangIdMultiLineCommentOverridden(langId: string): boolean { + private isLangIdMultiLineCommentOverridden(langId: LanguageId): boolean { const overriddenList = this.getConfigurationValue("overrideDefaultLanguageMultiLineComments"); return overriddenList.hasOwnProperty(langId); @@ -287,10 +287,10 @@ export class Configuration { /** * Get the overridden multi-line comment for the specified language ID. * - * @param {string} langId Language ID + * @param {LanguageId} langId Language ID * @returns {string} The overridden multi-line comment style. */ - private getOverriddenMultiLineComment(langId: string): string { + private getOverriddenMultiLineComment(langId: LanguageId): string { const overriddenList = this.getConfigurationValue("overrideDefaultLanguageMultiLineComments"); return overriddenList[langId]; @@ -349,7 +349,7 @@ export class Configuration { if (Object.hasOwn(packageJSON, "contributes") && Object.hasOwn(packageJSON.contributes, "languages")) { // Loop through the languages... for (let language of packageJSON.contributes.languages) { - const langId = language.id; + const langId: LanguageId = language.id; // Get the languages to skip. let skipLangs = this.getLanguagesToSkip(); @@ -466,10 +466,10 @@ export class Configuration { /** * Get the config of the specified language. * - * @param langId Language ID + * @param {LanguageId} langId Language ID * @returns {vscode.LanguageConfiguration | undefined} */ - private getLanguageConfig(langId: string): vscode.LanguageConfiguration | undefined { + private getLanguageConfig(langId: LanguageId): vscode.LanguageConfiguration | undefined { if (this.languageConfigs.has(langId)) { return this.languageConfigs.get(langId); } @@ -518,9 +518,9 @@ export class Configuration { * Get the multi-line languages from the Map. * * @param {"supportedLanguages" | "customSupportedLanguages"} key A stringed key, either `"supportedLanguages"` or `"customSupportedLanguages"` - * @returns {string[]} An array of language ID strings. + * @returns {LanguageId[]} An array of language ID strings. */ - private getMultiLineLanguages(key: "supportedLanguages" | "customSupportedLanguages"): string[] { + private getMultiLineLanguages(key: "supportedLanguages" | "customSupportedLanguages"): LanguageId[] { // The non-null assertion operator (!) ensures that the key is never undefined. return this.multiLineBlocksMap.get(key)!; } @@ -529,9 +529,9 @@ export class Configuration { * Get the single-line languages and styles. * * @param {"supportedLanguages" | "customSupportedLanguages"} key A stringed key, either `"supportedLanguages"` or `"customSupportedLanguages"` - * @returns {Map} The Map of the languages and styles. + * @returns {Map} The Map of the languages and styles. */ - private getSingleLineLanguages(key: "supportedLanguages" | "customSupportedLanguages"): Map { + private getSingleLineLanguages(key: "supportedLanguages" | "customSupportedLanguages"): Map { // The non-null assertion operator (!) ensures that the key is never undefined. return this.singleLineBlocksMap.get(key)!; } @@ -540,9 +540,9 @@ export class Configuration { * Set the multi-line comments language definitions. */ private setMultiLineCommentLanguageDefinitions() { - let langArray: string[] = []; + let langArray: LanguageId[] = []; - this.languageConfigs.forEach((config: vscode.LanguageConfiguration, langId: string) => { + this.languageConfigs.forEach((config: vscode.LanguageConfiguration, langId: LanguageId) => { // If the config object has own property of comments AND the comments key has // own property of blockComment... if (Object.hasOwn(config, "comments") && Object.hasOwn(config.comments, "blockComment")) { @@ -586,9 +586,9 @@ export class Configuration { * Set the single-line comments language definitions. */ private setSingleLineCommentLanguageDefinitions() { - const tempMap: Map = new Map(); + const tempMap: Map = new Map(); - this.languageConfigs.forEach((config: vscode.LanguageConfiguration, langId: string) => { + this.languageConfigs.forEach((config: vscode.LanguageConfiguration, langId: LanguageId) => { // console.log(langId, config.comments.lineComment); let style: SingleLineCommentStyle | null = null; @@ -694,7 +694,7 @@ export class Configuration { /** * Sets the language configuration for a given language ID. * - * @param {string} langId - The language ID for which the configuration is being set. + * @param {LanguageId} langId - The language ID for which the configuration is being set. * @param {boolean} multiLine - Optional. If `true`, sets multi-line comment configuration. * @param {SingleLineCommentStyle} singleLineStyle - Optional. Specifies the style of single-line comments (e.g., `"//"`, `"#"`, `";"`). * @@ -713,7 +713,7 @@ export class Configuration { * Note: This method ensures that the language configuration is correctly set and avoids issues * with rogue characters being inserted on new lines. */ - private setLanguageConfiguration(langId: string, multiLine?: boolean, singleLineStyle?: SingleLineCommentStyle): vscode.Disposable { + 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; @@ -894,7 +894,7 @@ export class Configuration { * @param {vscode.TextEditorEdit} edit The text editor edits. */ private handleSingleLineBlock(textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit) { - let langId = textEditor.document.languageId; + let langId: LanguageId = textEditor.document.languageId; const singleLineLangs = this.getSingleLineLanguages("supportedLanguages"); const customSingleLineLangs = this.getSingleLineLanguages("customSupportedLanguages"); @@ -952,7 +952,7 @@ export class Configuration { * @param {vscode.TextEditor} textEditor The text editor. */ private handleChangeBladeMultiLineBlock(textEditor: vscode.TextEditor) { - let langId = textEditor.document.languageId; + let langId: LanguageId = textEditor.document.languageId; const extensionName = this.extensionData.get("namespace"); // Only carry out function if languageId is blade. diff --git a/src/interfaces/utils.ts b/src/interfaces/utils.ts index d001118..404880f 100644 --- a/src/interfaces/utils.ts +++ b/src/interfaces/utils.ts @@ -32,3 +32,8 @@ export interface MultiLineLanguageDefinitions extends JsonObject { supportedLanguages: string[]; customSupportedLanguages: string[]; } + +/** + * Language ID + */ +export type LanguageId = string;