@@ -49,6 +49,52 @@ if true; then
49
49
fi
50
50
fi
51
51
52
+ # This function takes a single argument F and returns True iff F is an autoload stub.
53
+ _zsh_highlight__function_is_autoload_stub_p () {
54
+ if zmodload -e zsh/parameter; then
55
+ # (( ${+functions[$1]} )) &&
56
+ [[ " $functions [$1 ]" == * " builtin autoload -X" * ]]
57
+ else
58
+ # [[ $(type -wa -- "$1") == *'function'* ]] &&
59
+ [[ " ${${(@ f)" $( which -- " $1 " ) " } [2]} " == $' \t ' $histchars [3]' undefined' ]]
60
+ fi
61
+ # Do nothing here: return the exit code of the if.
62
+ }
63
+
64
+ # Return True iff the argument denotes a function name.
65
+ _zsh_highlight__is_function_p () {
66
+ if zmodload -e zsh/parameter; then
67
+ (( ${+functions[$1]} ))
68
+ else
69
+ [[ $( type -wa -- " $1 " ) == * ' function' * ]]
70
+ fi
71
+ }
72
+
73
+ # This function takes a single argument F and returns True iff F denotes the
74
+ # name of a callable function. A function is callable if it is fully defined
75
+ # or if it is marked for autoloading and autoloading it at the first call to it
76
+ # will succeed. In particular, if a function has been marked for autoloading
77
+ # but is not available in $fpath, then this function will return False therefor.
78
+ #
79
+ # See users/21671 http://www.zsh.org/cgi-bin/mla/redirect?USERNUMBER=21671
80
+ _zsh_highlight__function_callable_p () {
81
+ if _zsh_highlight__is_function_p " $1 " &&
82
+ ! _zsh_highlight__function_is_autoload_stub_p " $1 "
83
+ then
84
+ # Already fully loaded.
85
+ return 0 # true
86
+ else
87
+ # "$1" is either an autoload stub, or not a function at all.
88
+ #
89
+ # Use a subshell to avoid affecting the calling shell.
90
+ #
91
+ # We expect 'autoload +X' to return non-zero if it fails to fully load
92
+ # the function.
93
+ ( autoload -U +X -- " $1 " 2> /dev/null )
94
+ return $?
95
+ fi
96
+ }
97
+
52
98
# -------------------------------------------------------------------------------------------------
53
99
# Core highlighting update system
54
100
# -------------------------------------------------------------------------------------------------
@@ -347,76 +393,120 @@ _zsh_highlight_add_highlight()
347
393
# $1 is name of widget to call
348
394
_zsh_highlight_call_widget ()
349
395
{
350
- builtin zle " $@ " &&
396
+ builtin zle " $@ " &&
351
397
_zsh_highlight
352
398
}
353
399
354
- # Rebind all ZLE widgets to make them invoke _zsh_highlights.
355
- _zsh_highlight_bind_widgets ()
356
- {
357
- setopt localoptions noksharrays
358
- typeset -F SECONDS
359
- local prefix=orig-s$SECONDS -r$RANDOM # unique each time, in case we're sourced more than once
360
-
361
- # Load ZSH module zsh/zleparameter, needed to override user defined widgets.
362
- zmodload zsh/zleparameter 2> /dev/null || {
363
- print -r -- >&2 ' zsh-syntax-highlighting: failed loading zsh/zleparameter.'
364
- return 1
400
+ # Decide whether to use the zle-line-pre-redraw codepath (colloquially known as
401
+ # "feature/redrawhook", after the topic branch's name) or the legacy "bind all
402
+ # widgets" codepath.
403
+ #
404
+ # We use the new codepath under two conditions:
405
+ #
406
+ # 1. If it's available, which we check by testing for add-zle-hook-widget's availability.
407
+ #
408
+ # 2. If zsh has the memo= feature, which is required for interoperability reasons.
409
+ # See issues #579 and #735, and the issues referenced from them.
410
+ #
411
+ # We check this with a plain version number check, since a functional check,
412
+ # as done by _zsh_highlight, can only be done from inside a widget
413
+ # function — a catch-22.
414
+ #
415
+ # See _zsh_highlight for the magic version number. (The use of 5.8.0.2
416
+ # rather than 5.8.0.3 as in the _zsh_highlight is deliberate.)
417
+ if is-at-least 5.8.0.2 && _zsh_highlight__function_callable_p add-zle-hook-widget
418
+ then
419
+ autoload -U add-zle-hook-widget
420
+ _zsh_highlight__zle-line-finish () {
421
+ # Reset $WIDGET since the 'main' highlighter depends on it.
422
+ #
423
+ # Since $WIDGET is declared by zle as read-only in this function's scope,
424
+ # a nested function is required in order to shadow its built-in value;
425
+ # see "User-defined widgets" in zshall.
426
+ () {
427
+ local -h -r WIDGET=zle-line-finish
428
+ _zsh_highlight
429
+ }
365
430
}
431
+ _zsh_highlight__zle-line-pre-redraw () {
432
+ # Set $? to 0 for _zsh_highlight. Without this, subsequent
433
+ # zle-line-pre-redraw hooks won't run, since add-zle-hook-widget happens to
434
+ # call us with $? == 1 in the common case.
435
+ true && _zsh_highlight " $@ "
436
+ }
437
+ _zsh_highlight_bind_widgets (){}
438
+ if [[ -o zle ]]; then
439
+ add-zle-hook-widget zle-line-pre-redraw _zsh_highlight__zle-line-pre-redraw
440
+ add-zle-hook-widget zle-line-finish _zsh_highlight__zle-line-finish
441
+ fi
442
+ else
443
+ # Rebind all ZLE widgets to make them invoke _zsh_highlights.
444
+ _zsh_highlight_bind_widgets ()
445
+ {
446
+ setopt localoptions noksharrays
447
+ typeset -F SECONDS
448
+ local prefix=orig-s$SECONDS -r$RANDOM # unique each time, in case we're sourced more than once
449
+
450
+ # Load ZSH module zsh/zleparameter, needed to override user defined widgets.
451
+ zmodload zsh/zleparameter 2> /dev/null || {
452
+ print -r -- >&2 ' zsh-syntax-highlighting: failed loading zsh/zleparameter.'
453
+ return 1
454
+ }
366
455
367
- # Override ZLE widgets to make them invoke _zsh_highlight.
368
- local -U widgets_to_bind
369
- widgets_to_bind=(${${(k)widgets} :# (.* |run-help|which-command|beep|set-local-history|yank|yank-pop)} )
370
-
371
- # Always wrap special zle-line-finish widget. This is needed to decide if the
372
- # current line ends and special highlighting logic needs to be applied.
373
- # E.g. remove cursor imprint, don't highlight partial paths, ...
374
- widgets_to_bind+=(zle-line-finish)
375
-
376
- # Always wrap special zle-isearch-update widget to be notified of updates in isearch.
377
- # This is needed because we need to disable highlighting in that case.
378
- widgets_to_bind+=(zle-isearch-update)
379
-
380
- local cur_widget
381
- for cur_widget in $widgets_to_bind ; do
382
- case ${widgets[$cur_widget]:- " " } in
383
-
384
- # Already rebound event: do nothing.
385
- user:_zsh_highlight_widget_* );;
386
-
387
- # The "eval"'s are required to make $cur_widget a closure: the value of the parameter at function
388
- # definition time is used.
389
- #
390
- # We can't use ${0/_zsh_highlight_widget_} because these widgets are always invoked with
391
- # NO_function_argzero, regardless of the option's setting here.
392
-
393
- # User defined widget: override and rebind old one with prefix "orig-".
394
- user:* ) zle -N $prefix -$cur_widget ${widgets[$cur_widget]#*: }
395
- eval " _zsh_highlight_widget_${(q)prefix} -${(q)cur_widget} () { _zsh_highlight_call_widget ${(q)prefix} -${(q)cur_widget} -- \"\$ @\" }"
396
- zle -N $cur_widget _zsh_highlight_widget_$prefix -$cur_widget ;;
397
-
398
- # Completion widget: override and rebind old one with prefix "orig-".
399
- completion:* ) zle -C $prefix -$cur_widget ${${(s.: .)widgets[$cur_widget]} [2,3]}
400
- eval " _zsh_highlight_widget_${(q)prefix} -${(q)cur_widget} () { _zsh_highlight_call_widget ${(q)prefix} -${(q)cur_widget} -- \"\$ @\" }"
401
- zle -N $cur_widget _zsh_highlight_widget_$prefix -$cur_widget ;;
402
-
403
- # Builtin widget: override and make it call the builtin ".widget".
404
- builtin) eval " _zsh_highlight_widget_${(q)prefix} -${(q)cur_widget} () { _zsh_highlight_call_widget .${(q)cur_widget} -- \"\$ @\" }"
405
- zle -N $cur_widget _zsh_highlight_widget_$prefix -$cur_widget ;;
406
-
407
- # Incomplete or nonexistent widget: Bind to z-sy-h directly.
408
- * )
409
- if [[ $cur_widget == zle-* ]] && (( ! ${+widgets[$cur_widget]} )) ; then
410
- _zsh_highlight_widget_${cur_widget} () { : ; _zsh_highlight }
411
- zle -N $cur_widget _zsh_highlight_widget_$cur_widget
412
- else
413
- # Default: unhandled case.
414
- print -r -- >&2 " zsh-syntax-highlighting: unhandled ZLE widget ${(qq)cur_widget} "
415
- print -r -- >&2 " zsh-syntax-highlighting: (This is sometimes caused by doing \` bindkey <keys> ${(q-)cur_widget} \` without creating the ${(qq)cur_widget} widget with \` zle -N\` or \` zle -C\` .)"
416
- fi
417
- esac
418
- done
419
- }
456
+ # Override ZLE widgets to make them invoke _zsh_highlight.
457
+ local -U widgets_to_bind
458
+ widgets_to_bind=(${${(k)widgets} :# (.* |run-help|which-command|beep|set-local-history|yank|yank-pop)} )
459
+
460
+ # Always wrap special zle-line-finish widget. This is needed to decide if the
461
+ # current line ends and special highlighting logic needs to be applied.
462
+ # E.g. remove cursor imprint, don't highlight partial paths, ...
463
+ widgets_to_bind+=(zle-line-finish)
464
+
465
+ # Always wrap special zle-isearch-update widget to be notified of updates in isearch.
466
+ # This is needed because we need to disable highlighting in that case.
467
+ widgets_to_bind+=(zle-isearch-update)
468
+
469
+ local cur_widget
470
+ for cur_widget in $widgets_to_bind ; do
471
+ case ${widgets[$cur_widget]:- " " } in
472
+
473
+ # Already rebound event: do nothing.
474
+ user:_zsh_highlight_widget_* );;
475
+
476
+ # The "eval"'s are required to make $cur_widget a closure: the value of the parameter at function
477
+ # definition time is used.
478
+ #
479
+ # We can't use ${0/_zsh_highlight_widget_} because these widgets are always invoked with
480
+ # NO_function_argzero, regardless of the option's setting here.
481
+
482
+ # User defined widget: override and rebind old one with prefix "orig-".
483
+ user:* ) zle -N $prefix -$cur_widget ${widgets[$cur_widget]#*: }
484
+ eval " _zsh_highlight_widget_${(q)prefix} -${(q)cur_widget} () { _zsh_highlight_call_widget ${(q)prefix} -${(q)cur_widget} -- \"\$ @\" }"
485
+ zle -N $cur_widget _zsh_highlight_widget_$prefix -$cur_widget ;;
486
+
487
+ # Completion widget: override and rebind old one with prefix "orig-".
488
+ completion:* ) zle -C $prefix -$cur_widget ${${(s.: .)widgets[$cur_widget]} [2,3]}
489
+ eval " _zsh_highlight_widget_${(q)prefix} -${(q)cur_widget} () { _zsh_highlight_call_widget ${(q)prefix} -${(q)cur_widget} -- \"\$ @\" }"
490
+ zle -N $cur_widget _zsh_highlight_widget_$prefix -$cur_widget ;;
491
+
492
+ # Builtin widget: override and make it call the builtin ".widget".
493
+ builtin) eval " _zsh_highlight_widget_${(q)prefix} -${(q)cur_widget} () { _zsh_highlight_call_widget .${(q)cur_widget} -- \"\$ @\" }"
494
+ zle -N $cur_widget _zsh_highlight_widget_$prefix -$cur_widget ;;
495
+
496
+ # Incomplete or nonexistent widget: Bind to z-sy-h directly.
497
+ * )
498
+ if [[ $cur_widget == zle-* ]] && (( ! ${+widgets[$cur_widget]} )) ; then
499
+ _zsh_highlight_widget_${cur_widget} () { : ; _zsh_highlight }
500
+ zle -N $cur_widget _zsh_highlight_widget_$cur_widget
501
+ else
502
+ # Default: unhandled case.
503
+ print -r -- >&2 " zsh-syntax-highlighting: unhandled ZLE widget ${(qq)cur_widget} "
504
+ print -r -- >&2 " zsh-syntax-highlighting: (This is sometimes caused by doing \` bindkey <keys> ${(q-)cur_widget} \` without creating the ${(qq)cur_widget} widget with \` zle -N\` or \` zle -C\` .)"
505
+ fi
506
+ esac
507
+ done
508
+ }
509
+ fi
420
510
421
511
# Load highlighters from directory.
422
512
#
0 commit comments