@@ -72,6 +72,9 @@ _zsh_highlight_main_add_region_highlight() {
7272 integer start=$1 end=$2
7373 shift 2
7474
75+ (( highlighted_alias )) && return
76+ (( in_alias )) && highlighted_alias=1
77+
7578 # The calculation was relative to $buf but region_highlight is relative to $BUFFER.
7679 (( start += buf_offset ))
7780 (( end += buf_offset ))
@@ -363,10 +366,18 @@ _zsh_highlight_highlighter_main_paint()
363366_zsh_highlight_main_highlighter_highlight_list ()
364367{
365368 integer start_pos end_pos=0 buf_offset=$1 has_end=$3
366- local buf=$4 highlight_glob=true arg arg_raw style
369+ # last_alias is the last alias arg (lhs) expanded (if in an alias).
370+ # This allows for expanding alias ls='ls -l' while avoiding loops.
371+ local arg buf=$4 highlight_glob=true last_alias style
367372 local in_array_assignment=false # true between 'a=(' and the matching ')'
368- integer len=$# buf
373+ # highlighted_alias is 1 when the alias arg has been highlighted with a non-alias style.
374+ # E.g. alias x=ls; x has been highlighted as alias AND command.
375+ # in_alias is equal to the number of shifts needed until arg=args[1] pops an
376+ # arg from BUFFER and not added by an alias.
377+ integer highlighted_alias=0 in_alias=0 len=$# buf
369378 local -a match mbegin mend list_highlights
379+ # seen_alias is a map of aliases already seen to avoid loops like alias a=b b=a
380+ local -A seen_alias
370381 list_highlights=()
371382
372383 # "R" for round
@@ -427,10 +438,12 @@ _zsh_highlight_main_highlighter_highlight_list()
427438 args=(${(z)buf} )
428439 fi
429440 while (( $# args )) ; do
430- # Save an unmunged copy of the current word.
431441 arg=$args [1]
432- arg_raw=" $arg "
433442 shift args
443+ if (( in_alias )) ; then
444+ (( in_alias-- ))
445+ (( in_alias == 0 )) && highlighted_alias=0 last_alias= seen_alias=()
446+ fi
434447
435448 # Initialize this_word and next_word.
436449 if (( in_redirection == 0 )) ; then
@@ -455,7 +468,7 @@ _zsh_highlight_main_highlighter_highlight_list()
455468 fi
456469 fi
457470
458- if true ; then
471+ if (( in_alias == 0 )) ; then
459472 # Compute the new $start_pos and $end_pos, skipping over whitespace in $buf.
460473 start_pos=$end_pos
461474 if [[ $arg == ' ;' ]] ; then
@@ -531,62 +544,50 @@ _zsh_highlight_main_highlighter_highlight_list()
531544 # Expand aliases.
532545 _zsh_highlight_main__type " $arg "
533546 local res=" $REPLY "
534- if [[ $res == " alias" ]]; then
535- () {
536- local -A seen_alias
537- while [[ $REPLY == alias ]]; do
547+ if [[ $res == " alias" ]] && [[ $last_alias != $arg ]]; then
548+ # Avoid looping forever on alias a=b b=c c=b, but allow alias foo='foo bar'
549+ if (( $+ seen_alias[$arg ] )) ; then
550+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos unknown-token
551+ continue
552+ fi
538553 seen_alias[$arg ]=1
554+ last_alias=$arg
539555 _zsh_highlight_main__resolve_alias $arg
540- # Use a temporary array to ensure the subscript is interpreted as
541- # an array subscript, not as a scalar subscript
542556 local -a alias_args
543- # TODO: the ${interactive_comments+set} path needs to skip comments; see test-data/ alias-comment1.zsh
557+ # Elision is desired in case alias x=''
544558 alias_args=( ${interactive_comments-${(z)REPLY} }
545559 ${interactive_comments+${(zZ+c+)REPLY} } )
546- # Avoid looping forever on alias a=b b=c c=b, but allow alias foo='foo bar'
547- [[ $arg == $alias_args [1] ]] && break
548- arg=$alias_args [1]
549- if (( $+ seen_alias[$arg ] )) ; then
550- res=none
551- break
552- fi
553- _zsh_highlight_main__type " $arg "
554- done
555- }
556- _zsh_highlight_main_highlighter_expand_path $arg
557- arg=$REPLY
558- () {
559- # Make sure to use $arg_raw here, rather than $arg.
560- integer insane_alias
561- case $arg_raw in
560+ case $arg in
562561 # Issue #263: aliases with '=' on their LHS.
563562 #
564563 # There are three cases:
565564 #
566565 # - Unsupported, breaks 'alias -L' output, but invokable:
567566 (' =' * ) : ;;
568567 # - Unsupported, not invokable:
569- (* ' =' * ) insane_alias=1;;
568+ (* ' =' * )
569+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos unknown-token
570+ continue
571+ ;;
570572 # - The common case:
571573 (* ) : ;;
572574 esac
573- if (( insane_alias )) ; then
574- style=unknown-token
575- # Calling 'type' again; since __type memoizes the answer, this call is just a hash lookup.
576- elif ! _zsh_highlight_main__type " $arg " || [[ $REPLY == ' none' ]]; then
577- style=unknown-token
575+ args=( $alias_args $args )
576+ if (( in_alias == 0 )) ; then
577+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos alias
578+ # Add one because we will in_alias-- on the next loop iteration so
579+ # this iteration should be considered in in_alias as well
580+ (( in_alias += $# alias_args + 1 ))
578581 else
579- # The common case.
580- style=alias
581- if (( ${+precommand_options[(re)"$arg"]} )) && (( ! ${+precommand_options[(re)"$arg_raw"]} )) ; then
582- precommand_options[$arg_raw ]=$precommand_options [$arg ]
583- fi
582+ # This arg is already included in the count, so no need to + 1.
583+ (( in_alias += $# alias_args ))
584584 fi
585- }
585+ (( in_redirection++ )) # Stall this arg
586+ continue
586587 else
587588 _zsh_highlight_main_highlighter_expand_path $arg
588589 arg=$REPLY
589- _zsh_highlight_main__type " $arg "
590+ _zsh_highlight_main__type " $arg " 0
590591 res=" $REPLY "
591592 fi
592593 fi
@@ -635,7 +636,7 @@ _zsh_highlight_main_highlighter_highlight_list()
635636 arg=${(P)MATCH}
636637 ;;
637638 esac
638- _zsh_highlight_main__type " $arg "
639+ _zsh_highlight_main__type " $arg " 0
639640 res=$REPLY
640641 fi
641642 }
@@ -703,7 +704,7 @@ _zsh_highlight_main_highlighter_highlight_list()
703704 next_word=' :start:'
704705 elif ! (( in_redirection)) && [[ $this_word == * ' :start:' * ]]; then # $arg is the command word
705706 if (( ${+precommand_options[$arg]} )) && _zsh_highlight_main__is_runnable $arg ; then
706- [[ $res != alias ]] && style=precommand
707+ style=precommand
707708 flags_with_argument=${precommand_options[$arg]%:* }
708709 flags_sans_argument=${precommand_options[$arg]#*: }
709710 next_word=${next_word//: regular:/ }
0 commit comments