aboutsummaryrefslogtreecommitdiff
path: root/fzf-exec/writeup.md
diff options
context:
space:
mode:
authorCollin Williams <96917990+bluedragon1221@users.noreply.github.com>2025-08-06 07:23:11 -0500
committerCollin Williams <96917990+bluedragon1221@users.noreply.github.com>2025-08-06 07:23:11 -0500
commit5ddd2890435190f8b08b33cdb8d6cb6ae7fde66b (patch)
tree887f1decfba39dc889bc1d944ac2b7b3c5dfa2e1 /fzf-exec/writeup.md
parent104fc729bb13ccc99a81a74dc08b313923e0e330 (diff)
fzf-exec mostly done
Diffstat (limited to 'fzf-exec/writeup.md')
-rw-r--r--fzf-exec/writeup.md125
1 files changed, 125 insertions, 0 deletions
diff --git a/fzf-exec/writeup.md b/fzf-exec/writeup.md
new file mode 100644
index 0000000..5c6d87e
--- /dev/null
+++ b/fzf-exec/writeup.md
@@ -0,0 +1,125 @@
+Here's a writeup of how `fzf-exec` works, if your interested.
+
+## Setup
+The basic setup is three scripts, plus a binding in `tmux.conf` to launch it.
+- `tmux-doc.sh`: Fetches the documentation for a specific tmux command
+- `fzf-exec.sh`: Launches the menu of all tmux commands and previously entered commands
+- `minibuffer.sh`: Launches any program in a popup window at the bottom of the screen
+
+### `tmux-doc.sh`
+This ended up being the toughest part of the project, and its still kind of finickey.
+Also this is entirely optional if you want to just not have the docs for the selected command in the fzf preview.
+The basic procedure is to grep tmux's manpage for the part that has the documentation about our selected command, and pull that out.
+The problem is that if we're looking for, say, `source-file` in the docs, its likely that `source-file` appears before the part we're trying to extract.
+To avoid this problem, we can search for the command, along with its usage, to get a unique result that we can extract from.
+To get the search string, given the name of the command, we can use this command:
+```sh
+usage=$(tmux list-command -F "#{command_list_name} #{command_list_usage}" "$1")
+```
+
+Then, using this awk command, we can search through the manpage until we find an empty line, signaling the end of that doc section.
+```sh
+man tmux | awk -v usage="$usage" '
+ index($0, usage) > 0 { found=1 }
+ found {
+ print
+ if ($0 == "") exit
+ }
+'
+```
+
+Putting it all together:
+```sh
+usage=$(tmux list-command -F "#{command_list_name} #{command_list_usage}" "$1")
+man tmux | awk -v usage="$usage" '
+ index($0, usage) > 0 { found=1 }
+ found {
+ print
+ if ($0 == "") exit
+ }
+'
+```
+
+### `fzf-exec.sh`
+First, we'll get a list of all of the possible tmux commands.
+This is trivial using `tmux list-commands`:
+```sh
+all_cmds() {
+ tmux list-commands -F "#{command_list_name}"
+}
+```
+However, we want to list aliases as well.
+We can slightly modify our command to do that:
+```sh
+all_cmds() {
+ tmux list-commands -F $'#{command_list_name}#{?command_list_alias,\n#{command_list_alias},}'
+}
+```
+
+Now, for the actual menu that we're interacting with, I wanted specific behavior that is not default to fzf:
+- I want `tab` to replace the current query with the selected item in the menu.
+ This means you could type `sou<TAB>` to get `source` without exiting fzf
+- I want `enter` to, if nothing is selected, return the current query, otherwise return the selection.
+ This means if you press enter on a menu item, it will return it, but if your query isn't a menu item, it will return your query.
+- It would also be nice to have a binding to clear the query
+
+I ended up with this fzf command:
+```sh
+selected_cmd=$(all_cmds | fzf \
+ --prompt : \
+ --bind 'enter:accept-or-print-query,tab:replace-query,alt-backspace:clear-query'
+)
+```
+
+Assuming `tmux-doc.sh` is in the path, we can add `--preview ...` to the end to add the docs preview that we previously wrote:
+```sh
+selected_cmd=$(all_cmds | fzf \
+ --prompt : \
+ --bind 'enter:accept-or-print-query,tab:replace-query,alt-backspace:clear-query' \
+ --preview "tmux-doc.sh $(printf '{}' | cut -d' ' -f1)"
+)
+```
+
+Now we can execute the command:
+```sh
+tmux $selected_cmd
+```
+One problem: this will fail if one of the arguments is a path containing a tilde `~`, since it looks for the literal directory `./~`.
+We can fix this with a quick sed (but if anyone knows a better solution please let me know):
+```sh
+tmux $(echo "$selected_cmd" | sed "s@~@$HOME@g")
+```
+
+Putting it all together:
+```sh
+all_cmds() {
+ tmux list-commands -F $'#{command_list_name}#{?command_list_alias,\n#{command_list_alias},}'
+}
+
+selected_cmd=$(all_cmds | fzf \
+ --prompt : \
+ --bind 'enter:accept-or-print-query,tab:replace-query,alt-backspace:clear-query' \
+ --preview "tmux-doc.sh $(printf '{}' | cut -d' ' -f1)"
+)
+
+tmux $(echo "$selected_cmd" | sed "s@~@$HOME@g")
+```
+
+### `minibuffer.sh`
+This is the easiest one.
+Basically, we just make a popup window that's as wide as the screen, positioned at the bottom of the screen, with no border.
+```sh
+window_height="$(tmux display -p '#{window_height}')"
+tmux display-popup -EB \
+ -w 100% -h 16 \
+ -x 0 -y "$(($window_height + 1))" \
+ "$@"
+```
+
+## Binding
+Now we can just add a binding to override the default command prompt, and we're done!
+```sh
+# Replace command prompt with fzf
+bind-key -T prefix : run-shell "minibuffer.sh -h 10 'fzf-exec.sh'"
+```
+