From aed99f6a3e9f29859682d92003731c75e43362e8 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Wed, 30 Sep 2015 18:56:35 +0000 Subject: [PATCH 01/25] wrappers: Reimplement using Mikachu's zle-line-pre-redraw hook (workers/36650). --- zsh-syntax-highlighting.zsh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index 4caf3a8..02d45f9 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -49,6 +49,11 @@ if true; then fi fi +integer zsh_highlight_use_redrawhook +if zle -la .match-bracket; then + (( zsh_highlight_use_redrawhook=1 )) +fi + # ------------------------------------------------------------------------------------------------- # Core highlighting update system # ------------------------------------------------------------------------------------------------- @@ -357,6 +362,11 @@ _zsh_highlight_bind_widgets() done } +if (( $zsh_highlight_use_redrawhook )); then + _zsh_highlight_bind_widgets(){} + zle -N zle-line-pre-redraw _zsh_highlight +fi + # Load highlighters from directory. # # Arguments: From 85e62a81716e194a91ed1e9c78cac45e5cba4fa6 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Wed, 15 Jun 2016 23:26:23 +0000 Subject: [PATCH 02/25] driver: Reimplement using 'add-zle-hook-widget zle-line-pre-redraw' This feature will be released in zsh 5.3. Older zsh's will use the existing codepath. --- zsh-syntax-highlighting.zsh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index 02d45f9..8476c1a 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -50,7 +50,9 @@ if true; then fi integer zsh_highlight_use_redrawhook -if zle -la .match-bracket; then +if autoload -U +X -- add-zle-hook-widget 2>/dev/null; + [[ "${${(@f)"$(which -- add-zle-hook-widget)"}[2]}" != $'\t'$histchars[3]' undefined' ]]; +then (( zsh_highlight_use_redrawhook=1 )) fi @@ -364,7 +366,9 @@ _zsh_highlight_bind_widgets() if (( $zsh_highlight_use_redrawhook )); then _zsh_highlight_bind_widgets(){} - zle -N zle-line-pre-redraw _zsh_highlight + if [[ -o zle ]]; then + add-zle-hook-widget zle-line-pre-redraw _zsh_highlight + fi fi # Load highlighters from directory. From 74a27de70d7d21e92a50a6398a5eeeb12b884a4f Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Fri, 29 Jul 2016 20:32:29 +0000 Subject: [PATCH 03/25] driver: Hook zle-line-finish. Compare issue #288. --- zsh-syntax-highlighting.zsh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index 8476c1a..1f5b827 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -365,9 +365,20 @@ _zsh_highlight_bind_widgets() } if (( $zsh_highlight_use_redrawhook )); then + _zsh_highlight__zle-line-finish() { + # Reset $WIDGET since the 'main' highlighter depends on it. + # + # A nested function is required to hide zle parameters; see + # "User-defined widgets" in zshall. + () { + local -h +r WIDGET=zle-line-finish + _zsh_highlight "$@" + } + } _zsh_highlight_bind_widgets(){} if [[ -o zle ]]; then add-zle-hook-widget zle-line-pre-redraw _zsh_highlight + add-zle-hook-widget zle-line-finish _zsh_highlight__zle-line-finish fi fi From 30c6e7039453c9f62b52caac1d202f57c48be442 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Wed, 24 Aug 2016 22:56:09 +0000 Subject: [PATCH 04/25] driver: Pass zle-line-finish arguments on to _zsh_highlight. (Currently a noop) --- zsh-syntax-highlighting.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index 1f5b827..4769255 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -373,7 +373,7 @@ if (( $zsh_highlight_use_redrawhook )); then () { local -h +r WIDGET=zle-line-finish _zsh_highlight "$@" - } + } "$@" } _zsh_highlight_bind_widgets(){} if [[ -o zle ]]; then From 04fd6bbf53a637129f9a11137314d74d0e103d8a Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 5 Sep 2016 04:41:51 +0000 Subject: [PATCH 05/25] changelog: Note the effect of fixing #245/#90 and an alternative. --- changelog.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/changelog.md b/changelog.md index f20e69d..33cd461 100644 --- a/changelog.md +++ b/changelog.md @@ -292,6 +292,27 @@ in this area. (0a9b347483ae) +# Changes in version 0.5.0 + + +## Incompatible changes: + +- An unsuccessful completion (a ⮀ Tab press that doesn't change the + command line) no longer causes highlighting to be lost. Visual feedback can + alternatively be achieved by setting the `format` zstyle under the `warnings` + tag, for example, + + zstyle ':completion:*:warnings' format '%F{red}No matches%f' + + Refer to the [description of the `format` style in `zshcompsys(1)`] + [zshcompsys-Standard-Styles]. + + (#90, part of #245, XXXXXXXXXXXX) + +[zshcompsys-Standard-Styles]: http://zsh.sourceforge.net/Doc/Release/Completion-System.html#Standard-Styles + + + # Changes in version 0.4.1 ## Fixes: From d98622dcd03cf3714e0679f13582e56ebb3a8f22 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 5 Sep 2016 04:45:05 +0000 Subject: [PATCH 06/25] changelog: Use a more specific link. --- changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 33cd461..6ed90f1 100644 --- a/changelog.md +++ b/changelog.md @@ -305,11 +305,12 @@ in this area. zstyle ':completion:*:warnings' format '%F{red}No matches%f' Refer to the [description of the `format` style in `zshcompsys(1)`] - [zshcompsys-Standard-Styles]. + [zshcompsys-Standard-Styles-format]. (#90, part of #245, XXXXXXXXXXXX) [zshcompsys-Standard-Styles]: http://zsh.sourceforge.net/Doc/Release/Completion-System.html#Standard-Styles +[zshcompsys-Standard-Styles-format]: http://zsh.sourceforge.net/Doc/Release/Completion-System.html#index-format_002c-completion-style From 38477f2a3d3ffd06ec2b7496d18cc97d6ee0c4bc Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 5 Sep 2016 03:23:08 +0000 Subject: [PATCH 07/25] driver: Use a different way of checking whether add-zle-hook-widget is present. Based on code by Bart Schaefer (reference within). Tested with zsh 5.0.7-5 (debian package) and with 5b4cbcc842c6 (39158, 5.3-to-be of today). --- zsh-syntax-highlighting.zsh | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index 4769255..dd28bc7 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -49,10 +49,37 @@ if true; then fi fi +# This function takes a single argument F and returns True iff F denotes the +# name of a callable function. A function is callable if it is fully defined +# or if it is marked for autoloading and autoloading it at the first call to it +# will succeed. In particular, if a function has been marked for autoloading +# but is not available in $fpath, then this function will return False therefor. +# +# See users/21671 http://www.zsh.org/cgi-bin/mla/redirect?USERNUMBER=21671 +_zsh_highlight__function_callable_p() { + { # Trenary condition: if the zle/parameter module is available, ... + if (( ${+functions} )); then + # ... then use it to make the decision, ... + if (( ${+functions[$1]} )); then + [[ "$functions[$1]" != *"builtin autoload -X" ]] \ + || + ( unfunction -- "$1" && autoload -U +X -- "$1" 2>/dev/null ) + else + ( autoload -U +X -- "$1" 2>/dev/null ) + fi + else + # ... otherwise, use a fallback approach. + autoload -U +X -- "$1" 2>/dev/null + [[ "${${(@f)"$(which -- "$1")"}[2]}" != $'\t'$histchars[3]' undefined' ]] + fi + } + # Do nothing here! We return the exit code of the if. +} + integer zsh_highlight_use_redrawhook -if autoload -U +X -- add-zle-hook-widget 2>/dev/null; - [[ "${${(@f)"$(which -- add-zle-hook-widget)"}[2]}" != $'\t'$histchars[3]' undefined' ]]; +if _zsh_highlight__function_callable_p add-zle-hook-widget then + autoload -U add-zle-hook-widget (( zsh_highlight_use_redrawhook=1 )) fi From d4ab7e51d2f0d1231221e87e3f5899104bf1f0ad Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Fri, 16 Sep 2016 04:34:59 +0000 Subject: [PATCH 08/25] redo _zsh_highlight__function_callable_p --- zsh-syntax-highlighting.zsh | 51 ++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index dd28bc7..4aa7b99 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -49,6 +49,25 @@ if true; then fi fi +# This function takes a single argument F and returns True iff F is an autoload stub. +_zsh_highlight__function_is_autoload_stub_p() { + if (( ${+functions} )); then + ## zsh/parameter is available + #(( ${+functions[$1]} )) && + [[ "$functions[$1]" == *"builtin autoload -X" ]] + else + ## zsh/parameter isn't available + #[[ $(type -wa -- "$1") == *'function'* ]] && + [[ "${${(@f)"$(which -- "$1")"}[2]}" == $'\t'$histchars[3]' undefined' ]] + fi + # Do nothing here: return the exit code of the if. +} + +# Return True iff the argument denotes a function name. +_zsh_highlight__is_function_p() { + (( ${+functions[$1]} )) || [[ $(type -wa -- "$1") == *'function'* ]] +} + # This function takes a single argument F and returns True iff F denotes the # name of a callable function. A function is callable if it is fully defined # or if it is marked for autoloading and autoloading it at the first call to it @@ -57,23 +76,21 @@ fi # # See users/21671 http://www.zsh.org/cgi-bin/mla/redirect?USERNUMBER=21671 _zsh_highlight__function_callable_p() { - { # Trenary condition: if the zle/parameter module is available, ... - if (( ${+functions} )); then - # ... then use it to make the decision, ... - if (( ${+functions[$1]} )); then - [[ "$functions[$1]" != *"builtin autoload -X" ]] \ - || - ( unfunction -- "$1" && autoload -U +X -- "$1" 2>/dev/null ) - else - ( autoload -U +X -- "$1" 2>/dev/null ) - fi - else - # ... otherwise, use a fallback approach. - autoload -U +X -- "$1" 2>/dev/null - [[ "${${(@f)"$(which -- "$1")"}[2]}" != $'\t'$histchars[3]' undefined' ]] - fi - } - # Do nothing here! We return the exit code of the if. + if _zsh_highlight__is_function_p "$1" && + ! _zsh_highlight__function_is_autoload_stub_p "$1" + then + # Already fully loaded. + return 0 # true + else + # "$1" is either an autoload stub, or not a function at all. + # + # Use a subshell to avoid affecting the calling shell. + # + # We expect 'autoload +X' to return non-zero if it fails to fully load + # the function. + ( autoload -U +X -- "$1" 2>/dev/null ) + return $? + fi } integer zsh_highlight_use_redrawhook From 1651137f5ca9673e4d40b43d8bcea450e10ec9f9 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Fri, 7 Oct 2016 14:21:57 +0000 Subject: [PATCH 09/25] docs: Update FAQ answer per changes on this branch. --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 08af2b6..34b5ef1 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,23 @@ FAQ ### Why must `zsh-syntax-highlighting.zsh` be sourced at the end of the `.zshrc` file? -`zsh-syntax-highlighting.zsh` wraps ZLE widgets. It must be sourced after all +zsh-syntax-highlighting works by hooking into the Zsh Line Editor (ZLE) and +computing syntax highlighting for the command-line buffer as it stands at the +time z-sy-h's hook is invoked. + +In zsh 5.2 and older, +`zsh-syntax-highlighting.zsh` hooks into ZLE by wrapping ZLE widgets. It must be sourced after all custom widgets have been created (i.e., after all `zle -N` calls and after -running `compinit`). Widgets created later will work, but will not update the +running `compinit`) in order to be able to wrap all of them. +Widgets created after z-sy-h is sourced will work, but will not update the syntax highlighting. +In zsh 5.3 and newer, +zsh-syntax-highlighting uses the `add-zle-hook-widget` facility to install +a `zle-line-pre-redraw` hook. Hooks are run in order of registration, +therefore, z-sy-h must be sourced (and register its hook) after anything else +that adds hooks that modify the command-line buffer. + ### Does syntax highlighting work during incremental history search? Highlighting the command line during an incremental history search (by default bound to From 66ae59ecccffb31722e4924028ae4949c3380af2 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Fri, 7 Oct 2016 14:22:23 +0000 Subject: [PATCH 10/25] docs: Rewrap. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 34b5ef1..dbfc8b8 100644 --- a/README.md +++ b/README.md @@ -44,9 +44,9 @@ computing syntax highlighting for the command-line buffer as it stands at the time z-sy-h's hook is invoked. In zsh 5.2 and older, -`zsh-syntax-highlighting.zsh` hooks into ZLE by wrapping ZLE widgets. It must be sourced after all -custom widgets have been created (i.e., after all `zle -N` calls and after -running `compinit`) in order to be able to wrap all of them. +`zsh-syntax-highlighting.zsh` hooks into ZLE by wrapping ZLE widgets. It must +be sourced after all custom widgets have been created (i.e., after all `zle -N` +calls and after running `compinit`) in order to be able to wrap all of them. Widgets created after z-sy-h is sourced will work, but will not update the syntax highlighting. From d2594c115796a90e548c3249684bc5ce5fdfb68e Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 17 Oct 2016 14:49:53 +0000 Subject: [PATCH 11/25] noop: Make a whitespace-only change to reduce noise in the next commit. --- zsh-syntax-highlighting.zsh | 132 ++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index 4aa7b99..5e16fd2 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -337,76 +337,78 @@ _zsh_highlight_add_highlight() # $1 is name of widget to call _zsh_highlight_call_widget() { - builtin zle "$@" && + builtin zle "$@" && _zsh_highlight } -# Rebind all ZLE widgets to make them invoke _zsh_highlights. -_zsh_highlight_bind_widgets() -{ - setopt localoptions noksharrays - typeset -F SECONDS - local prefix=orig-s$SECONDS-r$RANDOM # unique each time, in case we're sourced more than once +if true; then + # Rebind all ZLE widgets to make them invoke _zsh_highlights. + _zsh_highlight_bind_widgets() + { + setopt localoptions noksharrays + typeset -F SECONDS + local prefix=orig-s$SECONDS-r$RANDOM # unique each time, in case we're sourced more than once - # Load ZSH module zsh/zleparameter, needed to override user defined widgets. - zmodload zsh/zleparameter 2>/dev/null || { - print -r -- >&2 'zsh-syntax-highlighting: failed loading zsh/zleparameter.' - return 1 + # Load ZSH module zsh/zleparameter, needed to override user defined widgets. + zmodload zsh/zleparameter 2>/dev/null || { + print -r -- >&2 'zsh-syntax-highlighting: failed loading zsh/zleparameter.' + return 1 + } + + # Override ZLE widgets to make them invoke _zsh_highlight. + local -U widgets_to_bind + widgets_to_bind=(${${(k)widgets}:#(.*|run-help|which-command|beep|set-local-history|yank|yank-pop)}) + + # Always wrap special zle-line-finish widget. This is needed to decide if the + # current line ends and special highlighting logic needs to be applied. + # E.g. remove cursor imprint, don't highlight partial paths, ... + widgets_to_bind+=(zle-line-finish) + + # Always wrap special zle-isearch-update widget to be notified of updates in isearch. + # This is needed because we need to disable highlighting in that case. + widgets_to_bind+=(zle-isearch-update) + + local cur_widget + for cur_widget in $widgets_to_bind; do + case ${widgets[$cur_widget]:-""} in + + # Already rebound event: do nothing. + user:_zsh_highlight_widget_*);; + + # The "eval"'s are required to make $cur_widget a closure: the value of the parameter at function + # definition time is used. + # + # We can't use ${0/_zsh_highlight_widget_} because these widgets are always invoked with + # NO_function_argzero, regardless of the option's setting here. + + # User defined widget: override and rebind old one with prefix "orig-". + user:*) zle -N $prefix-$cur_widget ${widgets[$cur_widget]#*:} + eval "_zsh_highlight_widget_${(q)prefix}-${(q)cur_widget}() { _zsh_highlight_call_widget ${(q)prefix}-${(q)cur_widget} -- \"\$@\" }" + zle -N $cur_widget _zsh_highlight_widget_$prefix-$cur_widget;; + + # Completion widget: override and rebind old one with prefix "orig-". + completion:*) zle -C $prefix-$cur_widget ${${(s.:.)widgets[$cur_widget]}[2,3]} + eval "_zsh_highlight_widget_${(q)prefix}-${(q)cur_widget}() { _zsh_highlight_call_widget ${(q)prefix}-${(q)cur_widget} -- \"\$@\" }" + zle -N $cur_widget _zsh_highlight_widget_$prefix-$cur_widget;; + + # Builtin widget: override and make it call the builtin ".widget". + builtin) eval "_zsh_highlight_widget_${(q)prefix}-${(q)cur_widget}() { _zsh_highlight_call_widget .${(q)cur_widget} -- \"\$@\" }" + zle -N $cur_widget _zsh_highlight_widget_$prefix-$cur_widget;; + + # Incomplete or nonexistent widget: Bind to z-sy-h directly. + *) + if [[ $cur_widget == zle-* ]] && (( ! ${+widgets[$cur_widget]} )); then + _zsh_highlight_widget_${cur_widget}() { :; _zsh_highlight } + zle -N $cur_widget _zsh_highlight_widget_$cur_widget + else + # Default: unhandled case. + print -r -- >&2 "zsh-syntax-highlighting: unhandled ZLE widget ${(qq)cur_widget}" + print -r -- >&2 "zsh-syntax-highlighting: (This is sometimes caused by doing \`bindkey ${(q-)cur_widget}\` without creating the ${(qq)cur_widget} widget with \`zle -N\` or \`zle -C\`.)" + fi + esac + done } - - # Override ZLE widgets to make them invoke _zsh_highlight. - local -U widgets_to_bind - widgets_to_bind=(${${(k)widgets}:#(.*|run-help|which-command|beep|set-local-history|yank|yank-pop)}) - - # Always wrap special zle-line-finish widget. This is needed to decide if the - # current line ends and special highlighting logic needs to be applied. - # E.g. remove cursor imprint, don't highlight partial paths, ... - widgets_to_bind+=(zle-line-finish) - - # Always wrap special zle-isearch-update widget to be notified of updates in isearch. - # This is needed because we need to disable highlighting in that case. - widgets_to_bind+=(zle-isearch-update) - - local cur_widget - for cur_widget in $widgets_to_bind; do - case ${widgets[$cur_widget]:-""} in - - # Already rebound event: do nothing. - user:_zsh_highlight_widget_*);; - - # The "eval"'s are required to make $cur_widget a closure: the value of the parameter at function - # definition time is used. - # - # We can't use ${0/_zsh_highlight_widget_} because these widgets are always invoked with - # NO_function_argzero, regardless of the option's setting here. - - # User defined widget: override and rebind old one with prefix "orig-". - user:*) zle -N $prefix-$cur_widget ${widgets[$cur_widget]#*:} - eval "_zsh_highlight_widget_${(q)prefix}-${(q)cur_widget}() { _zsh_highlight_call_widget ${(q)prefix}-${(q)cur_widget} -- \"\$@\" }" - zle -N $cur_widget _zsh_highlight_widget_$prefix-$cur_widget;; - - # Completion widget: override and rebind old one with prefix "orig-". - completion:*) zle -C $prefix-$cur_widget ${${(s.:.)widgets[$cur_widget]}[2,3]} - eval "_zsh_highlight_widget_${(q)prefix}-${(q)cur_widget}() { _zsh_highlight_call_widget ${(q)prefix}-${(q)cur_widget} -- \"\$@\" }" - zle -N $cur_widget _zsh_highlight_widget_$prefix-$cur_widget;; - - # Builtin widget: override and make it call the builtin ".widget". - builtin) eval "_zsh_highlight_widget_${(q)prefix}-${(q)cur_widget}() { _zsh_highlight_call_widget .${(q)cur_widget} -- \"\$@\" }" - zle -N $cur_widget _zsh_highlight_widget_$prefix-$cur_widget;; - - # Incomplete or nonexistent widget: Bind to z-sy-h directly. - *) - if [[ $cur_widget == zle-* ]] && (( ! ${+widgets[$cur_widget]} )); then - _zsh_highlight_widget_${cur_widget}() { :; _zsh_highlight } - zle -N $cur_widget _zsh_highlight_widget_$cur_widget - else - # Default: unhandled case. - print -r -- >&2 "zsh-syntax-highlighting: unhandled ZLE widget ${(qq)cur_widget}" - print -r -- >&2 "zsh-syntax-highlighting: (This is sometimes caused by doing \`bindkey ${(q-)cur_widget}\` without creating the ${(qq)cur_widget} widget with \`zle -N\` or \`zle -C\`.)" - fi - esac - done -} +fi if (( $zsh_highlight_use_redrawhook )); then _zsh_highlight__zle-line-finish() { From b5249f17abe4850ae3a6fa9130b3be78923e41dc Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 17 Oct 2016 15:12:24 +0000 Subject: [PATCH 12/25] driver: Rewrite without a state variable Suggested-by: m0viefreak --- zsh-syntax-highlighting.zsh | 45 ++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index 5e16fd2..f020b33 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -93,13 +93,6 @@ _zsh_highlight__function_callable_p() { fi } -integer zsh_highlight_use_redrawhook -if _zsh_highlight__function_callable_p add-zle-hook-widget -then - autoload -U add-zle-hook-widget - (( zsh_highlight_use_redrawhook=1 )) -fi - # ------------------------------------------------------------------------------------------------- # Core highlighting update system # ------------------------------------------------------------------------------------------------- @@ -341,7 +334,25 @@ _zsh_highlight_call_widget() _zsh_highlight } -if true; then +if _zsh_highlight__function_callable_p add-zle-hook-widget +then + autoload -U add-zle-hook-widget + _zsh_highlight__zle-line-finish() { + # Reset $WIDGET since the 'main' highlighter depends on it. + # + # A nested function is required to hide zle parameters; see + # "User-defined widgets" in zshall. + () { + local -h +r WIDGET=zle-line-finish + _zsh_highlight "$@" + } "$@" + } + _zsh_highlight_bind_widgets(){} + if [[ -o zle ]]; then + add-zle-hook-widget zle-line-pre-redraw _zsh_highlight + add-zle-hook-widget zle-line-finish _zsh_highlight__zle-line-finish + fi +else # Rebind all ZLE widgets to make them invoke _zsh_highlights. _zsh_highlight_bind_widgets() { @@ -410,24 +421,6 @@ if true; then } fi -if (( $zsh_highlight_use_redrawhook )); then - _zsh_highlight__zle-line-finish() { - # Reset $WIDGET since the 'main' highlighter depends on it. - # - # A nested function is required to hide zle parameters; see - # "User-defined widgets" in zshall. - () { - local -h +r WIDGET=zle-line-finish - _zsh_highlight "$@" - } "$@" - } - _zsh_highlight_bind_widgets(){} - if [[ -o zle ]]; then - add-zle-hook-widget zle-line-pre-redraw _zsh_highlight - add-zle-hook-widget zle-line-finish _zsh_highlight__zle-line-finish - fi -fi - # Load highlighters from directory. # # Arguments: From a868b6942ea017dcd09cdcd6461d27825838364e Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 17 Oct 2016 17:51:04 +0000 Subject: [PATCH 13/25] test harness: Actually test the new code. Currently, without zsh/zle loaded, the tests silently fall back to the 5.2-and-earlier codepath; see: . https://github.com/zsh-users/zsh-syntax-highlighting/pull/356#issuecomment-243651251 --- tests/generate.zsh | 3 +++ tests/test-highlighting.zsh | 3 +++ tests/test-perfs.zsh | 3 +++ 3 files changed, 9 insertions(+) diff --git a/tests/generate.zsh b/tests/generate.zsh index 64a1ede..ef89f94 100755 --- a/tests/generate.zsh +++ b/tests/generate.zsh @@ -31,6 +31,9 @@ emulate -LR zsh setopt localoptions extendedglob +# Required for add-zle-hook-widget. +zmodload zsh/zle + # Argument parsing. if (( $# != 3 )) || [[ $1 == -* ]]; then print -r -- >&2 "$0: usage: $0 BUFFER HIGHLIGHTER BASENAME" diff --git a/tests/test-highlighting.zsh b/tests/test-highlighting.zsh index 812b1a7..e5c85a1 100755 --- a/tests/test-highlighting.zsh +++ b/tests/test-highlighting.zsh @@ -31,6 +31,9 @@ setopt NO_UNSET WARN_CREATE_GLOBAL +# Required for add-zle-hook-widget. +zmodload zsh/zle + # Check an highlighter was given as argument. [[ -n "$1" ]] || { echo >&2 "Bail out! You must provide the name of a valid highlighter as argument." diff --git a/tests/test-perfs.zsh b/tests/test-perfs.zsh index 3411754..49d95c7 100755 --- a/tests/test-perfs.zsh +++ b/tests/test-perfs.zsh @@ -29,6 +29,9 @@ # ------------------------------------------------------------------------------------------------- +# Required for add-zle-hook-widget. +zmodload zsh/zle + # Check an highlighter was given as argument. [[ -n "$1" ]] || { echo >&2 "Bail out! You must provide the name of a valid highlighter as argument." From f665eec230e66cc62f0c6a02cc63dc13e3124e5c Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 8 Jan 2018 05:44:54 +0000 Subject: [PATCH 14/25] driver: Avoid a fork in the common case. Found-by: Matthew Martin --- zsh-syntax-highlighting.zsh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index f020b33..04152c4 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -65,7 +65,13 @@ _zsh_highlight__function_is_autoload_stub_p() { # Return True iff the argument denotes a function name. _zsh_highlight__is_function_p() { - (( ${+functions[$1]} )) || [[ $(type -wa -- "$1") == *'function'* ]] + if (( ${+functions} )); then + ## zsh/parameter is available + (( ${+functions[$1]} )) + else + ## zsh/parameter isn't available + [[ $(type -wa -- "$1") == *'function'* ]] + fi } # This function takes a single argument F and returns True iff F denotes the From d0fb0df4ff96209d6623896f07be7db2655cf1b4 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 8 Jan 2018 06:03:38 +0000 Subject: [PATCH 15/25] driver: Make the shadowing $WIDGET read only. --- zsh-syntax-highlighting.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index 04152c4..6fb1c12 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -349,7 +349,7 @@ then # A nested function is required to hide zle parameters; see # "User-defined widgets" in zshall. () { - local -h +r WIDGET=zle-line-finish + local -h -r WIDGET=zle-line-finish _zsh_highlight "$@" } "$@" } From f265ef0b9aadfea02c696821664c8598f5809b91 Mon Sep 17 00:00:00 2001 From: Matthew Martin Date: Mon, 8 Jan 2018 12:35:45 -0600 Subject: [PATCH 16/25] driver: Use idiomatic module check --- zsh-syntax-highlighting.zsh | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index 6fb1c12..c03c434 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -51,12 +51,10 @@ fi # This function takes a single argument F and returns True iff F is an autoload stub. _zsh_highlight__function_is_autoload_stub_p() { - if (( ${+functions} )); then - ## zsh/parameter is available + if zmodload -e zsh/parameter; then #(( ${+functions[$1]} )) && [[ "$functions[$1]" == *"builtin autoload -X" ]] else - ## zsh/parameter isn't available #[[ $(type -wa -- "$1") == *'function'* ]] && [[ "${${(@f)"$(which -- "$1")"}[2]}" == $'\t'$histchars[3]' undefined' ]] fi @@ -65,11 +63,9 @@ _zsh_highlight__function_is_autoload_stub_p() { # Return True iff the argument denotes a function name. _zsh_highlight__is_function_p() { - if (( ${+functions} )); then - ## zsh/parameter is available + if zmodload -e zsh/parameter; then (( ${+functions[$1]} )) else - ## zsh/parameter isn't available [[ $(type -wa -- "$1") == *'function'* ]] fi } From 2cbb3fb24ecf0880165f57d823746e2f257c3619 Mon Sep 17 00:00:00 2001 From: Matthew Martin Date: Mon, 8 Jan 2018 12:59:56 -0600 Subject: [PATCH 17/25] driver: Allow for -U in autoloaded function definition --- zsh-syntax-highlighting.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index c03c434..17ce59c 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -53,7 +53,7 @@ fi _zsh_highlight__function_is_autoload_stub_p() { if zmodload -e zsh/parameter; then #(( ${+functions[$1]} )) && - [[ "$functions[$1]" == *"builtin autoload -X" ]] + [[ "$functions[$1]" == *"builtin autoload -X"* ]] else #[[ $(type -wa -- "$1") == *'function'* ]] && [[ "${${(@f)"$(which -- "$1")"}[2]}" == $'\t'$histchars[3]' undefined' ]] From 56ba7f082d171102deaeb35a14e900ab1a6a6495 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Tue, 9 Jan 2018 19:23:45 +0000 Subject: [PATCH 18/25] driver: Clarify comment. No functional change. --- zsh-syntax-highlighting.zsh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index 17ce59c..9dc6c1f 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -342,8 +342,9 @@ then _zsh_highlight__zle-line-finish() { # Reset $WIDGET since the 'main' highlighter depends on it. # - # A nested function is required to hide zle parameters; see - # "User-defined widgets" in zshall. + # Since $WIDGET is declared by zle as read-only in this function's scope, + # a nested function is required in order to shadow its built-in value; + # see "User-defined widgets" in zshall. () { local -h -r WIDGET=zle-line-finish _zsh_highlight "$@" From 8d4c6355e6d252f1d67beca4df946f8335fac51c Mon Sep 17 00:00:00 2001 From: Matthew Martin Date: Sat, 13 Oct 2018 21:55:45 -0500 Subject: [PATCH 19/25] driver: Do not pass widget arguments to _zsh_highlight This avoids a bug in zsh 4.3.12 and prior which affects passing arguments to an anonymous function. --- zsh-syntax-highlighting.zsh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index 9dc6c1f..aa650d3 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -347,8 +347,8 @@ then # see "User-defined widgets" in zshall. () { local -h -r WIDGET=zle-line-finish - _zsh_highlight "$@" - } "$@" + _zsh_highlight + } } _zsh_highlight_bind_widgets(){} if [[ -o zle ]]; then From b08d508cd8792df2b6c8e044e42dffeb7f9118fe Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 4 May 2020 15:49:24 +0000 Subject: [PATCH 20/25] driver: Fix a bug that prevented subsequent, third-party zle-line-pre-redraw hooks from running. Without this patch, `_zsh_highlight` was invoked by add-zle-hook-widget with `$?` being non-zero (see add-zle-hook-widget:48-52). Since `_zsh_highlight` preserves `$?` from its caller's point of view, add-zle-hook-widget saw a non-zero exit code from `_zsh_highlight` and did not run any the remaining zle-line-pre-redraw hooks. See https://github.com/zsh-users/zsh-syntax-highlighting/issues/579#issuecomment-623576907. --- zsh-syntax-highlighting.zsh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index aa650d3..47a4e07 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -350,9 +350,15 @@ then _zsh_highlight } } + _zsh_highlight__zle-line-pre-redraw() { + # Set $? to 0 for _zsh_highlight. Without this, subsequent + # zle-line-pre-redraw hooks won't run, since add-zle-hook-widget happens to + # call us with $? == 1 in the common case. + true && _zsh_highlight "$@" + } _zsh_highlight_bind_widgets(){} if [[ -o zle ]]; then - add-zle-hook-widget zle-line-pre-redraw _zsh_highlight + add-zle-hook-widget zle-line-pre-redraw _zsh_highlight__zle-line-pre-redraw add-zle-hook-widget zle-line-finish _zsh_highlight__zle-line-finish fi else From daf0d94baedab4f8bf76dbfb0bcdae3dba005e11 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Tue, 14 Jul 2020 02:00:45 +0000 Subject: [PATCH 21/25] On the feature/redrawhook branch, move the changelog entry to the current release's section. --- changelog.md | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/changelog.md b/changelog.md index 280f434..7951f38 100644 --- a/changelog.md +++ b/changelog.md @@ -16,6 +16,25 @@ added to zsh at z-sy-h's initiative. The new feature is used in the fix to issue #418. +## Incompatible changes: + +- An unsuccessful completion (a ⮀ Tab press that doesn't change the + command line) no longer causes highlighting to be lost. Visual feedback can + alternatively be achieved by setting the `format` zstyle under the `warnings` + tag, for example, + + zstyle ':completion:*:warnings' format '%F{red}No matches%f' + + Refer to the [description of the `format` style in `zshcompsys(1)`] + [zshcompsys-Standard-Styles-format]. + + (#90, part of #245 (feature/redrawhook)) + +[zshcompsys-Standard-Styles]: http://zsh.sourceforge.net/Doc/Release/Completion-System.html#Standard-Styles +[zshcompsys-Standard-Styles-format]: http://zsh.sourceforge.net/Doc/Release/Completion-System.html#index-format_002c-completion-style + + + ## Other changes: - Document `$ZSH_HIGHLIGHT_MAXLENGTH`. @@ -485,28 +504,6 @@ in this area. (0a9b347483ae) -# Changes in version 0.5.0 - - -## Incompatible changes: - -- An unsuccessful completion (a ⮀ Tab press that doesn't change the - command line) no longer causes highlighting to be lost. Visual feedback can - alternatively be achieved by setting the `format` zstyle under the `warnings` - tag, for example, - - zstyle ':completion:*:warnings' format '%F{red}No matches%f' - - Refer to the [description of the `format` style in `zshcompsys(1)`] - [zshcompsys-Standard-Styles-format]. - - (#90, part of #245, XXXXXXXXXXXX) - -[zshcompsys-Standard-Styles]: http://zsh.sourceforge.net/Doc/Release/Completion-System.html#Standard-Styles -[zshcompsys-Standard-Styles-format]: http://zsh.sourceforge.net/Doc/Release/Completion-System.html#index-format_002c-completion-style - - - # Changes in version 0.4.1 ## Fixes: From 59cb9a560d3b2acdb21c666d09ff43411c2153a0 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Tue, 14 Jul 2020 02:13:51 +0000 Subject: [PATCH 22/25] driver: Make the redrawhook codepath conditional upon the memo= feature. Fixes #579 (zsh-autosuggestions interoperability). Fixes #735 (ditto). See https://github.com/zsh-users/zsh-syntax-highlighting/issues/579#issuecomment-650126055 See https://github.com/zsh-users/zsh-autosuggestions/issues/529#issuecomment-650481227 --- zsh-syntax-highlighting.zsh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index c2811a0..87ffb6a 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -403,7 +403,17 @@ _zsh_highlight_call_widget() _zsh_highlight } -if _zsh_highlight__function_callable_p add-zle-hook-widget +# Decide whether to use the zle-line-pre-redraw codepath (colloquially known as +# "feature/redrawhook", after the topic branch's name) or the legacy "bind all +# widgets" codepath. +# +# We use the new codepath under two conditions: +# +# 1. If it's available, which we check by testing for add-zle-hook-widget's availability. +# +# 2. If zsh has the memo= feature, which is required for interoperability reasons. +# See issues #579 and #735, and the issues referenced from them. +if (( zsh_highlight__memo_feature )) && _zsh_highlight__function_callable_p add-zle-hook-widget then autoload -U add-zle-hook-widget _zsh_highlight__zle-line-finish() { From cb33cc0081234c0f8e48d89e0ba81cf58a8f7635 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Tue, 14 Jul 2020 02:37:41 +0000 Subject: [PATCH 23/25] On the feature/redrawhook branch, change the detection of the 'memo=' feature to avoid a catch-22. --- zsh-syntax-highlighting.zsh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/zsh-syntax-highlighting.zsh b/zsh-syntax-highlighting.zsh index cbdd530..3608203 100644 --- a/zsh-syntax-highlighting.zsh +++ b/zsh-syntax-highlighting.zsh @@ -407,7 +407,14 @@ _zsh_highlight_call_widget() # # 2. If zsh has the memo= feature, which is required for interoperability reasons. # See issues #579 and #735, and the issues referenced from them. -if (( zsh_highlight__memo_feature )) && _zsh_highlight__function_callable_p add-zle-hook-widget +# +# We check this with a plain version number check, since a functional check, +# as done by _zsh_highlight, can only be done from inside a widget +# function — a catch-22. +# +# See _zsh_highlight for the magic version number. (The use of 5.8.0.2 +# rather than 5.8.0.3 as in the _zsh_highlight is deliberate.) +if is-at-least 5.8.0.2 && _zsh_highlight__function_callable_p add-zle-hook-widget then autoload -U add-zle-hook-widget _zsh_highlight__zle-line-finish() { From cba4a1bc2e62833a4f5631b89e0974585db39103 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Tue, 14 Jul 2020 23:20:29 +0000 Subject: [PATCH 24/25] On the feature/redrawhook branch, changelog: Add entries for issues fixed by this branch. This covers issues referenced from #245 or labelled "feature:redrawhook" or "widget-wrapping". See #749 for details. --- changelog.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/changelog.md b/changelog.md index 44ddbe0..1bc1ac0 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,57 @@ # Changes in HEAD +## Changes fixed as part of the switch to zle-line-pre-redraw + +The changes in this section were fixed by switching to a `zle-line-pre-redraw`-based +implementation. + +Note: The new implementation will only be used on future zsh releases, +numbered 5.8.0.3 and newer, due to interoperability issues with other plugins +(issues #418 and #579). The underlying zsh feature has been available since +zsh 5.2. + +Whilst under development, the new implementation was known as the +"feature/redrawhook" topic branch. + +- Fixed: Highlighting not triggered after popping a buffer from the buffer stack + (using the `push-line` widget, default binding: `M-q`) + [#40] + +- Fixed: Invoking completion when there were no matches removed highlighting + [#90, #470] + +- Fixed: Two successive deletes followed by a yank only yanked the latest + delete, rather than both of them + [#150, #151, #160; cf. #183] + +- Presumed fixed: Completing `$(xsel)` results in an error message from `xsel`, + with pre-2017 versions of `xsel`. (For 2017 vintage and newer, see the issue + for details.) + [#154] + +- Fixed: When the standard `bracketed-paste-magic` widget is in use, pastes were slow + [#295] + +- Fixed: No way to prevent a widget from being wrapped + [#324] + +- Fixed: No highlighting while cycling menu completion + [#375] + +- Fixed: Does not coexist with the `IGNORE_EOF` option + [#377] + +- Fixed: The `undefined-key` widget was wrapped + [#421] + +- Fixed: Does not coexist with the standard `surround` family of widgets + [#520] + +- Fixed: First completed filename doesn't get `path` highlighting + [#632] + + # Changes in 0.8.0-alpha1-pre-redrawhook ## Notice about an improbable-but-not-impossible forward incompatibility From 7cc6226477a46e7f8217239686912c9cc4a7696b Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Sun, 9 Aug 2020 10:54:01 +0000 Subject: [PATCH 25/25] docs: Track making the new codepath conditional upon the 'memo=' feature. The new codepath is used in 5.8.0.2 and newer; see zsh-syntax-highlighting.zsh:417. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52896c0..303ee1e 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ calls and after running `compinit`) in order to be able to wrap all of them. Widgets created after z-sy-h is sourced will work, but will not update the syntax highlighting. -In zsh 5.3 and newer, +In zsh newer than 5.8 (not including 5.8 itself), zsh-syntax-highlighting uses the `add-zle-hook-widget` facility to install a `zle-line-pre-redraw` hook. Hooks are run in order of registration, therefore, z-sy-h must be sourced (and register its hook) after anything else