@@ -748,78 +748,84 @@ function calculatePropertyDeleteRange(
748748 * Provides "Remove section" and "Link to plugin" quick fixes.
749749 */
750750function registerInvalidConfigSectionFixes ( context : vscode . ExtensionContext ) : void {
751- // Register the command for linking a config section to a plugin
752- context . subscriptions . push (
753- vscode . commands . registerCommand (
754- 'dev-proxy-toolkit.linkConfigSectionToPlugin' ,
755- async ( documentUri : vscode . Uri , configSectionName : string ) => {
756- const document = await vscode . workspace . openTextDocument ( documentUri ) ;
757-
758- let documentNode : parse . ObjectNode ;
759- try {
760- documentNode = parse ( document . getText ( ) ) as parse . ObjectNode ;
761- } catch {
762- return ;
763- }
764-
765- const pluginsNode = getASTNode ( documentNode . children , 'Identifier' , 'plugins' ) ;
766- if ( ! pluginsNode || pluginsNode . value . type !== 'Array' ) {
767- return ;
768- }
769-
770- const pluginNodes = ( pluginsNode . value as parse . ArrayNode )
771- . children as parse . ObjectNode [ ] ;
751+ // Register the command for linking a config section to a plugin.
752+ // Use try-catch to handle cases where the command is already registered
753+ // (e.g., during test runs that call registerCodeActions multiple times).
754+ try {
755+ context . subscriptions . push (
756+ vscode . commands . registerCommand (
757+ 'dev-proxy-toolkit.linkConfigSectionToPlugin' ,
758+ async ( documentUri : vscode . Uri , configSectionName : string ) => {
759+ const document = await vscode . workspace . openTextDocument ( documentUri ) ;
760+
761+ let documentNode : parse . ObjectNode ;
762+ try {
763+ documentNode = parse ( document . getText ( ) ) as parse . ObjectNode ;
764+ } catch {
765+ return ;
766+ }
772767
773- // Find plugins that don't have a configSection property
774- const availablePlugins : { name : string ; node : parse . ObjectNode } [ ] = [ ] ;
775- pluginNodes . forEach ( pluginNode => {
776- const nameNode = getASTNode ( pluginNode . children , 'Identifier' , 'name' ) ;
777- const configSectionNode = getASTNode ( pluginNode . children , 'Identifier' , 'configSection' ) ;
778- if ( nameNode && ! configSectionNode ) {
779- availablePlugins . push ( {
780- name : ( nameNode . value as parse . LiteralNode ) . value as string ,
781- node : pluginNode ,
782- } ) ;
768+ const pluginsNode = getASTNode ( documentNode . children , 'Identifier' , 'plugins' ) ;
769+ if ( ! pluginsNode || pluginsNode . value . type !== 'Array' ) {
770+ return ;
783771 }
784- } ) ;
785772
786- if ( availablePlugins . length === 0 ) {
787- vscode . window . showInformationMessage ( 'All plugins already have a configSection.' ) ;
788- return ;
789- }
773+ const pluginNodes = ( pluginsNode . value as parse . ArrayNode )
774+ . children as parse . ObjectNode [ ] ;
775+
776+ // Find plugins that don't have a configSection property
777+ const availablePlugins : { name : string ; node : parse . ObjectNode } [ ] = [ ] ;
778+ pluginNodes . forEach ( pluginNode => {
779+ const nameNode = getASTNode ( pluginNode . children , 'Identifier' , 'name' ) ;
780+ const configSectionNode = getASTNode ( pluginNode . children , 'Identifier' , 'configSection' ) ;
781+ if ( nameNode && ! configSectionNode ) {
782+ availablePlugins . push ( {
783+ name : ( nameNode . value as parse . LiteralNode ) . value as string ,
784+ node : pluginNode ,
785+ } ) ;
786+ }
787+ } ) ;
788+
789+ if ( availablePlugins . length === 0 ) {
790+ vscode . window . showInformationMessage ( 'All plugins already have a configSection.' ) ;
791+ return ;
792+ }
790793
791- const selected = await vscode . window . showQuickPick (
792- availablePlugins . map ( p => p . name ) ,
793- { placeHolder : 'Select a plugin to link this config section to' }
794- ) ;
794+ const selected = await vscode . window . showQuickPick (
795+ availablePlugins . map ( p => p . name ) ,
796+ { placeHolder : 'Select a plugin to link this config section to' }
797+ ) ;
795798
796- if ( ! selected ) {
797- return ;
798- }
799+ if ( ! selected ) {
800+ return ;
801+ }
799802
800- const selectedPlugin = availablePlugins . find ( p => p . name === selected ) ;
801- if ( ! selectedPlugin || selectedPlugin . node . children . length === 0 ) {
802- return ;
803- }
803+ const selectedPlugin = availablePlugins . find ( p => p . name === selected ) ;
804+ if ( ! selectedPlugin || selectedPlugin . node . children . length === 0 ) {
805+ return ;
806+ }
804807
805- const edit = new vscode . WorkspaceEdit ( ) ;
806- const lastProperty = selectedPlugin . node . children [ selectedPlugin . node . children . length - 1 ] ;
807- const insertPos = new vscode . Position (
808- lastProperty . loc ! . end . line - 1 ,
809- lastProperty . loc ! . end . column
810- ) ;
808+ const edit = new vscode . WorkspaceEdit ( ) ;
809+ const lastProperty = selectedPlugin . node . children [ selectedPlugin . node . children . length - 1 ] ;
810+ const insertPos = new vscode . Position (
811+ lastProperty . loc ! . end . line - 1 ,
812+ lastProperty . loc ! . end . column
813+ ) ;
811814
812- edit . insert (
813- documentUri ,
814- insertPos ,
815- `,\n "configSection": "${ configSectionName } "`
816- ) ;
815+ edit . insert (
816+ documentUri ,
817+ insertPos ,
818+ `,\n "configSection": "${ configSectionName } "`
819+ ) ;
817820
818- await vscode . workspace . applyEdit ( edit ) ;
819- await vscode . commands . executeCommand ( 'editor.action.formatDocument' ) ;
820- }
821- )
822- ) ;
821+ await vscode . workspace . applyEdit ( edit ) ;
822+ await vscode . commands . executeCommand ( 'editor.action.formatDocument' ) ;
823+ }
824+ )
825+ ) ;
826+ } catch {
827+ // Command already registered, skip
828+ }
823829
824830 const invalidConfigSection : vscode . CodeActionProvider = {
825831 provideCodeActions : ( document , range , context ) => {
0 commit comments