Skip to content

Commit

Permalink
feat: add installation process for bash (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
renancaraujo committed Nov 15, 2022
1 parent d7cdfd5 commit af74cec
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 10 deletions.
40 changes: 39 additions & 1 deletion lib/src/install/shell_completion_configuration.dart
Expand Up @@ -28,7 +28,7 @@ class ShellCompletionConfiguration {
final String name;

/// The location of a config file that is run upon shell start.
/// Eg: .bashrc or .zshrc
/// Eg: .bash_profile or .zshrc
final String shellRCFile;

/// Generates a line to sources of a script file.
Expand Down Expand Up @@ -68,3 +68,41 @@ fi
''';
},
);

/// A [ShellCompletionConfiguration] for bash.
final bashConfiguration = ShellCompletionConfiguration(
name: 'bash',
shellRCFile: '~/.bash_profile',
sourceLineTemplate: (String scriptPath) {
return '[ -f $scriptPath ] && . $scriptPath || true';
},
scriptTemplate: (String rootCommand) {
// Completion script for bash.
//
// Based on https://github.com/mklabs/tabtab/blob/master/lib/scripts/bash.sh
return '''
if type complete &>/dev/null; then
_${rootCommand}_completion () {
local words cword
if type _get_comp_words_by_ref &>/dev/null; then
_get_comp_words_by_ref -n = -n @ -n : -w words -i cword
else
cword="\$COMP_CWORD"
words=("\${COMP_WORDS[@]}")
fi
local si="\$IFS"
IFS=\$'\n' COMPREPLY=(\$(COMP_CWORD="\$cword" \\
COMP_LINE="\$COMP_LINE" \\
COMP_POINT="\$COMP_POINT" \\
$rootCommand completion -- "\${words[@]}" \\
2>/dev/null)) || return \$?
IFS="\$si"
if type __ltrim_colon_completions &>/dev/null; then
__ltrim_colon_completions "\${words[cword]}"
fi
}
complete -o default -F _${rootCommand}_completion $rootCommand
fi
''';
},
);
3 changes: 3 additions & 0 deletions lib/src/install/shell_completion_installation.dart
Expand Up @@ -42,6 +42,9 @@ class ShellCompletionInstallation {
final ShellCompletionConfiguration configuration;
if (basename == 'zsh') {
configuration = zshConfiguration;
} else if (RegExp(r'bash(\.exe)?$').hasMatch(basename)) {
// on windows basename can be bash.exe
configuration = bashConfiguration;
} else {
return null;
}
Expand Down
39 changes: 35 additions & 4 deletions test/src/install/install_completion_test.dart
Expand Up @@ -44,10 +44,41 @@ void main() {
),
);

expect(tempDir.listSync().map((e) => path.basename(e.path)), [
'.zshrc',
'.dart-cli-completion',
]);
expect(
tempDir.listSync().map((e) => path.basename(e.path)),
unorderedEquals(['.zshrc', '.dart-cli-completion']),
);
});

test('installs for bash', () {
File(path.join(tempDir.path, '.bash_profile')).createSync();

installCompletion(
logger: logger,
rootCommand: 'very_good',
isWindowsOverride: false,
environmentOverride: {
'SHELL': '/foo/bar/bash',
'HOME': tempDir.path,
},
);

verify(
() => logger.detail(
'Completion installation for very_good started',
),
);

verify(
() => logger.detail(
'Shell identified as bash',
),
);

expect(
tempDir.listSync().map((e) => path.basename(e.path)),
unorderedEquals(['.dart-cli-completion', '.bash_profile']),
);
});

test('do nothing on unknown shells', () {
Expand Down
50 changes: 50 additions & 0 deletions test/src/install/shell_completion_configuration_test.dart
Expand Up @@ -42,5 +42,55 @@ fi
);
});
});

group('bashConfiguration', () {
test('name', () {
expect(bashConfiguration.name, 'bash');
});

test('shellRCFile', () {
expect(bashConfiguration.shellRCFile, '~/.bash_profile');
});

test('sourceStringTemplate', () {
final result = bashConfiguration.sourceLineTemplate('./pans/snaps');
expect(result, '[ -f ./pans/snaps ] && . ./pans/snaps || true');
});

test('completionScriptTemplate', () {
final result = bashConfiguration.scriptTemplate('very_good');
expect(result, '''
if type complete &>/dev/null; then
_very_good_completion () {
local words cword
if type _get_comp_words_by_ref &>/dev/null; then
_get_comp_words_by_ref -n = -n @ -n : -w words -i cword
else
cword="\$COMP_CWORD"
words=("\${COMP_WORDS[@]}")
fi
local si="\$IFS"
IFS=\$'\n' COMPREPLY=(\$(COMP_CWORD="\$cword" \\
COMP_LINE="\$COMP_LINE" \\
COMP_POINT="\$COMP_POINT" \\
very_good completion -- "\${words[@]}" \\
2>/dev/null)) || return \$?
IFS="\$si"
if type __ltrim_colon_completions &>/dev/null; then
__ltrim_colon_completions "\${words[cword]}"
fi
}
complete -o default -F _very_good_completion very_good
fi
''');
});

test('completionConfigForShellFileName', () {
expect(
bashConfiguration.completionConfigForShellFileName,
'bash-config.bash',
);
});
});
});
}
72 changes: 67 additions & 5 deletions test/src/install/shell_completion_installation_test.dart
Expand Up @@ -40,6 +40,29 @@ void main() {
expect(result?.configuration, zshConfiguration);
});

test('identifies bash shell', () {
final logger = MockLogger();
final result = ShellCompletionInstallation.fromCurrentShell(
logger: logger,
isWindowsOverride: false,
environmentOverride: {
'SHELL': '/foo/bar/bash',
},
);

expect(result?.configuration, bashConfiguration);

final resultWindows = ShellCompletionInstallation.fromCurrentShell(
logger: logger,
isWindowsOverride: true,
environmentOverride: {
'SHELL': r'c:\foo\bar\bash.exe',
},
);

expect(resultWindows?.configuration, bashConfiguration);
});

group('identifies no shell', () {
test('for no shell env', () {
final result = ShellCompletionInstallation.fromCurrentShell(
Expand Down Expand Up @@ -401,11 +424,50 @@ void main() {
''');

expect(configDir.listSync().map((e) => path.basename(e.path)), [
'not_good.zsh',
'very_good.zsh',
'zsh-config.zsh',
]);
expect(
configDir.listSync().map((e) => path.basename(e.path)),
unorderedEquals([
'not_good.zsh',
'very_good.zsh',
'zsh-config.zsh',
]),
);

final bashInstallation = ShellCompletionInstallation(
logger: logger,
isWindows: false,
environment: {
'HOME': tempDir.path,
},
configuration: bashConfiguration,
);

final bashProfile = File(path.join(tempDir.path, '.bash_profile'))
..createSync();

bashInstallation
..install('very_good')
..install('not_good');

// ignore: leading_newlines_in_multiline_strings
expect(bashProfile.readAsStringSync(), '''## [Completion]
## Completion scripts setup. Remove the following line to uninstall
[ -f ${configDir.path}/bash-config.bash ] && . ${configDir.path}/bash-config.bash || true
## [/Completion]
''');

expect(
configDir.listSync().map((e) => path.basename(e.path)),
unorderedEquals([
'not_good.bash',
'not_good.zsh',
'very_good.bash',
'very_good.zsh',
'zsh-config.zsh',
'bash-config.bash'
]),
);
},
);
});
Expand Down

0 comments on commit af74cec

Please sign in to comment.