@@ -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