diff --git a/lib/src/installer/completion_installation.dart b/lib/src/installer/completion_installation.dart index b0e0943..9403d7a 100644 --- a/lib/src/installer/completion_installation.dart +++ b/lib/src/installer/completion_installation.dart @@ -274,8 +274,24 @@ class CompletionInstallation { ); } - String get _shellRCFilePath => - _resolveHome(configuration!.shellRCFile, environment); + final _missingFiles = []; + String? __shellRCFilePath; + String get _shellRCFilePath { + String? firstPath; + if (__shellRCFilePath == null) { + for (final fileName in configuration!.shellRCFiles) { + final filePath = _resolveHome(fileName, environment); + firstPath ??= filePath; + if (File(filePath).existsSync()) { + __shellRCFilePath = filePath; + return __shellRCFilePath!; + } + _missingFiles.add(filePath); + } + __shellRCFilePath = firstPath; + } + return __shellRCFilePath!; + } /// Write a source to the completion global script in the shell configuration /// file, which its location is described by the [configuration]. @@ -290,9 +306,17 @@ class CompletionInstallation { final shellRCFile = File(_shellRCFilePath); if (!shellRCFile.existsSync()) { + var message = ''; + if (_missingFiles.length > 1) { + message = + 'No configuration files where found at ' + '\n ${_missingFiles.join('\n ')}'; + } else { + message = 'No configuration file found at ${shellRCFile.path}'; + } throw CompletionInstallationException( rootCommand: rootCommand, - message: 'No configuration file found at ${shellRCFile.path}', + message: message, ); } diff --git a/lib/src/installer/shell_completion_configuration.dart b/lib/src/installer/shell_completion_configuration.dart index ee19dca..f4ef92b 100644 --- a/lib/src/installer/shell_completion_configuration.dart +++ b/lib/src/installer/shell_completion_configuration.dart @@ -22,7 +22,7 @@ class ShellCompletionConfiguration { /// {@macro shell_completion_configuration} const ShellCompletionConfiguration._({ required this.shell, - required this.shellRCFile, + required this.shellRCFiles, required this.sourceLineTemplate, required this.scriptTemplate, }); @@ -42,9 +42,12 @@ class ShellCompletionConfiguration { /// {@macro system_shell} final SystemShell shell; - /// The location of a config file that is run upon shell start. + /// A preferential ordered list of locations of a config file that is run upon + /// shell start. The list is to allow multiple options eg both .bash_profile + /// and .bashrc. The first option will be tried first and and if the file + /// doesn't exist the next one will be tried. /// Eg: .bash_profile or .zshrc - final String shellRCFile; + final List shellRCFiles; /// Generates a line to sources of a script file. final SourceStringTemplate sourceLineTemplate; @@ -61,7 +64,7 @@ class ShellCompletionConfiguration { @visibleForTesting final zshConfiguration = ShellCompletionConfiguration._( shell: SystemShell.zsh, - shellRCFile: '~/.zshrc', + shellRCFiles: const ['~/.zshrc'], sourceLineTemplate: (String scriptPath) { return '[[ -f $scriptPath ]] && . $scriptPath || true'; }, @@ -94,7 +97,7 @@ fi @visibleForTesting final bashConfiguration = ShellCompletionConfiguration._( shell: SystemShell.bash, - shellRCFile: '~/.bash_profile', + shellRCFiles: const ['~/.bashrc', '~/.bash_profile'], sourceLineTemplate: (String scriptPath) { return '[ -f $scriptPath ] && . $scriptPath || true'; }, diff --git a/test/src/installer/completion_installation_test.dart b/test/src/installer/completion_installation_test.dart index aefea0b..bcd302d 100644 --- a/test/src/installer/completion_installation_test.dart +++ b/test/src/installer/completion_installation_test.dart @@ -727,6 +727,92 @@ void main() { ); }, ); + + test( + 'installing completion for .bashrc', + () { + final bashInstallation = CompletionInstallation( + logger: logger, + isWindows: false, + environment: { + 'HOME': tempDir.path, + }, + configuration: bashConfiguration, + ); + + final configDir = bashInstallation.completionConfigDir; + + final bashProfile = File(path.join(tempDir.path, '.bash_profile')) + ..createSync(); + + bashInstallation.install('very_good'); + + // Different format needed for matching cli output + expect(bashProfile.readAsStringSync(), ''' +\n## [Completion] +## Completion scripts setup. Remove the following line to uninstall +[ -f ${configDir.path}/bash-config.bash ] && . ${configDir.path}/bash-config.bash || true +## [/Completion] + +'''); + }, + ); + + test( + 'installing completion for .bash_profile', + () { + final bashInstallation = CompletionInstallation( + logger: logger, + isWindows: false, + environment: { + 'HOME': tempDir.path, + }, + configuration: bashConfiguration, + ); + + final configDir = bashInstallation.completionConfigDir; + + final bashRc = File(path.join(tempDir.path, '.bashrc'))..createSync(); + + bashInstallation.install('very_good'); + + // Different format needed for matching cli output + expect(bashRc.readAsStringSync(), ''' +\n## [Completion] +## Completion scripts setup. Remove the following line to uninstall +[ -f ${configDir.path}/bash-config.bash ] && . ${configDir.path}/bash-config.bash || true +## [/Completion] + +'''); + }, + ); + + test( + 'missing .bashrc and .bash_profile', + () { + final bashInstallation = CompletionInstallation( + logger: logger, + isWindows: false, + environment: { + 'HOME': tempDir.path, + }, + configuration: bashConfiguration, + ); + + expect( + () => bashInstallation.install('very_good'), + throwsA( + isA().having( + (e) => e.message, + 'message', + 'No configuration files where found at ' + '\n ${path.join(tempDir.path, '.bashrc')}' + '\n ${path.join(tempDir.path, '.bash_profile')}', + ), + ), + ); + }, + ); }); group('uninstall', () { diff --git a/test/src/installer/shell_completion_configuration_test.dart b/test/src/installer/shell_completion_configuration_test.dart index 1e885aa..5726471 100644 --- a/test/src/installer/shell_completion_configuration_test.dart +++ b/test/src/installer/shell_completion_configuration_test.dart @@ -16,8 +16,8 @@ void main() { expect(zshConfiguration.shell, SystemShell.zsh); }); - test('shellRCFile', () { - expect(zshConfiguration.shellRCFile, '~/.zshrc'); + test('shellRCFiles', () { + expect(zshConfiguration.shellRCFiles.first, '~/.zshrc'); }); test('sourceStringTemplate', () { @@ -68,8 +68,12 @@ fi expect(bashConfiguration.shell, SystemShell.bash); }); - test('shellRCFile', () { - expect(bashConfiguration.shellRCFile, '~/.bash_profile'); + test('shellRCFiles first', () { + expect(bashConfiguration.shellRCFiles.first, '~/.bashrc'); + }); + + test('shellRCFiles last', () { + expect(bashConfiguration.shellRCFiles.last, '~/.bash_profile'); }); test('sourceStringTemplate', () {