0999ab05ba
Originally, zsh-syntax-highlighting called highlight functions at every inputs. It's OK for user's keyboard inputs. But when long inputs come from clipboard, it cause noticeable slowdown.
304 lines
12 KiB
Bash
304 lines
12 KiB
Bash
#!/usr/bin/env zsh
|
|
# -------------------------------------------------------------------------------------------------
|
|
# Copyright (c) 2010-2011 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
|
|
# -------------------------------------------------------------------------------------------------
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------
|
|
# Core highlighting update system
|
|
# -------------------------------------------------------------------------------------------------
|
|
|
|
# Array used by highlighters to declare overridable styles.
|
|
typeset -gA ZSH_HIGHLIGHT_STYLES
|
|
|
|
# An `object' implemented by below 3 arrays' elements could be called a
|
|
# `highlighter', registered by `_zsh_highlight_add-highlighter`. In other words, these
|
|
# arrays are indexed and tied by their own functionality. If they have been
|
|
# arranged inconsistently, things goes wrong.
|
|
# Please see `_zsh_highlight-zle-buffer` and `_zsh_highlight_add-highlighter`.
|
|
|
|
# Actual recolorize functions to be called.
|
|
typeset -a zsh_highlight_functions; zsh_highlight_functions=()
|
|
|
|
# Predicate functions whether its recolorize function should be called or not.
|
|
typeset -a zsh_highlight_predicates; zsh_highlight_predicates=()
|
|
|
|
# Highlight storages for each recolorize functions.
|
|
typeset -a zsh_highlight_caches; zsh_highlight_caches=()
|
|
|
|
_zsh_highlight-zle-buffer() {
|
|
if (( PENDING )); then
|
|
return
|
|
fi
|
|
|
|
local ret=$?
|
|
{
|
|
local -a funinds
|
|
local -i rh_size=$#region_highlight
|
|
for i in {1..${#zsh_highlight_functions}}; do
|
|
local pred=${zsh_highlight_predicates[i]} cache_place=${zsh_highlight_caches[i]}
|
|
if _zsh_highlight-zle-buffer-p "$rh_size" "$pred"; then
|
|
if ((${#${(P)cache_place}} > 0)); then
|
|
region_highlight=(${region_highlight:#(${(P~j.|.)cache_place})})
|
|
local -a empty; empty=(); : ${(PA)cache_place::=$empty}
|
|
fi
|
|
funinds+=$i
|
|
fi
|
|
done
|
|
for i in $funinds; do
|
|
local func=${zsh_highlight_functions[i]} cache_place=${zsh_highlight_caches[i]}
|
|
local -a rh; rh=($region_highlight)
|
|
{
|
|
"$func"
|
|
} always {
|
|
: ${(PA)cache_place::=${region_highlight:#(${(~j.|.)rh})}}
|
|
}
|
|
done
|
|
} always {
|
|
ZSH_PRIOR_CURSOR=$CURSOR
|
|
ZSH_PRIOR_HIGHLIGHTED_BUFFER=$BUFFER
|
|
return $ret
|
|
}
|
|
}
|
|
|
|
# Whether supplied highlight_predicate satisfies or not.
|
|
_zsh_highlight-zle-buffer-p() {
|
|
local region_highlight_size="$1" highlight_predicate="$2"
|
|
# If any highlightings are not taken into account, asume it is needed.
|
|
# This holds for some up/down-history commands, for example.
|
|
((region_highlight_size == 0)) || "$highlight_predicate"
|
|
}
|
|
|
|
# Whether the command line buffer is modified or not.
|
|
_zsh_highlight_buffer-modified-p() {
|
|
[[ ${ZSH_PRIOR_HIGHLIGHTED_BUFFER:-} != $BUFFER ]]
|
|
}
|
|
|
|
# Whether the cursor is moved or not.
|
|
_zsh_highlight_cursor-moved-p() {
|
|
((ZSH_PRIOR_CURSOR != $CURSOR))
|
|
}
|
|
|
|
# Register an highlighter.
|
|
_zsh_highlight_add-highlighter() {
|
|
zsh_highlight_functions+="$1"
|
|
zsh_highlight_predicates+="${2-${1}-p}"
|
|
zsh_highlight_caches+="${3-${1//-/_}}"
|
|
}
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------
|
|
# Main highlighter
|
|
# -------------------------------------------------------------------------------------------------
|
|
|
|
ZSH_HIGHLIGHT_STYLES+=(
|
|
default 'none'
|
|
unknown-token 'fg=red,bold'
|
|
reserved-word 'fg=yellow'
|
|
alias 'fg=green'
|
|
builtin 'fg=green'
|
|
function 'fg=green'
|
|
command 'fg=green'
|
|
hashed-command 'fg=green'
|
|
path 'underline'
|
|
globbing 'fg=blue'
|
|
history-expansion 'fg=blue'
|
|
single-hyphen-option 'none'
|
|
double-hyphen-option 'none'
|
|
back-quoted-argument 'none'
|
|
single-quoted-argument 'fg=yellow'
|
|
double-quoted-argument 'fg=yellow'
|
|
dollar-double-quoted-argument 'fg=cyan'
|
|
back-double-quoted-argument 'fg=cyan'
|
|
assign 'none'
|
|
)
|
|
|
|
# Tokens that are always immediately followed by a command.
|
|
ZSH_HIGHLIGHT_TOKENS_FOLLOWED_BY_COMMANDS=(
|
|
'|' '||' ';' '&' '&&' 'noglob' 'nocorrect' 'builtin'
|
|
)
|
|
|
|
# Check if the argument is variable assignment
|
|
_zsh_highlight_check-assign() {
|
|
setopt localoptions extended_glob
|
|
[[ ${(Q)arg} == [[:alpha:]_]([[:alnum:]_])#=* ]]
|
|
}
|
|
|
|
# Check if the argument is a path.
|
|
_zsh_highlight_check-path() {
|
|
[[ -z ${(Q)arg} ]] && return 1
|
|
[[ -e ${(Q)arg} ]] && return 0
|
|
[[ ! -e ${(Q)arg:h} ]] && return 1
|
|
[[ ${#BUFFER} == $end_pos && -n $(print ${(Q)arg}*(N)) ]] && return 0
|
|
return 1
|
|
}
|
|
|
|
# Highlight special chars inside double-quoted strings
|
|
_zsh_highlight_highlight_string() {
|
|
setopt localoptions noksharrays
|
|
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=$ZSH_HIGHLIGHT_STYLES[dollar-double-quoted-argument];;
|
|
"\\") style=$ZSH_HIGHLIGHT_STYLES[back-double-quoted-argument]
|
|
(( k += 1 )) # Color following char too.
|
|
(( i += 1 )) # Skip parsing the escaped char.
|
|
;;
|
|
*) continue;;
|
|
esac
|
|
region_highlight+=("$j $k $style")
|
|
done
|
|
}
|
|
|
|
# Core syntax highlighting.
|
|
_zsh_main-highlight() {
|
|
setopt localoptions extendedglob bareglobqual
|
|
local start_pos=0 end_pos highlight_glob=true new_expression=true arg style
|
|
region_highlight=()
|
|
for arg in ${(z)BUFFER}; do
|
|
local substr_color=0
|
|
[[ $start_pos -eq 0 && $arg = 'noglob' ]] && highlight_glob=false
|
|
((start_pos+=${#BUFFER[$start_pos+1,-1]}-${#${BUFFER[$start_pos+1,-1]##[[:space:]]#}}))
|
|
((end_pos=$start_pos+${#arg}))
|
|
if $new_expression; then
|
|
new_expression=false
|
|
res=$(LC_ALL=C builtin type -w $arg 2>/dev/null)
|
|
case $res in
|
|
*': reserved') style=$ZSH_HIGHLIGHT_STYLES[reserved-word];;
|
|
*': alias') style=$ZSH_HIGHLIGHT_STYLES[alias]
|
|
local aliased_command="${"$(alias $arg)"#*=}"
|
|
[[ -n ${(M)ZSH_HIGHLIGHT_TOKENS_FOLLOWED_BY_COMMANDS:#"$aliased_command"} && -z ${(M)ZSH_HIGHLIGHT_TOKENS_FOLLOWED_BY_COMMANDS:#"$arg"} ]] && ZSH_HIGHLIGHT_TOKENS_FOLLOWED_BY_COMMANDS+=($arg)
|
|
;;
|
|
*': builtin') style=$ZSH_HIGHLIGHT_STYLES[builtin];;
|
|
*': function') style=$ZSH_HIGHLIGHT_STYLES[function];;
|
|
*': command') style=$ZSH_HIGHLIGHT_STYLES[command];;
|
|
*': hashed') style=$ZSH_HIGHLIGHT_STYLES[hashed-command];;
|
|
*) if _zsh_highlight_check-assign; then
|
|
style=$ZSH_HIGHLIGHT_STYLES[assign]
|
|
new_expression=true
|
|
elif _zsh_highlight_check-path; then
|
|
style=$ZSH_HIGHLIGHT_STYLES[path]
|
|
elif [[ $arg[0,1] = $histchars[0,1] ]]; then
|
|
style=$ZSH_HIGHLIGHT_STYLES[history-expansion]
|
|
else
|
|
style=$ZSH_HIGHLIGHT_STYLES[unknown-token]
|
|
fi
|
|
;;
|
|
esac
|
|
else
|
|
case $arg in
|
|
'--'*) style=$ZSH_HIGHLIGHT_STYLES[double-hyphen-option];;
|
|
'-'*) style=$ZSH_HIGHLIGHT_STYLES[single-hyphen-option];;
|
|
"'"*"'") style=$ZSH_HIGHLIGHT_STYLES[single-quoted-argument];;
|
|
'"'*'"') style=$ZSH_HIGHLIGHT_STYLES[double-quoted-argument]
|
|
region_highlight+=("$start_pos $end_pos $style")
|
|
_zsh_highlight_highlight_string
|
|
substr_color=1
|
|
;;
|
|
'`'*'`') style=$ZSH_HIGHLIGHT_STYLES[back-quoted-argument];;
|
|
*"*"*) $highlight_glob && style=$ZSH_HIGHLIGHT_STYLES[globbing] || style=$ZSH_HIGHLIGHT_STYLES[default];;
|
|
*) if _zsh_highlight_check-path; then
|
|
style=$ZSH_HIGHLIGHT_STYLES[path]
|
|
elif [[ $arg[0,1] = $histchars[0,1] ]]; then
|
|
style=$ZSH_HIGHLIGHT_STYLES[history-expansion]
|
|
else
|
|
style=$ZSH_HIGHLIGHT_STYLES[default]
|
|
fi
|
|
;;
|
|
esac
|
|
fi
|
|
[[ $substr_color = 0 ]] && region_highlight+=("$start_pos $end_pos $style")
|
|
[[ -n ${(M)ZSH_HIGHLIGHT_TOKENS_FOLLOWED_BY_COMMANDS:#"$arg"} ]] && new_expression=true
|
|
start_pos=$end_pos
|
|
done
|
|
}
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------
|
|
# Setup functions
|
|
# -------------------------------------------------------------------------------------------------
|
|
|
|
# Intercept specified ZLE events to have highlighting triggered.
|
|
_zsh_highlight_bind-events() {
|
|
|
|
# Resolve event names what have to be bound to.
|
|
zmodload zsh/zleparameter 2>/dev/null || {
|
|
echo 'zsh-syntax-highlighting:zmodload error. exiting.' >&2
|
|
return -1
|
|
}
|
|
local -a events; : ${(A)events::=${@:#(_*|orig-*|.run-help|.which-command)}}
|
|
|
|
# Bind the events to _zsh_highlight-zle-buffer.
|
|
local clean_event
|
|
for event in $events; do
|
|
if [[ "$widgets[$event]" == completion:* ]]; then
|
|
eval "zle -C orig-$event ${${${widgets[$event]}#*:}/:/ } ; $event() { builtin zle orig-$event && _zsh_highlight-zle-buffer } ; zle -N $event"
|
|
else
|
|
case $event in
|
|
accept-and-menu-complete)
|
|
eval "$event() { builtin zle .$event && _zsh_highlight-zle-buffer } ; zle -N $event"
|
|
;;
|
|
.*)
|
|
clean_event=$event[2,${#event}] # Remove the leading dot in the event name
|
|
case ${widgets[$clean_event]-} in
|
|
(completion|user):*)
|
|
;;
|
|
*)
|
|
eval "$clean_event() { builtin zle $event && _zsh_highlight-zle-buffer } ; zle -N $clean_event"
|
|
;;
|
|
esac
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Load highlighters from specified directory if it exists.
|
|
_zsh_highlight_load-highlighters() {
|
|
[[ -d $1 ]] && for highlighter_def ($1/*.zsh) . $highlighter_def
|
|
}
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------
|
|
# Setup
|
|
# -------------------------------------------------------------------------------------------------
|
|
|
|
# Bind highlighting to all known events.
|
|
_zsh_highlight_bind-events "${(@f)"$(zle -la)"}"
|
|
|
|
# Register the main highlighter.
|
|
_zsh_highlight_add-highlighter _zsh_main-highlight _zsh_highlight_buffer-modified-p
|
|
|
|
# Load additional highlighters if available.
|
|
_zsh_highlight_load-highlighters "${${(%):-%N}:h}/highlighters"
|