Skip to content

Commit 692895e

Browse files
committed
fix: languageConfigFilePaths to support multiple config paths per lang
Due to some extensions either not specifying all the language configuration properties in the file or setting the comment properties to an empty array (like the "stillat-llc.vscode-antlers" extension does which overrides the HTML config and makes the comments not work without the extension activated), we need to ensure that they are merged with other possible configs for the language. - Changed `languageConfigFilePaths` Map to accept an array value instead of just a single string. - Changed `findAllLanguageConfigFilePaths` method to set the config path into an array as the value in `languageConfigFilePaths` Map, and to push a new path into the array if the `langId` key already exists in the Map. - Changed `setLanguageConfigDefinitions` method to allow it to loop through the new array of paths which sets the configs into the `languageConfigs` Map, and if the `langId` key already exists then merge the existing config with the new config. This merges the comments from both configs together and skips empty arrays. It also merges all other config properties together to ensure we are getting a full language configuration. By merging all available configs for the language, the language won't have any issues regarding malformed configs, or comments not working.
1 parent 9800b70 commit 692895e

File tree

1 file changed

+88
-41
lines changed

1 file changed

+88
-41
lines changed

src/configuration.ts

Lines changed: 88 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class Configuration {
3333
/**
3434
* A key:value Map object of language IDs and their config file paths.
3535
*/
36-
private languageConfigFilePaths = new Map<string, string>();
36+
private languageConfigFilePaths = new Map<string, string[]>();
3737

3838
/**
3939
* A key:value Map object of language IDs and their configs.
@@ -343,70 +343,117 @@ export class Configuration {
343343
if (Object.hasOwn(packageJSON, "contributes") && Object.hasOwn(packageJSON.contributes, "languages")) {
344344
// Loop through the languages...
345345
for (let language of packageJSON.contributes.languages) {
346+
const langId = language.id;
346347
// Get the languages to skip.
347348
let skipLangs = this.getLanguagesToSkip();
348349

349-
// If skipLangs doesn't include the language ID,
350+
// If skipLangs doesn't include the langId,
350351
// AND the language object has "configuration" key...
351-
if (!skipLangs?.includes(language.id) && Object.hasOwn(language, "configuration")) {
352+
if (!skipLangs?.includes(langId) && Object.hasOwn(language, "configuration")) {
352353
// Join the extension path with the configuration path.
353354
let configPath = path.join(extension.extensionPath, language.configuration);
354-
// Set the language ID and config path into the languageConfigFilePaths Map.
355-
this.languageConfigFilePaths.set(language.id, configPath);
355+
356+
// If the langId already exists...
357+
if (this.languageConfigFilePaths.has(langId)) {
358+
// Push the new config path into the array of the existing langId.
359+
this.languageConfigFilePaths.get(langId).push(configPath);
360+
}
361+
// Otherwise, if the langId doesn't exist...
362+
else {
363+
// Set the langId with a new config path array.
364+
this.languageConfigFilePaths.set(langId, [configPath]);
365+
}
356366
}
357367
}
358368
}
359369
}
360370

361371
// Set the languageConfigFilePaths to a new map with all the languages sorted in
362-
// ascending order,for sanity reasons.
372+
// ascending order, for sanity reasons.
363373
this.languageConfigFilePaths = new Map([...this.languageConfigFilePaths].sort());
364374
}
365375

366376
/**
367377
* Set the language config definitions.
368378
*/
369379
private setLanguageConfigDefinitions() {
370-
this.languageConfigFilePaths.forEach((filepath, langId) => {
371-
const config = utils.readJsonFile(filepath);
372-
373-
// If the config JSON has more than 0 keys (ie. not empty)
374-
if (Object.keys(config).length > 0) {
375-
/**
376-
* Change all autoClosingPairs items that are using the simpler syntax
377-
* (array instead of object) into the object with open and close keys.
378-
* Prevents vscode from failing quietly and not changing the editor language
379-
* properly, which makes the open file become unresponsive when changing tabs.
380-
*/
381-
382-
// If config has key autoClosingPairs...
383-
if (Object.hasOwn(config, "autoClosingPairs")) {
384-
// Define a new array as the new AutoClosingPair.
385-
const autoClosingPairsArray: vscode.AutoClosingPair[] = [];
386-
// Loop through the config's autoClosingPairs...
387-
config.autoClosingPairs.forEach((item) => {
388-
// If the item is an array...
389-
if (Array.isArray(item)) {
390-
// Create a new object with the 1st array element [0] as the
391-
// value of the open key, and the 2nd element [1] as the value
392-
// of the close key.
393-
const autoClosingPairsObj = {open: item[0], close: item[1]};
394-
// Push the object into the new array.
395-
autoClosingPairsArray.push(autoClosingPairsObj);
380+
this.languageConfigFilePaths.forEach((paths, langId) => {
381+
// Loop through the paths array...
382+
paths.forEach((filepath) => {
383+
let config = utils.readJsonFile(filepath);
384+
385+
// If the config JSON has more than 0 keys (ie. not empty)
386+
if (Object.keys(config).length > 0) {
387+
/**
388+
* Change all autoClosingPairs items that are using the simpler syntax
389+
* (array instead of object) into the object with open and close keys.
390+
* Prevents vscode from failing quietly and not changing the editor language
391+
* properly, which makes the open file become unresponsive when changing tabs.
392+
*/
393+
394+
// If config has key autoClosingPairs...
395+
if (Object.hasOwn(config, "autoClosingPairs")) {
396+
// Define a new array as the new AutoClosingPair.
397+
const autoClosingPairsArray: vscode.AutoClosingPair[] = [];
398+
// Loop through the config's autoClosingPairs...
399+
config.autoClosingPairs.forEach((item) => {
400+
// If the item is an array...
401+
if (Array.isArray(item)) {
402+
// Create a new object with the 1st array element [0] as the
403+
// value of the open key, and the 2nd element [1] as the value
404+
// of the close key.
405+
const autoClosingPairsObj = {open: item[0], close: item[1]};
406+
// Push the object into the new array.
407+
autoClosingPairsArray.push(autoClosingPairsObj);
408+
}
409+
// Otherwise, the item is an object, so just push it into the array.
410+
else {
411+
autoClosingPairsArray.push(item);
412+
}
413+
});
414+
415+
// Add the new array to the config's autoClosingPairs key.
416+
config.autoClosingPairs = autoClosingPairsArray;
417+
}
418+
419+
// If the langId already exists, then it has multiple config files from
420+
// different extensions, so we need to merge them together to ensure
421+
// a full configuration is set, to avoid issues of missing values.
422+
if (this.languageConfigs.has(langId)) {
423+
const existingConfig = this.languageConfigs.get(langId);
424+
425+
// Only merge if both configs have comments
426+
if (existingConfig.comments && config.comments) {
427+
// Start with existing comments as base
428+
const mergedComments = {...existingConfig.comments};
429+
430+
// Merge each comment type from new config.
431+
Object.entries(config.comments).forEach(([key, value]) => {
432+
// Skip empty arrays.
433+
if (Array.isArray(value) && value.length === 0) {
434+
return;
435+
}
436+
mergedComments[key] = value;
437+
});
438+
439+
// Update the config with merged comments
440+
config = {
441+
...existingConfig,
442+
...config,
443+
comments: mergedComments,
444+
};
396445
}
397-
// Otherwise, the item is an object, so just push it into the array.
446+
// If only one config has comments or neither has comments...
398447
else {
399-
autoClosingPairsArray.push(item);
448+
// Just merge the configs directly.
449+
config = {...existingConfig, ...config};
400450
}
401-
});
451+
}
402452

403-
// Add the new array to the config's autoClosingPairs key.
404-
config.autoClosingPairs = autoClosingPairsArray;
453+
// Set the language configs into the Map.
454+
this.languageConfigs.set(langId, config);
405455
}
406-
407-
// Set the language configs into the Map.
408-
this.languageConfigs.set(langId, config);
409-
}
456+
});
410457
});
411458
}
412459

0 commit comments

Comments
 (0)