Big refactoring.
* Don't override user defined styles * Better modularisation of highlighters * Allow to define which highlighters are activated * Allow to define the order in which they are defined * Minor performance optimizations * Fixed some variables leak * Improve documentation * Brackets highlighter: use ZSH_HIGHLIGHT_STYLES instead of a specific array
This commit is contained in:
		
							parent
							
								
									19f16752bb
								
							
						
					
					
						commit
						0772ddd346
					
				
							
								
								
									
										44
									
								
								README.md
								
								
								
								
							
							
						
						
									
										44
									
								
								README.md
								
								
								
								
							|  | @ -1,20 +1,13 @@ | ||||||
| zsh-syntax-highlighting  | zsh-syntax-highlighting | ||||||
| ========================================================================================================== | ======================= | ||||||
| 
 | 
 | ||||||
| **[Fish shell](http://www.fishshell.com) like syntax highlighting for [Zsh](http://www.zsh.org).** | **[Fish shell](http://www.fishshell.com) like syntax highlighting for [Zsh](http://www.zsh.org).** | ||||||
| 
 | 
 | ||||||
| *Requirements: zsh 4.3.9 or superior.* | *Requirements: zsh 4.3.9+.* | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ## Try it | How to install | ||||||
| 
 | -------------- | ||||||
| Here is a one-liner to try it without installing or modifying anything: |  | ||||||
| 
 |  | ||||||
|     wget --no-check-certificate --output-document=/tmp/zsh-syntax-highlighting.zsh https://github.com/nicoulaj/zsh-syntax-highlighting/raw/master/zsh-syntax-highlighting.zsh && . /tmp/zsh-syntax-highlighting.zsh |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ## Install it |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| ### In your ~/.zshrc | ### In your ~/.zshrc | ||||||
| 
 | 
 | ||||||
|  | @ -47,27 +40,8 @@ Here is a one-liner to try it without installing or modifying anything: | ||||||
|         source ~/.zshrc |         source ~/.zshrc | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ## Tweak it | How to tweak | ||||||
|  | ------------ | ||||||
| 
 | 
 | ||||||
| Optionally, you can override the default styles used for highlighting. The styles are declared in the `ZSH_HIGHLIGHT_STYLES` array. You can override styles this way: | Syntax highlighting is done by pluggable highlighter scripts, see the [highlighters directory](highlighters) | ||||||
| 
 | for documentation and configuration settings. | ||||||
|     # To differentiate aliases from other command types |  | ||||||
|     ZSH_HIGHLIGHT_STYLES[alias]='fg=magenta,bold' |  | ||||||
|      |  | ||||||
|     # To have paths colored instead of underlined |  | ||||||
|     ZSH_HIGHLIGHT_STYLES[path]='fg=cyan' |  | ||||||
|      |  | ||||||
|     # To disable highlighting of globbing expressions |  | ||||||
|     ZSH_HIGHLIGHT_STYLES[globbing]='none' |  | ||||||
| 
 |  | ||||||
| You can tweak the styles used to colorize matching brackets by overriding the `ZSH_HIGHLIGHT_MATCHING_BRACKETS_STYLES`. |  | ||||||
| 
 |  | ||||||
|     ZSH_HIGHLIGHT_MATCHING_BRACKETS_STYLES=( |  | ||||||
|       'fg=blue,bold'    # Style for first level of imbrication |  | ||||||
|       'fg=green,bold'   # Style for second level of imbrication |  | ||||||
|       'fg=magenta,bold' # etc... Put as many styles as you wish, or leave |  | ||||||
|       'fg=yellow,bold'  # empty to disable brackets matching. |  | ||||||
|       'fg=cyan,bold' |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
| This must be done **after** the script is sourced, otherwise your styles will be overwritten. The syntax for declaring styles is [documented here](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#SEC135). |  | ||||||
|  |  | ||||||
|  | @ -0,0 +1,47 @@ | ||||||
|  | zsh-syntax-highlighting / highlighters | ||||||
|  | ====================================== | ||||||
|  | 
 | ||||||
|  | Syntax highlighting is done by pluggable highlighters: | ||||||
|  | 
 | ||||||
|  | * [***main***](main) - the base highlighter, and the only one active by default. | ||||||
|  | * [***brackets***](brackets) - matches brackets and parenthesis. | ||||||
|  | * [***pattern***](pattern) - matches user-defined patterns. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | How to activate highlighters | ||||||
|  | ---------------------------- | ||||||
|  | 
 | ||||||
|  | To activate an highlighter, add it to the `ZSH_HIGHLIGHT_HIGHLIGHTERS` array in `~/.zshrc`, for example: | ||||||
|  | 
 | ||||||
|  |     ZSH_HIGHLIGHT_HIGHLIGHTERS=(main brackets pattern) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | How to tweak highlighters | ||||||
|  | ------------------------- | ||||||
|  | 
 | ||||||
|  | Highlighters look up styles from the `ZSH_HIGHLIGHT_STYLES` array. Navigate into each highlighter directory to see what styles it defines and how to configure it. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | How to implement a new highlighter | ||||||
|  | ---------------------------------- | ||||||
|  | 
 | ||||||
|  | To create your own ***myhighlighter*** highlighter: | ||||||
|  |   | ||||||
|  | * Create your script at **highlighters/*myhighlighter*/*myhighlighter*-highlighter.zsh**. | ||||||
|  | * Implement the `_zsh_highlight_myhighlighter_highlighter_predicate` function. This function must return 0 when the highlighter needs to be called, for example: | ||||||
|  | 
 | ||||||
|  |         _zsh_highlight_myhighlighter_highlighter_predicate() { | ||||||
|  |           # Call this highlighter in SVN repositories | ||||||
|  |           [[ -d .svn ]] | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | * Implement the `_zsh_highlight_myhighlighter_highlighter` function. This function does the actual syntax highlighting, by modifying `region_highlight`, for example: | ||||||
|  | 
 | ||||||
|  |         _zsh_highlight_myhighlighter_highlighter() { | ||||||
|  |           # Colorize the whole buffer with blue background | ||||||
|  |           region_highlight+=(0 $#BUFFER bg=blue) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | * Activate your highlighter in `~/.zshrc`: | ||||||
|  | 
 | ||||||
|  |         ZSH_HIGHLIGHT_HIGHLIGHTERS+=(myhighlighter) | ||||||
|  | @ -0,0 +1,28 @@ | ||||||
|  | zsh-syntax-highlighting / highlighters / brackets | ||||||
|  | ================================================= | ||||||
|  | 
 | ||||||
|  | This is the ***brackets*** highlighter, that highlights brackets, parenthesis and matches them. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | How to activate it | ||||||
|  | ------------------ | ||||||
|  | To activate it, add it to `ZSH_HIGHLIGHT_HIGHLIGHTERS`: | ||||||
|  | 
 | ||||||
|  |     ZSH_HIGHLIGHT_HIGHLIGHTERS=( [...] brackets) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | How to tweak it | ||||||
|  | --------------- | ||||||
|  | This highlighter defines the following styles: | ||||||
|  | * `bracket-error` - unmatched brackets | ||||||
|  | * `bracket-level-N` - brackets with nest level N | ||||||
|  | 
 | ||||||
|  | To override one of those styles, change its entry in `ZSH_HIGHLIGHT_STYLES`, for example in `~/.zshrc`: | ||||||
|  | 
 | ||||||
|  |     # To define styles for nested brackets up to level 4 | ||||||
|  |     ZSH_HIGHLIGHT_STYLES[bracket-level-1]='fg=blue,bold' | ||||||
|  |     ZSH_HIGHLIGHT_STYLES[bracket-level-2]='fg=red,bold' | ||||||
|  |     ZSH_HIGHLIGHT_STYLES[bracket-level-3]='fg=yellow,bold' | ||||||
|  |     ZSH_HIGHLIGHT_STYLES[bracket-level-4]='fg=magenta,bold' | ||||||
|  | 
 | ||||||
|  | The syntax for declaring styles is [documented here](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#SEC135). | ||||||
|  | @ -29,29 +29,24 @@ | ||||||
| # ------------------------------------------------------------------------------------------------- | # ------------------------------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ZSH_HIGHLIGHT_STYLES+=( | # Define default styles. | ||||||
|   bracket-error                 'fg=red,bold' | : ${ZSH_HIGHLIGHT_STYLES[bracket-error]:=fg=red,bold} | ||||||
| ) | : ${ZSH_HIGHLIGHT_STYLES[bracket-level-1]:=fg=blue,bold} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[bracket-level-2]:=fg=green,bold} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[bracket-level-3]:=fg=magenta,bold} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[bracket-level-4]:=fg=yellow,bold} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[bracket-level-5]:=fg=cyan,bold} | ||||||
| 
 | 
 | ||||||
| # Colors for bracket levels. | # Whether the brackets highlighter should be called or not. | ||||||
| # Put as many color as you wish. | _zsh_highlight_brackets_highlighter_predicate() | ||||||
| # Leave it as an empty array to disable. | { | ||||||
| ZSH_HIGHLIGHT_MATCHING_BRACKETS_STYLES=( |   _zsh_highlight_cursor_moved || _zsh_highlight_buffer_modified | ||||||
|   'fg=blue,bold' |  | ||||||
|   'fg=green,bold' |  | ||||||
|   'fg=magenta,bold' |  | ||||||
|   'fg=yellow,bold' |  | ||||||
|   'fg=cyan,bold' |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| # Whether the bracket match highlighting shound be called or not. |  | ||||||
| _zsh_highlight_bracket-match-p() { |  | ||||||
|   _zsh_highlight_cursor-moved-p || _zsh_highlight_buffer-modified-p |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| # Bracket match highlighting. | # Brackets highlighting function. | ||||||
| _zsh_highlight_bracket-match() { | _zsh_highlight_brackets_highlighter() | ||||||
|   bracket_color_size=${#ZSH_HIGHLIGHT_MATCHING_BRACKETS_STYLES} | { | ||||||
|  |   bracket_color_size=${#ZSH_HIGHLIGHT_STYLES[(I)bracket-level-*]} | ||||||
|   if ((bracket_color_size > 0)); then |   if ((bracket_color_size > 0)); then | ||||||
|     typeset -A levelpos lastoflevel matching revmatching |     typeset -A levelpos lastoflevel matching revmatching | ||||||
|     ((level = 0)) |     ((level = 0)) | ||||||
|  | @ -73,7 +68,7 @@ _zsh_highlight_bracket-match() { | ||||||
|       if ((level < 1)); then |       if ((level < 1)); then | ||||||
|         region_highlight+=("$((pos - 1)) $pos "$ZSH_HIGHLIGHT_STYLES[bracket-error]) |         region_highlight+=("$((pos - 1)) $pos "$ZSH_HIGHLIGHT_STYLES[bracket-error]) | ||||||
|       else |       else | ||||||
|         region_highlight+=("$((pos - 1)) $pos "$ZSH_HIGHLIGHT_MATCHING_BRACKETS_STYLES[(( (level - 1) % bracket_color_size + 1 ))]) |         region_highlight+=("$((pos - 1)) $pos "$ZSH_HIGHLIGHT_STYLES[bracket-level-$(( (level - 1) % bracket_color_size + 1 ))]) | ||||||
|       fi |       fi | ||||||
|     done |     done | ||||||
|     ((c = CURSOR + 1)) |     ((c = CURSOR + 1)) | ||||||
|  | @ -85,5 +80,3 @@ _zsh_highlight_bracket-match() { | ||||||
|     fi |     fi | ||||||
|   fi |   fi | ||||||
| } | } | ||||||
| 
 |  | ||||||
| _zsh_highlight_add-highlighter _zsh_highlight_bracket-match |  | ||||||
|  | @ -0,0 +1,55 @@ | ||||||
|  | zsh-syntax-highlighting / highlighters / main | ||||||
|  | ============================================= | ||||||
|  | 
 | ||||||
|  | This is the ***main*** highlighter, that highlights: | ||||||
|  | 
 | ||||||
|  | * Commands | ||||||
|  | * Options | ||||||
|  | * Arguments | ||||||
|  | * Paths | ||||||
|  | * Strings | ||||||
|  | 
 | ||||||
|  | How to activate it | ||||||
|  | ------------------ | ||||||
|  | To activate it, add it to `ZSH_HIGHLIGHT_HIGHLIGHTERS`: | ||||||
|  | 
 | ||||||
|  |     ZSH_HIGHLIGHT_HIGHLIGHTERS=( [...] main) | ||||||
|  | 
 | ||||||
|  | This highlighter is active by default. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | How to tweak it | ||||||
|  | --------------- | ||||||
|  | This highlighter defines the following styles: | ||||||
|  | * `unknown-token` - unknown tokens / errors | ||||||
|  | * `reserved-word` - shell reserved words | ||||||
|  | * `alias` - aliases | ||||||
|  | * `builtin` - shell builtin commands | ||||||
|  | * `function` - functions | ||||||
|  | * `command` - commands | ||||||
|  | * `hashed-command` - hashed commands | ||||||
|  | * `path` - paths | ||||||
|  | * `globbing` - globbing expressions | ||||||
|  | * `history-expansion` - history expansion expressions | ||||||
|  | * `single-hyphen-option` - single hyphen options | ||||||
|  | * `double-hyphen-option` - double hyphen options | ||||||
|  | * `back-quoted-argument` - backquoted expressions | ||||||
|  | * `single-quoted-argument` - single quoted arguments | ||||||
|  | * `double-quoted-argument` - double quoted arguments | ||||||
|  | * `dollar-double-quoted-argument` -  dollar double quoted arguments | ||||||
|  | * `back-double-quoted-argument` -  back double quoted arguments | ||||||
|  | * `assign` - variable assignments | ||||||
|  | * `default` - parts of the buffer that do not match anything | ||||||
|  | 
 | ||||||
|  | To override one of those styles, change its entry in `ZSH_HIGHLIGHT_STYLES`, for example in `~/.zshrc`: | ||||||
|  | 
 | ||||||
|  |     # To differentiate aliases from other command types | ||||||
|  |     ZSH_HIGHLIGHT_STYLES[alias]='fg=magenta,bold' | ||||||
|  |      | ||||||
|  |     # To have paths colored instead of underlined | ||||||
|  |     ZSH_HIGHLIGHT_STYLES[path]='fg=cyan' | ||||||
|  |      | ||||||
|  |     # To disable highlighting of globbing expressions | ||||||
|  |     ZSH_HIGHLIGHT_STYLES[globbing]='none' | ||||||
|  | 
 | ||||||
|  | The syntax for declaring styles is [documented here](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#SEC135). | ||||||
|  | @ -0,0 +1,164 @@ | ||||||
|  | #!/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 | ||||||
|  | # ------------------------------------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Define default styles. | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[default]:=none} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[unknown-token]:=fg=red,bold} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[reserved-word]:=fg=yellow} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[alias]:=fg=green} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[builtin]:=fg=green} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[function]:=fg=green} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[command]:=fg=green} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[hashed-command]:=fg=green} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[path]:=underline} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[globbing]:=fg=blue} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[history-expansion]:=fg=blue} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[single-hyphen-option]:=none} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[double-hyphen-option]:=none} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[back-quoted-argument]:=none} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[single-quoted-argument]:=fg=yellow} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[double-quoted-argument]:=fg=yellow} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[dollar-double-quoted-argument]:=fg=cyan} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[back-double-quoted-argument]:=fg=cyan} | ||||||
|  | : ${ZSH_HIGHLIGHT_STYLES[assign]:=none} | ||||||
|  | 
 | ||||||
|  | # Tokens that are always immediately followed by a command. | ||||||
|  | ZSH_HIGHLIGHT_TOKENS_FOLLOWED_BY_COMMANDS=( | ||||||
|  |   '|' '||' ';' '&' '&&' 'noglob' 'nocorrect' 'builtin' | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | # Whether the highlighter should be called or not. | ||||||
|  | _zsh_highlight_main_highlighter_predicate() | ||||||
|  | { | ||||||
|  |   _zsh_highlight_buffer_modified | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Main syntax highlighting function. | ||||||
|  | _zsh_highlight_main_highlighter() | ||||||
|  | { | ||||||
|  |   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_main_highlighter_check_assign; then | ||||||
|  |                           style=$ZSH_HIGHLIGHT_STYLES[assign] | ||||||
|  |                           new_expression=true | ||||||
|  |                         elif _zsh_highlight_main_highlighter_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_main_highlighter_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_main_highlighter_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 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Check if the argument is variable assignment | ||||||
|  | _zsh_highlight_main_highlighter_check_assign() | ||||||
|  | { | ||||||
|  |     setopt localoptions extended_glob | ||||||
|  |     [[ ${(Q)arg} == [[:alpha:]_]([[:alnum:]_])#=* ]] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Check if the argument is a path. | ||||||
|  | _zsh_highlight_main_highlighter_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_main_highlighter_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 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,21 @@ | ||||||
|  | zsh-syntax-highlighting / highlighters / pattern | ||||||
|  | ================================================ | ||||||
|  | 
 | ||||||
|  | This is the ***pattern*** highlighter, that highlights user defined patterns. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | How to activate it | ||||||
|  | ------------------ | ||||||
|  | To activate it, add it to `ZSH_HIGHLIGHT_HIGHLIGHTERS`: | ||||||
|  | 
 | ||||||
|  |     ZSH_HIGHLIGHT_HIGHLIGHTERS=( [...] pattern) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | How to tweak it | ||||||
|  | --------------- | ||||||
|  | To use this highlighter, associate patterns with styles in the `ZSH_HIGHLIGHT_PATTERNS` array, for example in `~/.zshrc`: | ||||||
|  | 
 | ||||||
|  |     # To have commands starting with `rm -rf` in red: | ||||||
|  |     ZSH_HIGHLIGHT_PATTERNS+=('rm -rf *' 'fg=white,bold,bg=red') | ||||||
|  | 
 | ||||||
|  | The syntax for declaring styles is [documented here](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#SEC135). | ||||||
|  | @ -29,35 +29,31 @@ | ||||||
| # ------------------------------------------------------------------------------------------------- | # ------------------------------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # A simple keyword highlighting extension for zsh-syntax-highlighting. |  | ||||||
| 
 |  | ||||||
| # To use this, please do the following steps. |  | ||||||
| # 1) Source this file after zsh-syntax-highlighting.zsh |  | ||||||
| # % source zsh-syntax-highlighting.zsh |  | ||||||
| # % source contrib/keyword.zsh |  | ||||||
| # 2) Add keyword and color pairs to `ZSH_HIGHLIGHT_KEYWORD_KEYWORDS`. |  | ||||||
| # % ZSH_HIGHLIGHT_KEYWORD_KEYWORDS+=('rm -rf *' 'fg=white,bold,bg=red') |  | ||||||
| # 3) Please see the effect. |  | ||||||
| # % ;# rm -rf /tmp/doesnotexist |  | ||||||
| 
 |  | ||||||
| # List of keyword and color pairs. | # List of keyword and color pairs. | ||||||
| typeset -gA ZSH_HIGHLIGHT_KEYWORD_KEYWORDS | typeset -gA ZSH_HIGHLIGHT_PATTERNS | ||||||
| 
 | 
 | ||||||
| _zsh_highlight-keyword() { | # Whether the pattern highlighter should be called or not. | ||||||
|  | _zsh_highlight_pattern_highlighter_predicate() | ||||||
|  | { | ||||||
|  |   _zsh_highlight_buffer_modified | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Pattern syntax highlighting function. | ||||||
|  | _zsh_highlight_pattern_highlighter() | ||||||
|  | { | ||||||
|   setopt localoptions extendedglob |   setopt localoptions extendedglob | ||||||
|   for pattern in ${(k)ZSH_HIGHLIGHT_KEYWORD_KEYWORDS}; do |   for pattern in ${(k)ZSH_HIGHLIGHT_PATTERNS}; do | ||||||
|     _zsh_highlight-keyword-loop "$BUFFER" "$pattern" |     _zsh_highlight_pattern_highlighter_loop "$BUFFER" "$pattern" | ||||||
|   done |   done | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| _zsh_highlight-keyword-loop() { | _zsh_highlight_pattern_highlighter_loop() | ||||||
|  | { | ||||||
|   # This does *not* do its job syntactically, sorry. |   # This does *not* do its job syntactically, sorry. | ||||||
|   local buf="$1" pat="$2" |   local buf="$1" pat="$2" | ||||||
|   local -a match mbegin mend |   local -a match mbegin mend | ||||||
|   if [[ "$buf" == (#b)(*)(${~pat})* ]]; then |   if [[ "$buf" == (#b)(*)(${~pat})* ]]; then | ||||||
|     region_highlight+=("$((mbegin[2] - 1)) $mend[2] $ZSH_HIGHLIGHT_KEYWORD_KEYWORDS[$pat]") |     region_highlight+=("$((mbegin[2] - 1)) $mend[2] $ZSH_HIGHLIGHT_PATTERNS[$pat]") | ||||||
|     "$0" "$match[1]" "$pat"; return $? |     "$0" "$match[1]" "$pat"; return $? | ||||||
|   fi |   fi | ||||||
| } | } | ||||||
| 
 |  | ||||||
| _zsh_highlight_add-highlighter _zsh_highlight-keyword _zsh_highlight_buffer-modified-p |  | ||||||
|  | @ -0,0 +1,20 @@ | ||||||
|  | zsh-syntax-highlighting / tests | ||||||
|  | =============================== | ||||||
|  | 
 | ||||||
|  | Utility scripts for testing zsh-syntax-highlighting highlighters. | ||||||
|  | 
 | ||||||
|  | The tests expect the highlighter directory to contain a `test-data` directory with test data files. See the [main highlighter](../highlighters/main/test-data) for examples. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | highlighting test | ||||||
|  | ----------------- | ||||||
|  | [`test-highlighting.zsh`](test-highlighting.zsh) tests the correctness of the highlighting. Usage: | ||||||
|  | 
 | ||||||
|  |     zsh test-highlighting.zsh <HIGHLIGHTER NAME> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | performance test | ||||||
|  | ---------------- | ||||||
|  | [`test-perfs.zsh`](test-highlighting.zsh) measures the time spent doing the highlighting. Usage: | ||||||
|  | 
 | ||||||
|  |     zsh test-perfs.zsh <HIGHLIGHTER NAME> | ||||||
|  | @ -29,14 +29,35 @@ | ||||||
| # ------------------------------------------------------------------------------------------------- | # ------------------------------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | # Check an highlighter was given as argument. | ||||||
|  | [[ -n "$1" ]] || { | ||||||
|  |   echo "You must provide the name of a valid highlighter as argument." >&2 | ||||||
|  |   exit 1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Check the highlighter is valid. | ||||||
|  | [[ -f ${0:h:h}/highlighters/$1/$1-highlighter.zsh ]] || { | ||||||
|  |   echo "Could not find highlighter '$1'." >&2 | ||||||
|  |   exit 1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Check the highlighter has test data. | ||||||
|  | [[ -d ${0:h:h}/highlighters/$1/test-data ]] || { | ||||||
|  |   echo "Highlighter '$1' has no test data." >&2 | ||||||
|  |   exit 1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| local -a errors highlight_zone | local -a errors highlight_zone | ||||||
| local -A observed_result | local -A observed_result | ||||||
| 
 | 
 | ||||||
| # Load the main script. | # Load the main script. | ||||||
| . $(dirname $0)/../zsh-syntax-highlighting.zsh | . ${0:h:h}/zsh-syntax-highlighting.zsh | ||||||
| 
 | 
 | ||||||
| # Process each test data file in data/. | # Activate the highlighter. | ||||||
| for data_file in $(dirname $0)/data/*.zsh; do | ZSH_HIGHLIGHT_HIGHLIGHTERS=($1) | ||||||
|  | 
 | ||||||
|  | # Process each test data file in test data directory. | ||||||
|  | for data_file in ${0:h:h}/highlighters/$1/test-data/*; do | ||||||
| 
 | 
 | ||||||
|   # Load the data and prepare checking it. |   # Load the data and prepare checking it. | ||||||
|   BUFFER= ; expected_region_highlight=(); errors=() |   BUFFER= ; expected_region_highlight=(); errors=() | ||||||
|  | @ -55,7 +76,7 @@ for data_file in $(dirname $0)/data/*.zsh; do | ||||||
| 
 | 
 | ||||||
|       # Process the data. |       # Process the data. | ||||||
|       region_highlight=() |       region_highlight=() | ||||||
|       _zsh_highlight-zle-buffer |       _zsh_highlight | ||||||
| 
 | 
 | ||||||
|       # Overlapping regions can be declared in region_highlight, so we first build an array of the |       # Overlapping regions can be declared in region_highlight, so we first build an array of the | ||||||
|       # observed highlighting. |       # observed highlighting. | ||||||
|  |  | ||||||
|  | @ -29,11 +29,32 @@ | ||||||
| # ------------------------------------------------------------------------------------------------- | # ------------------------------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Load the main script. | # Check an highlighter was given as argument. | ||||||
| . $(dirname $0)/../zsh-syntax-highlighting.zsh | [[ -n "$1" ]] || { | ||||||
|  |   echo "You must provide the name of a valid highlighter as argument." >&2 | ||||||
|  |   exit 1 | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| # Process each test data file in data/. | # Check the highlighter is valid. | ||||||
| for data_file in $(dirname $0)/data/*.zsh; do | [[ -f ${0:h:h}/highlighters/$1/$1-highlighter.zsh ]] || { | ||||||
|  |   echo "Could not find highlighter '$1'." >&2 | ||||||
|  |   exit 1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Check the highlighter has test data. | ||||||
|  | [[ -d ${0:h:h}/highlighters/$1/test-data ]] || { | ||||||
|  |   echo "Highlighter '$1' has no test data." >&2 | ||||||
|  |   exit 1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # Load the main script. | ||||||
|  | . ${0:h:h}/zsh-syntax-highlighting.zsh | ||||||
|  | 
 | ||||||
|  | # Activate the highlighter. | ||||||
|  | ZSH_HIGHLIGHT_HIGHLIGHTERS=($1) | ||||||
|  | 
 | ||||||
|  | # Process each test data file in test data directory. | ||||||
|  | for data_file in ${0:h:h}/highlighters/$1/test-data/*; do | ||||||
| 
 | 
 | ||||||
|   # Load the data and prepare checking it. |   # Load the data and prepare checking it. | ||||||
|   BUFFER= |   BUFFER= | ||||||
|  | @ -45,9 +66,9 @@ for data_file in $(dirname $0)/data/*.zsh; do | ||||||
|     echo "KO\n   - 'BUFFER' is not declared or blank." |     echo "KO\n   - 'BUFFER' is not declared or blank." | ||||||
|   else |   else | ||||||
| 
 | 
 | ||||||
|     # Measure the time taken by _zsh_highlight-zle-buffer. |     # Measure the time taken by _zsh_highlight. | ||||||
|     TIMEFMT="%*Es" |     TIMEFMT="%*Es" | ||||||
|     time ( BUFFER="$BUFFER" && _zsh_highlight-zle-buffer) |     time ( BUFFER="$BUFFER" && _zsh_highlight) | ||||||
| 
 | 
 | ||||||
|   fi |   fi | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -33,255 +33,81 @@ | ||||||
| # Core highlighting update system | # Core highlighting update system | ||||||
| # ------------------------------------------------------------------------------------------------- | # ------------------------------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| # Array used by highlighters to declare overridable styles. | # Array declaring active highlighters names. | ||||||
| typeset -gA ZSH_HIGHLIGHT_STYLES | typeset -ga ZSH_HIGHLIGHT_HIGHLIGHTERS | ||||||
| 
 | 
 | ||||||
| # An `object' implemented by below 3 arrays' elements could be called a | # Update ZLE buffer syntax highlighting. | ||||||
| # `highlighter', registered by `_zsh_highlight_add-highlighter`. In other words, these | # | ||||||
| # arrays are indexed and tied by their own functionality. If they have been | # Invokes each highlighter that needs updating. | ||||||
| # arranged inconsistently, things goes wrong. | # This function is supposed to be called whenever the ZLE state changes. | ||||||
| # Please see `_zsh_highlight-zle-buffer` and `_zsh_highlight_add-highlighter`. | _zsh_highlight() | ||||||
| 
 |  | ||||||
| # 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() { |  | ||||||
|   local ret=$? |  | ||||||
| { | { | ||||||
|     local -a funinds |   # Store the previous command return code to restore it whatever happens. | ||||||
|     local -i rh_size=$#region_highlight |   local ret=$? | ||||||
|     for i in {1..${#zsh_highlight_functions}}; do | 
 | ||||||
|       local pred=${zsh_highlight_predicates[i]} cache_place=${zsh_highlight_caches[i]} |   # Do not highlight if there are pending inputs (copy/paste). | ||||||
|       if _zsh_highlight-zle-buffer-p "$rh_size" "$pred"; then |   [[ $PENDING -gt 0 ]] && return $ret | ||||||
|         if ((${#${(P)cache_place}} > 0)); then | 
 | ||||||
|           region_highlight=(${region_highlight:#(${(P~j.|.)cache_place})}) |   { | ||||||
|           local -a empty; empty=(); : ${(PA)cache_place::=$empty} |     local -a selected_highlighters | ||||||
|         fi |     local cache_place | ||||||
|         funinds+=$i | 
 | ||||||
|  |     # Select which highlighters in ZSH_HIGHLIGHT_HIGHLIGHTERS need to be invoked. | ||||||
|  |     local highlighter; for highlighter in $ZSH_HIGHLIGHT_HIGHLIGHTERS; do | ||||||
|  | 
 | ||||||
|  |       # If highlighter needs to be invoked | ||||||
|  |       if "_zsh_highlight_${highlighter}_highlighter_predicate"; then | ||||||
|  | 
 | ||||||
|  |         # Mark the highlighter as selected for update. | ||||||
|  |         selected_highlighters+=($highlighter) | ||||||
|  | 
 | ||||||
|  |         # Remove what was stored in its cache from region_highlight. | ||||||
|  |         cache_place="_zsh_highlight_${highlighter}_highlighter_cache" | ||||||
|  |         [[ ${#${(P)cache_place}} -gt 0 ]] && region_highlight=(${region_highlight:#(${(P~j.|.)cache_place})}) | ||||||
|       fi |       fi | ||||||
|     done |     done | ||||||
|     for i in $funinds; do | 
 | ||||||
|       local func=${zsh_highlight_functions[i]} cache_place=${zsh_highlight_caches[i]} |     # Invoke each selected highlighter and store the result in its cache. | ||||||
|       local -a rh; rh=($region_highlight) |     local -a region_highlight_copy | ||||||
|  |     for highlighter in $selected_highlighters; do | ||||||
|  |       cache_place="_zsh_highlight_${highlighter}_highlighter_cache" | ||||||
|  |       region_highlight_copy=($region_highlight) | ||||||
|       { |       { | ||||||
|         "$func" |         "_zsh_highlight_${highlighter}_highlighter" | ||||||
|       } always  { |       } always  { | ||||||
|         : ${(PA)cache_place::=${region_highlight:#(${(~j.|.)rh})}} |         : ${(PA)cache_place::=${region_highlight:#(${(~j.|.)region_highlight_copy})}} | ||||||
|       } |       } | ||||||
|     done |     done | ||||||
|  | 
 | ||||||
|   } always { |   } always { | ||||||
|     ZSH_PRIOR_CURSOR=$CURSOR |     _ZSH_HIGHLIGHT_PRIOR_BUFFER=$BUFFER | ||||||
|     ZSH_PRIOR_HIGHLIGHTED_BUFFER=$BUFFER |     _ZSH_HIGHLIGHT_PRIOR_CURSOR=$CURSOR | ||||||
|     return $ret |     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 | # API/utility functions for highlighters | ||||||
| # ------------------------------------------------------------------------------------------------- | # ------------------------------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| ZSH_HIGHLIGHT_STYLES+=( | # Array used by highlighters to declare user overridable styles. | ||||||
|   default                       'none' | typeset -gA ZSH_HIGHLIGHT_STYLES | ||||||
|   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. | # Whether the command line buffer has been modified or not. | ||||||
| ZSH_HIGHLIGHT_TOKENS_FOLLOWED_BY_COMMANDS=( | # | ||||||
|   '|' '||' ';' '&' '&&' 'noglob' 'nocorrect' 'builtin' | # Returns 0 if the buffer has changed since _zsh_highlight was last called. | ||||||
| ) | _zsh_highlight_buffer_modified() | ||||||
| 
 | { | ||||||
| # Check if the argument is variable assignment |   [[ ${_ZSH_HIGHLIGHT_PRIOR_BUFFER:-} != $BUFFER ]] | ||||||
| _zsh_highlight_check-assign() { |  | ||||||
|     setopt localoptions extended_glob |  | ||||||
|     [[ ${(Q)arg} == [[:alpha:]_]([[:alnum:]_])#=* ]] |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| # Check if the argument is a path. | # Whether the cursor has moved or not. | ||||||
| _zsh_highlight_check-path() { | # | ||||||
|   [[ -z ${(Q)arg} ]] && return 1 | # Returns 0 if the cursor has moved since _zsh_highlight was last called. | ||||||
|   [[ -e ${(Q)arg} ]] && return 0 | _zsh_highlight_cursor_moved() | ||||||
|   [[ ! -e ${(Q)arg:h} ]] && return 1 | { | ||||||
|   [[ ${#BUFFER} == $end_pos && -n $(print ${(Q)arg}*(N)) ]] && return 0 |   ((_ZSH_HIGHLIGHT_PRIOR_CURSOR != $CURSOR)) | ||||||
|   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 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -289,11 +115,57 @@ _zsh_highlight_load-highlighters() { | ||||||
| # Setup | # Setup | ||||||
| # ------------------------------------------------------------------------------------------------- | # ------------------------------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| # Bind highlighting to all known events. | # Load ZSH module zsh/zleparameter, needed to override user defined widgets. | ||||||
| _zsh_highlight_bind-events "${(@f)"$(zle -la)"}" | zmodload zsh/zleparameter 2>/dev/null || { | ||||||
|  |   echo 'zsh-syntax-highlighting: failed loading zsh/zleparameter, exiting.' >&2 | ||||||
|  |   return -1 | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| # Register the main highlighter. | # Resolve highlighters directory location. | ||||||
| _zsh_highlight_add-highlighter _zsh_main-highlight _zsh_highlight_buffer-modified-p | highlighters_dir="${ZSH_HIGHLIGHT_HIGHLIGHTERS_DIR:-${${(%):-%N}:A:h}/highlighters}" | ||||||
|  | [[ -d $highlighters_dir ]] || { | ||||||
|  |   echo "zsh-syntax-highlighting: highlighters directory '$highlighters_dir' not found, exiting." >&2 | ||||||
|  |   return -1 | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| # Load additional highlighters if available. | # Override ZLE widgets to make them invoke _zsh_highlight. | ||||||
| _zsh_highlight_load-highlighters "${${(%):-%N}:h}/highlighters" | for event in ${${(f)"$(zle -la)"}:#(_*|orig-*|.run-help|.which-command)}; do | ||||||
|  |   if [[ "$widgets[$event]" == completion:* ]]; then | ||||||
|  |     eval "zle -C orig-$event ${${${widgets[$event]}#*:}/:/ } ; $event() { builtin zle orig-$event && _zsh_highlight } ; zle -N $event" | ||||||
|  |   else | ||||||
|  |     case $event in | ||||||
|  |       accept-and-menu-complete) | ||||||
|  |         eval "$event() { builtin zle .$event && _zsh_highlight } ; 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 -N $clean_event" | ||||||
|  |             ;; | ||||||
|  |         esac | ||||||
|  |         ;; | ||||||
|  |       *) | ||||||
|  |         ;; | ||||||
|  |     esac | ||||||
|  |   fi | ||||||
|  | done | ||||||
|  | unset event clean_event | ||||||
|  | 
 | ||||||
|  | # Load highlighters from highlighters directory and check they define required functions. | ||||||
|  | for highlighter_dir ($highlighters_dir/*/); do | ||||||
|  |   highlighter="${highlighter_dir:t}" | ||||||
|  |   [[ -f "$highlighter_dir/${highlighter}-highlighter.zsh" ]] && { | ||||||
|  |     . "$highlighter_dir/${highlighter}-highlighter.zsh" | ||||||
|  |     type "_zsh_highlight_${highlighter}_highlighter" &> /dev/null && | ||||||
|  |     type "_zsh_highlight_${highlighter}_highlighter_predicate" &> /dev/null || { | ||||||
|  |       echo "zsh-syntax-highlighting: '${highlighter}' highlighter should define both required functions '_zsh_highlight_${highlighter}_highlighter' and '_zsh_highlight_${highlighter}_highlighter_predicate' in '${highlighter_dir}/${highlighter}-highlighter.zsh'." >&2 | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | done | ||||||
|  | unset highlighter highlighter_dir highlighters_dir | ||||||
|  | 
 | ||||||
|  | # Initialize the array of active highlighters if needed. | ||||||
|  | [[ $#ZSH_HIGHLIGHT_HIGHLIGHTERS -eq 0 ]] && ZSH_HIGHLIGHT_HIGHLIGHTERS=(main) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue