Eric Bower
·
2026-09-08
1#!/usr/bin/env bash
2set -euo pipefail
3
4git_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
5
6tag_files=()
7if [[ $# -gt 0 ]]; then
8 tag_files=("$@")
9else
10 for f in tags .tags_deps; do
11 [[ -f "$f" ]] && tag_files+=("$f")
12 done
13 if [[ ${#tag_files[@]} -eq 0 && -n "$git_root" ]]; then
14 for f in "$git_root/tags" "$git_root/.tags_deps"; do
15 [[ -f "$f" ]] && tag_files+=("$f")
16 done
17 fi
18fi
19
20if [[ ${#tag_files[@]} -eq 0 ]]; then
21 echo "ftags: no tags file found (try: ftags <tagfile>)" >&2
22 exit 1
23fi
24
25load_tags() {
26 for tf in "${tag_files[@]}"; do
27 readtags -t "$tf" -e -n -l 2>/dev/null || true
28 done
29}
30
31selected="$(load_tags | awk -F'\t' '{
32 line = 1
33 kind = ""
34 for (i = 4; i <= NF; i++) {
35 if ($i ~ /^line:/) { line = substr($i, 6) }
36 else if ($i ~ /^kind:/) { kind = substr($i, 6) }
37 }
38 printf "%-28s %-5s %s\t%s\t%s\n", $1, kind, $2, $2, line
39}' | fzf \
40 --delimiter=$'\t' \
41 --with-nth=1 \
42 --border=sharp \
43 --pointer='>' \
44 --preview='f={2}; [ ! -f "$f" ] && [ -n "'"$git_root"'" ] && f="'"$git_root"'/$f"; bat --style=numbers --color=always --wrap=never -H {3} "$f" 2>/dev/null || cat "$f" 2>/dev/null' \
45 --preview-window='right,60%,+{3}-10,border-sharp,nowrap,<80(up,50%,border-sharp,nowrap)' \
46 --bind='ctrl-/:toggle-preview' \
47 --prompt='tags> ' || true)"
48
49[[ -z "$selected" ]] && exit 0
50
51file="$(printf '%s' "$selected" | cut -f2)"
52line="$(printf '%s' "$selected" | cut -f3)"
53
54if [[ ! -f "$file" && -n "$git_root" && -f "$git_root/$file" ]]; then
55 file="$git_root/$file"
56fi
57
58editor="${EDITOR:-kak}"
59
60if [[ -n "$line" && "$line" -gt 1 ]]; then
61 exec "$editor" "+$line" "$file"
62else
63 exec "$editor" "$file"
64fi