12b879caf7
If possible, try to use the zsh/parameter module to get information about a shell words. This avoids subshells and is a huge speed improvement on systems such as cygwin. Note 1: $commands does not know about PATH_DIRS. So in case PATH_DIRS is set, 'type -w' is still used if nothing else matches. Note 2: zsh/parameter can't distinguish between 'command' and 'hashed'. Adjusted the test for that case to XFAIL. The ideal solution would be if whence had an option to put the result in REPLY instead of printing it to stdout.
665 lines
26 KiB
Bash
665 lines
26 KiB
Bash
# -------------------------------------------------------------------------------------------------
|
|
# Copyright (c) 2010-2016 zsh-syntax-highlighting contributors
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without modification, are permitted
|
|
# provided that the following conditions are met:
|
|
#
|
|
# * Redistributions of source code must retain the above copyright notice, this list of conditions
|
|
# and the following disclaimer.
|
|
# * Redistributions in binary form must reproduce the above copyright notice, this list of
|
|
# conditions and the following disclaimer in the documentation and/or other materials provided
|
|
# with the distribution.
|
|
# * Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
|
|
# may be used to endorse or promote products derived from this software without specific prior
|
|
# written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
# -------------------------------------------------------------------------------------------------
|
|
# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
|
|
# vim: ft=zsh sw=2 ts=2 et
|
|
# -------------------------------------------------------------------------------------------------
|
|
|
|
|
|
# Define default styles.
|
|
: ${ZSH_HIGHLIGHT_STYLES[default]:=none}
|
|
: ${ZSH_HIGHLIGHT_STYLES[unknown-token]:=fg=red,bold}
|
|
: ${ZSH_HIGHLIGHT_STYLES[reserved-word]:=fg=yellow}
|
|
: ${ZSH_HIGHLIGHT_STYLES[alias]:=fg=green}
|
|
: ${ZSH_HIGHLIGHT_STYLES[suffix-alias]:=fg=green,underline}
|
|
: ${ZSH_HIGHLIGHT_STYLES[builtin]:=fg=green}
|
|
: ${ZSH_HIGHLIGHT_STYLES[function]:=fg=green}
|
|
: ${ZSH_HIGHLIGHT_STYLES[command]:=fg=green}
|
|
: ${ZSH_HIGHLIGHT_STYLES[precommand]:=fg=green,underline}
|
|
: ${ZSH_HIGHLIGHT_STYLES[commandseparator]:=none}
|
|
: ${ZSH_HIGHLIGHT_STYLES[hashed-command]:=fg=green}
|
|
: ${ZSH_HIGHLIGHT_STYLES[path]:=underline}
|
|
: ${ZSH_HIGHLIGHT_STYLES[path_prefix]:=underline}
|
|
: ${ZSH_HIGHLIGHT_STYLES[globbing]:=fg=blue}
|
|
: ${ZSH_HIGHLIGHT_STYLES[history-expansion]:=fg=blue}
|
|
: ${ZSH_HIGHLIGHT_STYLES[single-hyphen-option]:=none}
|
|
: ${ZSH_HIGHLIGHT_STYLES[double-hyphen-option]:=none}
|
|
: ${ZSH_HIGHLIGHT_STYLES[back-quoted-argument]:=none}
|
|
: ${ZSH_HIGHLIGHT_STYLES[single-quoted-argument]:=fg=yellow}
|
|
: ${ZSH_HIGHLIGHT_STYLES[double-quoted-argument]:=fg=yellow}
|
|
: ${ZSH_HIGHLIGHT_STYLES[dollar-quoted-argument]:=fg=yellow}
|
|
: ${ZSH_HIGHLIGHT_STYLES[dollar-double-quoted-argument]:=fg=cyan}
|
|
: ${ZSH_HIGHLIGHT_STYLES[back-double-quoted-argument]:=fg=cyan}
|
|
: ${ZSH_HIGHLIGHT_STYLES[back-dollar-quoted-argument]:=fg=cyan}
|
|
: ${ZSH_HIGHLIGHT_STYLES[assign]:=none}
|
|
: ${ZSH_HIGHLIGHT_STYLES[redirection]:=none}
|
|
: ${ZSH_HIGHLIGHT_STYLES[comment]:=fg=black,bold}
|
|
|
|
# Whether the highlighter should be called or not.
|
|
_zsh_highlight_main_highlighter_predicate()
|
|
{
|
|
# accept-* may trigger removal of path_prefix highlighting
|
|
[[ $WIDGET == accept-* ]] ||
|
|
_zsh_highlight_buffer_modified
|
|
}
|
|
|
|
# Helper to deal with tokens crossing line boundaries.
|
|
_zsh_highlight_main_add_region_highlight() {
|
|
integer start=$1 end=$2
|
|
shift 2
|
|
|
|
# The calculation was relative to $PREBUFFER$BUFFER, but region_highlight is
|
|
# relative to $BUFFER.
|
|
(( start -= $#PREBUFFER ))
|
|
(( end -= $#PREBUFFER ))
|
|
|
|
(( end < 0 )) && return # having end<0 would be a bug
|
|
(( start < 0 )) && start=0 # having start<0 is normal with e.g. multiline strings
|
|
_zsh_highlight_add_highlight $start $end "$@"
|
|
}
|
|
|
|
# Get the type of a command.
|
|
#
|
|
# Uses the zsh/parameter module if available to avoid forks, and a
|
|
# wrapper around 'type -w' as fallback.
|
|
#
|
|
# Takes a single argument.
|
|
#
|
|
# The result will be stored in REPLY.
|
|
_zsh_highlight_main__type() {
|
|
if (( $#options_to_set )); then
|
|
setopt localoptions $options_to_set;
|
|
fi
|
|
unset REPLY
|
|
if zmodload -e zsh/parameter; then
|
|
if (( $+aliases[(e)$1] )); then
|
|
REPLY=alias
|
|
elif (( $+saliases[(e)${1##*.}] )); then
|
|
REPLY='suffix alias'
|
|
elif (( $reswords[(Ie)$1] )); then
|
|
REPLY=reserved
|
|
elif (( $+functions[(e)$1] )); then
|
|
REPLY=function
|
|
elif (( $+builtins[(e)$1] )); then
|
|
REPLY=builtin
|
|
elif (( $+commands[(e)$1] )); then
|
|
REPLY=command
|
|
elif ! builtin type -w -- $1 >/dev/null 2>&1; then
|
|
REPLY=none
|
|
fi
|
|
fi
|
|
if ! (( $+REPLY )); then
|
|
REPLY="${$(LC_ALL=C builtin type -w -- $1 2>/dev/null)#*: }"
|
|
fi
|
|
}
|
|
|
|
# Check whether the first argument is a redirection operator token.
|
|
# Report result via the exit code.
|
|
_zsh_highlight_main__is_redirection() {
|
|
# A redirection operator token:
|
|
# - starts with an optional single-digit number;
|
|
# - then, has a '<' or '>' character;
|
|
# - is not a process substitution [<(...) or >(...)].
|
|
[[ $1 == (<0-9>|)(\<|\>)* ]] && [[ $1 != (\<|\>)$'\x28'* ]]
|
|
}
|
|
|
|
# Resolve alias.
|
|
#
|
|
# Takes a single argument.
|
|
#
|
|
# The result will be stored in REPLY.
|
|
_zsh_highlight_main__resolve_alias() {
|
|
if zmodload -e zsh/parameter; then
|
|
REPLY=${aliases[$arg]}
|
|
else
|
|
REPLY="${"$(alias -- $arg)"#*=}"
|
|
fi
|
|
}
|
|
|
|
# Main syntax highlighting function.
|
|
_zsh_highlight_main_highlighter()
|
|
{
|
|
## Before we even 'emulate -L', we must test a few options that would reset.
|
|
if [[ -o interactive_comments ]]; then
|
|
local interactive_comments= # set to empty
|
|
fi
|
|
if [[ -o path_dirs ]]; then
|
|
integer path_dirs_was_set=1
|
|
else
|
|
integer path_dirs_was_set=0
|
|
fi
|
|
emulate -L zsh
|
|
setopt localoptions extendedglob bareglobqual
|
|
|
|
# At the PS3 prompt and in vared, highlight nothing.
|
|
#
|
|
# (We can't check this in _zsh_highlight_main_highlighter_predicate because
|
|
# if the predicate returns false, the previous value of region_highlight
|
|
# would be reused.)
|
|
if [[ $CONTEXT == (select|vared) ]]; then
|
|
return
|
|
fi
|
|
|
|
## Variable declarations and initializations
|
|
local start_pos=0 end_pos highlight_glob=true arg style
|
|
local in_array_assignment=false # true between 'a=(' and the matching ')'
|
|
typeset -a ZSH_HIGHLIGHT_TOKENS_COMMANDSEPARATOR
|
|
typeset -a ZSH_HIGHLIGHT_TOKENS_PRECOMMANDS
|
|
typeset -a ZSH_HIGHLIGHT_TOKENS_CONTROL_FLOW
|
|
local -a options_to_set # used in callees
|
|
local buf="$PREBUFFER$BUFFER"
|
|
integer len="${#buf}"
|
|
|
|
if (( path_dirs_was_set )); then
|
|
options_to_set+=( PATH_DIRS )
|
|
fi
|
|
unset path_dirs_was_set
|
|
|
|
ZSH_HIGHLIGHT_TOKENS_COMMANDSEPARATOR=(
|
|
'|' '||' ';' '&' '&&'
|
|
'|&'
|
|
'&!' '&|'
|
|
# ### 'case' syntax, but followed by a pattern, not by a command
|
|
# ';;' ';&' ';|'
|
|
)
|
|
ZSH_HIGHLIGHT_TOKENS_PRECOMMANDS=(
|
|
'builtin' 'command' 'exec' 'nocorrect' 'noglob'
|
|
'pkexec' # immune to #121 because it's usually not passed --option flags
|
|
)
|
|
|
|
# Tokens that, at (naively-determined) "command position", are followed by
|
|
# a de jure command position. All of these are reserved words.
|
|
ZSH_HIGHLIGHT_TOKENS_CONTROL_FLOW=(
|
|
$'\x7b' # block
|
|
$'\x28' # subshell
|
|
'()' # anonymous function
|
|
'while'
|
|
'until'
|
|
'if'
|
|
'then'
|
|
'elif'
|
|
'else'
|
|
'do'
|
|
'time'
|
|
'coproc'
|
|
'!' # reserved word; unrelated to $histchars[1]
|
|
)
|
|
|
|
# State machine
|
|
#
|
|
# The states are:
|
|
# - :start: Command word
|
|
# - :sudo_opt: A leading-dash option to sudo (such as "-u" or "-i")
|
|
# - :sudo_arg: The argument to a sudo leading-dash option that takes one,
|
|
# when given as a separate word; i.e., "foo" in "-u foo" (two
|
|
# words) but not in "-ufoo" (one word).
|
|
# - :regular: "Not a command word", and command delimiters are permitted.
|
|
# Mainly used to detect premature termination of commands.
|
|
#
|
|
# When the kind of a word is not yet known, $this_word / $next_word may contain
|
|
# multiple states. For example, after "sudo -i", the next word may be either
|
|
# another --flag or a command name, hence the state would include both :start:
|
|
# and :sudo_opt:.
|
|
#
|
|
# The tokens are always added with both leading and trailing colons to serve as
|
|
# word delimiters (an improvised array); [[ $x == *:foo:* ]] and x=${x//:foo:/}
|
|
# will DTRT regardless of how many elements or repetitions $x has..
|
|
#
|
|
# Handling of redirections: upon seeing a redirection token, we must stall
|
|
# the current state --- that is, the value of $this_word --- for two iterations
|
|
# (one for the redirection operator, one for the word following it representing
|
|
# the redirection target). Therefore, we set $in_redirection to 2 upon seeing a
|
|
# redirection operator, decrement it each iteration, and stall the current state
|
|
# when it is non-zero. Thus, upon reaching the next word (the one that follows
|
|
# the redirection operator and target), $this_word will still contain values
|
|
# appropriate for the word immediately following the word that preceded the
|
|
# redirection operator.
|
|
#
|
|
# The "the previous word was a redirection operator" state is not communicated
|
|
# to the next iteration via $next_word/$this_word as usual, but via
|
|
# $in_redirection. The value of $next_word from the iteration that processed
|
|
# the operator is discarded.
|
|
#
|
|
local this_word=':start:' next_word
|
|
integer in_redirection
|
|
# Processing buffer
|
|
local proc_buf="$buf"
|
|
for arg in ${interactive_comments-${(z)buf}} \
|
|
${interactive_comments+${(zZ+c+)buf}}; do
|
|
if (( in_redirection )); then
|
|
(( --in_redirection ))
|
|
fi
|
|
if (( in_redirection == 0 )); then
|
|
# Initialize $next_word to its default value.
|
|
next_word=':regular:'
|
|
else
|
|
# Stall $next_word.
|
|
fi
|
|
# $already_added is set to 1 to disable adding an entry to region_highlight
|
|
# for this iteration. Currently, that is done for "" and $'' strings,
|
|
# which add the entry early so escape sequences within the string override
|
|
# the string's color.
|
|
integer already_added=0
|
|
local style_override=""
|
|
if [[ $this_word == *':start:'* ]]; then
|
|
in_array_assignment=false
|
|
if [[ $arg == 'noglob' ]]; then
|
|
highlight_glob=false
|
|
fi
|
|
fi
|
|
|
|
# advance $start_pos, skipping over whitespace in $buf.
|
|
if [[ $arg == ';' ]] ; then
|
|
# We're looking for either a semicolon or a newline, whichever comes
|
|
# first. Both of these are rendered as a ";" (SEPER) by the ${(z)..}
|
|
# flag.
|
|
#
|
|
# We can't use the (Z+n+) flag because that elides the end-of-command
|
|
# token altogether, so 'echo foo\necho bar' (two commands) becomes
|
|
# indistinguishable from 'echo foo echo bar' (one command with three
|
|
# words for arguments).
|
|
local needle=$'[;\n]'
|
|
integer offset=$(( ${proc_buf[(i)$needle]} - 1 ))
|
|
(( start_pos += offset ))
|
|
(( end_pos = start_pos + $#arg ))
|
|
else
|
|
integer offset=$(((len-start_pos)-${#${proc_buf##([[:space:]]|\\[[:space:]])#}}))
|
|
((start_pos+=offset))
|
|
((end_pos=$start_pos+${#arg}))
|
|
fi
|
|
|
|
# Above `if` computes new start_pos and end_pos.
|
|
# Here we compute new proc_buf. We advance it
|
|
# (chop off characters from the beginning)
|
|
# beyond what end_pos points to, by skipping
|
|
# as many characters as end_pos was advanced.
|
|
#
|
|
# end_pos was advanced by $offset (via start_pos)
|
|
# and by $#arg. Note the `start_pos=$end_pos`
|
|
# below.
|
|
#
|
|
# As for the [,len]. We could use [,len-start_pos+offset]
|
|
# here, but to make it easier on eyes, we use len and
|
|
# rely on the fact that Zsh simply handles that. The
|
|
# length of proc_buf is len-start_pos+offset because
|
|
# we're chopping it to match current start_pos, so its
|
|
# length matches the previous value of start_pos.
|
|
#
|
|
# Why [,-1] is slower than [,length] isn't clear.
|
|
proc_buf="${proc_buf[offset + $#arg + 1,len]}"
|
|
|
|
if [[ -n ${interactive_comments+'set'} && $arg[1] == $histchars[3] ]]; then
|
|
if [[ $this_word == *(':regular:'|':start:')* ]]; then
|
|
style=comment
|
|
else
|
|
style=unknown-token # prematurely terminated
|
|
fi
|
|
_zsh_highlight_main_add_region_highlight $start_pos $end_pos $style
|
|
already_added=1
|
|
continue
|
|
fi
|
|
|
|
# Parse the sudo command line
|
|
if (( ! in_redirection )); then
|
|
if [[ $this_word == *':sudo_opt:'* ]]; then
|
|
case "$arg" in
|
|
# Flag that requires an argument
|
|
'-'[Cgprtu]) this_word=${this_word//:start:/};
|
|
next_word=':sudo_arg:';;
|
|
# This prevents misbehavior with sudo -u -otherargument
|
|
'-'*) this_word=${this_word//:start:/};
|
|
next_word+=':start:';
|
|
next_word+=':sudo_opt:';;
|
|
*) ;;
|
|
esac
|
|
elif [[ $this_word == *':sudo_arg:'* ]]; then
|
|
next_word+=':sudo_opt:'
|
|
next_word+=':start:'
|
|
fi
|
|
fi
|
|
|
|
if [[ $this_word == *':start:'* ]] && (( in_redirection == 0 )); then # $arg is the command word
|
|
if [[ -n ${(M)ZSH_HIGHLIGHT_TOKENS_PRECOMMANDS:#"$arg"} ]]; then
|
|
style=precommand
|
|
elif [[ "$arg" = "sudo" ]]; then
|
|
style=precommand
|
|
next_word=${next_word//:regular:/}
|
|
next_word+=':sudo_opt:'
|
|
next_word+=':start:'
|
|
else
|
|
_zsh_highlight_main_highlighter_expand_path $arg
|
|
local expanded_arg="$REPLY"
|
|
_zsh_highlight_main__type ${expanded_arg}
|
|
local res="$REPLY"
|
|
() {
|
|
# Special-case: command word is '$foo', like that, without braces or anything.
|
|
#
|
|
# That's not entirely correct --- if the parameter's value happens to be a reserved
|
|
# word, the parameter expansion will be highlighted as a reserved word --- but that
|
|
# incorrectness is outweighed by the usability improvement of permitting the use of
|
|
# parameters that refer to commands, functions, and builtins.
|
|
local -a match mbegin mend
|
|
local MATCH; integer MBEGIN MEND
|
|
if [[ $res == none ]] && (( ${+parameters} )) &&
|
|
[[ ${arg[1]} == \$ ]] && [[ ${arg:1} =~ ^([A-Za-z_][A-Za-z0-9_]*|[0-9]+)$ ]]; then
|
|
_zsh_highlight_main__type ${(P)MATCH}
|
|
res=$REPLY
|
|
fi
|
|
}
|
|
case $res in
|
|
reserved) style=reserved-word;;
|
|
'suffix alias') style=suffix-alias;;
|
|
alias) () {
|
|
integer insane_alias
|
|
case $arg in
|
|
# Issue #263: aliases with '=' on their LHS.
|
|
#
|
|
# There are three cases:
|
|
#
|
|
# - Unsupported, breaks 'alias -L' output, but invokable:
|
|
('='*) :;;
|
|
# - Unsupported, not invokable:
|
|
(*'='*) insane_alias=1;;
|
|
# - The common case:
|
|
(*) :;;
|
|
esac
|
|
if (( insane_alias )); then
|
|
style=unknown-token
|
|
else
|
|
style=alias
|
|
_zsh_highlight_main__resolve_alias $arg
|
|
local alias_target="$REPLY"
|
|
[[ -n ${(M)ZSH_HIGHLIGHT_TOKENS_PRECOMMANDS:#"$alias_target"} && -z ${(M)ZSH_HIGHLIGHT_TOKENS_PRECOMMANDS:#"$arg"} ]] && ZSH_HIGHLIGHT_TOKENS_PRECOMMANDS+=($arg)
|
|
fi
|
|
}
|
|
;;
|
|
builtin) style=builtin;;
|
|
function) style=function;;
|
|
command) style=command;;
|
|
hashed) style=hashed-command;;
|
|
none) if _zsh_highlight_main_highlighter_check_assign; then
|
|
style=assign
|
|
if [[ $arg[-1] == '(' ]]; then
|
|
in_array_assignment=true
|
|
else
|
|
# assignment to a scalar parameter.
|
|
# (For array assignments, the command doesn't start until the ")" token.)
|
|
next_word+=':start:'
|
|
fi
|
|
elif [[ $arg[0,1] = $histchars[0,1] ]] && (( $#arg[0,2] == 2 )); then
|
|
style=history-expansion
|
|
elif [[ $arg[0,1] == $histchars[2,2] ]]; then
|
|
style=history-expansion
|
|
elif [[ -n ${(M)ZSH_HIGHLIGHT_TOKENS_COMMANDSEPARATOR:#"$arg"} ]]; then
|
|
if [[ $this_word == *':regular:'* ]]; then
|
|
# This highlights empty commands (semicolon follows nothing) as an error.
|
|
# Zsh accepts them, though.
|
|
style=commandseparator
|
|
else
|
|
style=unknown-token
|
|
fi
|
|
elif _zsh_highlight_main__is_redirection $arg; then
|
|
# A '<' or '>', possibly followed by a digit
|
|
style=redirection
|
|
(( in_redirection=2 ))
|
|
elif [[ $arg[1,2] == '((' ]]; then
|
|
# Arithmetic evaluation.
|
|
#
|
|
# Note: prior to zsh-5.1.1-52-g4bed2cf (workers/36669), the ${(z)...}
|
|
# splitter would only output the '((' token if the matching '))' had
|
|
# been typed. Therefore, under those versions of zsh, BUFFER="(( 42"
|
|
# would be highlighted as an error until the matching "))" are typed.
|
|
#
|
|
# We highlight just the opening parentheses, as a reserved word; this
|
|
# is how [[ ... ]] is highlighted, too.
|
|
style=reserved-word
|
|
_zsh_highlight_main_add_region_highlight $start_pos $((start_pos + 2)) $style
|
|
already_added=1
|
|
if [[ $arg[-2,-1] == '))' ]]; then
|
|
_zsh_highlight_main_add_region_highlight $((end_pos - 2)) $end_pos $style
|
|
already_added=1
|
|
fi
|
|
elif [[ $arg == '()' || $arg == $'\x28' ]]; then
|
|
# anonymous function
|
|
# subshell
|
|
style=reserved-word
|
|
else
|
|
if _zsh_highlight_main_highlighter_check_path; then
|
|
style=path
|
|
else
|
|
style=unknown-token
|
|
fi
|
|
fi
|
|
;;
|
|
*) _zsh_highlight_main_add_region_highlight commandtypefromthefuture-$res
|
|
already_added=1
|
|
;;
|
|
esac
|
|
fi
|
|
else # $arg is a non-command word
|
|
case $arg in
|
|
$'\x29') # subshell or end of array assignment
|
|
if $in_array_assignment; then
|
|
style=assign
|
|
in_array_assignment=false
|
|
else
|
|
style=reserved-word
|
|
fi;;
|
|
$'\x7d') style=reserved-word;; # block
|
|
'--'*) style=double-hyphen-option;;
|
|
'-'*) style=single-hyphen-option;;
|
|
"'"*) style=single-quoted-argument;;
|
|
'"'*) style=double-quoted-argument
|
|
_zsh_highlight_main_add_region_highlight $start_pos $end_pos $style
|
|
_zsh_highlight_main_highlighter_highlight_string
|
|
already_added=1
|
|
;;
|
|
\$\'*) style=dollar-quoted-argument
|
|
_zsh_highlight_main_add_region_highlight $start_pos $end_pos $style
|
|
_zsh_highlight_main_highlighter_highlight_dollar_string
|
|
already_added=1
|
|
;;
|
|
'`'*) style=back-quoted-argument;;
|
|
[*?]*|*[^\\][*?]*)
|
|
$highlight_glob && style=globbing || style=default;;
|
|
*) if false; then
|
|
elif [[ $arg[0,1] = $histchars[0,1] ]] && (( $#arg[0,2] == 2 )); then
|
|
style=history-expansion
|
|
elif [[ -n ${(M)ZSH_HIGHLIGHT_TOKENS_COMMANDSEPARATOR:#"$arg"} ]]; then
|
|
if [[ $this_word == *':regular:'* ]]; then
|
|
style=commandseparator
|
|
else
|
|
style=unknown-token
|
|
fi
|
|
elif _zsh_highlight_main__is_redirection $arg; then
|
|
style=redirection
|
|
(( in_redirection=2 ))
|
|
else
|
|
if _zsh_highlight_main_highlighter_check_path; then
|
|
style=path
|
|
else
|
|
style=default
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
fi
|
|
# if a style_override was set (eg in _zsh_highlight_main_highlighter_check_path), use it
|
|
[[ -n $style_override ]] && style=$style_override
|
|
(( already_added )) || _zsh_highlight_main_add_region_highlight $start_pos $end_pos $style
|
|
if [[ -n ${(M)ZSH_HIGHLIGHT_TOKENS_COMMANDSEPARATOR:#"$arg"} ]]; then
|
|
next_word=':start:'
|
|
highlight_glob=true
|
|
elif
|
|
[[ -n ${(M)ZSH_HIGHLIGHT_TOKENS_CONTROL_FLOW:#"$arg"} && $this_word == *':start:'* ]] ||
|
|
[[ -n ${(M)ZSH_HIGHLIGHT_TOKENS_PRECOMMANDS:#"$arg"} && $this_word == *':start:'* ]]; then
|
|
next_word=':start:'
|
|
elif [[ $arg == "repeat" && $this_word == *':start:'* ]]; then
|
|
# skip the repeat-count word
|
|
in_redirection=2
|
|
# The redirection mechanism assumes $this_word describes the word
|
|
# following the redirection. Make it so.
|
|
#
|
|
# The repeat-count word will be handled like a redirection target.
|
|
this_word=':start:'
|
|
fi
|
|
start_pos=$end_pos
|
|
(( in_redirection == 0 )) && this_word=$next_word
|
|
done
|
|
}
|
|
|
|
# Check if $arg is variable assignment
|
|
_zsh_highlight_main_highlighter_check_assign()
|
|
{
|
|
setopt localoptions extended_glob
|
|
[[ $arg == [[:alpha:]_][[:alnum:]_]#(|\[*\])(|[+])=* ]]
|
|
}
|
|
|
|
# Check if $arg is a path.
|
|
_zsh_highlight_main_highlighter_check_path()
|
|
{
|
|
_zsh_highlight_main_highlighter_expand_path $arg;
|
|
local expanded_path="$REPLY"
|
|
|
|
[[ -z $expanded_path ]] && return 1
|
|
[[ -e $expanded_path ]] && return 0
|
|
|
|
# Search the path in CDPATH
|
|
local cdpath_dir
|
|
for cdpath_dir in $cdpath ; do
|
|
[[ -e "$cdpath_dir/$expanded_path" ]] && return 0
|
|
done
|
|
|
|
# If dirname($arg) doesn't exist, neither does $arg.
|
|
[[ ! -e ${expanded_path:h} ]] && return 1
|
|
|
|
# If this word ends the buffer, check if it's the prefix of a valid path.
|
|
if [[ ${BUFFER[1]} != "-" && ${#BUFFER} == $end_pos ]] &&
|
|
[[ $WIDGET != accept-* ]]; then
|
|
local -a tmp
|
|
tmp=( ${expanded_path}*(N) )
|
|
(( $#tmp > 0 )) && style_override=path_prefix && return 0
|
|
fi
|
|
|
|
# It's not a path.
|
|
return 1
|
|
}
|
|
|
|
# Highlight special chars inside double-quoted strings
|
|
_zsh_highlight_main_highlighter_highlight_string()
|
|
{
|
|
setopt localoptions noksharrays
|
|
local -a match mbegin mend
|
|
local MATCH; integer MBEGIN MEND
|
|
local i j k style
|
|
# Starting quote is at 1, so start parsing at offset 2 in the string.
|
|
for (( i = 2 ; i < end_pos - start_pos ; i += 1 )) ; do
|
|
(( j = i + start_pos - 1 ))
|
|
(( k = j + 1 ))
|
|
case "$arg[$i]" in
|
|
'$' ) style=dollar-double-quoted-argument
|
|
# Look for an alphanumeric parameter name.
|
|
if [[ ${arg:$i} =~ ^([A-Za-z_][A-Za-z0-9_]*|[0-9]+) ]] ; then
|
|
(( k += $#MATCH )) # highlight the parameter name
|
|
(( i += $#MATCH )) # skip past it
|
|
elif [[ ${arg:$i} =~ ^[{]([A-Za-z_][A-Za-z0-9_]*|[0-9]+)[}] ]] ; then
|
|
(( k += $#MATCH )) # highlight the parameter name and braces
|
|
(( i += $#MATCH )) # skip past it
|
|
else
|
|
continue
|
|
fi
|
|
;;
|
|
"\\") style=back-double-quoted-argument
|
|
if [[ \\\`\"\$ == *$arg[$i+1]* ]]; then
|
|
(( k += 1 )) # Color following char too.
|
|
(( i += 1 )) # Skip parsing the escaped char.
|
|
else
|
|
continue
|
|
fi
|
|
;;
|
|
*) continue ;;
|
|
|
|
esac
|
|
_zsh_highlight_main_add_region_highlight $j $k $style
|
|
done
|
|
}
|
|
|
|
# Highlight special chars inside dollar-quoted strings
|
|
_zsh_highlight_main_highlighter_highlight_dollar_string()
|
|
{
|
|
setopt localoptions noksharrays
|
|
local -a match mbegin mend
|
|
local MATCH; integer MBEGIN MEND
|
|
local i j k style
|
|
local AA
|
|
integer c
|
|
# Starting dollar-quote is at 1:2, so start parsing at offset 3 in the string.
|
|
for (( i = 3 ; i < end_pos - start_pos ; i += 1 )) ; do
|
|
(( j = i + start_pos - 1 ))
|
|
(( k = j + 1 ))
|
|
case "$arg[$i]" in
|
|
"\\") style=back-dollar-quoted-argument
|
|
for (( c = i + 1 ; c <= end_pos - start_pos ; c += 1 )); do
|
|
[[ "$arg[$c]" != ([0-9xXuUa-fA-F]) ]] && break
|
|
done
|
|
AA=$arg[$i+1,$c-1]
|
|
# Matching for HEX and OCT values like \0xA6, \xA6 or \012
|
|
if [[ "$AA" =~ "^(x|X)[0-9a-fA-F]{1,2}"
|
|
|| "$AA" =~ "^[0-7]{1,3}"
|
|
|| "$AA" =~ "^u[0-9a-fA-F]{1,4}"
|
|
|| "$AA" =~ "^U[0-9a-fA-F]{1,8}"
|
|
]]; then
|
|
(( k += $#MATCH ))
|
|
(( i += $#MATCH ))
|
|
else
|
|
if (( $#arg > $i+1 )) && [[ $arg[$i+1] == [xXuU] ]]; then
|
|
# \x not followed by hex digits is probably an error
|
|
style=unknown-token
|
|
fi
|
|
(( k += 1 )) # Color following char too.
|
|
(( i += 1 )) # Skip parsing the escaped char.
|
|
fi
|
|
;;
|
|
*) continue ;;
|
|
|
|
esac
|
|
_zsh_highlight_main_add_region_highlight $j $k $style
|
|
done
|
|
}
|
|
|
|
# Called with a single positional argument.
|
|
# Perform filename expansion (tilde expansion) on the argument and set $REPLY to the expanded value.
|
|
#
|
|
# Does not perform filename generation (globbing).
|
|
_zsh_highlight_main_highlighter_expand_path()
|
|
{
|
|
(( $# == 1 )) || echo "zsh-syntax-highlighting: BUG: _zsh_highlight_main_highlighter_expand_path: called without argument" >&2
|
|
|
|
# The $~1 syntax normally performs filename generation, but not when it's on the right-hand side of ${x:=y}.
|
|
setopt localoptions nonomatch
|
|
unset REPLY
|
|
: ${REPLY:=${(Q)~1}}
|
|
}
|