Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
dd01418
feat: add `Settings` interface to allow intellisense & full type safety.
yCodeTech Jan 11, 2026
d3e9bac
fix: `overrideDefaultLanguageMultiLineComments` setting type.
yCodeTech Jan 11, 2026
dce0661
fix: remove `SupportUnsupportedLanguages` interface import.
yCodeTech Jan 11, 2026
00db701
fix: removed the generic typing in extension.ts
yCodeTech Jan 11, 2026
16e481d
ci: change job name from `deploy` to `check-ts`
yCodeTech Jan 11, 2026
b5d1bcb
refactor: merge `createExtensionData` into `setExtensionData` method
yCodeTech Jan 17, 2026
83c51ed
feat: add new `ExtensionMetaData` interface for better typings
yCodeTech Jan 17, 2026
81f797b
refactor: `setBladeComments` method and fix typings.
yCodeTech Jan 21, 2026
8af89da
fix: parameter type `any` to `unknown` in `updateConfigurationValue`
yCodeTech Jan 21, 2026
21bfecb
fix: language config types to vscode's `LanguageConfiguration`
yCodeTech Jan 21, 2026
2821c58
refactor: the `lineComment` style check to use strict equality checks
yCodeTech Jan 21, 2026
f2504bc
fix: types for line comments in `setSingleLineCommentLanguageDefiniti…
yCodeTech Jan 21, 2026
5965287
fix: ts type error while trying to set blade comments.
yCodeTech Jan 21, 2026
0b776fc
remove: the unused duplicate `style` variable.
yCodeTech Jan 21, 2026
1365c46
fix: the missing imports for `LineComment` & `SingleLineCommentStyle`
yCodeTech Jan 21, 2026
824ce9b
fix: `data` param in logger's `debug` method to be optional
yCodeTech Jan 23, 2026
6534893
feat: add a dedicated `namespace` property for the `extensionData`.
yCodeTech Jan 23, 2026
07def7e
docs: add docblocks to the `ExtensionMetaData` interface.
yCodeTech Jan 23, 2026
be9d972
refactor: paths to installed extensions into a separate method
yCodeTech Jan 24, 2026
2b0ce9a
refactor: move creation of the extension ID into `setExtensionData`.
yCodeTech Jan 25, 2026
6f77cc5
refactor: move `settingsNamespace` creation into `setExtensionData`.
yCodeTech Jan 25, 2026
c2711eb
fix: JSON file reading with optional `null` return for missing files.
yCodeTech Jan 26, 2026
ea401c8
feat: add `extensionPath` property to the `ExtensionData` class.
yCodeTech Jan 26, 2026
b693795
fix: add `null` check for `packageJsonData` before setting extensionData
yCodeTech Jan 26, 2026
279d3b4
refactor: allow the `extensionData` Map for any extension's data.
yCodeTech Jan 26, 2026
9ff0857
fix: remove the testing only code mistakenly added in commit be9d972
yCodeTech Jan 26, 2026
a3a2b90
docs: add some code comments
yCodeTech Jan 30, 2026
c246fe2
fix: improve type safety and clarity
yCodeTech Jan 30, 2026
4c4e361
chore(prettier): add `trailingComma` option and set to `es5`.
yCodeTech Jan 30, 2026
a832c7e
fix: introduce proper typing for json objects.
yCodeTech Jan 30, 2026
e6db661
fix: types for `writeJsonFile` & `convertMapToReversedObject` functions.
yCodeTech Feb 1, 2026
c224f7b
fix: `reconstructRegex` return type and docblock types.
yCodeTech Feb 1, 2026
dce869c
fix: `mergeArraysBy` generic return type.
yCodeTech Feb 1, 2026
3bebd6b
docs: add docblock to `ExtensionData::setExtensionDiscoveryPaths` method
yCodeTech Feb 1, 2026
90f9351
feat: add types to various `Configuration` methods and variables.
yCodeTech Feb 2, 2026
d611ee7
feat: add `SingleLineCommentStyle` type to variables and params.
yCodeTech Feb 2, 2026
3e9d9a4
fix: docblock param name in `Logger::setupOutputChannel` method.
yCodeTech Feb 2, 2026
73b3fbb
feat: add new `LanguageId` type to be more self documenting.
yCodeTech Feb 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check-ts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion .prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"*.ts"
],
"options": {
"quoteProps": "consistent"
"quoteProps": "consistent",
"trailingComma": "es5"
}
}
]
Expand Down
277 changes: 157 additions & 120 deletions src/configuration.ts

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ 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");

let disabledLangConfig: string[] = configuration.getConfigurationValue<string[]>("disabledLanguages");
let disabledLangConfig: string[] = configuration.getConfigurationValue("disabledLanguages");

if (disabledLangConfig.length > 0) {
vscode.window.showInformationMessage(`${disabledLangConfig.join(", ")} languages are disabled for ${extensionDisplayName}.`);
Expand All @@ -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.

/**
Expand All @@ -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<boolean>("bladeOverrideComments");
let bladeOverrideComments: boolean = configuration.getConfigurationValue("bladeOverrideComments");

configuration.setBladeComments(bladeOverrideComments);

Expand Down
167 changes: 107 additions & 60 deletions src/extensionData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,29 @@ import isWsl from "is-wsl";
import {IPackageJson} from "package-json-type";

import {readJsonFile} from "./utils";
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<string, string>}
* @type {Map<keyof ExtensionMetaData, ExtensionMetaDataValue>}
*/
private extensionData = new Map<string, string>();
private extensionData = new Map<keyof ExtensionMetaData, ExtensionMetaDataValue>();

/**
* Extension discovery paths in the form of a key:value Map object.
*
* @type {Map<keyof ExtensionPaths, string>}
*/
private extensionDiscoveryPaths = new Map<keyof ExtensionPaths, string>();

/**
* The absolute path of the requested extension.
*
* @type {string}
*/
private readonly extensionPath: string;

/**
* The package.json data for this extension.
Expand All @@ -20,93 +35,125 @@ export class ExtensionData {
*/
private packageJsonData: IPackageJson;

public constructor() {
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();
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 {
const extensionPath = path.join(__dirname, "../../");

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.
// 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;
private getExtensionPackageJsonData(): IPackageJson | null {
// Get the package.json file path.
const packageJSONPath = path.join(this.extensionPath, "package.json");
return readJsonFile<IPackageJson>(packageJSONPath, false);
}

/**
* 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 ID (publisher.name).
const id = `${this.packageJsonData.publisher}.${this.packageJsonData.name}`;

// Set each key-value pair directly into the Map
this.extensionData.set("id", id);
this.extensionData.set("name", this.packageJsonData.name);

// 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);
}

/**
* 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.
* Set the extension discovery paths into the extensionDiscoveryPaths Map.
*/
private createExtensionData() {
private setExtensionDiscoveryPaths() {
// 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"),

// 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;
//
// 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);
// The path to the built-in extensions.
// This env variable changes when on WSL to it's WSL-built-in extensions path.
this.extensionDiscoveryPaths.set("builtInExtensionsPath", path.join(vscode.env.appRoot, "extensions"));

// Only set these if running in WSL
if (isWsl) {
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"));
}
}

/**
* Get the extension's data by a specified key.
*
* @param {K} key The key of the extension detail to get.
*
* @returns {ReturnType<typeof this.createExtensionData>[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<K extends keyof ExtensionMetaData>(key: K): ExtensionMetaData[K] | undefined {
return this.extensionData.get(key) as ExtensionMetaData[K] | undefined;
}

/**
* Get all extension data as a plain object.
*
* @returns {ExtensionMetaData} A plain object containing all extension details.
*/
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;
}

/**
* 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 get<K extends keyof ReturnType<typeof this.createExtensionData>>(key: K): ReturnType<typeof this.createExtensionData>[K] | undefined {
return this.extensionData.get(key) as ReturnType<typeof this.createExtensionData>[K] | undefined;
public getExtensionDiscoveryPath<K extends keyof ExtensionPaths>(key: K): ExtensionPaths[K] | undefined {
return this.extensionDiscoveryPaths.get(key) as ExtensionPaths[K] | undefined;
}

/**
* Get all extension data.
* Get all extension discovery paths.
*
* @returns {ReadonlyMap<string, string>} A read-only Map containing all extension details.
* @returns {ReadonlyMap<keyof ExtensionPaths, string>} A read-only Map containing all extension discovery paths.
*/
public getAll(): ReadonlyMap<string, string> {
return this.extensionData;
public getAllExtensionDiscoveryPaths(): ReadonlyMap<keyof ExtensionPaths, string> {
return this.extensionDiscoveryPaths;
}
}
41 changes: 41 additions & 0 deletions src/interfaces/commentStyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Define the single-line comment styles.
*/
export type SingleLineCommentStyle = "//" | "#" | ";";

/**
* Define the extra single-line comment styles, like `///`, etc.
*/
export type ExtraSingleLineCommentStyles = "##" | ";;" | "///" | "//!";

/**
* 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;
}
77 changes: 77 additions & 0 deletions src/interfaces/extensionMetaData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {IPackageJson} from "package-json-type";

// Utility types for cleaner Map typing
export type ExtensionMetaDataValue = ExtensionMetaData[keyof ExtensionMetaData];

/**
* 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 absolute path to the extension.
*/
extensionPath: string;

/**
* The full package.json data
*/
packageJSON: IPackageJson;
}

/**
* Extension discovery paths configuration for this extension
*/
export interface ExtensionPaths {
/**
* 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;
}
10 changes: 10 additions & 0 deletions src/interfaces/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface Settings {
singleLineBlockOnEnter: boolean;
disabledLanguages: string[];
slashStyleBlocks: string[];
hashStyleBlocks: string[];
semicolonStyleBlocks: string[];
multiLineStyleBlocks: string[];
overrideDefaultLanguageMultiLineComments: Record<string, string>;
bladeOverrideComments: boolean;
}
Loading