@@ -39,8 +39,9 @@ const DOCTOR_FLAGS = ['--host', '--port', '--json', '--help', '-h'];
3939const CONFIG_FLAGS = [ '--json' , '--help' , '-h' ] ;
4040const AUTH_FLAGS = [ '--json' , '--help' , '-h' ] ;
4141const COMPLETION_FLAGS = [ '--help' , '-h' ] ;
42- const COMPLETION_COMMANDS = [ 'install' , ...SUPPORTED_COMPLETION_SHELLS ] ;
43- const COMPLETION_INSTALL_FLAGS = [ '--json' , '--help' , '-h' ] ;
42+ const COMPLETION_COMMANDS = [ 'install' , 'uninstall' , ...SUPPORTED_COMPLETION_SHELLS ] ;
43+ const COMPLETION_INSTALL_FLAGS = [ '--json' , '--force' , '--help' , '-h' ] ;
44+ const COMPLETION_UNINSTALL_FLAGS = [ '--json' , '--help' , '-h' ] ;
4445const CONFIG_PASSWORD_SET_FLAGS = [ '--stdin' , '--help' , '-h' ] ;
4546const AUTH_IP_UNBLOCK_FLAGS = [ '--all' , '--json' , '--help' , '-h' ] ;
4647
@@ -128,6 +129,14 @@ function generateBashScript() {
128129 ' fi' ,
129130 ' return 0' ,
130131 ' fi' ,
132+ ' if [[ "$subcommand" == "uninstall" ]]; then' ,
133+ ' if [[ $COMP_CWORD -eq 3 ]]; then' ,
134+ ` COMPREPLY=( $(compgen -W "${ words ( SUPPORTED_COMPLETION_SHELLS ) } " -- "$cur") )` ,
135+ ' else' ,
136+ ` COMPREPLY=( $(compgen -W "${ words ( COMPLETION_UNINSTALL_FLAGS ) } " -- "$cur") )` ,
137+ ' fi' ,
138+ ' return 0' ,
139+ ' fi' ,
131140 ` COMPREPLY=( $(compgen -W "${ words ( SUPPORTED_COMPLETION_SHELLS . concat ( COMPLETION_FLAGS ) ) } " -- "$cur") )` ,
132141 ' return 0' ,
133142 ' ;;' ,
@@ -187,7 +196,7 @@ function generateBashScript() {
187196 ' ;;' ,
188197 ' ip)' ,
189198 ' if [[ $COMP_CWORD -eq 3 ]]; then' ,
190- ` COMPREPLY=( $(compgen -W "${ words ( AUTH_IP_SUBCOMMANDS . concat ( COMPLETION_FLAGS ) ) } " -- "$cur") )` ,
199+ ` COMPREPLY=( $(compgen -W "${ words ( AUTH_IP_SUBCOMMANDS . concat ( COMPLETION_FLAGS ) ) } " -- "$cur") )` ,
191200 ' return 0' ,
192201 ' fi' ,
193202 ' if [[ "$nested" == "list" ]]; then' ,
@@ -267,6 +276,12 @@ function generateZshScript() {
267276 ' else' ,
268277 ` suggestions=(${ words ( COMPLETION_INSTALL_FLAGS ) } )` ,
269278 ' fi' ,
279+ ' elif [[ "$subcommand" == "uninstall" ]]; then' ,
280+ ' if (( CURRENT == 4 )); then' ,
281+ ` suggestions=(${ words ( SUPPORTED_COMPLETION_SHELLS ) } )` ,
282+ ' else' ,
283+ ` suggestions=(${ words ( COMPLETION_UNINSTALL_FLAGS ) } )` ,
284+ ' fi' ,
270285 ' else' ,
271286 ` suggestions=(${ words ( SUPPORTED_COMPLETION_SHELLS ) } ${ words ( COMPLETION_FLAGS ) } )` ,
272287 ' fi' ,
@@ -400,10 +415,13 @@ function generateFishScript() {
400415 'complete -c coder-studio -n "__fish_seen_subcommand_from doctor" -l json' ,
401416 'complete -c coder-studio -n "__fish_seen_subcommand_from doctor" -l help -s h' ,
402417 '' ,
403- 'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and not __fish_seen_subcommand_from install bash zsh fish" -a "install bash zsh fish"' ,
418+ 'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and not __fish_seen_subcommand_from install uninstall bash zsh fish" -a "install uninstall bash zsh fish"' ,
404419 'complete -c coder-studio -n "__fish_seen_subcommand_from completion" -l help -s h' ,
405420 'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and __fish_seen_subcommand_from install; and not __fish_seen_subcommand_from bash zsh fish" -a "bash zsh fish"' ,
421+ 'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and __fish_seen_subcommand_from install" -l force' ,
406422 'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and __fish_seen_subcommand_from install" -l json' ,
423+ 'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and __fish_seen_subcommand_from uninstall; and not __fish_seen_subcommand_from bash zsh fish" -a "bash zsh fish"' ,
424+ 'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and __fish_seen_subcommand_from uninstall" -l json' ,
407425 '' ,
408426 'complete -c coder-studio -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from path show get set unset validate root password auth" -a "path show get set unset validate root password auth"' ,
409427 'complete -c coder-studio -n "__fish_seen_subcommand_from config" -l json' ,
@@ -463,8 +481,12 @@ function buildManagedBlock(sourceLine) {
463481 return `${ MANAGED_BLOCK_START } \n${ sourceLine } \n${ MANAGED_BLOCK_END } ` ;
464482}
465483
484+ function escapeRegex ( value ) {
485+ return value . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ;
486+ }
487+
466488function upsertManagedBlock ( currentText , block ) {
467- const pattern = new RegExp ( `${ MANAGED_BLOCK_START } [\\s\\S]*?${ MANAGED_BLOCK_END } ` , 'm' ) ;
489+ const pattern = new RegExp ( `${ escapeRegex ( MANAGED_BLOCK_START ) } [\\s\\S]*?${ escapeRegex ( MANAGED_BLOCK_END ) } ` , 'm' ) ;
468490 if ( pattern . test ( currentText ) ) {
469491 return currentText . replace ( pattern , block ) ;
470492 }
@@ -473,6 +495,16 @@ function upsertManagedBlock(currentText, block) {
473495 return normalized ? `${ normalized } \n\n${ block } \n` : `${ block } \n` ;
474496}
475497
498+ function removeManagedBlock ( currentText ) {
499+ const pattern = new RegExp ( `\\n*${ escapeRegex ( MANAGED_BLOCK_START ) } \\n[\\s\\S]*?\\n${ escapeRegex ( MANAGED_BLOCK_END ) } \\n*` , 'm' ) ;
500+ if ( ! pattern . test ( currentText ) ) {
501+ return currentText ;
502+ }
503+
504+ const next = currentText . replace ( pattern , '\n' ) . replace ( / \n { 3 , } / g, '\n\n' ) . trimEnd ( ) ;
505+ return next ? `${ next } \n` : '' ;
506+ }
507+
476508async function readOptionalText ( filePath ) {
477509 try {
478510 return await fs . readFile ( filePath , 'utf8' ) ;
@@ -484,18 +516,22 @@ async function readOptionalText(filePath) {
484516 }
485517}
486518
487- export async function installCompletionScript ( shell , { env = process . env } = { } ) {
519+ export async function installCompletionScript ( shell , { env = process . env , force = false } = { } ) {
488520 const plan = resolveInstallPlan ( shell , env ) ;
489521 const script = generateCompletionScript ( shell ) ;
490522
491523 await fs . mkdir ( path . dirname ( plan . scriptPath ) , { recursive : true } ) ;
492- await fs . writeFile ( plan . scriptPath , script , 'utf8' ) ;
524+ const currentScript = await readOptionalText ( plan . scriptPath ) ;
525+ const scriptUpdated = force || currentScript !== script ;
526+ if ( scriptUpdated ) {
527+ await fs . writeFile ( plan . scriptPath , script , 'utf8' ) ;
528+ }
493529
494530 let profileUpdated = false ;
495531 if ( plan . profilePath && plan . sourceLine ) {
496532 const currentProfile = await readOptionalText ( plan . profilePath ) ;
497533 const nextProfile = upsertManagedBlock ( currentProfile , buildManagedBlock ( plan . sourceLine ) ) ;
498- profileUpdated = nextProfile !== currentProfile ;
534+ profileUpdated = force || nextProfile !== currentProfile ;
499535 if ( profileUpdated ) {
500536 await fs . writeFile ( plan . profilePath , nextProfile , 'utf8' ) ;
501537 }
@@ -504,9 +540,36 @@ export async function installCompletionScript(shell, { env = process.env } = {})
504540 return {
505541 shell : plan . shell ,
506542 scriptPath : plan . scriptPath ,
543+ scriptUpdated,
507544 profilePath : plan . profilePath ,
508545 profileUpdated,
509546 activationCommand : plan . activationCommand ,
547+ forced : Boolean ( force ) ,
548+ } ;
549+ }
550+
551+ export async function uninstallCompletionScript ( shell , { env = process . env } = { } ) {
552+ const plan = resolveInstallPlan ( shell , env ) ;
553+ const currentScript = await readOptionalText ( plan . scriptPath ) ;
554+ const scriptRemoved = currentScript . length > 0 ;
555+ await fs . rm ( plan . scriptPath , { force : true } ) ;
556+
557+ let profileUpdated = false ;
558+ if ( plan . profilePath ) {
559+ const currentProfile = await readOptionalText ( plan . profilePath ) ;
560+ const nextProfile = removeManagedBlock ( currentProfile ) ;
561+ profileUpdated = nextProfile !== currentProfile ;
562+ if ( profileUpdated ) {
563+ await fs . writeFile ( plan . profilePath , nextProfile , 'utf8' ) ;
564+ }
565+ }
566+
567+ return {
568+ shell : plan . shell ,
569+ scriptPath : plan . scriptPath ,
570+ scriptRemoved,
571+ profilePath : plan . profilePath ,
572+ profileUpdated,
510573 } ;
511574}
512575
0 commit comments