Skip to content

Commit 7ee2409

Browse files
committedMar 26, 2017
Initial commit
0 parents  commit 7ee2409

File tree

3 files changed

+988
-0
lines changed

3 files changed

+988
-0
lines changed
 

‎LICENSE

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Copyright (c) 2010-2016 zsh-syntax-highlighting contributors
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without modification, are permitted
5+
provided that the following conditions are met:
6+
7+
* Redistributions of source code must retain the above copyright notice, this list of conditions
8+
and the following disclaimer.
9+
* Redistributions in binary form must reproduce the above copyright notice, this list of
10+
conditions and the following disclaimer in the documentation and/or other materials provided
11+
with the distribution.
12+
* Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
13+
may be used to endorse or promote products derived from this software without specific prior
14+
written permission.
15+
16+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
17+
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
18+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22+
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
23+
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

‎fast-highlight

+693
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,693 @@
1+
# -------------------------------------------------------------------------------------------------
2+
# Copyright (c) 2010-2016 zsh-syntax-highlighting contributors
3+
# Copyright (c) 2016-2017 Sebastian Gniazdowski (modifications)
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without modification, are permitted
7+
# provided that the following conditions are met:
8+
#
9+
# * Redistributions of source code must retain the above copyright notice, this list of conditions
10+
# and the following disclaimer.
11+
# * Redistributions in binary form must reproduce the above copyright notice, this list of
12+
# conditions and the following disclaimer in the documentation and/or other materials provided
13+
# with the distribution.
14+
# * Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
15+
# may be used to endorse or promote products derived from this software without specific prior
16+
# written permission.
17+
#
18+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
19+
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20+
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
21+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
24+
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25+
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
# -------------------------------------------------------------------------------------------------
27+
28+
typeset -gA __hsmw_highlight_main__command_type_cache
29+
30+
# Define default styles.
31+
typeset -gA FAST_HIGHLIGHT_STYLES
32+
: ${FAST_HIGHLIGHT_STYLES[default]:=none}
33+
: ${FAST_HIGHLIGHT_STYLES[unknown-token]:=fg=red,bold}
34+
: ${FAST_HIGHLIGHT_STYLES[reserved-word]:=fg=yellow}
35+
: ${FAST_HIGHLIGHT_STYLES[alias]:=fg=green}
36+
: ${FAST_HIGHLIGHT_STYLES[suffix-alias]:=fg=green}
37+
: ${FAST_HIGHLIGHT_STYLES[builtin]:=fg=green}
38+
: ${FAST_HIGHLIGHT_STYLES[function]:=fg=green}
39+
: ${FAST_HIGHLIGHT_STYLES[command]:=fg=green}
40+
: ${FAST_HIGHLIGHT_STYLES[precommand]:=fg=green}
41+
: ${FAST_HIGHLIGHT_STYLES[commandseparator]:=none}
42+
: ${FAST_HIGHLIGHT_STYLES[hashed-command]:=fg=green}
43+
: ${FAST_HIGHLIGHT_STYLES[path]:=fg=magenta}
44+
: ${FAST_HIGHLIGHT_STYLES[path_pathseparator]:=}
45+
: ${FAST_HIGHLIGHT_STYLES[globbing]:=fg=blue,bold}
46+
: ${FAST_HIGHLIGHT_STYLES[history-expansion]:=fg=blue,bold}
47+
: ${FAST_HIGHLIGHT_STYLES[single-hyphen-option]:=fg=cyan}
48+
: ${FAST_HIGHLIGHT_STYLES[double-hyphen-option]:=fg=cyan}
49+
: ${FAST_HIGHLIGHT_STYLES[back-quoted-argument]:=none}
50+
: ${FAST_HIGHLIGHT_STYLES[single-quoted-argument]:=fg=yellow}
51+
: ${FAST_HIGHLIGHT_STYLES[double-quoted-argument]:=fg=yellow}
52+
: ${FAST_HIGHLIGHT_STYLES[dollar-quoted-argument]:=fg=yellow}
53+
: ${FAST_HIGHLIGHT_STYLES[back-or-dollar-double-quoted-argument]:=fg=cyan}
54+
: ${FAST_HIGHLIGHT_STYLES[back-dollar-quoted-argument]:=fg=cyan}
55+
: ${FAST_HIGHLIGHT_STYLES[assign]:=none}
56+
: ${FAST_HIGHLIGHT_STYLES[redirection]:=none}
57+
: ${FAST_HIGHLIGHT_STYLES[comment]:=fg=black,bold}
58+
: ${FAST_HIGHLIGHT_STYLES[variable]:=none}
59+
60+
61+
typeset -gA __HSMW_HIGHLIGHT_TOKENS_TYPES
62+
63+
__HSMW_HIGHLIGHT_TOKENS_TYPES=(
64+
65+
# Precommand
66+
67+
'builtin' 1
68+
'command' 1
69+
'exec' 1
70+
'nocorrect' 1
71+
'noglob' 1
72+
'pkexec' 1 # immune to #121 because it's usually not passed --option flags
73+
74+
# Control flow
75+
# Tokens that, at (naively-determined) "command position", are followed by
76+
# a de jure command position. All of these are reserved words.
77+
78+
$'\x7b' 2 # block
79+
$'\x28' 2 # subshell
80+
'()' 2 # anonymous function
81+
'while' 2
82+
'until' 2
83+
'if' 2
84+
'then' 2
85+
'elif' 2
86+
'else' 2
87+
'do' 2
88+
'time' 2
89+
'coproc' 2
90+
'!' 2 # reserved word; unrelated to $histchars[1]
91+
92+
# Command separators
93+
94+
'|' 3
95+
'||' 3
96+
';' 3
97+
'&' 3
98+
'&&' 3
99+
'|&' 3
100+
'&!' 3
101+
'&|' 3
102+
# ### 'case' syntax, but followed by a pattern, not by a command
103+
# ';;' ';&' ';|'
104+
)
105+
106+
107+
108+
109+
# Get the type of a command.
110+
#
111+
# Uses the zsh/parameter module if available to avoid forks, and a
112+
# wrapper around 'type -w' as fallback.
113+
#
114+
# Takes a single argument.
115+
#
116+
# The result will be stored in REPLY.
117+
-fast-highlight-main-type() {
118+
REPLY=$__hsmw_highlight_main__command_type_cache[(e)$1]
119+
[[ -z "$REPLY" ]] && {
120+
121+
if zmodload -e zsh/parameter; then
122+
if (( $+aliases[(e)$1] )); then
123+
REPLY=alias
124+
elif (( $+functions[(e)$1] )); then
125+
REPLY=function
126+
elif (( $+builtins[(e)$1] )); then
127+
REPLY=builtin
128+
elif (( $+commands[(e)$1] )); then
129+
REPLY=command
130+
elif (( $+saliases[(e)${1##*.}] )); then
131+
REPLY='suffix alias'
132+
elif (( $reswords[(Ie)$1] )); then
133+
REPLY=reserved
134+
# zsh 5.2 and older have a bug whereby running 'type -w ./sudo' implicitly
135+
# runs 'hash ./sudo=/usr/local/bin/./sudo' (assuming /usr/local/bin/sudo
136+
# exists and is in $PATH). Avoid triggering the bug, at the expense of
137+
# falling through to the $() below, incurring a fork. (Issue #354.)
138+
#
139+
# The second disjunct mimics the isrelative() C call from the zsh bug.
140+
elif [[ $1 != */* || "${+ZSH_ARGZERO}" = "1" ]] && ! builtin type -w -- $1 >/dev/null 2>&1; then
141+
REPLY=none
142+
fi
143+
fi
144+
145+
[[ -z "$REPLY" ]] && REPLY="${$(LC_ALL=C builtin type -w -- $1 2>/dev/null)##*: }"
146+
147+
__hsmw_highlight_main__command_type_cache[(e)$1]=$REPLY
148+
149+
}
150+
}
151+
152+
# Below are variables that must be defined in outer
153+
# scope so that they are reachable in *-process()
154+
#
155+
# local right_brace_is_recognised_everywhere
156+
# integer path_dirs_was_set
157+
# integer multi_func_def
158+
# integer ointeractive_comments
159+
-fast-highlight-fill-option-variables() {
160+
if [[ -o ignore_braces ]] || eval '[[ -o ignore_close_braces ]] 2>/dev/null'; then
161+
right_brace_is_recognised_everywhere=0
162+
else
163+
right_brace_is_recognised_everywhere=1
164+
fi
165+
166+
if [[ -o path_dirs ]]; then
167+
path_dirs_was_set=1
168+
else
169+
path_dirs_was_set=0
170+
fi
171+
172+
if [[ -o multi_func_def ]]; then
173+
multi_func_def=1
174+
else
175+
multi_func_def=0
176+
fi
177+
178+
if [[ -o interactive_comments ]]; then
179+
ointeractive_comments=1
180+
else
181+
ointeractive_comments=0
182+
fi
183+
}
184+
185+
# Main syntax highlighting function.
186+
-fast-highlight-process()
187+
{
188+
emulate -L zsh
189+
setopt extendedglob bareglobqual nonomatch noksharrays
190+
191+
(( path_dirs_was_set )) && setopt PATH_DIRS
192+
(( ointeractive_comments )) && local interactive_comments= # _set_ to empty
193+
194+
# Variable declarations and initializations
195+
# in_array_assignment true between 'a=(' and the matching ')'
196+
# braces_stack: "R" for round, "Q" for square, "Y" for curly
197+
# mybuf, cdpath_dir are used in sub-functions
198+
local start_pos=0 end_pos highlight_glob=1 arg style in_array_assignment=0 MATCH expanded_path braces_stack buf="$BUFFER" mybuf cdpath_dir cur_cmd alias_target
199+
# arg_type can be 0, 1, 2 or 3, i.e. precommand, control flow, command separator
200+
# idx and end_idx are used in sub-functions
201+
# for this_word and next_word look below at commented integers and at state machine description
202+
integer arg_type=0 MBEGIN MEND in_redirection len=${#buf} already_added offset idx end_idx this_word=1 next_word=0 insane_alias
203+
local -a match mbegin mend
204+
205+
# integer BIT_start=1 BIT_regular=2 BIT_sudo_opt=4 BIT_sudo_arg=8 BIT_always=16
206+
207+
# State machine
208+
#
209+
# The states are:
210+
# - :start: Command word
211+
# - :sudo_opt: A leading-dash option to sudo (such as "-u" or "-i")
212+
# - :sudo_arg: The argument to a sudo leading-dash option that takes one,
213+
# when given as a separate word; i.e., "foo" in "-u foo" (two
214+
# words) but not in "-ufoo" (one word).
215+
# - :regular: "Not a command word", and command delimiters are permitted.
216+
# Mainly used to detect premature termination of commands.
217+
# - :always: The word 'always' in the «{ foo } always { bar }» syntax.
218+
#
219+
# When the kind of a word is not yet known, $this_word / $next_word may contain
220+
# multiple states. For example, after "sudo -i", the next word may be either
221+
# another --flag or a command name, hence the state would include both :start:
222+
# and :sudo_opt:.
223+
#
224+
# The tokens are always added with both leading and trailing colons to serve as
225+
# word delimiters (an improvised array); [[ $x == *:foo:* ]] and x=${x//:foo:/}
226+
# will DTRT regardless of how many elements or repetitions $x has..
227+
#
228+
# Handling of redirections: upon seeing a redirection token, we must stall
229+
# the current state --- that is, the value of $this_word --- for two iterations
230+
# (one for the redirection operator, one for the word following it representing
231+
# the redirection target). Therefore, we set $in_redirection to 2 upon seeing a
232+
# redirection operator, decrement it each iteration, and stall the current state
233+
# when it is non-zero. Thus, upon reaching the next word (the one that follows
234+
# the redirection operator and target), $this_word will still contain values
235+
# appropriate for the word immediately following the word that preceded the
236+
# redirection operator.
237+
#
238+
# The "the previous word was a redirection operator" state is not communicated
239+
# to the next iteration via $next_word/$this_word as usual, but via
240+
# $in_redirection. The value of $next_word from the iteration that processed
241+
# the operator is discarded.
242+
#
243+
244+
# Processing buffer
245+
local proc_buf="$buf" needle
246+
for arg in ${interactive_comments-${(z)buf}} \
247+
${interactive_comments+${(zZ+c+)buf}}; do
248+
# Initialize $next_word to its default value?
249+
(( in_redirection )) && (( --in_redirection ))
250+
(( in_redirection == 0 )) && next_word=2 # else Stall $next_word.
251+
252+
# Initialize per-"simple command" [zshmisc(1)] variables:
253+
#
254+
# $already_added (see next paragraph)
255+
# $style how to highlight $arg
256+
# $in_array_assignment boolean flag for "between '(' and ')' of array assignment"
257+
# $highlight_glob boolean flag for "'noglob' is in effect"
258+
#
259+
# $already_added is set to 1 to disable adding an entry to region_highlight
260+
# for this iteration. Currently, that is done for "" and $'' strings,
261+
# which add the entry early so escape sequences within the string override
262+
# the string's color.
263+
already_added=0
264+
style=unknown-token
265+
if (( this_word & 1 )); then
266+
in_array_assignment=0
267+
[[ $arg == 'noglob' ]] && highlight_glob=0
268+
fi
269+
270+
# Compute the new $start_pos and $end_pos, skipping over whitespace in $buf.
271+
if [[ $arg == ';' ]] ; then
272+
# We're looking for either a semicolon or a newline, whichever comes
273+
# first. Both of these are rendered as a ";" (SEPER) by the ${(z)..}
274+
# flag.
275+
#
276+
# We can't use the (Z+n+) flag because that elides the end-of-command
277+
# token altogether, so 'echo foo\necho bar' (two commands) becomes
278+
# indistinguishable from 'echo foo echo bar' (one command with three
279+
# words for arguments).
280+
needle=$'[;\n]'
281+
offset=$(( ${proc_buf[(i)$needle]} - 1 ))
282+
(( start_pos += offset ))
283+
(( end_pos = start_pos + $#arg ))
284+
285+
# Do not run default code for case when there is a new line
286+
# It shouldn't be treated as ';', i.e. shouldn't be highlighted
287+
# as unknown-token when appears after command-starting arg like "{"
288+
if [[ "${proc_buf[offset+1]}" = $'\n' ]]; then
289+
(( in_array_assignment )) && (( this_word = 2 )) || { (( this_word = 1 )); highlight_glob=1; }
290+
in_redirection=0
291+
proc_buf="${proc_buf[offset + $#arg + 1,len]}"
292+
start_pos=$end_pos
293+
continue
294+
else
295+
# One more short path – for ';' command separator
296+
(( in_array_assignment )) && (( this_word = 2 )) || { (( this_word = 1 )); highlight_glob=1; }
297+
in_redirection=0
298+
[[ "${FAST_HIGHLIGHT_STYLES[commandseparator]}" != "none" ]] && reply+=("$start_pos $end_pos ${FAST_HIGHLIGHT_STYLES[commandseparator]}")
299+
proc_buf="${proc_buf[offset + $#arg + 1,len]}"
300+
start_pos=$end_pos
301+
continue
302+
fi
303+
304+
arg_type=3
305+
else
306+
offset=0
307+
if [[ "$proc_buf" = (#b)(#s)(([[:space:]]|\\[[:space:]])##)* ]]; then
308+
# The first, outer parenthesis
309+
offset="${mend[1]}"
310+
fi
311+
((start_pos+=offset))
312+
((end_pos=start_pos+${#arg}))
313+
314+
# No-hit will result in value 0
315+
arg_type=${__HSMW_HIGHLIGHT_TOKENS_TYPES[$arg]}
316+
fi
317+
318+
proc_buf="${proc_buf[offset + $#arg + 1,len]}"
319+
320+
# Handle the INTERACTIVE_COMMENTS option.
321+
#
322+
# We use the (Z+c+) flag so the entire comment is presented as one token in $arg.
323+
if [[ -n ${interactive_comments+'set'} && $arg[1] == $histchars[3] ]]; then
324+
if (( this_word & 3 )); then
325+
style=comment
326+
else
327+
style=unknown-token # prematurely terminated
328+
fi
329+
# ADD
330+
reply+=("$start_pos $end_pos ${FAST_HIGHLIGHT_STYLES[$style]}")
331+
start_pos=$end_pos
332+
continue
333+
fi
334+
335+
# Analyse the current word.
336+
if [[ $arg == (<0-9>|)(\<|\>)* ]] && [[ $arg != (\<|\>)$'\x28'* ]]; then
337+
# A '<' or '>', possibly followed by a digit
338+
in_redirection=2
339+
fi
340+
341+
# Special-case the first word after 'sudo'.
342+
if (( ! in_redirection )); then
343+
if (( this_word & 4 )) && [[ $arg != -* ]]; then
344+
(( this_word = this_word ^ 4 ))
345+
fi
346+
347+
# Parse the sudo command line
348+
if (( this_word & 4 )); then
349+
case "$arg" in
350+
# Flag that requires an argument
351+
'-'[Cgprtu])
352+
(( this_word & 1 )) && (( this_word = this_word ^ 1 ))
353+
(( next_word = 8 ))
354+
;;
355+
# This prevents misbehavior with sudo -u -otherargument
356+
'-'*)
357+
(( this_word & 1 )) && (( this_word = this_word ^ 1 ))
358+
(( next_word = next_word | 1 ))
359+
(( next_word = next_word | 4 ))
360+
;;
361+
*) ;;
362+
esac
363+
elif (( this_word & 8 )); then
364+
(( next_word = next_word | 4 ))
365+
(( next_word = next_word | 1 ))
366+
fi
367+
fi
368+
369+
expanded_path=""
370+
371+
# The Great Fork: is this a command word? Is this a non-command word?
372+
if (( this_word & 16 )) && [[ $arg == 'always' ]]; then
373+
# try-always construct
374+
style=reserved-word # de facto a reserved word, although not de jure
375+
(( next_word = 1 ))
376+
elif (( this_word & 1 )) && (( in_redirection == 0 )); then # $arg is the command word
377+
cur_cmd="$arg"
378+
if (( arg_type == 1 )); then
379+
style=precommand
380+
elif [[ "$arg" = "sudo" ]]; then
381+
style=precommand
382+
(( next_word & 2 )) && (( next_word = next_word ^ 2 ))
383+
(( next_word = next_word | 4 ))
384+
(( next_word = next_word | 1 ))
385+
else
386+
# Special-case: command word is '$foo', like that, without braces or anything.
387+
#
388+
# That's not entirely correct --- if the parameter's value happens to be a reserved
389+
# word, the parameter expansion will be highlighted as a reserved word --- but that
390+
# incorrectness is outweighed by the usability improvement of permitting the use of
391+
# parameters that refer to commands, functions, and builtins.
392+
if [[ ${arg[1]} == \$ ]] && (( ${+parameters} )) && [[ ${arg:1} = (#m)([a-zA-Z_][a-zA-Z0-9_]#|[0-9]##) ]] && (( ${+parameters[${MATCH}]} )); then
393+
-fast-highlight-main-type ${(P)MATCH}
394+
else
395+
: ${expanded_path::=${(Q)~arg}}
396+
-fast-highlight-main-type $expanded_path
397+
fi
398+
399+
case $REPLY in
400+
reserved) # reserved word
401+
style=reserved-word
402+
if [[ $arg == $'\x7b' ]]; then
403+
braces_stack='Y'"$braces_stack"
404+
elif [[ $arg == $'\x7d' && $braces_stack[1] == "Y" ]]; then
405+
# We're at command word, so no need to check $right_brace_is_recognised_everywhere
406+
braces_stack[1]=""
407+
style=reserved-word
408+
(( next_word = next_word | 16 ))
409+
fi
410+
;;
411+
'suffix alias') style=suffix-alias;;
412+
alias)
413+
insane_alias=0
414+
case $arg in
415+
# Issue #263: aliases with '=' on their LHS.
416+
#
417+
# There are three cases:
418+
#
419+
# - Unsupported, breaks 'alias -L' output, but invokable:
420+
('='*) :;;
421+
# - Unsupported, not invokable:
422+
(*'='*) insane_alias=1;;
423+
# - The common case:
424+
(*) :;;
425+
esac
426+
if (( insane_alias )); then
427+
style=unknown-token
428+
else
429+
style=alias
430+
zmodload -e zsh/parameter && alias_target=${aliases[$arg]} || alias_target="${"$(alias -- $arg)"#*=}"
431+
[[ ${__HSMW_HIGHLIGHT_TOKENS_TYPES[$alias_target]} = "1" && "$arg_type" != "1" ]] && __HSMW_HIGHLIGHT_TOKENS_TYPES[$arg]="1"
432+
fi
433+
;;
434+
builtin) style=builtin;;
435+
function) style=function;;
436+
command) style=command;;
437+
hashed) style=hashed-command;;
438+
none) # Assign?
439+
if [[ $arg == [[:alpha:]_][[:alnum:]_]#(|\[[^\]]#\])(|[+])=* ]] || [[ $arg == [0-9]##(|[+])=* ]]; then
440+
style=assign
441+
# Assignment to a scalar parameter or to array
442+
# (For array assignments, the command doesn't start until the ")" token.)
443+
[[ $arg[-1] == '(' ]] && in_array_assignment=1 || (( next_word = next_word | 1 ))
444+
elif [[ $arg[1] = $histchars[1] && -n "${arg[2]}" ]]; then
445+
style=history-expansion
446+
elif [[ $arg[1] == $histchars[2] ]]; then
447+
style=history-expansion
448+
elif (( arg_type == 3 )); then
449+
# This highlights empty commands (semicolon follows nothing) as an error.
450+
# Zsh accepts them, though.
451+
(( this_word & 2 )) && style=commandseparator
452+
elif [[ $arg[1,2] == '((' ]]; then
453+
# Arithmetic evaluation.
454+
#
455+
# Note: prior to zsh-5.1.1-52-g4bed2cf (workers/36669), the ${(z)...}
456+
# splitter would only output the '((' token if the matching '))' had
457+
# been typed. Therefore, under those versions of zsh, BUFFER="(( 42"
458+
# would be highlighted as an error until the matching "))" are typed.
459+
#
460+
# We highlight just the opening parentheses, as a reserved word; this
461+
# is how [[ ... ]] is highlighted, too.
462+
463+
# ADD
464+
reply+=("$start_pos $(( start_pos + 2 )) ${FAST_HIGHLIGHT_STYLES[reserved-word]}")
465+
already_added=1
466+
# ADD
467+
[[ $arg[-2,-1] == '))' ]] && reply+=("$(( end_pos - 2 )) $end_pos ${FAST_HIGHLIGHT_STYLES[reserved-word]}")
468+
elif [[ $arg == '()' ]]; then
469+
# anonymous function
470+
style=reserved-word
471+
elif [[ $arg == $'\x28' ]]; then
472+
# subshell
473+
style=reserved-word
474+
braces_stack='R'"$braces_stack"
475+
elif (( this_word & 14 )); then
476+
style=default
477+
fi
478+
;;
479+
*)
480+
# ADD
481+
# reply+=("$start_pos $end_pos commandtypefromthefuture-$REPLY")
482+
already_added=1
483+
;;
484+
esac
485+
fi
486+
# in_redirection || BIT_regular || BIT_sudo_opt || BIT_sudo_arg
487+
elif (( in_redirection + this_word & 14 ))
488+
then # $arg is a non-command word
489+
case $arg in
490+
']]')
491+
style=reserved-word
492+
;;
493+
$'\x29') # subshell or end of array assignment
494+
if (( in_array_assignment )); then
495+
style=assign
496+
in_array_assignment=0
497+
(( next_word = next_word | 1 ))
498+
elif [[ $braces_stack[1] == "R" ]]; then
499+
braces_stack[1]=""
500+
style=reserved-word
501+
fi;;
502+
$'\x28\x29') # possibly a function definition
503+
# || false # TODO: or if the previous word was a command word
504+
(( multi_func_def )) && (( next_word = next_word | 1 ))
505+
style=reserved-word
506+
# Remove possible annoying unknown-token style, or misleading function style
507+
reply[-1]=()
508+
;;
509+
'--'*) style=double-hyphen-option;;
510+
'-'*) style=single-hyphen-option;;
511+
"'"*) style=single-quoted-argument;;
512+
'"'*)
513+
# ADD
514+
reply+=("$start_pos $end_pos ${FAST_HIGHLIGHT_STYLES[double-quoted-argument]}")
515+
-fast-highlight-string
516+
already_added=1
517+
;;
518+
\$\'*)
519+
# ADD
520+
reply+=("$start_pos $end_pos ${FAST_HIGHLIGHT_STYLES[dollar-quoted-argument]}")
521+
-fast-highlight-dollar-string
522+
already_added=1
523+
;;
524+
\$[^\(]*)
525+
style=variable
526+
;;
527+
'`'*) style=back-quoted-argument;;
528+
[*?]*|*[^\\][*?]*)
529+
(( highlight_glob )) && style=globbing || style=default;;
530+
*) if [[ $arg = $'\x7d' && $braces_stack[1] == "Y" && "$right_brace_is_recognised_everywhere" = "1" ]]; then
531+
# right brace
532+
# Parsing rule: # {
533+
#
534+
# Additionally, `tt(})' is recognized in any position if neither the
535+
# tt(IGNORE_BRACES) option nor the tt(IGNORE_CLOSE_BRACES) option is set."""
536+
braces_stack[1]=""
537+
style=reserved-word
538+
(( next_word = next_word | 16 ))
539+
elif [[ $arg[1] = $histchars[1] && -n "${arg[2]}" ]]; then
540+
style=history-expansion
541+
elif (( arg_type == 3 )); then
542+
style=commandseparator
543+
elif (( in_redirection == 2 )); then
544+
style=redirection
545+
else
546+
if (( __hsmw_no_check_paths == 0 )) && -fast-highlight-check-path; then
547+
# ADD
548+
reply+=("$start_pos $end_pos ${FAST_HIGHLIGHT_STYLES[path]}")
549+
already_added=1
550+
551+
[[ -n "$FAST_HIGHLIGHT_STYLES[path_pathseparator]" && "$FAST_HIGHLIGHT_STYLES[path]" != "$FAST_HIGHLIGHT_STYLES[path_pathseparator]" ]] && {
552+
local pos
553+
for (( pos = start_pos; pos <= end_pos; pos++ )) ; do
554+
# ADD
555+
[[ ${buf[pos]} == "/" ]] && reply+=("$(( pos - 1 )) $pos ${FAST_HIGHLIGHT_STYLES[path_pathseparator]}")
556+
done
557+
}
558+
else
559+
style=default
560+
fi
561+
fi
562+
;;
563+
esac
564+
fi
565+
566+
# ADD
567+
(( already_added == 0 )) && [[ "${FAST_HIGHLIGHT_STYLES[$style]}" != "none" ]] && reply+=("$start_pos $end_pos ${FAST_HIGHLIGHT_STYLES[$style]}")
568+
569+
if (( arg_type == 3 )); then
570+
if [[ $arg == ';' ]] && (( in_array_assignment )); then
571+
# literal newline inside an array assignment
572+
(( next_word = 2 ))
573+
else
574+
(( next_word = 1 ))
575+
highlight_glob=1
576+
fi
577+
elif (( arg_type == 1 || arg_type == 2 )) && (( this_word & 1 )); then
578+
(( next_word = 1 ))
579+
elif [[ $arg == "repeat" ]] && (( this_word & 1 )); then
580+
# skip the repeat-count word
581+
in_redirection=2
582+
# The redirection mechanism assumes $this_word describes the word
583+
# following the redirection. Make it so.
584+
#
585+
# That word can be a command word with shortloops (`repeat 2 ls`)
586+
# or a command separator (`repeat 2; ls` or `repeat 2; do ls; done`).
587+
#
588+
# The repeat-count word will be handled like a redirection target.
589+
(( this_word = 3 ))
590+
fi
591+
start_pos=$end_pos
592+
# This is the default/common codepath.
593+
(( in_redirection == 0 )) && (( this_word = next_word )) #else # Stall $this_word.
594+
done
595+
}
596+
597+
# Check if $arg is a path.
598+
# If yes, return 0 and in $REPLY the style to use.
599+
# Else, return non-zero (and the contents of $REPLY is undefined).
600+
-fast-highlight-check-path()
601+
{
602+
: ${expanded_path:=${(Q)~arg}}
603+
604+
[[ -z $expanded_path ]] && return 1
605+
[[ -e $expanded_path ]] && return 0
606+
607+
# Search the path in CDPATH, only for CD command
608+
[[ "$cur_cmd" = "cd" ]] && for cdpath_dir in $cdpath ; do
609+
[[ -e "$cdpath_dir/$expanded_path" ]] && return 0
610+
done
611+
612+
# It's not a path.
613+
return 1
614+
}
615+
616+
# Highlight special chars inside double-quoted strings
617+
-fast-highlight-string()
618+
{
619+
mybuf="$arg"
620+
idx=start_pos
621+
622+
while [[ "$mybuf" = (#b)[^\$\\]#((\$(#B)([a-zA-Z_:][a-zA-Z0-9_:]#|[0-9]##)(#b)(\[[^\]]#\])(#c0,1))|(\$[{](\([a-zA-Z0@%#]##\))(#c0,1)[a-zA-Z0-9_:#]##(\[[^\]]#\])(#c0,1)[}])|[\\][\'\"\$]|[\\](*))(*) ]]; do
623+
[[ -n "${match[7]}" ]] && {
624+
# Skip following char – it is quoted. Choice is
625+
# made to not highlight such quoting
626+
idx+=${mbegin[1]}+1
627+
mybuf="${match[7]:1}"
628+
} || {
629+
idx+=${mbegin[1]}-1
630+
end_idx=idx+${mend[1]}-${mbegin[1]}+1
631+
mybuf="${match[8]}"
632+
633+
# ADD
634+
reply+=("$idx $end_idx ${FAST_HIGHLIGHT_STYLES[back-or-dollar-double-quoted-argument]}")
635+
636+
idx=end_idx
637+
}
638+
done
639+
}
640+
641+
# Highlight special chars inside dollar-quoted strings
642+
-fast-highlight-dollar-string()
643+
{
644+
local i j k style
645+
local AA
646+
integer c
647+
# Starting dollar-quote is at 1:2, so start parsing at offset 3 in the string.
648+
for (( i = 3 ; i < end_pos - start_pos ; i += 1 )) ; do
649+
(( j = i + start_pos - 1 ))
650+
(( k = j + 1 ))
651+
case "$arg[$i]" in
652+
"\\") style=back-dollar-quoted-argument
653+
for (( c = i + 1 ; c <= end_pos - start_pos ; c += 1 )); do
654+
[[ "$arg[$c]" != ([0-9xXuUa-fA-F]) ]] && break
655+
done
656+
AA=$arg[$i+1,$c-1]
657+
# Matching for HEX and OCT values like \0xA6, \xA6 or \012
658+
if [[ "$AA" =~ "^(x|X)[0-9a-fA-F]{1,2}"
659+
|| "$AA" =~ "^[0-7]{1,3}"
660+
|| "$AA" =~ "^u[0-9a-fA-F]{1,4}"
661+
|| "$AA" =~ "^U[0-9a-fA-F]{1,8}"
662+
]]; then
663+
(( k += $#MATCH ))
664+
(( i += $#MATCH ))
665+
else
666+
if (( $#arg > $i+1 )) && [[ $arg[$i+1] == [xXuU] ]]; then
667+
# \x not followed by hex digits is probably an error
668+
style=unknown-token
669+
fi
670+
(( k += 1 )) # Color following char too.
671+
(( i += 1 )) # Skip parsing the escaped char.
672+
fi
673+
;;
674+
*) continue ;;
675+
676+
esac
677+
# ADD
678+
reply+=("$j $k ${FAST_HIGHLIGHT_STYLES[$style]}")
679+
done
680+
}
681+
682+
# -------------------------------------------------------------------------------------------------
683+
# Main highlighter initialization
684+
# -------------------------------------------------------------------------------------------------
685+
686+
-fast-highlight-init() {
687+
__hsmw_highlight_main__command_type_cache=()
688+
}
689+
690+
__HSMW_MH_SOURCED=1
691+
692+
# vim:ft=zsh:sw=2:sts=2
693+
# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-

‎fast-syntax-highlighting.plugin.zsh

+272
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
# -------------------------------------------------------------------------------------------------
2+
# Copyright (c) 2010-2016 zsh-syntax-highlighting contributors
3+
# All rights reserved.
4+
#
5+
# Redistribution and use in source and binary forms, with or without modification, are permitted
6+
# provided that the following conditions are met:
7+
#
8+
# * Redistributions of source code must retain the above copyright notice, this list of conditions
9+
# and the following disclaimer.
10+
# * Redistributions in binary form must reproduce the above copyright notice, this list of
11+
# conditions and the following disclaimer in the documentation and/or other materials provided
12+
# with the distribution.
13+
# * Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
14+
# may be used to endorse or promote products derived from this software without specific prior
15+
# written permission.
16+
#
17+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
18+
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
19+
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23+
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
24+
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25+
# -------------------------------------------------------------------------------------------------
26+
# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
27+
# vim: ft=zsh sw=2 ts=2 et
28+
# -------------------------------------------------------------------------------------------------
29+
30+
31+
# Set $ZERO to the expected value, regardless of functionargzero.
32+
typeset -g ZERO=${(%):-%N}
33+
34+
# Invokes each highlighter that needs updating.
35+
# This function is supposed to be called whenever the ZLE state changes.
36+
_zsh_highlight()
37+
{
38+
# Store the previous command return code to restore it whatever happens.
39+
local ret=$?
40+
41+
# Remove all highlighting in isearch, so that only the underlining done by zsh itself remains.
42+
# For details see FAQ entry 'Why does syntax highlighting not work while searching history?'.
43+
if [[ $WIDGET == zle-isearch-update ]] && ! (( $+ISEARCHMATCH_ACTIVE )); then
44+
region_highlight=()
45+
return $ret
46+
fi
47+
48+
setopt localoptions warncreateglobal noksharrays
49+
local REPLY # don't leak $REPLY into global scope
50+
local -a reply
51+
52+
# Do not highlight if there are more than 300 chars in the buffer. It's most
53+
# likely a pasted command or a huge list of files in that case..
54+
[[ -n ${ZSH_HIGHLIGHT_MAXLENGTH:-} ]] && [[ $#BUFFER -gt $ZSH_HIGHLIGHT_MAXLENGTH ]] && return $ret
55+
56+
# Do not highlight if there are pending inputs (copy/paste).
57+
[[ $PENDING -gt 0 ]] && return $ret
58+
59+
# Reset region highlight to build it from scratch
60+
# may need to remove path_prefix highlighting when the line ends
61+
if [[ $WIDGET == zle-line-finish ]] || _zsh_highlight_buffer_modified; then
62+
-fast-highlight-init
63+
-fast-highlight-process
64+
region_highlight=( $reply )
65+
fi
66+
67+
{
68+
local cache_place
69+
local -a region_highlight_copy
70+
71+
# Re-apply zle_highlight settings
72+
73+
# region
74+
if (( REGION_ACTIVE == 1 )); then
75+
_zsh_highlight_apply_zle_highlight region standout "$MARK" "$CURSOR"
76+
elif (( REGION_ACTIVE == 2 )); then
77+
() {
78+
local needle=$'\n'
79+
integer min max
80+
if (( MARK > CURSOR )) ; then
81+
min=$CURSOR max=$MARK
82+
else
83+
min=$MARK max=$CURSOR
84+
fi
85+
(( min = ${${BUFFER[1,$min]}[(I)$needle]} ))
86+
(( max += ${${BUFFER:($max-1)}[(i)$needle]} - 1 ))
87+
_zsh_highlight_apply_zle_highlight region standout "$min" "$max"
88+
}
89+
fi
90+
91+
# yank / paste (zsh-5.1.1 and newer)
92+
(( $+YANK_ACTIVE )) && (( YANK_ACTIVE )) && _zsh_highlight_apply_zle_highlight paste standout "$YANK_START" "$YANK_END"
93+
94+
# isearch
95+
(( $+ISEARCHMATCH_ACTIVE )) && (( ISEARCHMATCH_ACTIVE )) && _zsh_highlight_apply_zle_highlight isearch underline "$ISEARCHMATCH_START" "$ISEARCHMATCH_END"
96+
97+
# suffix
98+
(( $+SUFFIX_ACTIVE )) && (( SUFFIX_ACTIVE )) && _zsh_highlight_apply_zle_highlight suffix bold "$SUFFIX_START" "$SUFFIX_END"
99+
100+
return $ret
101+
102+
} always {
103+
typeset -g _ZSH_HIGHLIGHT_PRIOR_BUFFER="$BUFFER"
104+
typeset -gi _ZSH_HIGHLIGHT_PRIOR_CURSOR=$CURSOR
105+
}
106+
}
107+
108+
# Apply highlighting based on entries in the zle_highlight array.
109+
# This function takes four arguments:
110+
# 1. The exact entry (no patterns) in the zle_highlight array:
111+
# region, paste, isearch, or suffix
112+
# 2. The default highlighting that should be applied if the entry is unset
113+
# 3. and 4. Two integer values describing the beginning and end of the
114+
# range. The order does not matter.
115+
_zsh_highlight_apply_zle_highlight() {
116+
local entry="$1" default="$2"
117+
integer first="$3" second="$4"
118+
119+
# read the relevant entry from zle_highlight
120+
local region="${zle_highlight[(r)${entry}:*]}"
121+
122+
if [[ -z "$region" ]]; then
123+
# entry not specified at all, use default value
124+
region=$default
125+
else
126+
# strip prefix
127+
region="${region#${entry}:}"
128+
129+
# no highlighting when set to the empty string or to 'none'
130+
if [[ -z "$region" ]] || [[ "$region" == none ]]; then
131+
return
132+
fi
133+
fi
134+
135+
integer start end
136+
if (( first < second )); then
137+
start=$first end=$second
138+
else
139+
start=$second end=$first
140+
fi
141+
region_highlight+=("$start $end $region")
142+
}
143+
144+
145+
# -------------------------------------------------------------------------------------------------
146+
# API/utility functions for highlighters
147+
# -------------------------------------------------------------------------------------------------
148+
149+
# Whether the command line buffer has been modified or not.
150+
#
151+
# Returns 0 if the buffer has changed since _zsh_highlight was last called.
152+
_zsh_highlight_buffer_modified()
153+
{
154+
[[ "${_ZSH_HIGHLIGHT_PRIOR_BUFFER:-}" != "$BUFFER" ]]
155+
}
156+
157+
# Whether the cursor has moved or not.
158+
#
159+
# Returns 0 if the cursor has moved since _zsh_highlight was last called.
160+
_zsh_highlight_cursor_moved()
161+
{
162+
[[ -n $CURSOR ]] && [[ -n ${_ZSH_HIGHLIGHT_PRIOR_CURSOR-} ]] && (($_ZSH_HIGHLIGHT_PRIOR_CURSOR != $CURSOR))
163+
}
164+
165+
# -------------------------------------------------------------------------------------------------
166+
# Setup functions
167+
# -------------------------------------------------------------------------------------------------
168+
169+
# Helper for _zsh_highlight_bind_widgets
170+
# $1 is name of widget to call
171+
_zsh_highlight_call_widget()
172+
{
173+
builtin zle "$@" && _zsh_highlight
174+
}
175+
176+
# Rebind all ZLE widgets to make them invoke _zsh_highlights.
177+
_zsh_highlight_bind_widgets()
178+
{
179+
setopt localoptions noksharrays
180+
typeset -F SECONDS
181+
local prefix=orig-s$SECONDS-r$RANDOM # unique each time, in case we're sourced more than once
182+
183+
# Load ZSH module zsh/zleparameter, needed to override user defined widgets.
184+
zmodload zsh/zleparameter 2>/dev/null || {
185+
print -r -- >&2 'zsh-syntax-highlighting: failed loading zsh/zleparameter.'
186+
return 1
187+
}
188+
189+
# Override ZLE widgets to make them invoke _zsh_highlight.
190+
local -U widgets_to_bind
191+
widgets_to_bind=(${${(k)widgets}:#(.*|run-help|which-command|beep|set-local-history|yank)})
192+
193+
# Always wrap special zle-line-finish widget. This is needed to decide if the
194+
# current line ends and special highlighting logic needs to be applied.
195+
# E.g. remove cursor imprint, don't highlight partial paths, ...
196+
widgets_to_bind+=(zle-line-finish)
197+
198+
# Always wrap special zle-isearch-update widget to be notified of updates in isearch.
199+
# This is needed because we need to disable highlighting in that case.
200+
widgets_to_bind+=(zle-isearch-update)
201+
202+
local cur_widget
203+
for cur_widget in $widgets_to_bind; do
204+
case $widgets[$cur_widget] in
205+
206+
# Already rebound event: do nothing.
207+
user:_zsh_highlight_widget_*);;
208+
209+
# The "eval"'s are required to make $cur_widget a closure: the value of the parameter at function
210+
# definition time is used.
211+
#
212+
# We can't use ${0/_zsh_highlight_widget_} because these widgets are always invoked with
213+
# NO_function_argzero, regardless of the option's setting here.
214+
215+
# User defined widget: override and rebind old one with prefix "orig-".
216+
user:*) zle -N $prefix-$cur_widget ${widgets[$cur_widget]#*:}
217+
eval "_zsh_highlight_widget_${(q)prefix}-${(q)cur_widget}() { _zsh_highlight_call_widget ${(q)prefix}-${(q)cur_widget} -- \"\$@\" }"
218+
zle -N $cur_widget _zsh_highlight_widget_$prefix-$cur_widget;;
219+
220+
# Completion widget: override and rebind old one with prefix "orig-".
221+
completion:*) zle -C $prefix-$cur_widget ${${(s.:.)widgets[$cur_widget]}[2,3]}
222+
eval "_zsh_highlight_widget_${(q)prefix}-${(q)cur_widget}() { _zsh_highlight_call_widget ${(q)prefix}-${(q)cur_widget} -- \"\$@\" }"
223+
zle -N $cur_widget _zsh_highlight_widget_$prefix-$cur_widget;;
224+
225+
# Builtin widget: override and make it call the builtin ".widget".
226+
builtin) eval "_zsh_highlight_widget_${(q)prefix}-${(q)cur_widget}() { _zsh_highlight_call_widget .${(q)cur_widget} -- \"\$@\" }"
227+
zle -N $cur_widget _zsh_highlight_widget_$prefix-$cur_widget;;
228+
229+
# Incomplete or nonexistent widget: Bind to z-sy-h directly.
230+
*)
231+
if [[ $cur_widget == zle-* ]] && [[ -z $widgets[$cur_widget] ]]; then
232+
_zsh_highlight_widget_${cur_widget}() { :; _zsh_highlight }
233+
zle -N $cur_widget _zsh_highlight_widget_$cur_widget
234+
else
235+
# Default: unhandled case.
236+
print -r -- >&2 "zsh-syntax-highlighting: unhandled ZLE widget ${(qq)cur_widget}"
237+
fi
238+
esac
239+
done
240+
}
241+
242+
# -------------------------------------------------------------------------------------------------
243+
# Setup
244+
# -------------------------------------------------------------------------------------------------
245+
246+
# Try binding widgets.
247+
_zsh_highlight_bind_widgets || {
248+
print -r -- >&2 'zsh-syntax-highlighting: failed binding ZLE widgets, exiting.'
249+
return 1
250+
}
251+
252+
# Reset scratch variables when commandline is done.
253+
_zsh_highlight_preexec_hook()
254+
{
255+
typeset -g _ZSH_HIGHLIGHT_PRIOR_BUFFER=
256+
typeset -gi _ZSH_HIGHLIGHT_PRIOR_CURSOR=0
257+
}
258+
259+
autoload -U add-zsh-hook
260+
add-zsh-hook preexec _zsh_highlight_preexec_hook 2>/dev/null || {
261+
print -r -- >&2 'zsh-syntax-highlighting: failed loading add-zsh-hook.'
262+
}
263+
264+
# Load zsh/parameter module if available
265+
zmodload zsh/parameter 2>/dev/null
266+
267+
autoload -U is-at-least
268+
source "${ZERO:h}/fast-highlight"
269+
270+
local right_brace_is_recognised_everywhere
271+
integer path_dirs_was_set multi_func_def ointeractive_comments
272+
-fast-highlight-fill-option-variables

0 commit comments

Comments
 (0)
Please sign in to comment.