diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 9528eb3..46f3501 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -10,8 +10,8 @@ ENV CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
RUN cargo install cargo-audit
RUN curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin
-# Install zsh
-RUN apt-get update && apt-get install zsh -y
+# Install zsh and fish
+RUN apt-get update && apt-get install zsh fish -y
# Create default non-root user
ARG USERNAME=rust
@@ -35,3 +35,18 @@ RUN echo "\n# IntelliShell debug" >> ~/.bashrc
RUN echo "alias intelli-shell=/workspaces/intelli-shell/target/debug/intelli-shell" >> ~/.bashrc
RUN echo "source /workspaces/intelli-shell/intelli-shell.sh" >> ~/.bashrc
RUN echo "alias ll='ls -alF'" >> ~/.bashrc
+
+RUN echo "\n# Search up & down" >> ~/.zshrc
+RUN echo "bindkey '^[[A' up-line-or-search" >> ~/.zshrc
+RUN echo "bindkey '^[[B' down-line-or-search" >> ~/.zshrc
+RUN echo "\n# IntelliShell debug" >> ~/.zshrc
+RUN echo "alias intelli-shell=/workspaces/intelli-shell/target/debug/intelli-shell" >> ~/.zshrc
+RUN echo "source /workspaces/intelli-shell/intelli-shell.sh" >> ~/.zshrc
+RUN echo "alias ll='ls -alF'" >> ~/.zshrc
+
+RUN echo "# IntelliShell debug" >> ~/.config/fish/config.fish
+RUN echo "set INTELLI_HOME /workspaces/intelli-shell" >> ~/.config/fish/config.fish
+RUN echo "source /workspaces/intelli-shell/intelli-shell.fish" >> ~/.config/fish/config.fish
+RUN echo "function intelli-shell --description 'IntelliShell'" >> ~/.config/fish/config.fish
+RUN echo '/workspaces/intelli-shell/target/debug/intelli-shell $argv;' >> ~/.config/fish/config.fish
+RUN echo "end" >> ~/.config/fish/config.fish
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 4027f74..5a8f18c 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -14,6 +14,7 @@
"aaron-bond.better-comments",
"oderwat.indent-rainbow",
"gruntfuggly.todo-tree",
+ "skyapps.fish-vscode",
"yzhang.markdown-all-in-one",
"davidanson.vscode-markdownlint"
],
@@ -25,6 +26,9 @@
},
"zsh": {
"path": "/bin/zsh"
+ },
+ "fish": {
+ "path": "/usr/bin/fish"
}
},
"files.watcherExclude": {
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6491977..90414ee 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -57,7 +57,7 @@ jobs:
- name: Package
shell: bash
run: |
- tar czvf intelli-shell-${{ matrix.target }}.tar.gz intelli-shell.sh -C target/${{ matrix.target }}/release intelli-shell
+ tar czvf intelli-shell-${{ matrix.target }}.tar.gz intelli-shell.sh intelli-shell.fish -C target/${{ matrix.target }}/release intelli-shell
- name: Release
uses: softprops/action-gh-release@v1
diff --git a/README.md b/README.md
index f57faf2..655ab9b 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ It currently works on Bash and Zsh and should be compatible with most Linux, Win
1. Install the binaries:
```sh
- curl -sSf https://raw.githubusercontent.com/lasantosr/intelli-shell/main/install.sh | $SHELL
+ curl -sSf https://raw.githubusercontent.com/lasantosr/intelli-shell/main/install.sh | bash
```
2. Bookmark your first command by typing it on a terminal and using `ctrl + b`
@@ -42,7 +42,7 @@ Remember to bookmark some commands or fetch them after the installation!
To install using prebuilt binaries:
```sh
-curl -sSf https://raw.githubusercontent.com/lasantosr/intelli-shell/main/install.sh | $SHELL
+curl -sSf https://raw.githubusercontent.com/lasantosr/intelli-shell/main/install.sh | bash
```
### From source code
@@ -90,8 +90,10 @@ You can customize key bindings using environment variables: `INTELLI_SAVE_HOTKEY
- [x] Labels support to store most used labels and select them using a dedicated UI
- [ ] Usability improvements to manage stored commands (including aliases)
+- [ ] Support for more terminals
+ - [x] [Fish](https://fishshell.com/)
+- [ ] Deploy to package managers
- [ ] Sync user bookmarks using some public / private Git repo
-- [ ] Support for more terminals, like PowerShell
## Alternatives
diff --git a/install.sh b/install.sh
index aeae705..30722c6 100755
--- a/install.sh
+++ b/install.sh
@@ -1,3 +1,5 @@
+#!/bin/bash
+
set -eo pipefail
# Retrieve default shell
@@ -35,23 +37,55 @@ curl -Lsf https://github.com/lasantosr/intelli-shell/releases/latest/download/in
echo "Successfully installed IntelliShell at: $INTELLI_HOME"
# Update rc
-if [[ "$os" = 'apple-darwin' ]] && [[ "$shell" = 'bash' ]];
-then
- rcfile=".bash_profile"
-else
- rcfile=".${shell}rc"
+files=()
+function update_rc () {
+ if [ -f "$1" ]; then
+ sourced=$(cat $1 | { grep -E '.*intelli-shell.*' || test $? = 1; })
+ else
+ sourced=
+ fi
+ if [[ -z "$sourced" ]];
+ then
+ files+=("$1")
+ echo -e '\n# IntelliShell' >> "$1"
+ echo "INTELLI_HOME=$INTELLI_HOME" >> "$1"
+ echo '# export INTELLI_SEARCH_HOTKEY=\C-@' >> "$1"
+ echo '# export INTELLI_LABEL_HOTKEY=C-l' >> "$1"
+ echo '# export INTELLI_SAVE_HOTKEY=C-b' >> "$1"
+ echo '# export INTELLI_SKIP_ESC_BIND=0' >> "$1"
+ echo 'alias intelli-shell="$INTELLI_HOME/bin/intelli-shell"' >> "$1"
+ echo 'source $INTELLI_HOME/bin/intelli-shell.sh' >> "$1"
+ fi
+}
+
+update_rc "$HOME/.bashrc"
+if [[ -f "$HOME/.bash_profile" ]]; then
+ update_rc "$HOME/.bash_profile"
fi
-sourced=$(cat ~/$rcfile | { grep -E '^source.*intelli-shell\.sh' || test $? = 1; })
-if [[ -z "$sourced" ]];
-then
- echo -e '\n# IntelliShell' >> ~/$rcfile
- echo "INTELLI_HOME=$INTELLI_HOME" >> ~/$rcfile
- echo '# export INTELLI_SEARCH_HOTKEY=\C-@' >> ~/$rcfile
- echo '# export INTELLI_LABEL_HOTKEY=C-l' >> ~/$rcfile
- echo '# export INTELLI_SAVE_HOTKEY=C-b' >> ~/$rcfile
- echo '# export INTELLI_SKIP_ESC_BIND=0' >> ~/$rcfile
- echo 'alias intelli-shell="$INTELLI_HOME/bin/intelli-shell"' >> ~/$rcfile
- echo 'source $INTELLI_HOME/bin/intelli-shell.sh' >> ~/$rcfile
-
- echo "Please restart the terminal or re-source ~/$rcfile, where further customizations can be made"
+if [[ -f "/bin/zsh" ]]; then
+ update_rc "$HOME/.zshrc"
+fi
+if [[ -f "/usr/bin/fish" ]]; then
+ config="$HOME/.config/fish/config.fish"
+ if [ -f "$config" ]; then
+ sourced=$(cat $config | { grep -E '.*intelli-shell.*' || test $? = 1; })
+ else
+ sourced=
+ fi
+ if [[ -z "$sourced" ]];
+ then
+ files+=("$config")
+ echo -e '\n# IntelliShell' >> "$config"
+ echo "set INTELLI_HOME $INTELLI_HOME" >> "$config"
+ echo '# set INTELLI_SEARCH_HOTKEY \cr' >> "$config"
+ echo '# set INTELLI_LABEL_HOTKEY \cl' >> "$config"
+ echo '# set INTELLI_SAVE_HOTKEY \cb' >> "$config"
+ echo '# set INTELLI_SKIP_ESC_BIND 0' >> "$config"
+ echo 'source $INTELLI_HOME/bin/intelli-shell.fish' >> "$config"
+ # TODO include fish on tar.gz, bindings, test from each terminal
+ fi
fi
+
+if [ ${#files[@]} -ne 0 ]; then
+ echo "The following files were updated: ${files[@]}"
+fi
\ No newline at end of file
diff --git a/intelli-shell.fish b/intelli-shell.fish
new file mode 100755
index 0000000..748733f
--- /dev/null
+++ b/intelli-shell.fish
@@ -0,0 +1,58 @@
+function intelli-shell --description 'IntelliShell'
+ $INTELLI_HOME/bin/intelli-shell $argv;
+end
+
+function _intelli_search
+ set LINE (commandline)
+ set TMP_FILE (mktemp -t intelli-shell.XXXXXXXX)
+ # Exec command
+ intelli-shell --inline --inline-extra-line --file-output="$TMP_FILE" search "$LINE"
+ set INTELLI_OUTPUT (cat "$TMP_FILE" | string collect)
+ rm -f $TMP_FILE
+ # Replace line
+ commandline -r "$INTELLI_OUTPUT"
+end
+
+function _intelli_save
+ set LINE (commandline)
+ set TMP_FILE (mktemp -t intelli-shell.XXXXXXXX)
+ # Exec command
+ intelli-shell --inline --inline-extra-line --file-output="$TMP_FILE" save "$LINE"
+ set INTELLI_OUTPUT (cat "$TMP_FILE" | string collect)
+ rm -f $TMP_FILE
+ # Replace line
+ commandline -r "$INTELLI_OUTPUT"
+end
+
+function _intelli_label
+ set LINE (commandline)
+ set TMP_FILE (mktemp -t intelli-shell.XXXXXXXX)
+ # Exec command
+ intelli-shell --inline --inline-extra-line --file-output="$TMP_FILE" label "$LINE"
+ set INTELLI_OUTPUT (cat "$TMP_FILE" | string collect)
+ rm -f $TMP_FILE
+ # Replace line
+ commandline -r "$INTELLI_OUTPUT"
+end
+
+function fish_user_key_bindings
+ if [ "$INTELLI_SKIP_ESC_BIND" != "1" ]
+ bind --preset \e 'kill-whole-line'
+ end
+ if test -n "$INTELLI_SEARCH_HOTKEY"
+ bind $INTELLI_SEARCH_HOTKEY '_intelli_search'
+ else
+ bind -k nul '_intelli_search'
+ end
+ if test -n "$INTELLI_SAVE_HOTKEY"
+ bind $INTELLI_SAVE_HOTKEY '_intelli_save'
+ else
+ bind \cb '_intelli_save'
+ end
+ if test -n "$INTELLI_LABEL_HOTKEY"
+ bind $INTELLI_LABEL_HOTKEY '_intelli_label'
+ else
+ bind \cl '_intelli_label'
+ end
+end
+
diff --git a/src/main.rs b/src/main.rs
index 94c742b..e5ca79c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -145,18 +145,18 @@ fn run(cli: Args) -> Result<()> {
LabelWidget::new(&mut storage, labeled_command)?,
)
.map_output_str(),
- None => Ok(WidgetOutput::new("The command contains no labels!", command)),
+ None => Ok(WidgetOutput::new(" -> The command contains no labels!", command)),
},
Actions::Export { file } => {
let file_path = file.as_deref().unwrap_or("user_commands.txt");
let exported = storage.export(USER_CATEGORY, file_path)?;
Ok(WidgetOutput::message(format!(
- "Successfully exported {exported} commands to '{file_path}'"
+ " -> Successfully exported {exported} commands to '{file_path}'"
)))
}
Actions::Import { file } => {
let new = storage.import(USER_CATEGORY, file)?;
- Ok(WidgetOutput::message(format!("Imported {new} new commands")))
+ Ok(WidgetOutput::message(format!(" -> Imported {new} new commands")))
}
#[cfg(feature = "tldr")]
Actions::Fetch { category } => exec(
diff --git a/src/storage.rs b/src/storage.rs
index f326c31..c948d50 100644
--- a/src/storage.rs
+++ b/src/storage.rs
@@ -510,9 +510,16 @@ impl SqliteStorage {
(CASE WHEN flat_label = ?2 THEN 1 ELSE 0 END) DESC
"#;
- let mut stmt = self
- .conn
- .prepare(&QUERY.replace("#LABELS#", ¶meters.iter().map(|_| "?").join(",")))?;
+ let mut stmt = self.conn.prepare(
+ &QUERY.replace(
+ "#LABELS#",
+ ¶meters
+ .iter()
+ .enumerate()
+ .map(|(i, _)| format!("?{}", i + 2))
+ .join(","),
+ ),
+ )?;
parameters.insert(0, flat_root_cmd);
diff --git a/src/widgets/fetch.rs b/src/widgets/fetch.rs
index c5cd71b..d4a5e31 100644
--- a/src/widgets/fetch.rs
+++ b/src/widgets/fetch.rs
@@ -30,9 +30,11 @@ impl<'a> Widget for FetchWidget<'a> {
let new = self.storage.insert_commands(&mut commands)?;
if new == 0 {
- Ok(Some(WidgetOutput::message("No new commands to retrieve".to_owned())))
+ Ok(Some(WidgetOutput::message(
+ " -> No new commands to retrieve".to_owned(),
+ )))
} else {
- Ok(Some(WidgetOutput::message(format!("Retrieved {new} new commands"))))
+ Ok(Some(WidgetOutput::message(format!(" -> Retrieved {new} new commands"))))
}
}
}
diff --git a/src/widgets/save.rs b/src/widgets/save.rs
index 860a5a4..dda74ff 100644
--- a/src/widgets/save.rs
+++ b/src/widgets/save.rs
@@ -53,8 +53,8 @@ impl<'s> SaveCommandWidget<'s> {
let cmd = command.into();
let mut command = Command::new(USER_CATEGORY, cmd, description);
Ok(match storage.insert_command(&mut command)? {
- true => WidgetOutput::new("Command was saved successfully", command),
- false => WidgetOutput::new("Command already existed, so it was updated", command),
+ true => WidgetOutput::new(" -> Command was saved successfully", command),
+ false => WidgetOutput::new(" -> Command already existed, so it was updated", command),
})
}
}
@@ -68,7 +68,7 @@ impl<'s> Widget for SaveCommandWidget<'s> {
fn peek(&mut self) -> Result