diff --git a/highlighters/main/main-highlighter.zsh b/highlighters/main/main-highlighter.zsh index 9776ba4..7f8c47b 100644 --- a/highlighters/main/main-highlighter.zsh +++ b/highlighters/main/main-highlighter.zsh @@ -72,6 +72,9 @@ _zsh_highlight_main_add_region_highlight() { integer start=$1 end=$2 shift 2 + (( highlighted_alias )) && return + (( in_alias )) && highlighted_alias=1 + # The calculation was relative to $buf but region_highlight is relative to $BUFFER. (( start += buf_offset )) (( end += buf_offset )) @@ -363,10 +366,18 @@ _zsh_highlight_highlighter_main_paint() _zsh_highlight_main_highlighter_highlight_list() { integer start_pos end_pos=0 buf_offset=$1 has_end=$3 - local buf=$4 highlight_glob=true arg arg_raw style + # last_alias is the last alias arg (lhs) expanded (if in an alias). + # This allows for expanding alias ls='ls -l' while avoiding loops. + local arg buf=$4 highlight_glob=true last_alias style local in_array_assignment=false # true between 'a=(' and the matching ')' - integer len=$#buf + # highlighted_alias is 1 when the alias arg has been highlighted with a non-alias style. + # E.g. alias x=ls; x has been highlighted as alias AND command. + # 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. + integer highlighted_alias=0 in_alias=0 len=$#buf local -a match mbegin mend list_highlights + # seen_alias is a map of aliases already seen to avoid loops like alias a=b b=a + local -A seen_alias list_highlights=() # "R" for round @@ -427,10 +438,12 @@ _zsh_highlight_main_highlighter_highlight_list() args=(${(z)buf}) fi while (( $#args )); do - # Save an unmunged copy of the current word. arg=$args[1] - arg_raw="$arg" shift args + if (( in_alias )); then + (( in_alias-- )) + (( in_alias == 0 )) && highlighted_alias=0 last_alias= seen_alias=() + fi # Initialize this_word and next_word. if (( in_redirection == 0 )); then @@ -455,7 +468,7 @@ _zsh_highlight_main_highlighter_highlight_list() fi fi - if true; then + if (( in_alias == 0 )); then # Compute the new $start_pos and $end_pos, skipping over whitespace in $buf. start_pos=$end_pos if [[ $arg == ';' ]] ; then @@ -531,34 +544,20 @@ _zsh_highlight_main_highlighter_highlight_list() # Expand aliases. _zsh_highlight_main__type "$arg" local res="$REPLY" - if [[ $res == "alias" ]]; then - () { - local -A seen_alias - while [[ $REPLY == alias ]]; do + if [[ $res == "alias" ]] && [[ $last_alias != $arg ]]; then + # Avoid looping forever on alias a=b b=c c=b, but allow alias foo='foo bar' + if (( $+seen_alias[$arg] )); then + _zsh_highlight_main_add_region_highlight $start_pos $end_pos unknown-token + continue + fi seen_alias[$arg]=1 + last_alias=$arg _zsh_highlight_main__resolve_alias $arg - # Use a temporary array to ensure the subscript is interpreted as - # an array subscript, not as a scalar subscript local -a alias_args - # TODO: the ${interactive_comments+set} path needs to skip comments; see test-data/alias-comment1.zsh + # Elision is desired in case alias x='' alias_args=( ${interactive_comments-${(z)REPLY}} ${interactive_comments+${(zZ+c+)REPLY}} ) - # Avoid looping forever on alias a=b b=c c=b, but allow alias foo='foo bar' - [[ $arg == $alias_args[1] ]] && break - arg=$alias_args[1] - if (( $+seen_alias[$arg] )); then - res=none - break - fi - _zsh_highlight_main__type "$arg" - done - } - _zsh_highlight_main_highlighter_expand_path $arg - arg=$REPLY - () { - # Make sure to use $arg_raw here, rather than $arg. - integer insane_alias - case $arg_raw in + case $arg in # Issue #263: aliases with '=' on their LHS. # # There are three cases: @@ -566,27 +565,29 @@ _zsh_highlight_main_highlighter_highlight_list() # - Unsupported, breaks 'alias -L' output, but invokable: ('='*) :;; # - Unsupported, not invokable: - (*'='*) insane_alias=1;; + (*'='*) + _zsh_highlight_main_add_region_highlight $start_pos $end_pos unknown-token + continue + ;; # - The common case: (*) :;; esac - if (( insane_alias )); then - style=unknown-token - # Calling 'type' again; since __type memoizes the answer, this call is just a hash lookup. - elif ! _zsh_highlight_main__type "$arg" || [[ $REPLY == 'none' ]]; then - style=unknown-token + args=( $alias_args $args ) + if (( in_alias == 0 )); then + _zsh_highlight_main_add_region_highlight $start_pos $end_pos alias + # 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 )) else - # The common case. - style=alias - if (( ${+precommand_options[(re)"$arg"]} )) && (( ! ${+precommand_options[(re)"$arg_raw"]} )); then - precommand_options[$arg_raw]=$precommand_options[$arg] - fi + # This arg is already included in the count, so no need to + 1. + (( in_alias += $#alias_args )) fi - } + (( in_redirection++ )) # Stall this arg + continue else _zsh_highlight_main_highlighter_expand_path $arg arg=$REPLY - _zsh_highlight_main__type "$arg" + _zsh_highlight_main__type "$arg" 0 res="$REPLY" fi fi @@ -635,7 +636,7 @@ _zsh_highlight_main_highlighter_highlight_list() arg=${(P)MATCH} ;; esac - _zsh_highlight_main__type "$arg" + _zsh_highlight_main__type "$arg" 0 res=$REPLY fi } @@ -703,7 +704,7 @@ _zsh_highlight_main_highlighter_highlight_list() next_word=':start:' elif ! (( in_redirection)) && [[ $this_word == *':start:'* ]]; then # $arg is the command word if (( ${+precommand_options[$arg]} )) && _zsh_highlight_main__is_runnable $arg; then - [[ $res != alias ]] && style=precommand + style=precommand flags_with_argument=${precommand_options[$arg]%:*} flags_sans_argument=${precommand_options[$arg]#*:} next_word=${next_word//:regular:/} diff --git a/highlighters/main/test-data/alias-comment1.zsh b/highlighters/main/test-data/alias-comment1.zsh index a2b56c6..0c449e1 100644 --- a/highlighters/main/test-data/alias-comment1.zsh +++ b/highlighters/main/test-data/alias-comment1.zsh @@ -33,5 +33,6 @@ alias x=$'# foo\npwd' BUFFER='x' expected_region_highlight=( - "1 1 alias 'interactivecomments applies to aliases'" # x becomes pwd + '1 1 alias' # x + '1 1 comment' # x (#) ) diff --git a/highlighters/main/test-data/alias-comment2.zsh b/highlighters/main/test-data/alias-comment2.zsh index e9ecfca..8bdc5a8 100644 --- a/highlighters/main/test-data/alias-comment2.zsh +++ b/highlighters/main/test-data/alias-comment2.zsh @@ -33,5 +33,6 @@ alias x=$'# foo\npwd' BUFFER='x' expected_region_highlight=( - "1 1 unknown-token" # x becomes # + '1 1 alias' # x + '1 1 unknown-token' # x (#) ) diff --git a/highlighters/main/test-data/alias-loop.zsh b/highlighters/main/test-data/alias-loop.zsh index 46e900b..b36d1c9 100644 --- a/highlighters/main/test-data/alias-loop.zsh +++ b/highlighters/main/test-data/alias-loop.zsh @@ -33,7 +33,8 @@ alias a=b b=c c=b BUFFER='a foo; :' expected_region_highlight=( - '1 1 unknown-token' # a + '1 1 alias' # a + '1 1 unknown-token' # a (invalid alias loop) '3 5 default' # foo '6 6 commandseparator' # ; '8 8 builtin' # : diff --git a/highlighters/main/test-data/alias-nested-precommand.zsh b/highlighters/main/test-data/alias-nested-precommand.zsh index 6d4172b..7c2eeeb 100644 --- a/highlighters/main/test-data/alias-nested-precommand.zsh +++ b/highlighters/main/test-data/alias-nested-precommand.zsh @@ -35,6 +35,7 @@ BUFFER='a -u phy1729 echo; :' expected_region_highlight=( '1 1 alias' # a + '1 1 precommand' # a (sudo) '3 4 single-hyphen-option' # -u '6 12 default' # phy1729 '14 17 builtin' # echo diff --git a/highlighters/main/test-data/alias-nested.zsh b/highlighters/main/test-data/alias-nested.zsh index b24b496..44ab22b 100644 --- a/highlighters/main/test-data/alias-nested.zsh +++ b/highlighters/main/test-data/alias-nested.zsh @@ -34,6 +34,7 @@ BUFFER='a foo; :' expected_region_highlight=( '1 1 alias' # a + '1 1 builtin' # a (:) '3 5 default' # foo '6 6 commandseparator' # ; '8 8 builtin' # : diff --git a/highlighters/main/test-data/alias-precommand-option-argument.zsh b/highlighters/main/test-data/alias-precommand-option-argument1.zsh similarity index 97% rename from highlighters/main/test-data/alias-precommand-option-argument.zsh rename to highlighters/main/test-data/alias-precommand-option-argument1.zsh index ac96ad6..ad16962 100644 --- a/highlighters/main/test-data/alias-precommand-option-argument.zsh +++ b/highlighters/main/test-data/alias-precommand-option-argument1.zsh @@ -35,7 +35,8 @@ BUFFER='sdu phy1729 echo foo' expected_region_highlight=( '1 3 alias' # sdu - '5 11 default "issue #540"' # phy1729 + '1 3 precommand' # sdu (sudo) + '5 11 default' # phy1729 '13 16 commmand "issue #540"' # echo (not builtin) '18 20 default' # foo ) diff --git a/highlighters/main/test-data/alias-precommand-option-argument2.zsh b/highlighters/main/test-data/alias-precommand-option-argument2.zsh new file mode 100644 index 0000000..2fceff8 --- /dev/null +++ b/highlighters/main/test-data/alias-precommand-option-argument2.zsh @@ -0,0 +1,43 @@ +#!/usr/bin/env zsh +# ------------------------------------------------------------------------------------------------- +# Copyright (c) 2018 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 +# ------------------------------------------------------------------------------------------------- + +alias sde='sudo -e' +alias seu='sde -u' +sudo(){} + +BUFFER='seu phy1729 echo foo' + +expected_region_highlight=( + '1 3 alias' # seu + '1 3 precommand' # seu (sudo) + '5 11 default' # phy1729 + '13 16 commmand "issue #540"' # echo (not builtin) + '18 20 default' # foo +) diff --git a/highlighters/main/test-data/alias-quoted.zsh b/highlighters/main/test-data/alias-quoted.zsh index bf397f9..e42e2f0 100644 --- a/highlighters/main/test-data/alias-quoted.zsh +++ b/highlighters/main/test-data/alias-quoted.zsh @@ -35,5 +35,5 @@ expected_region_highlight=( '1 3 unknown-token' # "a" '5 7 default' # foo '8 8 commandseparator' # ; - '10 12 command "issue #544' # \ls + '10 12 command' # \ls ) diff --git a/highlighters/main/test-data/alias-redirect.zsh b/highlighters/main/test-data/alias-redirect.zsh index 518cdc3..a6a0aab 100644 --- a/highlighters/main/test-data/alias-redirect.zsh +++ b/highlighters/main/test-data/alias-redirect.zsh @@ -31,7 +31,8 @@ alias x=\> BUFFER='x foo echo bar' expected_region_highlight=( - '1 1 redirection' # x becomes > + '1 1 alias' # x + '1 1 redirection' # x (>) '3 5 default' # foo '7 10 builtin' # echo '12 14 default' # bar diff --git a/highlighters/main/test-data/alias-self.zsh b/highlighters/main/test-data/alias-self.zsh index c6f12b1..88ed3c8 100644 --- a/highlighters/main/test-data/alias-self.zsh +++ b/highlighters/main/test-data/alias-self.zsh @@ -34,5 +34,6 @@ BUFFER='echo bar' expected_region_highlight=( '1 4 alias' # echo + '1 4 builtin' # echo '6 8 default' # bar ) diff --git a/highlighters/main/test-data/alias-to-dir.zsh b/highlighters/main/test-data/alias-to-dir.zsh index 5e78e14..93aaa62 100644 --- a/highlighters/main/test-data/alias-to-dir.zsh +++ b/highlighters/main/test-data/alias-to-dir.zsh @@ -32,5 +32,6 @@ alias x=/ BUFFER=$'x' expected_region_highlight=( - '1 1 unknown-token' # x + '1 1 alias' # x + '1 1 unknown-token "issue #202"' # x (/) ) diff --git a/highlighters/main/test-data/alias.zsh b/highlighters/main/test-data/alias.zsh index 9699f0b..8330a04 100644 --- a/highlighters/main/test-data/alias.zsh +++ b/highlighters/main/test-data/alias.zsh @@ -48,4 +48,5 @@ fi expected_region_highlight+=( "9 9 commandseparator" # ; "11 16 alias" # alias1 + "11 16 command" # alias1 (ls) ) diff --git a/highlighters/main/test-data/noglob-alias.zsh b/highlighters/main/test-data/noglob-alias.zsh index 2f96445..5676b5a 100644 --- a/highlighters/main/test-data/noglob-alias.zsh +++ b/highlighters/main/test-data/noglob-alias.zsh @@ -32,5 +32,6 @@ BUFFER='x ls' expected_region_highlight=( "1 1 alias" # x + "1 1 precommand" # x (command) "3 4 command" # ls ) diff --git a/highlighters/main/test-data/off-by-one.zsh b/highlighters/main/test-data/off-by-one.zsh index 3870e20..7d6961a 100644 --- a/highlighters/main/test-data/off-by-one.zsh +++ b/highlighters/main/test-data/off-by-one.zsh @@ -33,8 +33,9 @@ f() {} BUFFER='a;f;' expected_region_highlight=( - "1 1 alias" # f + "1 1 alias" # a + "1 1 builtin" # a (:) "2 2 commandseparator" # ; - "3 3 function" # g + "3 3 function" # f "4 4 commandseparator" # ; )