-
Notifications
You must be signed in to change notification settings - Fork 722
/
Copy pathclang.kak
196 lines (173 loc) · 8.79 KB
/
clang.kak
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
hook -once global BufSetOption filetype=(c|cpp) %{
require-module clang
}
provide-module clang %[
declare-option -docstring "options to pass to the `clang` shell command" \
str clang_options
declare-option -docstring "directory from which to invoke clang" \
str clang_directory
declare-option -hidden completions clang_completions
declare-option -hidden line-specs clang_flags
declare-option -hidden line-specs clang_errors
define-command -params ..1 \
-docstring %{
Parse the contents of the current buffer
The syntaxic errors detected during parsing are shown when auto-diagnostics are enabled
} clang-parse %{
evaluate-commands %sh{
dir=$(mktemp -d "${TMPDIR:-/tmp}"/kak-clang.XXXXXXXX)
mkfifo ${dir}/fifo
printf %s\\n "
evaluate-commands -no-hooks write -sync -method replace ${dir}/buf
evaluate-commands -draft %{
edit! -fifo ${dir}/fifo -debug *clang-output*
set-option buffer filetype make
set-option buffer jump_current_line 0
hook -once -always buffer BufCloseFifo .* %{ nop %sh{ rm -r ${dir} } }
}"
# this runs in a detached shell, asynchronously, so that kakoune does
# not hang while clang is running. As completions references a cursor
# position and a buffer timestamp, only valid completions should be
# displayed.
((
trap - INT QUIT
until [ -f ${dir}/buf ]; do :; done # wait for the buffer to be written
if [ -n "$kak_opt_clang_directory" ]; then
cd "$kak_opt_clang_directory"
fi
case ${kak_opt_filetype} in
(c) ft=c ;;
(cpp) ft=c++ ;;
(obj-c) ft=objective-c ;;
(*) ft=c++ ;;
esac
if [ "$1" = "-complete" ]; then
pos=-:${kak_cursor_line}:${kak_cursor_column}
header="${kak_cursor_line}.${kak_cursor_column}@${kak_timestamp}"
compl=$(clang++ -x ${ft} -fsyntax-only ${kak_opt_clang_options} \
-Xclang -code-completion-brief-comments -Xclang -code-completion-at=${pos} - < ${dir}/buf 2> ${dir}/stderr |
awk -F ': ' '
/^COMPLETION:/ && $2 !~ /[(,](Hidden|Inaccessible)[),]/ {
candidate=$3
gsub(/[[<{]#[^#]*#[]>}]/, "", candidate)
gsub(/~/, "~~", candidate)
gsub(/\|/, "\\|", candidate)
gsub(/[[{<]#|#[]}>]/, " ", $3)
gsub(/:: /, "::", $3)
gsub(/ ,/, ",", $3)
gsub(/^ +| +$/, "", $3)
docstring=$4 ? $3 "\n" $4 : $3
gsub(/~|!/, "&&", docstring)
gsub(/\|/, "\\|", docstring)
if (candidate in candidates)
candidates[candidate]=candidates[candidate] "\n" docstring
else
candidates[candidate]=docstring
}
END {
for (candidate in candidates) {
menu=candidate
gsub(/(^|[^[:alnum:]_])(operator|new|delete)($|[^{}_[:alnum:]]+)/, "{keyword}&{}", menu)
gsub(/(^|[[:space:]])(int|size_t|bool|char|unsigned|signed|long)($|[[:space:]])/, "{type}&{}", menu)
gsub(/[^{}_[:alnum:]]+/, "{operator}&{}", menu)
printf "%%~%s|info -style menu %!%s!|%s~ ", candidate, candidates[candidate], menu
}
}')
printf %s\\n "evaluate-commands -client ${kak_client} echo 'clang completion done'
set-option 'buffer=${kak_buffile}' clang_completions ${header} ${compl}" | kak -p ${kak_session}
else
clang++ -x ${ft} -fsyntax-only ${kak_opt_clang_options} - < ${dir}/buf 2> ${dir}/stderr
printf %s\\n "evaluate-commands -client ${kak_client} echo 'clang parsing done'" | kak -p ${kak_session}
fi
flags=$(cat ${dir}/stderr | sed -Ene "
/^<stdin>:[0-9]+:([0-9]+:)? (fatal )?error/ { s/^<stdin>:([0-9]+):.*/'\1|{red}█'/; p }
/^<stdin>:[0-9]+:([0-9]+:)? warning/ { s/^<stdin>:([0-9]+):.*/'\1|{yellow}█'/; p }
" | paste -s -d ' ' -)
errors=$(cat ${dir}/stderr | sed -Ene "
/^<stdin>:[0-9]+:([0-9]+:)? ((fatal )?error|warning)/ {
s/'/''/g; s/^<stdin>:([0-9]+):([0-9]+:)? (.*)/'\1|\3'/; p
}" | sort -n | paste -s -d ' ' -)
sed -e "s|<stdin>|${kak_bufname}|g" < ${dir}/stderr > ${dir}/fifo
printf %s\\n "set-option 'buffer=${kak_buffile}' clang_flags ${kak_timestamp} ${flags}
set-option 'buffer=${kak_buffile}' clang_errors ${kak_timestamp} ${errors}" | kak -p ${kak_session}
) & ) > /dev/null 2>&1 < /dev/null
}
}
define-command clang-complete -docstring "Complete the current selection" %{ clang-parse -complete }
define-command -hidden clang-show-completion-info %[ try %[
evaluate-commands -draft %[
execute-keys ,{( <a-k> ^\( <ret> b <a-k> \A\w+\z <ret>
evaluate-commands %sh[
desc=$(printf %s\\n "${kak_opt_clang_completions}" | sed -e "{ s/\([^\\]\):/\1\n/g }" | sed -ne "/^${kak_selection}|/ { s/^[^|]\+|//; s/|.*$//; s/\\\:/:/g; p }")
if [ -n "$desc" ]; then
printf %s\\n "evaluate-commands -client $kak_client %{info -anchor ${kak_cursor_line}.${kak_cursor_column} -style above %{${desc}}}"
fi
] ]
] ]
define-command clang-enable-autocomplete -docstring "Enable automatic clang completion" %{
set-option window completers "option=clang_completions" %opt{completers}
hook window -group clang-autocomplete InsertIdle .* %{
try %{
execute-keys -draft <a-h><a-k>(\.|->|::).\z<ret>
echo 'completing...'
clang-complete
}
clang-show-completion-info
}
alias window complete clang-complete
}
define-command clang-disable-autocomplete -docstring "Disable automatic clang completion" %{
evaluate-commands %sh{ printf "set-option window completers %s\n" $(printf %s "${kak_opt_completers}" | sed -e "s/'option=clang_completions'//g") }
remove-hooks window clang-autocomplete
unalias window complete clang-complete
}
define-command -hidden clang-show-error-info %{
update-option buffer clang_errors # Ensure we are up to date with buffer changes
evaluate-commands %sh{
eval "set -- ${kak_quoted_opt_clang_errors}"
shift # skip timestamp
desc=$(for error in "$@"; do
if [ "${error%%|*}" = "$kak_cursor_line" ]; then
printf '%s\n' "${error##*|}"
fi
done)
if [ -n "$desc" ]; then
desc=$(printf %s "${desc}" | sed "s/'/''/g")
printf "info -anchor %d.%d '%s'\n" "${kak_cursor_line}" "${kak_cursor_column}" "${desc}"
fi
} }
define-command clang-enable-diagnostics -docstring %{
Activate automatic error reporting and diagnostics
Information about the analysis will be shown after the buffer has been parsed with the clang-parse function
} %{
add-highlighter window/clang_flags flag-lines default clang_flags
hook window -group clang-diagnostics NormalIdle .* %{ clang-show-error-info }
hook window -group clang-diagnostics WinSetOption ^clang_errors=.* %{ info; clang-show-error-info }
}
define-command clang-disable-diagnostics -docstring "Disable automatic error reporting and diagnostics" %{
remove-highlighter window/clang_flags
remove-hooks window clang-diagnostics
}
define-command clang-diagnostics-next -docstring "Jump to the next line that contains an error" %{
update-option buffer clang_errors # Ensure we are up to date with buffer changes
evaluate-commands %sh{
eval "set -- ${kak_quoted_opt_clang_errors}"
shift # skip timestamp
unset line
unset first_line
for error in "$@"; do
candidate=${error%%|*}
first_line=${first_line-$candidate}
if [ "$candidate" -gt $kak_cursor_line ]; then
line=$candidate
break
fi
done
line=${line-$first_line}
if [ -n "$line" ]; then
printf %s\\n "execute-keys ${line} g"
else
echo "fail no next clang diagnostic"
fi
} }
]