From 998ad80afd4dac1b2773e5b8879405aa6e0e82ed Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Wed, 4 Jun 2025 20:16:11 -0500 Subject: [PATCH 01/14] (chore): update README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 66912d8..d1fdea0 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ based.fish is a lightweight Fish plugin that provides context-based autocompletion for commands, options, and arguments. It enhances the default Fish shell completion system by offering more intelligent and context-aware suggestions. For example, it suggests commands based on the frequency of use, date of use, and the context of the current command line such as the path where you are, the command you are typing, etc. + +**Important:** this project was mainly born because I wrongly thought that [atuin](https://atuin.sh/) didn't offered per-directory completions, but it actually does. So, if you are looking for a more complete solution that also includes history management, I recommend checking out [atuin](https://atuin.sh/). However, if you are looking for a lightweight and simple solution that purely focuses on context-based autocompletion, this plugin is a great choice. + ## Features - Context-aware autocompletion for commands, options, and arguments. From f1695694eda13016ec196f4fb2df86b23a4efdfa Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Wed, 4 Jun 2025 23:55:47 -0500 Subject: [PATCH 02/14] (chore): update README --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d1fdea0..6cedbf0 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,16 @@ # A context-based autocompletion plugin for the Fish shell -based.fish is a lightweight Fish plugin that provides context-based autocompletion for commands, options, and arguments. It enhances the default Fish shell completion system by offering more intelligent and context-aware suggestions. For example, it suggests commands based on the frequency of use, date of use, and the context of the current command line such as the path where you are, the command you are typing, etc. - - -**Important:** this project was mainly born because I wrongly thought that [atuin](https://atuin.sh/) didn't offered per-directory completions, but it actually does. So, if you are looking for a more complete solution that also includes history management, I recommend checking out [atuin](https://atuin.sh/). However, if you are looking for a lightweight and simple solution that purely focuses on context-based autocompletion, this plugin is a great choice. +based.fish is a lightweight Fish plugin that provides context-based autocompletion for commands, options, and arguments. It enhances the default Fish shell completion system by offering more intelligent and context-aware suggestions. For example, it suggests commands based on the frequency of use, date of use, and the context of the current command line such as the path where you are, the command you are typing, etc. See the [other tools](#other-tools) section for a comparison with similar tools. ## Features - Context-aware autocompletion for commands, options, and arguments. +- Supports combining different history sources, such as the current working directory, the global suggestions, and the previous command. +- Directory-aware completions that take into account the current working directory. - Integration with fzf for a better selection experience. - Easy installation and setup with Fisher. - Support for importing existing Fish history. - Customizable configuration options to tailor the behavior of the plugin. -- Directory-aware completions that take into account the current working directory. - Statistics about command usage, such as most frequently used commands, options, and arguments. - Customizable keybindings for navigating and selecting completions. - Support for disabling fuzzy matching and confirmation prompts. @@ -31,7 +29,7 @@ based.fish uses a SQLite database to store command history and statistics. It an - It considers the date of the last use, the frequency of use, and the context of the current command line input. Suggestions are made based on this information. - If you are in a directory where you have previously used a command multiple times, it will suggest that command first, then the second most used command, and so on. -- For convenience, during the current session, it will always suggest your previous command first and then go back to the most used commands in the current directory. +- For convenience, during the current session, it will always suggest your previous command first and then append the most used commands in the current directory, and then then global ones. It's to replicate the "normal" behavior of most shells. ## Installation @@ -95,6 +93,10 @@ Keybinds can be customized by modifying the `$HOME/.config/fish/functions/based_ ![based.fish demonstration](assets/based_fish_plugin.gif) +### Other tools + +- [Atuin](https://github.com/atuinsh/atuin): a very well-known. It's a great tool, but doesn't provide what I wanted, it basically shows you the most recent commands, it does provide per-directory completions, but it's very limited. Commands are always shown in the recent order, not per-directory repeats (unless you type something or set the "directory" filter, but then that limits the history to that directory only), and even with the filters, these filters works separately and you have to switch between them for history, they can't be combined and it's unlikely to happen https://github.com/atuinsh/atuin/issues/1611#issuecomment-1908451910, this is a dealbreaker **for me**. I discovered it trying to switch to Atuin. + ## Contributing If you want to contribute to based.fish, feel free to open issues or pull requests on the GitHub repository. Contributions are welcome! From d7c44fae633bdc276c110e37f4cc082e6207a524 Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Thu, 5 Jun 2025 00:06:29 -0500 Subject: [PATCH 03/14] (feat): show the last command on top per-directory --- functions/__based.fish | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/functions/__based.fish b/functions/__based.fish index bf55b17..ff315ed 100644 --- a/functions/__based.fish +++ b/functions/__based.fish @@ -44,10 +44,12 @@ function __based else set -f results (sqlite3 -batch $db " SELECT DISTINCT cmd FROM ( - -- Get the most recent command across all paths + -- Get the most recent command across the current path to emulate the normal history behavior + -- This ensures that the most recent command is always at the top SELECT cmd, max_ts, counter, priority FROM ( SELECT cmd, MAX(ts) as max_ts, MAX(counter) as counter, 0 as priority FROM log + WHERE path = '$path' GROUP BY cmd ORDER BY max_ts DESC LIMIT 1 From 6ed4d3a32d69cf3505e24fb94772fff906cd8190 Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Thu, 5 Jun 2025 00:18:38 -0500 Subject: [PATCH 04/14] (chore): optimize fzf popup --- functions/__based_fzf_popup.fish | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/functions/__based_fzf_popup.fish b/functions/__based_fzf_popup.fish index 127e956..736256a 100644 --- a/functions/__based_fzf_popup.fish +++ b/functions/__based_fzf_popup.fish @@ -1,21 +1,18 @@ function __based_fzf_popup - set -l suggestions (__based) - if test (count $suggestions) -gt 0 - set -l chosen (printf "%s\n" $suggestions | fzf --height 40% --reverse --prompt='Context > ' --query="$prefix" --no-sort --exact --border) + set -l chosen (printf "%s\n" (__based) | fzf --height 50% --reverse --prompt='Command > ' --no-sort --exact --border) + if test -n "$chosen" if test -n "$chosen" - set -l cleaned (string trim -- $chosen) - if test -n "$cleaned" - commandline -r -- "$cleaned" - commandline -f repaint - # If BASED_NO_CONFIRMATION is set to a non-zero value, execute the command immediately - if set -q BASED_NO_CONFIRMATION; and test "$BASED_NO_CONFIRMATION" != 0 - commandline -f execute - end - end - else + commandline -r -- "$chosen" commandline -f repaint - return + # If BASED_NO_CONFIRMATION is set to a non-zero value, execute the command immediately + if set -q BASED_NO_CONFIRMATION; and test "$BASED_NO_CONFIRMATION" != 0 + commandline -f execute + end end + else + commandline -f repaint + return end + end From 1eeb710cb7efed20df5f1a6076fb9702cbc419ba Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Thu, 5 Jun 2025 00:27:07 -0500 Subject: [PATCH 05/14] (feat): add completions --- completions/based.fish | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 completions/based.fish diff --git a/completions/based.fish b/completions/based.fish new file mode 100644 index 0000000..5ac30c6 --- /dev/null +++ b/completions/based.fish @@ -0,0 +1,6 @@ +# Fish shell completion script for the Based Fish plugin +complete -c based -n __fish_use_subcommand -f -a init -d "Initialize the Based database" +complete -c based -n __fish_use_subcommand -f -a import -d "Import Fish history into Based" +complete -c based -n __fish_use_subcommand -f -a stats -d "Show Based statistics" +complete -c based -n __fish_use_subcommand -f -a reset -d "Reset Based state" +complete -c based -n __fish_use_subcommand -f -a help -d "Show help message" From e7f2351f06bd49260f43bdff52d0596af5bc012f Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Thu, 5 Jun 2025 00:44:57 -0500 Subject: [PATCH 06/14] (feat): better keybinding for fzf # TAB: Accept the current selection but do not execute it, never # ENTER: Accept the current selection and execute it if confirmation is set --- functions/__based_fzf_popup.fish | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/functions/__based_fzf_popup.fish b/functions/__based_fzf_popup.fish index 736256a..da8f869 100644 --- a/functions/__based_fzf_popup.fish +++ b/functions/__based_fzf_popup.fish @@ -1,18 +1,24 @@ function __based_fzf_popup - set -l chosen (printf "%s\n" (__based) | fzf --height 50% --reverse --prompt='Command > ' --no-sort --exact --border) + # TAB: Accept the current selection but do not execute it, never + # ENTER: Accept the current selection and execute it if confirmation is set + set -l out (__based | fzf \ + --height 50% --reverse --prompt='Command > ' \ + --no-sort --exact --border \ + --bind "tab:accept" \ + --expect tab,enter) + + set -l key (echo $out[1]) + set -l chosen (string join "\n" $out[2..-1] | string collect) if test -n "$chosen" - if test -n "$chosen" - commandline -r -- "$chosen" - commandline -f repaint - # If BASED_NO_CONFIRMATION is set to a non-zero value, execute the command immediately - if set -q BASED_NO_CONFIRMATION; and test "$BASED_NO_CONFIRMATION" != 0 - commandline -f execute - end + commandline -r -- "$chosen" + commandline -f repaint + + # Only execute if the user pressed ENTER and confirmation is set + if test "$key" = enter; and set -q BASED_NO_CONFIRMATION; and test "$BASED_NO_CONFIRMATION" != 0 + commandline -f execute end else commandline -f repaint - return end - end From 97525dd0e280b34c8ff6cf0606629f0f8fcf6439 Mon Sep 17 00:00:00 2001 From: Daniel Bretoi Date: Thu, 19 Jun 2025 08:00:46 +0700 Subject: [PATCH 07/14] use relative source path (#1) $__fish_config_dir could be set to something completely different. but using a relative path here is safe either way. This way it can be sourced anywhere, even if the repo is just cloned in some random place. --- conf.d/based.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf.d/based.fish b/conf.d/based.fish index f69e26a..fc9678c 100644 --- a/conf.d/based.fish +++ b/conf.d/based.fish @@ -1,4 +1,4 @@ # Load the based logger (for fish_postexec event) -source ~/.config/fish/functions/__based_log.fish +source (status dirname)/../functions/__based_log.fish # Load keybindings (↑ for smart history, Ctrl+g for fzf popup) -source ~/.config/fish/functions/based_user_key_bindings.fish +source (status dirname)/../functions/based_user_key_bindings.fish From b85004121f7b1ac08b9176ca0e5f0e2de8dce955 Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Thu, 19 Jun 2025 14:30:08 -0500 Subject: [PATCH 08/14] (chore): change function name --- functions/based_user_key_bindings.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/based_user_key_bindings.fish b/functions/based_user_key_bindings.fish index 3549b73..1644baf 100644 --- a/functions/based_user_key_bindings.fish +++ b/functions/based_user_key_bindings.fish @@ -1,4 +1,4 @@ -function fish_user_key_bindings +function based_user_key_bindings # Arrow up and down for smart history bind \e\[A __based_or_history bind \e\[B __based_or_history From 02067281456045e4a476cb2303e098bd7006da0c Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Thu, 19 Jun 2025 17:23:35 -0500 Subject: [PATCH 09/14] Revert "(chore): change function name" This reverts commit b85004121f7b1ac08b9176ca0e5f0e2de8dce955. --- functions/based_user_key_bindings.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/based_user_key_bindings.fish b/functions/based_user_key_bindings.fish index 1644baf..3549b73 100644 --- a/functions/based_user_key_bindings.fish +++ b/functions/based_user_key_bindings.fish @@ -1,4 +1,4 @@ -function based_user_key_bindings +function fish_user_key_bindings # Arrow up and down for smart history bind \e\[A __based_or_history bind \e\[B __based_or_history From 2afe4b3aba96cab12842b7eaa319709e08e0398b Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Sat, 26 Jul 2025 22:23:57 -0500 Subject: [PATCH 10/14] (feat): add BASED_EXCLUDED_PATHS to exclude directories from history There are cases where you want to exclude some paths from being indexed. E.g: `..`, `/tmp/*`, and others. --- completions/based.fish | 1 + functions/__based_log.fish | 9 +++++++++ functions/__based_reset_state.fish | 1 + 3 files changed, 11 insertions(+) diff --git a/completions/based.fish b/completions/based.fish index 5ac30c6..0a999d6 100644 --- a/completions/based.fish +++ b/completions/based.fish @@ -3,4 +3,5 @@ complete -c based -n __fish_use_subcommand -f -a init -d "Initialize the Based d complete -c based -n __fish_use_subcommand -f -a import -d "Import Fish history into Based" complete -c based -n __fish_use_subcommand -f -a stats -d "Show Based statistics" complete -c based -n __fish_use_subcommand -f -a reset -d "Reset Based state" +complete -c based -n __fish_use_subcommand -f -a maintenance -d "Perform database maintenance" complete -c based -n __fish_use_subcommand -f -a help -d "Show help message" diff --git a/functions/__based_log.fish b/functions/__based_log.fish index 074bd5f..bc1550e 100644 --- a/functions/__based_log.fish +++ b/functions/__based_log.fish @@ -13,6 +13,15 @@ function __based_log --on-event fish_preexec return end + # Check if path should be excluded + if set -q BASED_EXCLUDED_PATHS + for excluded_pattern in $BASED_EXCLUDED_PATHS + if string match -q $excluded_pattern $path + return + end + end + end + set cmd (string replace -a "'" "''" -- $cmd) set path (string replace -a "'" "''" -- $path) diff --git a/functions/__based_reset_state.fish b/functions/__based_reset_state.fish index 65ff5d5..b9c0981 100644 --- a/functions/__based_reset_state.fish +++ b/functions/__based_reset_state.fish @@ -3,5 +3,6 @@ function __based_reset_state # Unset all variables related to Based state set -e BASED_NO_CONFIRMATION set -e BASED_NO_FUZZY + set -e BASED_EXCLUDED_PATHS echo "Based state has been reset." end From 88cbb3cc9bd1807cbd587118709993f931b08334 Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Sat, 26 Jul 2025 22:24:16 -0500 Subject: [PATCH 11/14] (chore): add more database improvements --- functions/__based_init_db.fish | 2 ++ 1 file changed, 2 insertions(+) diff --git a/functions/__based_init_db.fish b/functions/__based_init_db.fish index b342888..d618655 100644 --- a/functions/__based_init_db.fish +++ b/functions/__based_init_db.fish @@ -18,6 +18,8 @@ function __based_init_db CREATE INDEX IF NOT EXISTS idx_log_cmd ON log(cmd); CREATE INDEX IF NOT EXISTS idx_log_path_ts ON log(path, ts); CREATE INDEX IF NOT EXISTS idx_log_path_cmd ON log(path, cmd); + CREATE INDEX IF NOT EXISTS idx_log_counter_ts ON log(counter DESC, ts DESC); + CREATE INDEX IF NOT EXISTS idx_log_cmd_like ON log(cmd COLLATE NOCASE); " echo "Based initialized. You can also import your fish history with 'based import'." From 768bce39ecda71e17ee3fb98ee1e7f8f7aec7038 Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Sat, 26 Jul 2025 22:25:27 -0500 Subject: [PATCH 12/14] (feat): add `based maintenance` to perform analyze + vacuum It will help if your database has been runing for a very long time --- functions/__based_maintenance.fish | 25 +++++++++++++++++++++++++ functions/based.fish | 20 ++++++++++++++------ 2 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 functions/__based_maintenance.fish diff --git a/functions/__based_maintenance.fish b/functions/__based_maintenance.fish new file mode 100644 index 0000000..6827113 --- /dev/null +++ b/functions/__based_maintenance.fish @@ -0,0 +1,25 @@ +function __based_maintenance + set -l db ~/.local/share/fish/based/based.db + if not test -f $db + echo "Database not found. Run 'based init' first." + return 1 + end + + echo "Performing database maintenance..." + + # Get database size before + set -l size_before (du -h $db | cut -f1) + + # Vacuum the database + sqlite3 $db "VACUUM;" + + # Update statistics + sqlite3 $db "ANALYZE;" + + # Get database size after + set -l size_after (du -h $db | cut -f1) + + echo "Database maintenance completed." + echo "Size before: $size_before" + echo "Size after: $size_after" +end diff --git a/functions/based.fish b/functions/based.fish index cc2e0ae..bd49892 100644 --- a/functions/based.fish +++ b/functions/based.fish @@ -8,14 +8,22 @@ function based __based_stats case reset __based_reset_state + case maintenance vacuum + __based_maintenance case help '*' - echo "Usage: based init|import|stats|reset|help" + echo "Usage: based init|import|stats|reset|maintenance|help" echo "Commands:" - echo " init - Initialize the Based database" - echo " import - Import fish history into Based" - echo " stats - Show Based statistics" - echo " reset - Reset Based state (clears variables)" - echo " help - Show this help message" + echo " init - Initialize the Based database" + echo " import - Import fish history into Based" + echo " stats - Show Based statistics" + echo " reset - Reset Based state (clears variables)" + echo " maintenance - Perform database maintenance (vacuum & analyze)" + echo " help - Show this help message" + echo "" + echo "Configuration variables:" + echo " BASED_NO_CONFIRMATION - Skip confirmation prompts" + echo " BASED_NO_FUZZY - Disable fuzzy matching" + echo " BASED_EXCLUDED_PATHS - Paths to exclude from history (glob patterns)" case '*' echo "Unknown command: $argv[1]" echo "Use 'based help' for usage information." From 62cd9b2f1737bc28327b855eecd08e1fc0ededf5 Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Sat, 26 Jul 2025 22:25:40 -0500 Subject: [PATCH 13/14] (chore): update documentation fdor new implemented features --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 6cedbf0..635e6de 100644 --- a/README.md +++ b/README.md @@ -68,12 +68,20 @@ $ based stats ``` This will display statistics about your command usage, such as the most frequently used commands, options, and arguments. +For database maintenance (recommended to run if you notice performance issues): + +```fish +$ based maintenance +``` +This will optimize the database by running VACUUM and ANALYZE operations to keep it performing well. + ## Configuration You can customize the behavior of based.fish by setting environment variables in your Fish shell: - `BASED_NO_CONFIRMATION`: If set to `1`, automatically executes the selected suggestion without confirmation, otherwise, it requires another Enter key press to execute the suggestion. - `BASED_NO_FUZZY`: If set to `1`, disables fuzzy matching for completions and only commands that start with the typed prefix will be suggested. +- `BASED_EXCLUDED_PATHS`: A list of path patterns to exclude from history logging. Supports glob patterns like `*` and `?`. Useful for excluding temporary directories, sensitive locations, or paths where you don't want command history tracked. E.g. to disable fuzzy behavior when searching for completions @@ -81,6 +89,12 @@ E.g. to disable fuzzy behavior when searching for completions $ set -Ux BASED_NO_FUZZY 1 ``` +E.g. to exclude specific paths from history tracking + +```fish +$ set -Ux BASED_EXCLUDED_PATHS "/tmp/*" "/var/tmp/*" "$HOME/Downloads" +``` + The keybindings for the completions are as follows: - `Arrow Up` and `Arrow Down`: Navigate through the suggestions of the smart history. From aa00038f28534bd45cb463b8bdfdafbc51331157 Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Thu, 28 Aug 2025 16:27:22 -0500 Subject: [PATCH 14/14] (chore): better repaint handling --- functions/__based_fzf_popup.fish | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/functions/__based_fzf_popup.fish b/functions/__based_fzf_popup.fish index da8f869..3c2bd01 100644 --- a/functions/__based_fzf_popup.fish +++ b/functions/__based_fzf_popup.fish @@ -12,13 +12,18 @@ function __based_fzf_popup if test -n "$chosen" commandline -r -- "$chosen" + # Prevent autocomplete samples from remaining before the selected command. + # i.e: if you run `cd`, then arrow up, and select a command, the initial `cd` will be + # visible before the selected command, like: `cd cd `. It doesn't affect functionality, + # but it can be visually confusing. commandline -f repaint # Only execute if the user pressed ENTER and confirmation is set if test "$key" = enter; and set -q BASED_NO_CONFIRMATION; and test "$BASED_NO_CONFIRMATION" != 0 commandline -f execute end - else - commandline -f repaint end + + # Update the display for commands like directory changes + commandline -f repaint end