2011-06-13 04:57:14 +08:00
# -------------------------------------------------------------------------------------------------
2018-01-13 22:05:25 +08:00
# Copyright (c) 2010-2018 zsh-syntax-highlighting contributors
2011-06-13 04:57:14 +08:00
# 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 }
2015-09-08 18:13:16 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [suffix-alias] : =fg=green,underline }
2011-09-12 22:48:18 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [precommand] : =fg=green,underline }
2011-09-21 00:47:06 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [commandseparator] : =none }
2011-06-13 04:57:14 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [path] : =underline }
2016-06-11 20:15:57 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [path_pathseparator] : = }
: ${ ZSH_HIGHLIGHT_STYLES [path_prefix_pathseparator] : = }
2011-06-13 04:57:14 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [globbing] : =fg=blue }
: ${ ZSH_HIGHLIGHT_STYLES [history-expansion] : =fg=blue }
2018-04-15 03:00:03 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [command-substitution] : =none }
: ${ ZSH_HIGHLIGHT_STYLES [command-substitution-delimiter] : =fg=magenta }
: ${ ZSH_HIGHLIGHT_STYLES [process-substitution] : =none }
: ${ ZSH_HIGHLIGHT_STYLES [process-substitution-delimiter] : =fg=magenta }
2011-06-13 04:57:14 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [single-hyphen-option] : =none }
: ${ ZSH_HIGHLIGHT_STYLES [double-hyphen-option] : =none }
: ${ ZSH_HIGHLIGHT_STYLES [back-quoted-argument] : =none }
2018-04-15 03:00:03 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [back-quoted-argument-delimiter] : =fg=magenta }
2011-06-13 04:57:14 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [single-quoted-argument] : =fg=yellow }
: ${ ZSH_HIGHLIGHT_STYLES [double-quoted-argument] : =fg=yellow }
2015-09-11 03:58:02 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [dollar-quoted-argument] : =fg=yellow }
2017-11-15 12:43:40 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [rc-quote] : =fg=cyan }
2011-06-13 04:57:14 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [dollar-double-quoted-argument] : =fg=cyan }
: ${ ZSH_HIGHLIGHT_STYLES [back-double-quoted-argument] : =fg=cyan }
2015-09-26 05:18:37 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [back-dollar-quoted-argument] : =fg=cyan }
2011-06-13 04:57:14 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [assign] : =none }
2015-09-25 20:39:06 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [redirection] : =none }
2015-10-28 01:29:06 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [comment] : =fg=black,bold }
2018-10-22 12:30:43 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [named-fd] : =none }
2016-08-17 01:24:58 +08:00
: ${ ZSH_HIGHLIGHT_STYLES [arg0] : =fg=green }
2011-06-13 04:57:14 +08:00
# Whether the highlighter should be called or not.
2016-07-21 11:33:28 +08:00
_zsh_highlight_highlighter_main_predicate( )
2011-06-13 04:57:14 +08:00
{
2016-03-30 03:56:57 +08:00
# may need to remove path_prefix highlighting when the line ends
[ [ $WIDGET = = zle-line-finish ] ] || _zsh_highlight_buffer_modified
2011-06-13 04:57:14 +08:00
}
2015-01-26 20:49:19 +08:00
# Helper to deal with tokens crossing line boundaries.
_zsh_highlight_main_add_region_highlight( ) {
integer start = $1 end = $2
2016-05-13 03:04:35 +08:00
shift 2
2015-01-26 22:03:08 +08:00
2018-10-22 20:50:53 +08:00
if ( ( in_alias ) ) ; then
[ [ $1 = = unknown-token ] ] && alias_style = unknown-token
return
fi
2018-10-18 10:38:17 +08:00
2018-03-11 04:34:46 +08:00
# The calculation was relative to $buf but region_highlight is relative to $BUFFER.
( ( start += buf_offset ) )
( ( end += buf_offset ) )
2018-03-13 09:11:59 +08:00
2018-03-11 04:58:01 +08:00
list_highlights += ( $start $end $1 )
2018-03-13 09:11:59 +08:00
}
_zsh_highlight_main_add_many_region_highlights( ) {
for 1 2 3; do
_zsh_highlight_main_add_region_highlight $1 $2 $3
done
}
_zsh_highlight_main_calculate_fallback( ) {
local -A fallback_of; fallback_of = (
2018-03-13 08:34:25 +08:00
alias arg0
suffix-alias arg0
builtin arg0
function arg0
command arg0
precommand arg0
hashed-command arg0
arg0_\* arg0
path_prefix path
# The path separator fallback won't ever be used, due to the optimisation
# in _zsh_highlight_main_highlighter_highlight_path_separators().
path_pathseparator path
path_prefix_pathseparator path_prefix
single-quoted-argument{ -unclosed,}
double-quoted-argument{ -unclosed,}
dollar-quoted-argument{ -unclosed,}
back-quoted-argument{ -unclosed,}
2018-04-15 03:00:03 +08:00
2018-10-13 21:14:42 +08:00
command-substitution{ -quoted,,-unquoted,}
command-substitution-delimiter{ -quoted,,-unquoted,}
2018-04-15 03:00:03 +08:00
command-substitution{ -delimiter,}
process-substitution{ -delimiter,}
back-quoted-argument{ -delimiter,}
2018-03-13 08:34:25 +08:00
)
local needle = $1 value
2018-03-13 09:11:59 +08:00
reply = ( $1 )
2018-03-13 08:34:25 +08:00
while [ [ -n ${ value : : = $fallback_of [(k) $needle ] } ] ] ; do
unset " fallback_of[ $needle ] " # paranoia against infinite loops
2018-03-13 09:11:59 +08:00
reply += ( $value )
2018-03-13 08:34:25 +08:00
needle = $value
done
2017-11-15 12:41:27 +08:00
}
2019-04-01 01:52:19 +08:00
_zsh_highlight_main__test_type_builtin( ) {
if [ [ -n ${ ZSH_HIGHLIGHT_SPECIAL_PATH } ] ] ; then
if [ " $this_word " = ":sudo_opt::start:" ] ; then
# ZSH_HIGHLIGHT_SPECIAL_PATH exist is not empty : "Admin" says that user has access to commands in these paths with sudo
local PATH = " ${ ZSH_HIGHLIGHT_SPECIAL_PATH } "
fi
fi
builtin type -w -- $1
}
2016-03-29 05:42:31 +08:00
# Get the type of a command.
2015-11-17 09:27:38 +08:00
#
2016-03-29 05:42:31 +08:00
# Uses the zsh/parameter module if available to avoid forks, and a
# wrapper around 'type -w' as fallback.
2015-11-17 09:27:38 +08:00
#
2018-10-10 18:07:57 +08:00
# If $2 is 0, do not consider aliases.
2016-03-29 05:42:31 +08:00
#
# The result will be stored in REPLY.
2015-11-17 09:27:38 +08:00
_zsh_highlight_main__type( ) {
2018-10-10 18:07:57 +08:00
integer -r aliases_allowed = ${ 2 -1 }
# We won't cache replies of anything that exists as an alias at all, to
# ensure the cached value is correct regardless of $aliases_allowed.
#
# ### We probably _should_ cache them in a cache that's keyed on the value of
# ### $aliases_allowed, on the assumption that aliases are the common case.
integer may_cache = 1
# Cache lookup
2016-03-29 07:33:00 +08:00
if ( ( $+_zsh_highlight_main__command_type_cache ) ) ; then
REPLY = $_zsh_highlight_main__command_type_cache [ ( e) $1 ]
if [ [ -n " $REPLY " ] ] ; then
return
fi
fi
2018-10-10 18:07:57 +08:00
2019-04-01 01:52:19 +08:00
if [ [ -v ZSH_HIGHLIGHT_SPECIAL_COMMAND ] ] ; then
if ( ( ${ ZSH_HIGHLIGHT_SPECIAL_COMMAND [(I) $1 ] } ) ) ; then
# ZSH_HIGHLIGHT_SPECIAL_COMMAND exists : if foo of "sudo foo" is in ZSH_HIGHLIGHT_SPECIAL_COMMAND then "Admin" says that user has access to this command
REPLY = command
return 0
fi
fi
if [ [ -v ZSH_HIGHLIGHT_SPECIAL_PATH ] ] ; then
if [ " $this_word " = ":sudo_opt::start:" ] ; then
if [ -z " ${ ZSH_HIGHLIGHT_SPECIAL_PATH } " ] ; then
# ZSH_HIGHLIGHT_SPECIAL_PATH exist and is empty : "Admin" wants to say that user has no access to sudo : foo in "sudo foo" is red
REPLY = none
return 0
fi
fi
fi
2018-10-10 18:07:57 +08:00
# Main logic
2015-11-17 09:27:38 +08:00
if ( ( $# options_to_set ) ) ; then
2016-05-13 03:24:00 +08:00
setopt localoptions $options_to_set ;
2015-11-17 09:27:38 +08:00
fi
2016-03-29 05:42:31 +08:00
unset REPLY
if zmodload -e zsh/parameter; then
if ( ( $+aliases[ ( e) $1 ] ) ) ; then
2018-10-10 18:07:57 +08:00
may_cache = 0
fi
if ( ( $+aliases[ ( e) $1 ] ) ) && ( ( aliases_allowed ) ) ; then
2016-03-29 05:42:31 +08:00
REPLY = alias
2018-10-31 05:25:59 +08:00
elif [ [ $1 = = *.* && -n ${ 1 %.* } ] ] && ( ( $+saliases[ ( e) ${ 1 ##*. } ] ) ) ; then
2016-03-29 05:42:31 +08:00
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
2019-03-16 15:47:53 +08:00
# None of the special hashes had a match, so fall back to 'type -w', for
# forward compatibility with future versions of zsh that may add new command
# types.
#
2016-08-30 10:56:23 +08:00
# zsh 5.2 and older have a bug whereby running 'type -w ./sudo' implicitly
# runs 'hash ./sudo=/usr/local/bin/./sudo' (assuming /usr/local/bin/sudo
# exists and is in $PATH). Avoid triggering the bug, at the expense of
# falling through to the $() below, incurring a fork. (Issue #354.)
#
2016-10-18 04:46:46 +08:00
# The first disjunct mimics the isrelative() C call from the zsh bug.
2016-09-09 03:09:29 +08:00
elif { [ [ $1 != */* ] ] || is-at-least 5.3 } &&
2019-03-16 15:49:04 +08:00
# Add a subshell to avoid a zsh upstream bug; see issue #606.
2019-03-16 15:45:23 +08:00
# ### Remove the subshell when we stop supporting zsh 5.7.1 (I assume 5.8 will have the bugfix).
2019-04-01 01:52:19 +08:00
! ( _zsh_highlight_main__test_type_builtin $1 ) >/dev/null 2>& 1; then
2016-03-29 05:42:31 +08:00
REPLY = none
fi
fi
if ! ( ( $+REPLY ) ) ; then
2019-03-16 15:47:53 +08:00
# zsh/parameter not available or had no matches.
#
2017-11-25 09:25:07 +08:00
# Note that 'type -w' will run 'rehash' implicitly.
2018-10-10 18:07:57 +08:00
#
# We 'unalias' in a subshell, so the parent shell is not affected.
#
# The colon command is there just to avoid a command substitution that
# starts with an arithmetic expression [«((…))» as the first thing inside
# «$(…)»], which is area that has had some parsing bugs before 5.6
# (approximately).
2019-04-01 01:52:19 +08:00
REPLY = " ${ $( :; ( ( aliases_allowed ) ) || unalias -- $1 2>/dev/null; LC_ALL=C _zsh_highlight_main__test_type_builtin $1 2>/dev/null)##* : } "
2018-10-10 18:07:57 +08:00
if [ [ $REPLY = = 'alias' ] ] ; then
may_cache = 0
fi
2016-03-29 05:42:31 +08:00
fi
2018-10-10 18:07:57 +08:00
# Cache population
if ( ( may_cache ) ) && ( ( $+_zsh_highlight_main__command_type_cache ) ) ; then
2016-03-29 07:33:00 +08:00
_zsh_highlight_main__command_type_cache[ ( e) $1 ] = $REPLY
fi
2018-01-12 21:45:52 +08:00
[ [ -n $REPLY ] ]
return $?
2015-11-17 09:27:38 +08:00
}
2018-01-12 21:46:11 +08:00
# Checks whether $1 is something that can be run.
#
# Return 0 if runnable, 1 if not runnable, 2 if trouble.
_zsh_highlight_main__is_runnable( ) {
if _zsh_highlight_main__type " $1 " ; then
[ [ $REPLY != none ] ]
else
return 2
fi
}
2016-05-09 11:26:29 +08:00
# Check whether the first argument is a redirection operator token.
# Report result via the exit code.
_zsh_highlight_main__is_redirection( ) {
2016-05-09 11:26:06 +08:00
# A redirection operator token:
# - starts with an optional single-digit number;
# - then, has a '<' or '>' character;
# - is not a process substitution [<(...) or >(...)].
2017-12-23 11:59:53 +08:00
# - is not a numeric glob <->
[ [ $1 = = ( <0-9>| ) ( \< | \> ) * ] ] && [ [ $1 != ( \< | \> ) $'\x28' * ] ] && [ [ $1 != *'<' *'-' *'>' * ] ]
2016-05-09 11:26:29 +08:00
}
2016-03-29 05:38:51 +08:00
# 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
}
2016-09-22 12:52:32 +08:00
# Check that the top of $braces_stack has the expected value. If it does, set
# the style according to $2; otherwise, set style=unknown-token.
#
# $1: character expected to be at the top of $braces_stack
2018-03-11 04:31:15 +08:00
# $2: optional assignment to style it if matches
# return value is 0 if there is a match else 1
2016-09-22 12:52:32 +08:00
_zsh_highlight_main__stack_pop( ) {
if [ [ $braces_stack [ 1] = = $1 ] ] ; then
braces_stack = ${ braces_stack : 1 }
2018-03-11 04:31:15 +08:00
if ( ( $+2 ) ) ; then
style = $2
fi
return 0
2016-09-22 12:52:32 +08:00
else
style = unknown-token
2018-03-11 04:31:15 +08:00
return 1
2016-09-22 12:52:32 +08:00
fi
}
2011-06-13 04:57:14 +08:00
# Main syntax highlighting function.
2016-07-21 11:33:28 +08:00
_zsh_highlight_highlighter_main_paint( )
2011-06-13 04:57:14 +08:00
{
2017-12-16 10:09:39 +08:00
setopt localoptions extendedglob
2015-11-17 07:14:48 +08:00
2016-02-14 23:34:53 +08:00
# At the PS3 prompt and in vared, highlight nothing.
2016-01-21 06:10:45 +08:00
#
2016-07-21 11:33:28 +08:00
# (We can't check this in _zsh_highlight_highlighter_main_predicate because
2016-01-21 06:10:45 +08:00
# if the predicate returns false, the previous value of region_highlight
# would be reused.)
2016-02-14 23:34:53 +08:00
if [ [ $CONTEXT = = ( select | vared) ] ] ; then
2016-01-21 06:10:45 +08:00
return
fi
2011-09-12 22:48:18 +08:00
typeset -a ZSH_HIGHLIGHT_TOKENS_COMMANDSEPARATOR
2015-10-28 14:04:43 +08:00
typeset -a ZSH_HIGHLIGHT_TOKENS_CONTROL_FLOW
2018-03-11 04:58:01 +08:00
local -a options_to_set reply # used in callees
local REPLY
2016-09-22 12:52:32 +08:00
2016-07-28 15:37:59 +08:00
# $flags_with_argument is a set of letters, corresponding to the option letters
# that would be followed by a colon in a getopts specification.
local flags_with_argument
2018-01-12 21:32:30 +08:00
# $flags_sans_argument is a set of letters, corresponding to the option letters
# that wouldn't be followed by a colon in a getopts specification.
local flags_sans_argument
# $precommand_options maps precommand name to values of $flags_with_argument and
2019-03-27 19:45:17 +08:00
# $flags_sans_argument for that precommand, joined by a colon. (The value is NOT
# a getopt(3) spec, although it resembles one.)
2018-01-12 21:32:30 +08:00
#
# Currently, setting $flags_sans_argument is only important for commands that
# have a non-empty $flags_with_argument; see test-data/precommand4.zsh.
2016-07-28 15:37:59 +08:00
local -A precommand_options
precommand_options = (
2018-10-11 12:22:41 +08:00
# Precommand modifiers as of zsh 5.6.2 cf. zshmisc(1).
'-' ''
'builtin' ''
'command' :pvV
'exec' a:cl
'noglob' ''
2019-12-27 21:47:31 +08:00
# 'time' and 'nocorrect' shouldn't be added here; they're reserved words, not precommands.
2018-10-11 12:22:41 +08:00
2018-01-12 21:32:30 +08:00
'doas' aCu:Lns # as of OpenBSD's doas(1) dated September 4, 2016
2018-10-11 12:22:41 +08:00
'nice' n: # as of current POSIX spec
2018-01-12 22:03:39 +08:00
'pkexec' '' # doesn't take short options; immune to #121 because it's usually not passed --option flags
2018-10-11 12:22:41 +08:00
'sudo' Cgprtu:AEHKPSVbhiklnsv # as of sudo 1.8.21p2
2019-03-27 19:45:17 +08:00
'stdbuf' ioe:
2019-12-25 18:53:42 +08:00
'eatmydata' ''
'catchsegv' ''
'nohup' ''
'setsid' :wc
2016-07-28 15:37:59 +08:00
)
2017-12-16 10:09:39 +08:00
if [ [ $zsyh_user_options [ ignorebraces] = = on || ${ zsyh_user_options [ignoreclosebraces] :- off } = = on ] ] ; then
2017-11-25 00:05:13 +08:00
local right_brace_is_recognised_everywhere = false
else
local right_brace_is_recognised_everywhere = true
fi
2017-12-16 10:09:39 +08:00
if [ [ $zsyh_user_options [ pathdirs] = = on ] ] ; then
2015-11-17 07:14:48 +08:00
options_to_set += ( PATH_DIRS )
fi
2011-09-12 22:48:18 +08:00
ZSH_HIGHLIGHT_TOKENS_COMMANDSEPARATOR = (
'|' '||' ';' '&' '&&'
2015-11-18 04:19:19 +08:00
'|&'
'&!' '&|'
# ### 'case' syntax, but followed by a pattern, not by a command
# ';;' ';&' ';|'
2011-09-12 22:48:18 +08:00
)
2015-10-28 14:04:43 +08:00
# Tokens that, at (naively-determined) "command position", are followed by
2015-10-29 02:44:49 +08:00
# a de jure command position. All of these are reserved words.
2015-10-28 14:04:43 +08:00
ZSH_HIGHLIGHT_TOKENS_CONTROL_FLOW = (
2015-10-27 21:10:01 +08:00
$'\x7b' # block
$'\x28' # subshell
'()' # anonymous function
2015-10-28 14:04:43 +08:00
'while'
2015-10-29 02:44:49 +08:00
'until'
2015-10-28 14:04:43 +08:00
'if'
'then'
2015-10-29 02:44:49 +08:00
'elif'
2015-10-29 01:32:23 +08:00
'else'
2015-10-28 14:04:43 +08:00
'do'
2015-10-29 02:44:49 +08:00
'time'
'coproc'
'!' # reserved word; unrelated to $histchars[1]
2015-10-28 14:04:43 +08:00
)
2019-07-24 20:27:54 +08:00
if ( ( $+X_ZSH_HIGHLIGHT_DIRS_BLACKLIST ) ) ; then
print >& 2 'zsh-syntax-highlighting: X_ZSH_HIGHLIGHT_DIRS_BLACKLIST is deprecated. Please use ZSH_HIGHLIGHT_DIRS_BLACKLIST.'
ZSH_HIGHLIGHT_DIRS_BLACKLIST = ( $X_ZSH_HIGHLIGHT_DIRS_BLACKLIST )
unset X_ZSH_HIGHLIGHT_DIRS_BLACKLIST
fi
2018-03-21 10:03:22 +08:00
_zsh_highlight_main_highlighter_highlight_list -$# PREBUFFER '' 1 " $PREBUFFER $BUFFER "
2018-03-11 04:58:01 +08:00
# end is a reserved word
local start end_ style
for start end_ style in $reply ; do
( ( start >= end_ ) ) && { print -r -- >& 2 " zsh-syntax-highlighting: BUG: _zsh_highlight_highlighter_main_paint: start( $start ) >= end( $end_ ) " ; return }
( ( end_ <= 0 ) ) && continue
( ( start < 0 ) ) && start = 0 # having start<0 is normal with e.g. multiline strings
_zsh_highlight_main_calculate_fallback $style
_zsh_highlight_add_highlight $start $end_ $reply
done
2018-03-11 04:47:52 +08:00
}
2018-03-21 10:03:22 +08:00
# $1 is the offset of $4 from the parent buffer. Added to the returned highlights.
2018-03-11 04:47:52 +08:00
# $2 is the initial braces_stack (for a closing paren).
2018-03-21 10:03:22 +08:00
# $3 is 1 if $4 contains the end of $BUFFER, else 0.
# $4 is the buffer to highlight.
2018-03-11 04:58:01 +08:00
# Returns:
# $REPLY: $buf[REPLY] is the last character parsed.
# $reply is an array of region_highlight additions.
2018-04-15 02:38:02 +08:00
# exit code is 0 if the braces_stack is empty, 1 otherwise.
2018-03-11 04:47:52 +08:00
_zsh_highlight_main_highlighter_highlight_list( )
{
2018-10-02 01:47:13 +08:00
integer start_pos end_pos = 0 buf_offset = $1 has_end = $3
2018-10-22 20:50:53 +08:00
# alias_style is the style to apply to an alias once in_alias=0
# Usually 'alias' but set to 'unknown-token' if any word expanded from
# the alias would be highlighted as unknown-token
2018-10-18 10:38:17 +08:00
# last_alias is the last alias arg (lhs) expanded (if in an alias).
# This allows for expanding alias ls='ls -l' while avoiding loops.
2018-10-22 20:50:53 +08:00
local alias_style arg buf = $4 highlight_glob = true last_alias style
2018-03-11 04:45:47 +08:00
local in_array_assignment = false # true between 'a=(' and the matching ')'
2018-10-18 10:38:17 +08:00
# in_alias is equal to the number of shifts needed until arg=args[1] pops an
# arg from BUFFER and not added by an alias.
2018-10-22 20:50:53 +08:00
integer in_alias = 0 len = $# buf
2018-03-11 04:58:01 +08:00
local -a match mbegin mend list_highlights
2018-10-18 10:38:17 +08:00
# seen_alias is a map of aliases already seen to avoid loops like alias a=b b=a
local -A seen_alias
2018-10-22 12:56:50 +08:00
# Pattern for parameter names
readonly parameter_name_pattern = '([A-Za-z_][A-Za-z0-9_]*|[0-9]+)'
2018-03-11 04:58:01 +08:00
list_highlights = ( )
2016-09-25 23:26:37 +08:00
2018-03-11 04:45:47 +08:00
# "R" for round
# "Q" for square
# "Y" for curly
2018-10-21 12:47:37 +08:00
# "T" for [[ ]]
2018-03-11 04:45:47 +08:00
# "S" for $( )
# "D" for do/done
# "$" for 'end' (matches 'foreach' always; also used with cshjunkiequotes in repeat/while)
# "?" for 'if'/'fi'; also checked by 'elif'/'else'
# ":" for 'then'
2018-03-11 04:47:52 +08:00
local braces_stack = $2
2018-03-11 04:45:47 +08:00
2015-10-28 21:52:03 +08:00
# State machine
#
# The states are:
# - :start: Command word
2018-10-22 23:44:55 +08:00
# - :start_of_pipeline: Start of a 'pipeline' as defined in zshmisc(1).
# Only valid when :start: is present
2016-07-28 16:26:48 +08:00
# - :sudo_opt: A leading-dash option to a precommand, whether it takes an
# argument or not. (Example: sudo's "-u" or "-i".)
# - :sudo_arg: The argument to a precommand's leading-dash option,
2015-10-28 21:52:03 +08:00
# when given as a separate word; i.e., "foo" in "-u foo" (two
# words) but not in "-ufoo" (one word).
2015-11-18 03:40:58 +08:00
# - :regular: "Not a command word", and command delimiters are permitted.
2015-10-28 21:52:03 +08:00
# Mainly used to detect premature termination of commands.
2016-09-25 15:56:28 +08:00
# - :always: The word 'always' in the «{ foo } always { bar }» syntax.
2015-10-28 21:52:03 +08:00
#
2015-10-30 01:58:23 +08:00
# 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:.
#
2015-10-28 21:52:03 +08:00
# The tokens are always added with both leading and trailing colons to serve as
2017-11-13 13:12:22 +08:00
# word delimiters (an improvised array); [[ $x == *:foo:* ]] and x=${x//:foo:/}
2015-10-28 21:52:03 +08:00
# will DTRT regardless of how many elements or repetitions $x has..
2015-10-27 19:46:10 +08:00
#
# Handling of redirections: upon seeing a redirection token, we must stall
2015-10-28 22:09:07 +08:00
# the current state --- that is, the value of $this_word --- for two iterations
2015-10-27 19:46:10 +08:00
# (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
2015-10-28 22:09:07 +08:00
# 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.
#
2018-10-22 23:44:55 +08:00
local this_word next_word = ':start::start_of_pipeline:'
2015-10-27 19:46:10 +08:00
integer in_redirection
2016-05-12 23:22:57 +08:00
# Processing buffer
local proc_buf = " $buf "
2017-11-25 00:05:13 +08:00
local -a args
2017-12-16 10:09:39 +08:00
if [ [ $zsyh_user_options [ interactivecomments] = = on ] ] ; then
2017-11-25 00:05:13 +08:00
args = ( ${ (zZ+c+)buf } )
else
args = ( ${ (z)buf } )
fi
2018-10-18 09:08:45 +08:00
while ( ( $# args ) ) ; do
arg = $args [ 1]
shift args
2018-10-18 10:38:17 +08:00
if ( ( in_alias ) ) ; then
( ( in_alias-- ) )
2018-10-22 20:50:53 +08:00
if ( ( in_alias = = 0 ) ) ; then
last_alias = seen_alias = ( )
# start_pos and end_pos are of the alias (previous $arg) here
_zsh_highlight_main_add_region_highlight $start_pos $end_pos $alias_style
fi
2018-10-18 10:38:17 +08:00
fi
2016-07-28 16:16:22 +08:00
2018-03-26 10:44:39 +08:00
# Initialize this_word and next_word.
2015-10-27 19:46:10 +08:00
if ( ( in_redirection = = 0 ) ) ; then
2018-03-26 10:44:39 +08:00
this_word = $next_word
2015-10-27 19:46:10 +08:00
next_word = ':regular:'
2015-11-18 03:40:58 +08:00
else
# Stall $next_word.
2018-03-26 10:44:39 +08:00
( ( --in_redirection ) )
2015-10-27 19:46:10 +08:00
fi
2016-08-16 21:40:31 +08:00
# Initialize per-"simple command" [zshmisc(1)] variables:
#
# $style how to highlight $arg
# $in_array_assignment boolean flag for "between '(' and ')' of array assignment"
# $highlight_glob boolean flag for "'noglob' is in effect"
#
2016-06-03 12:16:13 +08:00
style = unknown-token
2015-11-17 02:44:44 +08:00
if [ [ $this_word = = *':start:' * ] ] ; then
in_array_assignment = false
if [ [ $arg = = 'noglob' ] ] ; then
highlight_glob = false
fi
2015-09-07 17:08:58 +08:00
fi
2015-01-26 23:39:42 +08:00
2018-10-18 10:38:17 +08:00
if ( ( in_alias = = 0 ) ) ; then
2018-10-18 10:18:32 +08:00
# Compute the new $start_pos and $end_pos, skipping over whitespace in $buf.
2018-10-21 11:42:00 +08:00
[ [ " $proc_buf " = ( #b)(#s)(([ $'\t']|\\$'\n')#)* ]]
# The first, outer parenthesis
integer offset = " ${# match [1] } "
( ( start_pos = end_pos + offset ) )
( ( end_pos = start_pos + $# arg ) )
2018-10-18 10:18:32 +08:00
# Compute the 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.
2015-09-02 16:28:03 +08:00
#
2018-10-18 10:18:32 +08:00
# end_pos was advanced by $offset (via start_pos)
# and by $#arg. Note the `start_pos=$end_pos`
# below.
2016-09-25 23:26:37 +08:00
#
2018-10-18 10:18:32 +08:00
# 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.
2016-09-25 23:26:37 +08:00
#
2018-10-18 10:18:32 +08:00
# Why [,-1] is slower than [,length] isn't clear.
proc_buf = " ${ proc_buf [offset + $# arg + 1,len] } "
2015-01-26 23:39:42 +08:00
fi
2016-08-16 21:40:31 +08:00
# Handle the INTERACTIVE_COMMENTS option.
#
# We use the (Z+c+) flag so the entire comment is presented as one token in $arg.
2017-12-16 10:09:39 +08:00
if [ [ $zsyh_user_options [ interactivecomments] = = on && $arg [ 1] = = $histchars [ 3] ] ] ; then
2015-10-28 21:48:55 +08:00
if [ [ $this_word = = *( ':regular:' | ':start:' ) * ] ] ; then
2016-04-25 05:04:32 +08:00
style = comment
2015-10-28 21:48:55 +08:00
else
2016-04-25 05:04:32 +08:00
style = unknown-token # prematurely terminated
2015-10-28 21:48:55 +08:00
fi
2015-10-28 01:29:06 +08:00
_zsh_highlight_main_add_region_highlight $start_pos $end_pos $style
2018-03-26 10:44:39 +08:00
# Stall this arg
in_redirection = 1
2015-10-28 01:29:06 +08:00
continue
fi
2018-03-26 10:42:36 +08:00
if [ [ $this_word = = *:start:* ] ] && ! ( ( in_redirection ) ) ; then
# Expand aliases.
2018-10-01 23:10:44 +08:00
_zsh_highlight_main__type " $arg "
2018-03-26 10:42:36 +08:00
local res = " $REPLY "
2018-10-18 10:38:17 +08:00
if [ [ $res = = "alias" ] ] && [ [ $last_alias != $arg ] ] ; then
# Avoid looping forever on alias a=b b=c c=b, but allow alias foo='foo bar'
2018-10-22 03:59:52 +08:00
# Also mark insane aliases as unknown-token (cf. #263).
if ( ( $+seen_alias[ $arg ] ) ) || [ [ $arg = = ?*= * ] ] ; then
2018-10-22 20:50:53 +08:00
( ( in_alias = = 0 ) ) && in_alias = 1
2018-10-18 10:38:17 +08:00
_zsh_highlight_main_add_region_highlight $start_pos $end_pos unknown-token
continue
fi
2018-10-19 11:36:01 +08:00
seen_alias[ $arg ] = 1
2018-10-18 10:38:17 +08:00
last_alias = $arg
2018-10-18 10:18:32 +08:00
_zsh_highlight_main__resolve_alias $arg
2018-10-19 11:36:01 +08:00
local -a alias_args
2018-10-18 10:38:17 +08:00
# Elision is desired in case alias x=''
2018-10-23 06:14:48 +08:00
if [ [ $zsyh_user_options [ interactivecomments] = = on ] ] ; then
alias_args = ( ${ (zZ+c+)REPLY } )
else
alias_args = ( ${ (z)REPLY } )
fi
2018-10-18 10:38:17 +08:00
args = ( $alias_args $args )
if ( ( in_alias = = 0 ) ) ; then
2018-10-22 20:50:53 +08:00
alias_style = alias
2018-10-18 10:38:17 +08:00
# Add one because we will in_alias-- on the next loop iteration so
# this iteration should be considered in in_alias as well
( ( in_alias += $# alias_args + 1 ) )
2018-10-18 10:18:32 +08:00
else
2018-10-18 10:38:17 +08:00
# This arg is already included in the count, so no need to + 1.
( ( in_alias += $# alias_args ) )
2018-10-18 10:18:32 +08:00
fi
2018-10-18 10:38:17 +08:00
( ( in_redirection++ ) ) # Stall this arg
continue
2018-10-01 23:23:59 +08:00
else
_zsh_highlight_main_highlighter_expand_path $arg
arg = $REPLY
2018-10-18 10:38:17 +08:00
_zsh_highlight_main__type " $arg " 0
2018-10-01 23:10:44 +08:00
res = " $REPLY "
fi
2016-07-28 16:16:22 +08:00
fi
2018-03-26 10:41:03 +08:00
# Analyse the current word.
if _zsh_highlight_main__is_redirection $arg ; then
2018-10-22 12:53:42 +08:00
if ( ( in_redirection = = 1 ) ) ; then
2018-10-22 12:30:43 +08:00
# The condition excludes the case that BUFFER='{foo}>&2' and we're on the '>&'.
2018-03-26 10:41:03 +08:00
_zsh_highlight_main_add_region_highlight $start_pos $end_pos unknown-token
else
in_redirection = 2
2018-10-02 01:55:14 +08:00
_zsh_highlight_main_add_region_highlight $start_pos $end_pos redirection
2018-03-26 10:41:03 +08:00
fi
2018-10-02 01:55:14 +08:00
continue
2018-10-22 12:56:50 +08:00
elif [ [ $arg = = '{' ${ ~parameter_name_pattern } '}' ] ] && _zsh_highlight_main__is_redirection $args [ 1] ; then
2018-10-22 12:30:43 +08:00
# named file descriptor: {foo}>&2
in_redirection = 3
_zsh_highlight_main_add_region_highlight $start_pos $end_pos named-fd
continue
2018-03-26 10:41:03 +08:00
fi
2016-07-29 06:41:23 +08:00
# Expand parameters.
#
2018-01-12 21:05:07 +08:00
# ### For now, expand just '$foo' or '${foo}', possibly with braces, but with
# ### no other features of the parameter expansion syntax. (No ${(x)foo},
# ### no ${foo[x]}, no ${foo:-x}.)
2016-07-29 06:41:23 +08:00
( ) {
# 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
2017-12-25 13:20:40 +08:00
local parameter_name
if [ [ $arg [ 1] = = '$' ] ] && [ [ ${ arg [2] } = = '{' ] ] && [ [ ${ arg [-1] } = = '}' ] ] ; then
parameter_name = ${ ${ arg : 2 } %? }
elif [ [ $arg [ 1] = = '$' ] ] ; then
parameter_name = ${ arg : 1 }
fi
2018-01-12 21:49:03 +08:00
if [ [ $res = = none ] ] && zmodload -e zsh/parameter &&
2018-10-22 12:56:50 +08:00
[ [ ${ parameter_name } = ~ ^${ ~parameter_name_pattern } $ ] ] &&
2016-07-29 06:41:23 +08:00
( ( ${ +parameters[(e) ${ MATCH } ] } ) ) && [ [ ${ parameters [(e) $MATCH ] } != *special* ] ]
then
2018-01-12 21:54:02 +08:00
# Set $arg.
case ${ (tP)MATCH } in
( *array*| *assoc*)
local -a words; words = ( ${ (P)MATCH } )
arg = ${ words [1] }
; ;
( *)
# scalar, presumably
arg = ${ (P)MATCH }
; ;
esac
2018-10-18 10:38:17 +08:00
_zsh_highlight_main__type " $arg " 0
2016-07-29 06:41:23 +08:00
res = $REPLY
fi
}
2016-08-16 21:40:31 +08:00
# Parse the sudo command line
if ( ( ! in_redirection ) ) ; then
2015-10-27 19:46:10 +08:00
if [ [ $this_word = = *':sudo_opt:' * ] ] ; then
2016-07-28 15:42:54 +08:00
if [ [ -n $flags_with_argument ] ] &&
2018-10-11 12:02:33 +08:00
{ [ [ -n $flags_sans_argument ] ] && [ [ $arg = = '-' [ $flags_sans_argument ] #[$flags_with_argument] ]] ||
[ [ $arg = = '-' [ $flags_with_argument ] ] ] } ; then
2015-10-27 19:46:10 +08:00
# Flag that requires an argument
2016-07-28 15:42:54 +08:00
this_word = ${ this_word // : start : / }
next_word = ':sudo_arg:'
2018-10-10 11:51:54 +08:00
elif [ [ -n $flags_with_argument ] ] &&
2018-10-11 12:02:33 +08:00
{ [ [ -n $flags_sans_argument ] ] && [ [ $arg = = '-' [ $flags_sans_argument ] #[$flags_with_argument]* ]] ||
[ [ $arg = = '-' [ $flags_with_argument ] * ] ] } ; then
2018-10-10 11:51:54 +08:00
# Argument attached in the same word
2016-07-28 15:42:54 +08:00
this_word = ${ this_word // : start : / }
next_word += ':start:'
next_word += ':sudo_opt:'
2018-10-11 12:02:33 +08:00
elif [ [ -n $flags_sans_argument ] ] &&
[ [ $arg = = '-' [ $flags_sans_argument ] # ]]; then
2018-10-10 11:51:54 +08:00
# Flag that requires no argument
this_word = :sudo_opt:
next_word += ':start:'
next_word += ':sudo_opt:'
elif [ [ $arg = = '-' * ] ] ; then
# Unknown flag
this_word = :sudo_opt:
next_word += ':start:'
next_word += ':sudo_opt:'
_zsh_highlight_main_add_region_highlight $start_pos $end_pos unknown-token
continue
2016-07-28 15:42:54 +08:00
else
2018-01-12 21:38:06 +08:00
# Not an option flag; nothing to do. (If the command line is
# syntactically valid, ${this_word//:sudo_opt:/} should be
# non-empty now.)
2018-01-13 00:08:22 +08:00
this_word = ${ this_word // : sudo_opt : / }
2016-07-28 15:42:54 +08:00
fi
2015-10-27 19:46:10 +08:00
elif [ [ $this_word = = *':sudo_arg:' * ] ] ; then
next_word += ':sudo_opt:'
2015-10-28 19:37:45 +08:00
next_word += ':start:'
2015-10-27 19:46:10 +08:00
fi
2016-08-16 21:00:55 +08:00
fi
2015-10-27 19:46:10 +08:00
2016-08-16 21:40:31 +08:00
# The Great Fork: is this a command word? Is this a non-command word?
2018-10-02 06:24:51 +08:00
if [ [ -n ${ (M)ZSH_HIGHLIGHT_TOKENS_COMMANDSEPARATOR : # " $arg " } ] ] ; then
2018-10-21 12:47:37 +08:00
if _zsh_highlight_main__stack_pop T || _zsh_highlight_main__stack_pop Q; then
# Missing closing square bracket(s)
style = unknown-token
elif [ [ $this_word = = *':regular:' * ] ] ; then
2018-10-02 06:24:51 +08:00
# This highlights empty commands (semicolon follows nothing) as an error.
# Zsh accepts them, though.
style = commandseparator
else
style = unknown-token
fi
if [ [ $arg = = ';' ] ] && $in_array_assignment ; then
# literal newline inside an array assignment
next_word = ':regular:'
else
next_word = ':start:'
highlight_glob = true
2018-10-22 23:44:55 +08:00
if [ [ $arg != '|' && $arg != '|&' ] ] ; then
next_word += ':start_of_pipeline:'
fi
2018-10-02 06:24:51 +08:00
fi
2018-10-02 06:06:33 +08:00
elif ! ( ( in_redirection) ) && [ [ $this_word = = *':always:' * && $arg = = 'always' ] ] ; then
2016-09-25 15:56:28 +08:00
# try-always construct
style = reserved-word # de facto a reserved word, although not de jure
2018-10-22 23:44:55 +08:00
next_word = ':start:' # only left brace is allowed, apparently
2018-10-02 06:06:33 +08:00
elif ! ( ( in_redirection) ) && [ [ $this_word = = *':start:' * ] ] ; then # $arg is the command word
2018-01-12 21:46:11 +08:00
if ( ( ${ +precommand_options[ $arg ] } ) ) && _zsh_highlight_main__is_runnable $arg ; then
2018-10-18 10:38:17 +08:00
style = precommand
2018-01-12 21:32:30 +08:00
flags_with_argument = ${ precommand_options [ $arg ]% : * }
flags_sans_argument = ${ precommand_options [ $arg ]#* : }
2015-10-28 19:22:48 +08:00
next_word = ${ next_word // : regular : / }
2015-10-28 19:37:45 +08:00
next_word += ':sudo_opt:'
next_word += ':start:'
2011-09-12 22:48:18 +08:00
else
2011-06-13 04:57:14 +08:00
case $res in
2016-09-22 12:52:32 +08:00
reserved) # reserved word
style = reserved-word
2018-10-24 00:36:14 +08:00
# Match braces and handle special cases.
2016-09-25 17:20:40 +08:00
case $arg in
2019-12-27 21:47:31 +08:00
( time| nocorrect)
next_word = ${ next_word // : regular : / }
next_word += ':start:'
; ;
2016-09-25 17:20:40 +08:00
( $'\x7b' )
braces_stack = 'Y' " $braces_stack "
; ;
( $'\x7d' )
# We're at command word, so no need to check $right_brace_is_recognised_everywhere
2018-03-11 04:31:15 +08:00
_zsh_highlight_main__stack_pop 'Y' reserved-word
2016-09-25 17:20:40 +08:00
if [ [ $style = = reserved-word ] ] ; then
next_word += ':always:'
fi
; ;
2018-10-21 12:47:37 +08:00
( $'\x5b\x5b' )
braces_stack = 'T' " $braces_stack "
; ;
2016-09-25 17:20:40 +08:00
( 'do' )
braces_stack = 'D' " $braces_stack "
; ;
( 'done' )
2018-03-11 04:31:15 +08:00
_zsh_highlight_main__stack_pop 'D' reserved-word
2016-09-25 17:20:40 +08:00
; ;
2017-03-06 01:33:36 +08:00
( 'if' )
braces_stack = ':?' " $braces_stack "
; ;
( 'then' )
2018-03-11 04:31:15 +08:00
_zsh_highlight_main__stack_pop ':' reserved-word
2017-03-06 01:33:36 +08:00
; ;
( 'elif' )
if [ [ ${ braces_stack [1] } = = '?' ] ] ; then
braces_stack = ':' " $braces_stack "
else
style = unknown-token
fi
; ;
( 'else' )
if [ [ ${ braces_stack [1] } = = '?' ] ] ; then
:
else
style = unknown-token
fi
; ;
( 'fi' )
2018-03-11 04:31:15 +08:00
_zsh_highlight_main__stack_pop '?'
2017-03-06 01:33:36 +08:00
; ;
2016-09-25 17:20:40 +08:00
( 'foreach' )
braces_stack = '$' " $braces_stack "
; ;
( 'end' )
2018-03-11 04:31:15 +08:00
_zsh_highlight_main__stack_pop '$' reserved-word
2016-09-25 17:20:40 +08:00
; ;
2018-10-24 00:34:35 +08:00
( 'repeat' )
# skip the repeat-count word
in_redirection = 2
# The redirection mechanism assumes $this_word describes the word
# following the redirection. Make it so.
#
# That word can be a command word with shortloops (`repeat 2 ls`)
# or a command separator (`repeat 2; ls` or `repeat 2; do ls; done`).
#
# The repeat-count word will be handled like a redirection target.
this_word = ':start::regular:'
2018-10-24 00:36:14 +08:00
; ;
( '!' )
if [ [ $this_word != *':start_of_pipeline:' * ] ] ; then
style = unknown-token
else
# '!' reserved word at start of pipeline; style already set above
fi
; ;
2016-09-25 17:20:40 +08:00
esac
2016-09-22 12:52:32 +08:00
; ;
2016-03-29 05:42:31 +08:00
'suffix alias' ) style = suffix-alias; ;
2018-10-01 23:54:25 +08:00
alias ) :; ;
2018-10-21 12:47:37 +08:00
builtin ) style = builtin
[ [ $arg = = $'\x5b' ] ] && braces_stack = 'Q' " $braces_stack "
; ;
2016-03-29 05:42:31 +08:00
function ) style = function ; ;
command ) style = command; ;
hashed) style = hashed-command; ;
none) if _zsh_highlight_main_highlighter_check_assign; then
2018-10-02 01:55:14 +08:00
_zsh_highlight_main_add_region_highlight $start_pos $end_pos assign
2018-03-22 08:51:48 +08:00
local i = $(( arg[ ( i) = ] + 1 ))
if [ [ $arg [ i] = = '(' ] ] ; then
2015-11-17 02:44:44 +08:00
in_array_assignment = true
else
2015-08-28 17:44:14 +08:00
# assignment to a scalar parameter.
# (For array assignments, the command doesn't start until the ")" token.)
2018-10-22 23:44:55 +08:00
#
# Discard :start_of_pipeline:, if present, as '!' is not valid
# after assignments.
2015-10-27 18:19:51 +08:00
next_word += ':start:'
2019-11-08 09:50:17 +08:00
if ( ( i <= $# arg ) ) ; then
2018-04-14 21:31:15 +08:00
( ) {
local highlight_glob = false
[ [ $zsyh_user_options [ globassign] = = on ] ] && highlight_glob = true
_zsh_highlight_main_highlighter_highlight_argument $i
}
2018-03-22 08:59:53 +08:00
fi
2015-08-28 17:44:14 +08:00
fi
2018-10-02 01:55:14 +08:00
continue
2016-03-13 10:47:25 +08:00
elif [ [ $arg [ 0,1] = $histchars [ 0,1] ] ] && ( ( $# arg[ 0,2] = = 2 ) ) ; then
2016-04-25 05:04:32 +08:00
style = history-expansion
2016-03-13 10:47:25 +08:00
elif [ [ $arg [ 0,1] = = $histchars [ 2,2] ] ] ; then
2016-04-25 05:04:32 +08:00
style = history-expansion
2015-09-25 02:24:07 +08:00
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.
2018-10-02 01:55:14 +08:00
_zsh_highlight_main_add_region_highlight $start_pos $(( start_pos + 2 )) reserved-word
2015-10-31 01:28:48 +08:00
if [ [ $arg [ -2,-1] = = '))' ] ] ; then
2018-10-02 01:55:14 +08:00
_zsh_highlight_main_add_region_highlight $(( end_pos - 2 )) $end_pos reserved-word
2015-10-31 01:28:48 +08:00
fi
2018-10-02 01:55:14 +08:00
continue
2016-09-22 12:52:32 +08:00
elif [ [ $arg = = '()' ] ] ; then
2015-10-27 21:10:01 +08:00
# anonymous function
2016-09-22 12:52:32 +08:00
style = reserved-word
elif [ [ $arg = = $'\x28' ] ] ; then
2015-10-30 17:38:32 +08:00
# subshell
2016-04-25 05:04:32 +08:00
style = reserved-word
2016-09-22 12:52:32 +08:00
braces_stack = 'R' " $braces_stack "
2016-09-25 20:06:39 +08:00
elif [ [ $arg = = $'\x29' ] ] ; then
2018-03-11 05:03:02 +08:00
# end of subshell or command substitution
if _zsh_highlight_main__stack_pop 'S' ; then
REPLY = $start_pos
reply = ( $list_highlights )
2018-04-15 02:38:02 +08:00
return 0
2018-03-11 05:03:02 +08:00
fi
2018-03-11 04:31:15 +08:00
_zsh_highlight_main__stack_pop 'R' reserved-word
2011-06-13 04:57:14 +08:00
else
2018-03-22 08:24:53 +08:00
if _zsh_highlight_main_highlighter_check_path $arg ; then
2016-05-27 18:21:28 +08:00
style = $REPLY
2015-09-25 20:50:39 +08:00
else
2016-04-25 05:04:32 +08:00
style = unknown-token
2015-09-25 20:50:39 +08:00
fi
2011-06-13 04:57:14 +08:00
fi
; ;
2018-03-13 08:34:25 +08:00
*) _zsh_highlight_main_add_region_highlight $start_pos $end_pos arg0_$res
2018-10-02 01:55:14 +08:00
continue
2016-03-29 05:42:31 +08:00
; ;
2011-06-13 04:57:14 +08:00
esac
2011-09-12 22:48:18 +08:00
fi
2018-10-02 06:24:03 +08:00
if [ [ -n ${ (M)ZSH_HIGHLIGHT_TOKENS_CONTROL_FLOW : # " $arg " } ] ] ; then
2018-10-22 23:44:55 +08:00
next_word = ':start::start_of_pipeline:'
2018-10-02 06:24:03 +08:00
fi
2018-10-02 06:06:33 +08:00
else # $arg is a non-command word
2011-06-13 04:57:14 +08:00
case $arg in
2015-11-17 02:44:44 +08:00
$'\x29' ) # subshell or end of array assignment
if $in_array_assignment ; then
2016-04-25 05:04:32 +08:00
style = assign
2015-11-17 02:44:44 +08:00
in_array_assignment = false
2016-06-03 12:16:13 +08:00
next_word += ':start:'
2018-10-02 06:18:52 +08:00
elif ( ( in_redirection ) ) ; then
style = unknown-token
2015-11-17 02:44:44 +08:00
else
2018-03-11 05:03:02 +08:00
if _zsh_highlight_main__stack_pop 'S' ; then
REPLY = $start_pos
reply = ( $list_highlights )
2018-04-15 02:38:02 +08:00
return 0
2018-03-11 05:03:02 +08:00
fi
2018-03-11 04:31:15 +08:00
_zsh_highlight_main__stack_pop 'R' reserved-word
2015-11-17 02:44:44 +08:00
fi ; ;
2016-09-23 01:56:00 +08:00
$'\x28\x29' ) # possibly a function definition
2018-10-02 06:18:52 +08:00
if ( ( in_redirection ) ) || $in_array_assignment ; then
style = unknown-token
else
if [ [ $zsyh_user_options [ multifuncdef] = = on ] ] || false # TODO: or if the previous word was a command word
then
2018-10-22 23:44:55 +08:00
next_word += ':start::start_of_pipeline:'
2018-10-02 06:18:52 +08:00
fi
style = reserved-word
2016-09-23 01:56:00 +08:00
fi
; ;
2018-01-15 11:27:07 +08:00
*) if false; then
elif [ [ $arg = $'\x7d' ] ] && $right_brace_is_recognised_everywhere ; then
2018-10-22 12:27:18 +08:00
# Parsing rule: {
2018-01-15 11:27:07 +08:00
#
# Additionally, `tt(})' is recognized in any position if neither the
# tt(IGNORE_BRACES) option nor the tt(IGNORE_CLOSE_BRACES) option is set.
2018-10-02 06:18:52 +08:00
if ( ( in_redirection ) ) || $in_array_assignment ; then
style = unknown-token
else
_zsh_highlight_main__stack_pop 'Y' reserved-word
if [ [ $style = = reserved-word ] ] ; then
next_word += ':always:'
fi
2016-09-25 15:56:28 +08:00
fi
2016-03-13 10:47:25 +08:00
elif [ [ $arg [ 0,1] = $histchars [ 0,1] ] ] && ( ( $# arg[ 0,2] = = 2 ) ) ; then
2016-04-25 05:04:32 +08:00
style = history-expansion
2018-10-21 12:47:37 +08:00
elif [ [ $arg = = $'\x5d\x5d' ] ] && _zsh_highlight_main__stack_pop 'T' reserved-word; then
:
elif [ [ $arg = = $'\x5d' ] ] && _zsh_highlight_main__stack_pop 'Q' builtin; then
:
2011-06-13 04:57:14 +08:00
else
2018-10-22 12:30:43 +08:00
_zsh_highlight_main_highlighter_highlight_argument 1 $(( 1 != in_redirection ))
2018-10-02 01:55:14 +08:00
continue
2011-06-13 04:57:14 +08:00
fi
; ;
esac
fi
2018-10-02 01:55:14 +08:00
_zsh_highlight_main_add_region_highlight $start_pos $end_pos $style
2011-06-13 04:57:14 +08:00
done
2018-10-22 20:50:53 +08:00
( ( in_alias = = 1 ) ) && in_alias = 0 _zsh_highlight_main_add_region_highlight $start_pos $end_pos $alias_style
2018-10-21 01:37:49 +08:00
[ [ " $proc_buf " = ( #b)(#s)(([[:space:]]|\\$'\n')#) ]]
REPLY = $(( end_pos + ${# match [1] } - 1 ))
2018-03-11 04:58:01 +08:00
reply = ( $list_highlights )
2018-04-15 02:38:02 +08:00
return $(( $# braces_stack > 0 ))
2011-06-13 04:57:14 +08:00
}
2015-09-29 09:20:54 +08:00
# Check if $arg is variable assignment
2011-06-13 04:57:14 +08:00
_zsh_highlight_main_highlighter_check_assign( )
{
setopt localoptions extended_glob
2016-06-03 10:44:38 +08:00
[ [ $arg = = [ [ :alpha:] _] [ [ :alnum:] _] #(|\[*\])(|[+])=* ]] ||
[ [ $arg = = [ 0-9] ##(|[+])=* ]]
2011-06-13 04:57:14 +08:00
}
2015-11-22 23:50:26 +08:00
_zsh_highlight_main_highlighter_highlight_path_separators( )
{
local pos style_pathsep
2018-01-19 04:52:31 +08:00
style_pathsep = $1 _pathseparator
2018-01-19 04:59:24 +08:00
reply = ( )
2018-01-19 04:52:31 +08:00
[ [ -z " $ZSH_HIGHLIGHT_STYLES [ $style_pathsep ] " || " $ZSH_HIGHLIGHT_STYLES [ $1 ] " = = " $ZSH_HIGHLIGHT_STYLES [ $style_pathsep ] " ] ] && return 0
2015-11-22 23:50:26 +08:00
for ( ( pos = start_pos; $pos <= end_pos; pos++ ) ) ; do
if [ [ $BUFFER [ pos] = = / ] ] ; then
2018-01-19 04:59:24 +08:00
reply += ( $(( pos - 1 )) $pos $style_pathsep )
2015-11-22 23:50:26 +08:00
fi
done
}
2018-03-22 08:24:53 +08:00
# Check if $1 is a path.
2016-05-27 18:21:28 +08:00
# If yes, return 0 and in $REPLY the style to use.
# Else, return non-zero (and the contents of $REPLY is undefined).
2011-06-13 04:57:14 +08:00
_zsh_highlight_main_highlighter_check_path( )
{
2018-10-10 12:25:31 +08:00
_zsh_highlight_main_highlighter_expand_path " $1 "
2018-01-22 04:48:47 +08:00
local expanded_path = " $REPLY " tmp_path
2015-09-29 09:49:19 +08:00
2016-05-27 18:21:28 +08:00
REPLY = path
2011-09-22 21:18:24 +08:00
[ [ -z $expanded_path ] ] && return 1
2015-09-30 09:26:27 +08:00
2018-01-22 04:48:47 +08:00
# Check if this is a blacklisted path
if [ [ $expanded_path [ 1] = = / ] ] ; then
tmp_path = $expanded_path
else
tmp_path = $PWD /$expanded_path
fi
tmp_path = $tmp_path :a
while [ [ $tmp_path != / ] ] ; do
2019-07-24 20:27:54 +08:00
[ [ -n ${ (M)ZSH_HIGHLIGHT_DIRS_BLACKLIST : # $tmp_path } ] ] && return 1
2018-01-22 04:48:47 +08:00
tmp_path = $tmp_path :h
done
2018-08-30 08:12:55 +08:00
[ [ -L $expanded_path ] ] && return 0
[ [ -e $expanded_path ] ] && return 0
2013-08-07 08:21:15 +08:00
# Search the path in CDPATH
2013-10-21 20:18:38 +08:00
local cdpath_dir
2013-08-07 08:21:15 +08:00
for cdpath_dir in $cdpath ; do
[ [ -e " $cdpath_dir / $expanded_path " ] ] && return 0
done
2015-09-30 09:26:27 +08:00
2018-03-22 08:24:53 +08:00
# If dirname($1) doesn't exist, neither does $1.
2016-06-01 13:54:30 +08:00
[ [ ! -d ${ expanded_path : h } ] ] && return 1
2015-09-30 09:26:27 +08:00
# If this word ends the buffer, check if it's the prefix of a valid path.
2018-03-21 10:03:22 +08:00
if ( ( has_end && ( len = = end_pos) ) ) &&
2016-03-30 03:56:57 +08:00
[ [ $WIDGET != zle-line-finish ] ] ; then
2013-07-25 10:41:09 +08:00
local -a tmp
tmp = ( ${ expanded_path } *( N) )
2016-05-27 18:21:28 +08:00
( ( $# tmp > 0 ) ) && REPLY = path_prefix && return 0
2013-07-25 10:41:09 +08:00
fi
2015-09-30 09:26:27 +08:00
# It's not a path.
2011-06-13 04:57:14 +08:00
return 1
}
2018-03-22 08:43:19 +08:00
# Highlight an argument and possibly special chars in quotes starting at $1 in $arg
# This command will at least highlight $1 to end_pos with the default style
2018-10-02 06:14:56 +08:00
# If $2 is set to 0, the argument cannot be highlighted as an option.
2017-11-15 12:45:07 +08:00
_zsh_highlight_main_highlighter_highlight_argument( )
{
2018-10-02 06:14:56 +08:00
local base_style = default i = $1 option_eligible = ${ 2 :- 1 } path_eligible = 1 ret start style
2018-03-11 05:03:02 +08:00
local -a highlights
2017-11-15 12:45:07 +08:00
2018-01-19 07:25:42 +08:00
local -a match mbegin mend
local MATCH; integer MBEGIN MEND
2018-03-22 08:43:19 +08:00
case " $arg [i] " in
2019-01-07 12:22:13 +08:00
'%' )
if [ [ $arg [ i+1] = = '?' ] ] ; then
( ( i += 2 ) )
fi
; ;
2018-03-15 11:46:49 +08:00
'-' )
2018-10-02 06:14:56 +08:00
if ( ( option_eligible ) ) ; then
if [ [ $arg [ i+1] = = - ] ] ; then
base_style = double-hyphen-option
else
base_style = single-hyphen-option
fi
path_eligible = 0
2018-03-15 11:46:49 +08:00
fi
; ;
2018-03-15 11:47:33 +08:00
'=' )
2018-03-22 08:43:19 +08:00
if [ [ $arg [ i+1] = = $'\x28' ] ] ; then
2018-03-15 11:47:33 +08:00
( ( i += 2 ) )
2019-11-08 09:50:17 +08:00
_zsh_highlight_main_highlighter_highlight_list $(( start_pos + i - 1 )) S $has_end $arg [ i,-1]
2018-04-15 03:00:03 +08:00
ret = $?
2018-03-15 11:47:33 +08:00
( ( i += REPLY ) )
2018-04-15 03:00:03 +08:00
highlights += (
$(( start_pos + $1 - 1 )) $(( start_pos + i )) process-substitution
$(( start_pos + $1 - 1 )) $(( start_pos + $1 + 1 )) process-substitution-delimiter
$reply
)
if ( ( ret = = 0 ) ) ; then
highlights += ( $(( start_pos + i - 1 )) $(( start_pos + i )) process-substitution-delimiter)
fi
2018-03-15 11:47:33 +08:00
fi
2018-03-15 11:46:49 +08:00
esac
2018-01-15 11:24:44 +08:00
2019-11-08 09:50:17 +08:00
for ( ( ; i <= $# arg ; i += 1 ) ) ; do
2017-11-15 12:45:07 +08:00
case " $arg [ $i ] " in
"\\" ) ( ( i += 1 ) ) ; continue ; ;
2018-01-19 04:59:24 +08:00
"'" )
_zsh_highlight_main_highlighter_highlight_single_quote $i
( ( i = REPLY ) )
highlights += ( $reply )
; ;
'"' )
_zsh_highlight_main_highlighter_highlight_double_quote $i
( ( i = REPLY ) )
highlights += ( $reply )
; ;
'`' )
_zsh_highlight_main_highlighter_highlight_backtick $i
( ( i = REPLY ) )
highlights += ( $reply )
; ;
2017-11-15 12:45:07 +08:00
'$' )
2019-06-17 04:21:58 +08:00
if [ [ $arg [ i+1] != "'" ] ] ; then
path_eligible = 0
fi
2017-11-15 12:45:07 +08:00
if [ [ $arg [ i+1] = = "'" ] ] ; then
_zsh_highlight_main_highlighter_highlight_dollar_quote $i
( ( i = REPLY ) )
2018-01-19 04:59:24 +08:00
highlights += ( $reply )
2018-02-12 01:07:08 +08:00
continue
2018-03-11 05:03:02 +08:00
elif [ [ $arg [ i+1] = = $'\x28' ] ] ; then
start = $i
( ( i += 2 ) )
2019-11-08 09:50:17 +08:00
_zsh_highlight_main_highlighter_highlight_list $(( start_pos + i - 1 )) S $has_end $arg [ i,-1]
2018-04-15 03:00:03 +08:00
ret = $?
2018-03-11 05:03:02 +08:00
( ( i += REPLY ) )
2018-04-15 03:00:03 +08:00
highlights += (
2018-10-13 21:14:42 +08:00
$(( start_pos + start - 1 )) $(( start_pos + i )) command-substitution-unquoted
$(( start_pos + start - 1 )) $(( start_pos + start + 1 )) command-substitution-delimiter-unquoted
2018-04-15 03:00:03 +08:00
$reply
)
if ( ( ret = = 0 ) ) ; then
2018-10-13 21:14:42 +08:00
highlights += ( $(( start_pos + i - 1 )) $(( start_pos + i )) command-substitution-delimiter-unquoted)
2018-04-15 03:00:03 +08:00
fi
2018-03-11 05:03:02 +08:00
continue
2018-02-12 01:07:08 +08:00
fi
while [ [ $arg [ i+1] = = [ \^ = ~#+] ] ] ; do
( ( i += 1 ) )
done
2018-02-12 01:08:27 +08:00
if [ [ $arg [ i+1] = = [ *@#?$! -] ] ] ; then
2017-11-15 12:45:07 +08:00
( ( i += 1 ) )
fi ; ;
2018-03-11 05:03:25 +08:00
[ \< \> ] )
if [ [ $arg [ i+1] = = $'\x28' ] ] ; then # \x28 = open paren
start = $i
( ( i += 2 ) )
2019-11-08 09:50:17 +08:00
_zsh_highlight_main_highlighter_highlight_list $(( start_pos + i - 1 )) S $has_end $arg [ i,-1]
2018-04-15 03:00:03 +08:00
ret = $?
2018-03-11 05:03:25 +08:00
( ( i += REPLY ) )
2018-04-15 03:00:03 +08:00
highlights += (
$(( start_pos + start - 1 )) $(( start_pos + i )) process-substitution
$(( start_pos + start - 1 )) $(( start_pos + start + 1 )) process-substitution-delimiter
$reply
)
if ( ( ret = = 0 ) ) ; then
highlights += ( $(( start_pos + i - 1 )) $(( start_pos + i )) process-substitution-delimiter)
fi
2018-03-11 05:03:25 +08:00
continue
fi
; |
2018-01-19 07:25:42 +08:00
*)
2018-01-19 10:08:14 +08:00
if $highlight_glob && [ [ ${ arg [ $i ] } = ~ ^[ *?] || ${ arg : $i -1 } = ~ ^\< [ 0-9] *-[ 0-9] *\> ] ] ; then
2018-01-19 10:10:25 +08:00
highlights += ( $(( start_pos + i - 1 )) $(( start_pos + i + $# MATCH - 1 )) globbing)
( ( i += $# MATCH - 1 ) )
2017-12-25 14:03:51 +08:00
path_eligible = 0
2018-01-19 07:25:42 +08:00
else
continue
fi
; ;
2017-11-15 12:45:07 +08:00
esac
done
2017-12-25 14:03:51 +08:00
2019-11-08 09:50:17 +08:00
if ( ( path_eligible ) ) && _zsh_highlight_main_highlighter_check_path $arg [ $1 ,-1] ; then
2018-01-19 05:01:06 +08:00
base_style = $REPLY
_zsh_highlight_main_highlighter_highlight_path_separators $base_style
2018-01-19 04:59:24 +08:00
highlights += ( $reply )
2017-12-25 14:03:51 +08:00
fi
2018-01-19 04:59:24 +08:00
2018-03-22 08:43:19 +08:00
highlights = ( $(( start_pos + $1 - 1 )) $end_pos $base_style $highlights )
2018-01-19 04:59:24 +08:00
_zsh_highlight_main_add_many_region_highlights $highlights
2017-11-15 12:45:07 +08:00
}
# Quote Helper Functions
#
# $arg is expected to be set to the current argument
# $start_pos is expected to be set to the start of $arg in $BUFFER
# $1 is the index in $arg which starts the quote
# $REPLY is returned as the end of quote index in $arg
2018-01-19 04:59:24 +08:00
# $reply is returned as an array of region_highlight additions
2017-11-15 12:45:07 +08:00
2017-11-15 12:43:40 +08:00
# Highlight single-quoted strings
_zsh_highlight_main_highlighter_highlight_single_quote( )
{
2017-11-16 12:38:19 +08:00
local arg1 = $1 i q = \' style
2017-11-15 12:43:40 +08:00
i = $arg [ ( ib:arg1+1:) $q ]
2018-01-19 04:59:24 +08:00
reply = ( )
2017-11-15 12:43:40 +08:00
if [ [ $zsyh_user_options [ rcquotes] = = on ] ] ; then
while [ [ $arg [ i+1] = = "'" ] ] ; do
2018-01-19 04:59:24 +08:00
reply += ( $(( start_pos + i - 1 )) $(( start_pos + i + 1 )) rc-quote)
2017-11-15 12:43:40 +08:00
( ( i++ ) )
i = $arg [ ( ib:i+1:) $q ]
done
fi
2017-11-16 12:38:19 +08:00
if [ [ $arg [ i] = = "'" ] ] ; then
style = single-quoted-argument
else
2018-02-07 08:54:48 +08:00
# If unclosed, i points past the end
( ( i-- ) )
2017-11-16 12:38:19 +08:00
style = single-quoted-argument-unclosed
fi
2018-01-19 22:00:25 +08:00
reply = ( $(( start_pos + arg1 - 1 )) $(( start_pos + i )) $style $reply )
2017-11-15 12:43:40 +08:00
REPLY = $i
}
2011-06-13 04:57:14 +08:00
# Highlight special chars inside double-quoted strings
2017-11-15 12:41:27 +08:00
_zsh_highlight_main_highlighter_highlight_double_quote( )
2011-06-13 04:57:14 +08:00
{
2018-10-13 23:40:13 +08:00
local -a breaks match mbegin mend saved_reply
local MATCH; integer last_break = $(( start_pos + $1 - 1 )) MBEGIN MEND
2018-04-15 03:00:03 +08:00
local i j k ret style
2018-01-19 04:59:24 +08:00
reply = ( )
2017-11-15 12:41:27 +08:00
2019-11-08 09:50:17 +08:00
for ( ( i = $1 + 1 ; i <= $# arg ; i += 1 ) ) ; do
2011-06-13 04:57:14 +08:00
( ( j = i + start_pos - 1 ) )
( ( k = j + 1 ) )
case " $arg [ $i ] " in
2017-11-15 12:45:07 +08:00
'"' ) break; ;
2018-01-15 11:28:42 +08:00
'`' ) saved_reply = ( $reply )
_zsh_highlight_main_highlighter_highlight_backtick $i
( ( i = REPLY ) )
reply = ( $saved_reply $reply )
continue
; ;
2016-04-25 05:04:32 +08:00
'$' ) style = dollar-double-quoted-argument
2015-09-18 04:07:37 +08:00
# 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
2015-11-17 08:37:58 +08:00
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
2016-09-25 22:35:16 +08:00
elif [ [ $arg [ i+1] = = '$' ] ] ; then
# $$ - pid
( ( k += 1 ) ) # highlight both dollar signs
( ( i += 1 ) ) # don't consider the second one as introducing another parameter expansion
2016-09-25 22:51:02 +08:00
elif [ [ $arg [ i+1] = = [ -#*@?] ] ] ; then
# $#, $*, $@, $?, $- - like $$ above
( ( k += 1 ) ) # highlight both dollar signs
( ( i += 1 ) ) # don't consider the second one as introducing another parameter expansion
2016-09-25 22:31:31 +08:00
elif [ [ $arg [ i+1] = = $'\x28' ] ] ; then
2018-10-13 23:40:13 +08:00
breaks += ( $last_break $(( start_pos + i - 1 )) )
2018-03-11 05:03:02 +08:00
( ( i += 2 ) )
saved_reply = ( $reply )
2019-11-08 09:50:17 +08:00
_zsh_highlight_main_highlighter_highlight_list $(( start_pos + i - 1 )) S $has_end $arg [ i,-1]
2018-04-15 03:00:03 +08:00
ret = $?
2018-03-11 05:03:02 +08:00
( ( i += REPLY ) )
2018-10-13 23:40:13 +08:00
last_break = $(( start_pos + i ))
2018-04-15 03:00:03 +08:00
reply = (
$saved_reply
2018-10-13 21:14:42 +08:00
$j $(( start_pos + i )) command-substitution-quoted
$j $(( j + 2 )) command-substitution-delimiter-quoted
2018-04-15 03:00:03 +08:00
$reply
)
if ( ( ret = = 0 ) ) ; then
2018-10-13 21:14:42 +08:00
reply += ( $(( start_pos + i - 1 )) $(( start_pos + i )) command-substitution-delimiter-quoted)
2018-04-15 03:00:03 +08:00
fi
2018-03-11 05:03:02 +08:00
continue
2015-09-25 02:57:56 +08:00
else
continue
2015-09-18 04:07:37 +08:00
fi
2013-07-29 21:33:34 +08:00
; ;
2016-04-25 05:04:32 +08:00
"\\" ) style = back-double-quoted-argument
2016-10-20 07:27:54 +08:00
if [ [ \\ \` \" \$ ${ histchars [1] } = = *$arg [ $i +1] * ] ] ; then
2015-09-26 05:18:37 +08:00
( ( k += 1 ) ) # Color following char too.
( ( i += 1 ) ) # Skip parsing the escaped char.
else
continue
fi
; ;
2016-11-22 15:09:29 +08:00
( $histchars [ 1] ) # ! - may be a history expansion
if [ [ $arg [ i+1] != ( '=' | $'\x28' | $'\x7b' | [ [ :blank:] ] ) ] ] ; then
style = history-expansion
else
continue
fi
; ;
2015-09-26 05:18:37 +08:00
*) continue ; ;
esac
2018-01-19 04:59:24 +08:00
reply += ( $j $k $style )
2015-09-26 05:18:37 +08:00
done
2017-11-15 12:41:27 +08:00
2017-11-16 12:38:19 +08:00
if [ [ $arg [ i] = = '"' ] ] ; then
style = double-quoted-argument
else
2018-02-07 08:54:48 +08:00
# If unclosed, i points past the end
( ( i-- ) )
2017-11-16 12:38:19 +08:00
style = double-quoted-argument-unclosed
fi
2018-10-13 23:40:13 +08:00
( ( last_break != start_pos + i ) ) && breaks += ( $last_break $(( start_pos + i )) )
saved_reply = ( $reply )
reply = ( )
for 1 2 in $breaks ; do
2019-07-09 06:05:42 +08:00
( ( $1 != $2 ) ) && reply += ( $1 $2 $style )
2018-10-13 23:40:13 +08:00
done
reply += ( $saved_reply )
2017-11-15 12:45:07 +08:00
REPLY = $i
2015-09-26 05:18:37 +08:00
}
# Highlight special chars inside dollar-quoted strings
2017-11-15 12:41:27 +08:00
_zsh_highlight_main_highlighter_highlight_dollar_quote( )
2015-09-26 05:18:37 +08:00
{
2018-01-19 04:59:24 +08:00
local -a match mbegin mend
2015-10-30 15:09:46 +08:00
local MATCH; integer MBEGIN MEND
2015-09-26 05:18:37 +08:00
local i j k style
local AA
integer c
2018-01-19 04:59:24 +08:00
reply = ( )
2017-11-15 12:41:27 +08:00
2019-11-08 09:50:17 +08:00
for ( ( i = $1 + 2 ; i <= $# arg ; i += 1 ) ) ; do
2015-09-26 05:18:37 +08:00
( ( j = i + start_pos - 1 ) )
( ( k = j + 1 ) )
case " $arg [ $i ] " in
2017-11-15 12:45:07 +08:00
"'" ) break; ;
2016-04-25 05:04:32 +08:00
"\\" ) style = back-dollar-quoted-argument
2019-11-08 09:50:17 +08:00
for ( ( c = i + 1 ; c <= $# arg ; c += 1 ) ) ; do
2015-09-26 05:36:20 +08:00
[ [ " $arg [ $c ] " != ( [ 0-9xXuUa-fA-F] ) ] ] && break
2013-07-29 21:33:34 +08:00
done
AA = $arg [ $i +1,$c -1]
2013-08-09 14:24:14 +08:00
# Matching for HEX and OCT values like \0xA6, \xA6 or \012
2015-09-26 05:36:20 +08:00
if [ [ " $AA " = ~ "^(x|X)[0-9a-fA-F]{1,2}"
2015-09-26 05:36:20 +08:00
|| " $AA " = ~ "^[0-7]{1,3}"
2015-09-26 05:36:20 +08:00
|| " $AA " = ~ "^u[0-9a-fA-F]{1,4}"
|| " $AA " = ~ "^U[0-9a-fA-F]{1,8}"
] ] ; then
2013-07-29 21:33:34 +08:00
( ( k += $# MATCH ) )
( ( i += $# MATCH ) )
else
2015-09-26 05:36:20 +08:00
if ( ( $# arg > $i +1 ) ) && [ [ $arg [ $i +1] = = [ xXuU] ] ] ; then
# \x not followed by hex digits is probably an error
2016-04-25 05:04:32 +08:00
style = unknown-token
2015-09-26 05:36:20 +08:00
fi
2013-07-29 21:33:34 +08:00
( ( k += 1 ) ) # Color following char too.
( ( i += 1 ) ) # Skip parsing the escaped char.
fi
2011-06-13 04:57:14 +08:00
; ;
2015-09-18 04:07:37 +08:00
*) continue ; ;
2013-07-29 21:33:34 +08:00
2011-06-13 04:57:14 +08:00
esac
2018-01-19 04:59:24 +08:00
reply += ( $j $k $style )
2011-06-13 04:57:14 +08:00
done
2017-11-15 12:41:27 +08:00
2017-11-16 12:38:19 +08:00
if [ [ $arg [ i] = = "'" ] ] ; then
style = dollar-quoted-argument
else
2018-02-07 08:54:48 +08:00
# If unclosed, i points past the end
( ( i-- ) )
2017-11-16 12:38:19 +08:00
style = dollar-quoted-argument-unclosed
fi
2018-01-19 22:00:25 +08:00
reply = ( $(( start_pos + $1 - 1 )) $(( start_pos + i )) $style $reply )
2017-11-15 12:45:07 +08:00
REPLY = $i
2011-06-13 04:57:14 +08:00
}
2015-09-29 09:49:19 +08:00
2018-03-13 08:29:09 +08:00
# Highlight backtick substitutions
2018-01-08 12:06:39 +08:00
_zsh_highlight_main_highlighter_highlight_backtick( )
{
2018-03-13 08:29:09 +08:00
# buf is the contents of the backticks with a layer of backslashes removed.
# last is the index of arg for the start of the string to be copied into buf.
# It is either one past the beginning backtick or one past the last backslash.
# offset is a count of consumed \ (the delta between buf and arg).
# offsets is an array indexed by buf offset of when the delta between buf and arg changes.
# It is sparse, so search backwards to the last value
2018-04-15 03:00:03 +08:00
local buf highlight style = back-quoted-argument-unclosed style_end
2018-03-13 08:29:09 +08:00
local -i arg1 = $1 end_ i = $1 last offset = 0 start subshell_has_end = 0
2018-04-15 03:00:03 +08:00
local -a highlight_zone highlights offsets
2018-01-19 04:59:24 +08:00
reply = ( )
2018-01-08 12:06:39 +08:00
2018-03-13 08:29:09 +08:00
last = $(( arg1 + 1 ))
# Remove one layer of backslashes and find the end
while i = $arg [ ( ib:i+1:) [ \\ \\ \` ] ] ; do # find the next \ or `
2019-11-08 09:50:17 +08:00
if ( ( i > $# arg ) ) ; then
2018-03-13 08:29:09 +08:00
buf = $buf $arg [ last,i]
offsets[ i-arg1-offset] = '' # So we never index past the end
( ( i-- ) )
subshell_has_end = $(( has_end & & ( start_pos + i = = len) ))
break
fi
if [ [ $arg [ i] = = '\' ] ] ; then
( ( i++ ) )
# POSIX XCU 2.6.3
if [ [ $arg [ i] = = ( '$' | '`' | '\' ) ] ] ; then
buf = $buf $arg [ last,i-2]
( ( offset++ ) )
# offsets is relative to buf, so adjust by -arg1
offsets[ i-arg1-offset] = $offset
else
buf = $buf $arg [ last,i-1]
fi
else # it's an unquoted ` and this is the end
style = back-quoted-argument
2018-04-15 03:00:03 +08:00
style_end = back-quoted-argument-delimiter
2018-03-13 08:29:09 +08:00
buf = $buf $arg [ last,i-1]
offsets[ i-arg1-offset] = '' # So we never index past the end
break
fi
last = $i
done
_zsh_highlight_main_highlighter_highlight_list 0 '' $subshell_has_end $buf
# Munge the reply to account for removed backslashes
for start end_ highlight in $reply ; do
start = $(( start_pos + arg1 + start + offsets[ ( Rb:start:) ?*] ))
end_ = $(( start_pos + arg1 + end_ + offsets[ ( Rb:end_:) ?*] ))
highlights += ( $start $end_ $highlight )
if [ [ $highlight = = back-quoted-argument-unclosed && $style = = back-quoted-argument ] ] ; then
# An inner backtick command substitution is unclosed, but this level is closed
2018-04-15 03:00:03 +08:00
style_end = unknown-token
2018-03-13 08:29:09 +08:00
fi
done
2018-04-15 03:00:03 +08:00
reply = (
$(( start_pos + arg1 - 1 )) $(( start_pos + i )) $style
$(( start_pos + arg1 - 1 )) $(( start_pos + arg1 )) back-quoted-argument-delimiter
$highlights
)
if ( ( $# style_end ) ) ; then
reply += ( $(( start_pos + i - 1 )) $(( start_pos + i )) $style_end )
fi
2018-01-08 12:06:39 +08:00
REPLY = $i
}
2015-09-29 09:49:19 +08:00
# 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( )
{
2016-07-21 12:01:50 +08:00
( ( $# = = 1 ) ) || print -r -- >& 2 "zsh-syntax-highlighting: BUG: _zsh_highlight_main_highlighter_expand_path: called without argument"
2015-09-29 09:49:19 +08:00
# 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
2017-12-12 21:58:35 +08:00
: ${ REPLY : = ${ (Q) ${ ~1 } } }
2015-09-29 09:49:19 +08:00
}
2016-03-29 07:33:00 +08:00
# -------------------------------------------------------------------------------------------------
# Main highlighter initialization
# -------------------------------------------------------------------------------------------------
_zsh_highlight_main__precmd_hook( ) {
_zsh_highlight_main__command_type_cache = ( )
}
2018-03-07 01:31:50 +08:00
autoload -Uz add-zsh-hook
2016-03-29 07:33:00 +08:00
if add-zsh-hook precmd _zsh_highlight_main__precmd_hook 2>/dev/null; then
# Initialize command type cache
typeset -gA _zsh_highlight_main__command_type_cache
else
2016-07-21 12:01:50 +08:00
print -r -- >& 2 'zsh-syntax-highlighting: Failed to load add-zsh-hook. Some speed optimizations will not be used.'
2016-03-29 07:33:00 +08:00
# Make sure the cache is unset
unset _zsh_highlight_main__command_type_cache
fi
2019-07-24 20:27:54 +08:00
typeset -ga ZSH_HIGHLIGHT_DIRS_BLACKLIST