From 28f308e8a3bfbda577e041a149c2a90bd041c062 Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Tue, 3 Jun 2025 06:36:13 -0500 Subject: [PATCH] Initial commit --- conf.d/based.fish | 4 ++ functions/__based.fish | 41 +++++++++++++++ functions/__based_fzf_popup.fish | 22 ++++++++ functions/__based_import_fish_history.fish | 60 ++++++++++++++++++++++ functions/__based_init_db.fish | 17 ++++++ functions/__based_log.fish | 26 ++++++++++ functions/__based_or_history.fish | 3 ++ functions/__based_reset_state.fish | 6 +++ functions/__based_stats.fish | 34 ++++++++++++ functions/based.fish | 23 +++++++++ functions/based_user_key_bindings.fish | 4 ++ 11 files changed, 240 insertions(+) create mode 100644 conf.d/based.fish create mode 100644 functions/__based.fish create mode 100644 functions/__based_fzf_popup.fish create mode 100644 functions/__based_import_fish_history.fish create mode 100644 functions/__based_init_db.fish create mode 100644 functions/__based_log.fish create mode 100644 functions/__based_or_history.fish create mode 100644 functions/__based_reset_state.fish create mode 100644 functions/__based_stats.fish create mode 100644 functions/based.fish create mode 100644 functions/based_user_key_bindings.fish diff --git a/conf.d/based.fish b/conf.d/based.fish new file mode 100644 index 0000000..f69e26a --- /dev/null +++ b/conf.d/based.fish @@ -0,0 +1,4 @@ +# Load the based logger (for fish_postexec event) +source ~/.config/fish/functions/__based_log.fish +# Load keybindings (↑ for smart history, Ctrl+g for fzf popup) +source ~/.config/fish/functions/based_user_key_bindings.fish diff --git a/functions/__based.fish b/functions/__based.fish new file mode 100644 index 0000000..f17ba2b --- /dev/null +++ b/functions/__based.fish @@ -0,0 +1,41 @@ +function __based + set -l db ~/.local/share/fish/based/based.db + if not test -f $db + return + end + + set -l path (realpath (pwd)) + set -l prefix "" + + # Extract commandline prefix up to cursor safely + set -l buffer (commandline --current-buffer) + set -l cursor_pos (commandline -C) + set -l prefix (string sub -l $cursor_pos -- "$buffer") + + set path (string replace -a "'" "''" -- $path) + set prefix (string replace -a "'" "''" -- $prefix) + + set -l cmd_expr "%$prefix%" + + # If the user has disabled fuzzy matching + if set -q BASED_NO_FUZZY + set cmd_expr "$prefix%" + end + + if test -n "$prefix" + sqlite3 $db " + SELECT cmd FROM log + WHERE path = '$path' + AND cmd LIKE '$cmd_expr' + ORDER BY counter DESC, ts DESC;" | string collect + else + sqlite3 $db " + SELECT cmd + FROM ( + SELECT cmd, MAX(ts) as max_ts + FROM log + GROUP BY cmd + ) + ORDER BY max_ts DESC;" | string collect + end +end diff --git a/functions/__based_fzf_popup.fish b/functions/__based_fzf_popup.fish new file mode 100644 index 0000000..386c3cd --- /dev/null +++ b/functions/__based_fzf_popup.fish @@ -0,0 +1,22 @@ +function __based_fzf_popup + set -l suggestions (__based) + if test (count $suggestions) -gt 0 + # Run fzf and capture exit status + set -l chosen (printf "%s\n" $suggestions | fzf --height 40% --reverse --prompt='Context > ' --query="$prefix" --no-sort --exact) + + 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 -f repaint + return + end + end +end diff --git a/functions/__based_import_fish_history.fish b/functions/__based_import_fish_history.fish new file mode 100644 index 0000000..006c29d --- /dev/null +++ b/functions/__based_import_fish_history.fish @@ -0,0 +1,60 @@ +function __based_import_fish_history + set -l history_file ~/.local/share/fish/fish_history + set -l db ~/.local/share/fish/based/based.db + set -l path fish_history + + if not test -f $history_file + echo "No fish history file found at $history_file" + return 1 + end + + if not test -f $db + echo "No Based database found at $db. Run 'based init' first." + return 1 + end + + set -l existing_count (sqlite3 $db "SELECT COUNT(*) FROM log WHERE path = '$path';") + + if test $existing_count -gt 0 + set -l existing_cmds (sqlite3 $db "SELECT cmd FROM log WHERE path = '$path';") + echo "Fish history was already imported under context: '$path'. Total commands already imported: $existing_count" + echo "If you want to re-import, please delete the existing entries or the database." + return 0 + end + + echo "Importing fish history into Based, this may take a while..." + + set -l temp_sql (mktemp) + + echo "BEGIN TRANSACTION;" >$temp_sql + + set -l cmd "" + set -l when 0 + + for line in (cat $history_file) + switch $line + case '- cmd:*' + set cmd (string replace -r '^- cmd: *' '' -- $line | string trim) + set cmd (string replace -a "'" "''" -- $cmd) + case ' when:*' + set when (string replace -r '^ when: *' '' -- $line | string trim) + + if test -n "$cmd" + echo "INSERT INTO log (path, cmd, counter, ts) + VALUES ('$path', '$cmd', 1, $when) + ON CONFLICT(path, cmd) DO UPDATE SET + counter = counter + 1, + ts = MAX(ts, $when);" >>$temp_sql + set cmd "" + set when 0 + end + end + end + + echo "COMMIT;" >>$temp_sql + sqlite3 $db <$temp_sql + set -l total_inserts (sqlite3 $db "SELECT COUNT(*) FROM log WHERE path = '$path';") + rm -f $temp_sql + + echo "Fish history imported into Based under context: '$path'. Total commands processed: $total_inserts" +end diff --git a/functions/__based_init_db.fish b/functions/__based_init_db.fish new file mode 100644 index 0000000..73614e1 --- /dev/null +++ b/functions/__based_init_db.fish @@ -0,0 +1,17 @@ +function __based_init_db + set -l db_path ~/.local/share/fish/based/based.db + if test -f $db_path + echo "Database already exists at $db_path. If you want to reinitialize, please delete it first." + return + end + mkdir -p (dirname $db_path) + sqlite3 $db_path "CREATE TABLE IF NOT EXISTS log ( + path TEXT NOT NULL, + cmd TEXT NOT NULL, + counter INTEGER NOT NULL DEFAULT 1, + ts INTEGER NOT NULL, + PRIMARY KEY (path, cmd) + );" + + echo "Based initialized. You can also import your fish history with 'based import'." +end diff --git a/functions/__based_log.fish b/functions/__based_log.fish new file mode 100644 index 0000000..0d6c492 --- /dev/null +++ b/functions/__based_log.fish @@ -0,0 +1,26 @@ +function __based_log --on-event fish_preexec + set -l db ~/.local/share/fish/based/based.db + if not test -f $db + echo "Database for the Based plugin not found. Please run based init to initialize it." + return + end + + set -l path (realpath (pwd)) + set -l cmd (string trim -- (string join " " $argv)) + set -l ts (date +%s) + + if test -z "$cmd" + return + end + + set cmd (string replace -a "'" "''" -- $cmd) + set path (string replace -a "'" "''" -- $path) + + sqlite3 $db " + INSERT INTO log (path, cmd, counter, ts) + VALUES ('$path', '$cmd', 1, $ts) + ON CONFLICT(path, cmd) DO UPDATE SET + counter = counter + 1, + ts = $ts; + " +end diff --git a/functions/__based_or_history.fish b/functions/__based_or_history.fish new file mode 100644 index 0000000..9e18d33 --- /dev/null +++ b/functions/__based_or_history.fish @@ -0,0 +1,3 @@ +function __based_or_history + __based_fzf_popup +end diff --git a/functions/__based_reset_state.fish b/functions/__based_reset_state.fish new file mode 100644 index 0000000..ee0c14b --- /dev/null +++ b/functions/__based_reset_state.fish @@ -0,0 +1,6 @@ +function __based_reset_state + echo "Resetting Based state..." + # Unset all variables related to Based state + set -e BASED_NO_CONFIRMATION + echo "Based state has been reset." +end diff --git a/functions/__based_stats.fish b/functions/__based_stats.fish new file mode 100644 index 0000000..be40d0a --- /dev/null +++ b/functions/__based_stats.fish @@ -0,0 +1,34 @@ +function __based_stats + set -l db ~/.local/share/fish/based/based.db + if not test -f $db + echo "Database not found. Run 'based init' first." + return + end + + echo "== Based Stats ==" + sqlite3 $db " + SELECT 'Total unique commands:', COUNT(DISTINCT cmd) FROM log; + SELECT 'Total executions:', SUM(counter) FROM log; + " + + echo + echo "== Top 10 Commands ==" + sqlite3 $db " + SELECT printf('%4d × %s', SUM(counter), cmd) + FROM log + GROUP BY cmd + ORDER BY SUM(counter) DESC, MAX(ts) DESC + LIMIT 10; + " + + echo + echo "== Top 5 Paths ==" + sqlite3 $db " + SELECT printf('%4d × %s', SUM(counter), path) + FROM log + WHERE path != 'fish_history' + GROUP BY path + ORDER BY SUM(counter) DESC + LIMIT 5; + " +end diff --git a/functions/based.fish b/functions/based.fish new file mode 100644 index 0000000..cc2e0ae --- /dev/null +++ b/functions/based.fish @@ -0,0 +1,23 @@ +function based + switch $argv[1] + case init + __based_init_db + case import + __based_import_fish_history + case stats + __based_stats + case reset + __based_reset_state + case help '*' + echo "Usage: based init|import|stats|reset|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" + case '*' + echo "Unknown command: $argv[1]" + echo "Use 'based help' for usage information." + end +end diff --git a/functions/based_user_key_bindings.fish b/functions/based_user_key_bindings.fish new file mode 100644 index 0000000..36191a4 --- /dev/null +++ b/functions/based_user_key_bindings.fish @@ -0,0 +1,4 @@ +function fish_user_key_bindings + bind \e\[A __based_or_history + bind \cg __based_fzf_popup # Ctrl+g +end