1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
# Kakoune CTags support script
#
# This script requires the readtags command available in universal-ctags
declare-option -docstring "minimum characters before triggering autocomplete" \
int ctags_min_chars 3
declare-option -docstring "list of paths to tag files to parse when looking up a symbol" \
str-list ctagsfiles 'tags'
declare-option -hidden completions ctags_completions
declare-option -docstring "shell command to run" str readtagscmd "readtags"
define-command -params ..1 \
-shell-script-candidates %{
realpath() { ( cd "$(dirname "$1")"; printf "%s/%s\n" "$(pwd -P)" "$(basename "$1")" ) }
eval "set -- $kak_quoted_opt_ctagsfiles"
for candidate in "$@"; do
[ -f "$candidate" ] && realpath "$candidate"
done | awk '!x[$0]++;' | # remove duplicates
while read -r tags; do
namecache="${tags%/*}/.kak.${tags##*/}.namecache"
if [ -z "$(find "$namecache" -prune -newer "$tags")" ]; then
cut -f 1 "$tags" | grep -v '^!' | uniq > "$namecache"
fi
cat "$namecache"
done
} \
-docstring %{
ctags-search [<symbol>]: jump to a symbol's definition
If no symbol is passed then the current selection is used as symbol name
} \
ctags-search %[ require-module menu; evaluate-commands %sh[
realpath() { ( cd "$(dirname "$1")"; printf "%s/%s\n" "$(pwd -P)" "$(basename "$1")" ) }
export tagname="${1:-${kak_selection}}"
eval "set -- $kak_quoted_opt_ctagsfiles"
for candidate in "$@"; do
[ -f "$candidate" ] && realpath "$candidate"
done | awk '!x[$0]++' | # remove duplicates
while read -r tags; do
printf '!TAGROOT\t%s\n' "$(realpath "${tags%/*}")/"
${kak_opt_readtagscmd} -t "$tags" "$tagname"
done | awk -F '\t|\n' '
/^!TAGROOT\t/ { tagroot=$2 }
/[^\t]+\t[^\t]+\t\/\^.*\$?\// {
line = $0; sub(".*\t/\\^", "", line); sub("\\$?/$", "", line);
menu_info = line; gsub("!", "!!", menu_info); gsub(/^[\t ]+/, "", menu_info); gsub(/\t/, " ", menu_info);
keys = line; gsub(/</, "<lt>", keys); gsub(/\t/, "<c-v><c-i>", keys); gsub("!", "!!", keys); gsub("&", "&&", keys); gsub("#", "##", keys); gsub("\\|", "||", keys); gsub("\\\\/", "/", keys);
menu_item = $2; gsub("!", "!!", menu_item);
edit_path = path($2); gsub("&", "&&", edit_path); gsub("#", "##", edit_path); gsub("\\|", "||", edit_path);
select = $1; gsub(/</, "<lt>", select); gsub(/\t/, "<c-v><c-i>", select); gsub("!", "!!", select); gsub("&", "&&", select); gsub("#", "##", select); gsub("\\|", "||", select);
out = out "%!" menu_item ": " menu_info "! %!evaluate-commands %# try %& edit -existing %|" edit_path "|; execute-keys %|/\\Q" keys "<ret>vc| & catch %& fail unable to find tag &; try %& execute-keys %|s\\Q" select "<ret>| & # !"
}
/[^\t]+\t[^\t]+\t[0-9]+/ {
menu_item = $2; gsub("!", "!!", menu_item);
select = $1; gsub(/</, "<lt>", select); gsub(/\t/, "<c-v><c-i>", select); gsub("!", "!!", select); gsub("&", "&&", select); gsub("#", "##", select); gsub("\\|", "||", select);
menu_info = $3; gsub("!", "!!", menu_info);
edit_path = path($2); gsub("!", "!!", edit_path); gsub("#", "##", edit_path); gsub("&", "&&", edit_path); gsub("\\|", "||", edit_path);
line_number = $3;
out = out "%!" menu_item ": " menu_info "! %!evaluate-commands %# try %& edit -existing %|" edit_path "|; execute-keys %|" line_number "gx| & catch %& fail unable to find tag &; try %& execute-keys %|s\\Q" select "<ret>| & # !"
}
END { print ( length(out) == 0 ? "fail no such tag " ENVIRON["tagname"] : "menu -markup -auto-single " out ) }
# Ensure x is an absolute file path, by prepending with tagroot
function path(x) { return x ~/^\// ? x : tagroot x }'
]]
define-command ctags-complete -docstring "Complete the current selection" %{
nop %sh{
(
header="${kak_cursor_line}.${kak_cursor_column}@${kak_timestamp}"
compl=$(
eval "set -- $kak_quoted_opt_ctagsfiles"
for ctagsfile in "$@"; do
${kak_opt_readtagscmd} -p -t "$ctagsfile" ${kak_selection}
done | awk '{ uniq[$1]++ } END { for (elem in uniq) printf " %s||%s", elem, elem }'
)
printf %s\\n "evaluate-commands -client ${kak_client} set-option buffer=${kak_bufname} ctags_completions ${header}${compl}" | \
kak -p ${kak_session}
) > /dev/null 2>&1 < /dev/null &
}
}
define-command ctags-funcinfo -docstring "Display ctags information about a selected function" %{
evaluate-commands -draft %{
try %{
execute-keys '[(;B<a-k>[a-zA-Z_]+\(<ret><a-;>'
evaluate-commands %sh{
f=${kak_selection%?}
sig='\tsignature:(.*)'
csn='\t(class|struct|namespace):(\S+)'
sigs=$(${kak_opt_readtagscmd} -e -Q '(eq? $kind "f")' "${f}" | sed -Ee "s/^.*${csn}.*${sig}$/\3 [\2::${f}]/ ;t ;s/^.*${sig}$/\1 [${f}]/")
if [ -n "$sigs" ]; then
printf %s\\n "evaluate-commands -client ${kak_client} %{info -anchor $kak_cursor_line.$kak_cursor_column -style above '$sigs'}"
fi
}
}
}
}
define-command ctags-enable-autoinfo -docstring "Automatically display ctags information about function" %{
hook window -group ctags-autoinfo NormalIdle .* ctags-funcinfo
hook window -group ctags-autoinfo InsertIdle .* ctags-funcinfo
}
define-command ctags-disable-autoinfo -docstring "Disable automatic ctags information displaying" %{ remove-hooks window ctags-autoinfo }
declare-option -docstring "shell command to run" \
str ctagscmd "ctags -R --fields=+S"
declare-option -docstring "path to the directory in which the tags file will be generated" str ctagspaths "."
define-command ctags-generate -docstring 'Generate tag file asynchronously' %{
echo -markup "{Information}launching tag generation in the background"
nop %sh{ (
trap - INT QUIT
while ! mkdir .tags.kaklock 2>/dev/null; do sleep 1; done
trap 'rmdir .tags.kaklock' EXIT
if ${kak_opt_ctagscmd} -f .tags.kaktmp ${kak_opt_ctagspaths}; then
mv .tags.kaktmp tags
msg="tags generation complete"
else
msg="tags generation failed"
fi
printf %s\\n "evaluate-commands -client $kak_client echo -markup '{Information}${msg}'" | kak -p ${kak_session}
) > /dev/null 2>&1 < /dev/null & }
}
define-command ctags-update-tags -docstring 'Update tags for the given file' %{
nop %sh{ (
trap - INT QUIT
while ! mkdir .tags.kaklock 2>/dev/null; do sleep 1; done
trap 'rmdir .tags.kaklock' EXIT
if ${kak_opt_ctagscmd} -f .file_tags.kaktmp $kak_bufname; then
export LC_COLLATE=C LC_ALL=C # ensure ASCII sorting order
# merge the updated tags tags with the general tags (filtering out out of date tags from it) into the target file
grep -Fv "$(printf '\t%s\t' "$kak_bufname")" tags | grep -v '^!' | sort --merge - .file_tags.kaktmp >> .tags.kaktmp
rm .file_tags.kaktmp
mv .tags.kaktmp tags
msg="tags updated for $kak_bufname"
else
msg="tags update failed for $kak_bufname"
fi
printf %s\\n "evaluate-commands -client $kak_client echo -markup '{Information}${msg}'" | kak -p ${kak_session}
) > /dev/null 2>&1 < /dev/null & }
}
define-command ctags-enable-autocomplete -docstring "Enable automatic ctags completion" %{
set-option window completers "option=ctags_completions" %opt{completers}
hook window -group ctags-autocomplete InsertIdle .* %{
try %{
evaluate-commands -draft %{ # select previous word >= ctags_min_chars
execute-keys ",b_<a-k>.{%opt{ctags_min_chars},}<ret>"
ctags-complete # run in draft context to preserve selection
}
}
}
}
define-command ctags-disable-autocomplete -docstring "Disable automatic ctags completion" %{
evaluate-commands %sh{
printf "set-option window completers %s\n" $(printf %s "${kak_opt_completers}" | sed -e "s/'option=ctags_completions'//g")
}
remove-hooks window ctags-autocomplete
}
|