addet blesh

This commit is contained in:
pika 2024-08-18 03:40:38 +02:00
parent 58205a50b9
commit 8c8d8e9962
302 changed files with 74275 additions and 0 deletions

View file

@ -0,0 +1,317 @@
# ble/contrib/layer/pattern.bash (C) 2023, akinomyoga
#
# @fn ble/highlight/layer:{pattern}/declare name [type]
# Define a new layer named `name`.
#
# @param[in] name
# The name of the layer.
# @param[in,opt] type
# This specifies the type of the pattern of this layer. One of the
# following values. The default is `regexp`.
#
# regexp
# Regular expression
# glob
# Extended glob pattern
# glob-shortest
# Extended glob pattern. This tries to find the shortest match.
#
# @fn ble/highlight/layer:{pattern}/register name pattern gspec
# Register a pattern to the layer specified by `name`.
#
# @param[in] name
# The name of the layer.
# @param[in] pattern
# The pattern. The type of the pattern is specified by the argument `type`
# when the layer is created by `ble/highlight/layer:{pattern}/declare`.
# @param[in] gspec
# A string specifying the graphic style. See the description of
# ble/color/gspec2g.
#
#
# ```bash
# # blerc
#
# ble-import layer/pattern
#
# ble/highlight/layer:{pattern}/declare pattern1
# ble/highlight/layer:{pattern}/register pattern1 'rm -rf [^;&|]*' 'fg=white,bold,bg=red'
# ble/array#insert-after _ble_highlight_layer_list syntax pattern1
#
# ble/highlight/layer:{pattern}/declare pattern2
# ble/highlight/layer:{pattern}/register pattern2 "$USER" 'fg=blue,bold'
# ble/highlight/layer:{pattern}/register pattern2 "$HOSTNAME" 'fg=green,bold'
# ble/highlight/layer:{pattern}/register pattern2 '[0-9]+' 'bg=216,fg=black'
# ble/array#insert-after _ble_highlight_layer_list pattern1 pattern2
# ```
function ble/highlight/layer:{pattern}/declare {
local layer_name=$1 type=${2-regexp}
case $type in
(regexp | glob) ;;
(glob-shortest) type=sglob ;;
(*)
ble/util/print "$FUNCNAME: unrecognized pattern type '$type'." >&2
return 2 ;;
esac
# define dynamic variables
local layer_prefix=_ble_highlight_layer_${layer_name}_
ble/highlight/layer:{selection}/declare "$layer_name"
ble/array#push "${layer_prefix}VARNAMES" "${layer_prefix}text"
ble/util/set "${layer_prefix}text" ''
# define settings
local keys=${layer_prefix}keys
local dict=${layer_prefix}dict
builtin eval -- "
${layer_prefix}type=$type
$keys=()
${_ble_util_gdict_declare//NAME/$dict}"
# define functions
local _ble_local_script='
function ble/highlight/layer:LAYER/initialize-vars {
ble/highlight/layer:{pattern}/initialize-vars LAYER
}
function ble/highlight/layer:LAYER/update {
ble/highlight/layer:{pattern}/update LAYER "$@"
}
function ble/highlight/layer:LAYER/getg {
ble/highlight/layer:{pattern}/getg LAYER "$@"
}'
builtin eval -- "${_ble_local_script//LAYER/$layer_name}"
}
function ble/highlight/layer:{pattern}/initialize-vars {
local layer_name=$1
ble/highlight/layer:{selection}/initialize-vars "$layer_name"
ble/util/set "_ble_highlight_layer_${layer_name}_text" ''
}
function ble/highlight/layer:{pattern}/register {
local layer_name=$1 pattern=$2 spec=${3-}
local keys=_ble_highlight_layer_${layer_name}_keys
local dict=_ble_highlight_layer_${layer_name}_dict
if [[ ${3+set} ]]; then
local ret
ble/color/gspec2g "$spec"
ble/gdict#has "$dict" "$pattern" ||
ble/array#push "$keys" "$pattern"
ble/gdict#set "$dict" "$pattern" "$ret"
else
ble/array#remove "$keys" "$pattern"
ble/gdict#unset "$dict" "$pattern"
fi
}
##-----------------------------------------------------------------------------
## Pattern types
##
## Each pattern layer instance is associated with a pattern type, and the
## pattern type defines how the pattern specified to layer:{pattern}/register
## should be treated. Currently, three types `regexp`, `glob`, and `sglob` are
## defined. To define a new pattern type, the following three functions should
## be prepared.
##
## @fn ble/highlight/layer:{pattern}/pattern:<TYPE>/create-gpat
## This function composes a pattern matching any of the registered patterns
##
## @arr[in] keys
## The list of patterns registered to the current layer.
## @var[out] gpat
## Stores a pattern that matches any of the registered pattern.
##
## @fn ble/highlight/layer:{pattern}/pattern:<TYPE>/match text pat
## This function tries to match PAT in TEXT and, if matching, stores the
## matched range in [MBEG, MEND) and returns the unmatched suffix in
## NEW_TAIL.
##
## @param[in] text
## The string where a matching substring is searched.
## @param[in] pat
## The pattern
## @var[out] mbeg mend
## Stores the matched range in TEXT. MBEG and MEND are the beginning and
## the end of the range, respectively.
## @var[out] new_tail
## Stores the unmatched remaining part of TEXT. In particular, the
## substring after MEND.
## @exit
## 0 if a matching is found, or otherwise 1.
##
## @fn ble/highlight/layer:{pattern}/pattern:regexp/match1 str pat
## This function tests if the specified string exactly matches the pattern.
##
## @param[in] str
## The string to be matched.
## @param[in] pat
## The pattern to matched STR.
## @exit
## 0 if the string matches the pattern, or otherwise 1
##
# pattern type: regexp
function ble/highlight/layer:{pattern}/pattern:regexp/create-gpat {
IFS='|' builtin eval -- 'gpat="(${keys[*]})"'
}
function ble/highlight/layer:{pattern}/pattern:regexp/match {
ble/string#match "$1" "$2(.*)\$" || return 1
new_tail=${BASH_REMATCH[${#BASH_REMATCH[@]}-1]}
mbeg=$((${#1}-${#BASH_REMATCH}))
mend=$((${#1}-${#new_tail}))
return 0
}
function ble/highlight/layer:{pattern}/pattern:regexp/match1 {
[[ $1 =~ ^($2)$ ]]
}
# pattern type: glob (longest extended glob matching)
function ble/highlight/layer:{pattern}/pattern:glob/create-gpat {
IFS='|' builtin eval -- 'gpat="@(${keys[*]})"'
}
function ble/highlight/layer:{pattern}/pattern:glob/match {
local extglob=
shopt -q extglob && extglob=1
shopt -s extglob
local prefix=${1%%$2*} ext=1
if [[ $prefix != "$1" ]]; then
mbeg=${#prefix}
new_tail=${1:mbeg}
new_tail=${new_tail##$2}
((mend=${#1}-${#new_tail}))
ext=0
fi
[[ $extglob ]] || shopt -u extglob
return "$ext"
}
function ble/highlight/layer:{pattern}/pattern:glob/match1 {
[[ $1 == $2 ]]
}
# pattern type: sglob (shortest extended glob matching)
function ble/highlight/layer:{pattern}/pattern:sglob/create-gpat {
ble/highlight/layer:{pattern}/pattern:glob/create-gpat
}
function ble/highlight/layer:{pattern}/pattern:sglob/match {
local extglob=
shopt -q extglob && extglob=1
shopt -s extglob
local prefix=${1%%$2*} ext=1
if [[ $prefix != "$1" ]]; then
mbeg=${#prefix}
new_tail=${1:mbeg}
new_tail=${new_tail#$2}
((mend=${#1}-${#new_tail}))
ext=0
fi
[[ $extglob ]] || shopt -u extglob
return "$ext"
}
function ble/highlight/layer:{pattern}/pattern:sglob/match1 {
ble/highlight/layer:{pattern}/pattern:glob/match1 "$@"
}
##-----------------------------------------------------------------------------
function ble/highlight/layer:{pattern}/.match {
# If the text has the same content as the previous time, we skip the
# matching.
#
# Note: Initially, ((DMIN<0)) was used for the condition but turned out to be
# unusable for this purpose. DMIN only changes when the full content
# including the auto_complete insertion is changed. Even if the substantial
# part (excluding the auto_complete insertion) changes, DMIN can be negative
# when the full content does not change.
local rtext=_ble_highlight_layer_${1}_text
local text=$2 otext=${!rtext}
[[ $otext && $text == "$otext" ]] && return 0
ble/util/set "$rtext" "$text"
local ret
local dict=_ble_highlight_layer_${1}_dict
sel=() gflags=()
# Retrieve regular expressions
local keys type
ble/util/restore-vars "_ble_highlight_layer_${1}_" keys type
((${#keys[@]})) || return 0
local gpat
ble/highlight/layer:{pattern}/pattern:"$type"/create-gpat
local g0=
if ((${#keys[@]}==1)); then
ble/gdict#get "$dict" "${keys[0]}" && g0=$ret
fi
local offset=0 tail=$text new_tail mbeg mend m
while [[ $tail ]] && ble/highlight/layer:{pattern}/pattern:"$type"/match "$tail" "$gpat"; do
((mbeg+=offset,mend+=offset))
if ((mbeg<mend)); then
# determine gflags of the selection
local g1=$g0
if [[ ! $g1 ]]; then
local pat1 m=${tail:mbeg-offset:mend-mbeg}
for pat1 in "${keys[@]}"; do
if ble/highlight/layer:{pattern}/pattern:"$type"/match1 "$m" "$pat1"; then
ble/gdict#get "$dict" "$pat1" && g1=$ret
break
fi
done
fi
# add selection with the gflags
if [[ $g1 ]]; then
if ((mbeg==offset&&${#sel[@]})) && [[ ${gflags[${#gflags[@]}-1]} == $g1 ]]; then
# extend the previous selection
sel[${#sel[@]}-1]=$mend
else
# add a new selection
ble/array#push sel "$mbeg" "$mend"
ble/array#push gflags "$g1"
fi
fi
fi
if ((mend==offset)); then
# step at least one character to avoid infinite matching
((offset++))
tail=${tail:1}
else
offset=$mend
tail=$new_tail
fi
done
}
function ble/highlight/layer:{pattern}/update {
local layer_name=$1 text=$2
local sel=-1 gflags=-1
local text1=$text
if [[ $_ble_edit_mark_active == auto_complete ]]; then
# When there is an insertion by auto-complete, we exclude that part from
# the matching target.
local a=$_ble_edit_ind b=$_ble_edit_mark
text1=${text::a}${text:b}
fi
ble/highlight/layer:{pattern}/.match "$layer_name" "$text1"
if [[ $_ble_edit_mark_active == auto_complete ]]; then
# The generated positions in the array "sel" is for "text1" where the
# auto-complete insertion is excluded. Here, we shift the positions in
# "sel" to consider the excluded part.
local i
for i in "${!sel[@]}"; do
((sel[i]>a||sel[i]==a&&i%2==0)) && ((sel[i]+=b-a))
done
fi
ble/highlight/layer:{selection}/update "$layer_name" "$text"
}
function ble/highlight/layer:{pattern}/getg {
ble/highlight/layer:{selection}/getg "$@"
}

View file

@ -0,0 +1,35 @@
# ble/contrib/layer/sample.RandomColor.bash (C) 2015, akinomyoga
#
# This is a sample implementation of a highlighting layer. This defines a
# random-color layer named `RandomColor'. To use the layer, the layer name can
# be inserted in the array `_ble_highlight_layer_list`:
#
# ```bash
# ble-import layer/sample.RandomColor
# ble/array#insert-before _ble_highlight_layer_list syntax RandomColor
# ```
#
# Note: This code was first introduced in commit ec3f3661b@akinomyoga/ble.sh
# (2015-02-23) but later moved to contrib in refactoring on 2023-09-19.
_ble_highlight_layer_RandomColor_buff=()
function ble/highlight/layer:RandomColor/update {
local text=$1 ret i
_ble_highlight_layer_RandomColor_buff=()
for ((i=0;i<${#text};i++)); do
# _ble_highlight_layer_RandomColor_buff[i] に "<sgr><表示文字>" を設定する。
# "<表示文字>" は ${_ble_highlight_layer_plain_buff[i]} でなければならない
# (或いはそれと文字幅が同じ物…ただそれが反映される保証はない)。
ble/color/gspec2sgr "fg=$((RANDOM%256))"
_ble_highlight_layer_RandomColor_buff[i]=$ret${_ble_highlight_layer_plain_buff[i]}
done
PREV_BUFF=_ble_highlight_layer_RandomColor_buff
((PREV_UMIN=0,PREV_UMAX=${#text}))
}
function ble/highlight/layer:RandomColor/getg {
# ここでは乱数を返しているが、実際は
# PREV_BUFF=_ble_highlight_layer_RandomColor_buff
# に設定した物に対応する物を指定しないと表示が変になる。
local ret; ble/color/gspec2g "fg=$((RANDOM%256))"; g=$ret
}

View file

@ -0,0 +1,34 @@
# ble/contrib/layer/sample.RandomColor2.bash (C) 2015, akinomyoga
#
# This is a sample implementation of a highlighting layer. This defines a
# random-color layer named `RandomColor2'. To use the layer, the layer name can
# be inserted in the array `_ble_highlight_layer_list`:
#
# ```bash
# ble-import layer/sample.RandomColor2
# ble/array#insert-before _ble_highlight_layer_list syntax RandomColor2
# ```
#
# Note: This code was first introduced in commit ec3f3661b@akinomyoga/ble.sh
# (2015-02-23) but later moved to contrib in refactoring on 2023-09-19.
_ble_highlight_layer_RandomColor2_buff=()
function ble/highlight/layer:RandomColor2/update {
local text=$1 ret i x
ble/highlight/layer/update/shift _ble_highlight_layer_RandomColor2_buff
for ((i=DMIN;i<DMAX;i++)); do
ble/color/gspec2sgr "fg=$((16+(x=RANDOM%27)*4-x%9*2-x%3))"
_ble_highlight_layer_RandomColor2_buff[i]=$ret${_ble_highlight_layer_plain_buff[i]}
done
PREV_BUFF=_ble_highlight_layer_RandomColor2_buff
((PREV_UMIN=0,PREV_UMAX=${#text}))
}
function ble/highlight/layer:RandomColor2/getg {
# ここでは乱数を返しているが、実際は
# PREV_BUFF=_ble_highlight_layer_RandomColor2_buff
# に設定した物に対応する物を指定しないと表示が変になる。
local x ret
ble/color/gspec2g "fg=$((16+(x=RANDOM%27)*4-x%9*2-x%3))"; g=$ret
}

View file

@ -0,0 +1,373 @@
# ble/contrib/layer/sample.adapter.bash (C) 2015, akinomyoga
#
# The first implementation of ``syntax highlighting'' in ble.sh.
#
# This file contains a historical implementation of syntax highlighting in
# ble.sh. The initial interface was different from the current one (see the
# functions ble/syntax-highlight+*). When the new interface is introduced in
# commit ec3f3661b (2015-02-23), the `adapter` layer was implemented to bridge
# the older implementation of highlighter and the new layer interface. This
# served the first implementation of syntax highlighting and later replaced by
# a serious implementation based on a real shell parser (ble-syntax.sh,
# currently src/core-syntax.sh). After a long time since it was superceded,
# the layer definition was moved from the main file `ble-color.sh` to the
# archive `archive/layer/adapter.sh` in commit afdcc1598 (2017-03-05 #D0386).
# Now, let us move the file to the `contrib` repository and preserve it as a
# historical implementation of ``syntax highlighting'' and an illustration of
# implementing a layer.
#
# To try this layer, you can set the following values to the array
# `_ble_highlight_layer_list`:
#
# ```bash
# ble-import layer/sample.adapter
# _ble_highlight_layer_list=(plain adapter)
# ```
#
# It should be noted that the behavior related the selected regions seems buggy
# within the current layer model.
##
## レイヤー ble/highlight/layer:adapter
##
## 古い枠組みに依る色つけのアダプターを
## レイヤーの実装のサンプルとして此処に残す。
## 使う場合は例えば以下の様にする。
##
## _ble_highlight_layer_list=(plain adapter)
##
##
bleopt/declare -v syntax_highlight_mode default
_ble_region_highlight_table=()
## 古い実装からの adapter
_ble_highlight_layer_adapter_buff=()
_ble_highlight_layer_adapter_table=()
function ble/highlight/layer:adapter/update {
local text=$1 player=$2
# update g table
local LAYER_UMIN LAYER_UMAX
local -a _ble_region_highlight_table
ble/highlight/layer/update/shift _ble_region_highlight_table _ble_highlight_layer_adapter_table
if [[ $bleopt_syntax_highlight_mode ]]; then
# LAYER_UMIN を設定しない highlight_mode の場合はそのまま。
# LAYER_UMIN を設定する highlight_mode の場合は参照せずに上書きされる。
LAYER_UMIN=0 LAYER_UMAX=$iN
"ble/syntax-highlight+$bleopt_syntax_highlight_mode" "$text"
else
LAYER_UMIN=$iN LAYER_UMAX=0
fi
_ble_highlight_layer_adapter_table=("${_ble_region_highlight_table[@]}")
# 描画文字を更新する範囲 [i1,i2]
# text[i2] (更新範囲の次の文字) の SGR に影響を与えない為、
# 実際の更新は text[i2] に対しても行う。
((PREV_UMIN>=0&&LAYER_UMIN>PREV_UMIN&&(LAYER_UMIN=PREV_UMIN),
PREV_UMAX>=0&&LAYER_UMAX<PREV_UMAX&&(LAYER_UMAX=PREV_UMAX)))
local i1=$LAYER_UMIN i2=$LAYER_UMAX
((i2>=iN&&(i2=iN-1)))
# update char buffer
ble/highlight/layer/update/shift _ble_highlight_layer_adapter_buff
local i g gprev=0 ctx=0 ret
((i1>0)) && { ble/highlight/layer/getg "$((i1-1))"; gprev=$g; }
# ble/edit/info/show text "layer:adapter u = $i1-$i2"
for ((i=i1;i<=i2;i++)); do
local ch
if [[ ${_ble_region_highlight_table[i]} ]]; then
ch=${_ble_highlight_layer_plain_buff[i]}
((g=_ble_region_highlight_table[i]))
if ((ctx!=0||g!=gprev)); then
((ctx=0,gprev=g))
ble/color/g2sgr "$g"
ch=$ret$ch
fi
else
builtin eval "ch=\${$PREV_BUFF[i]}"
if ((ctx!=1)); then
((ctx=1,gprev=-1))
ble/highlight/layer/update/getg
ble/color/g2sgr "$g"
ch=$ret$ch
fi
fi
_ble_highlight_layer_adapter_buff[i]=$ch
done
PREV_BUFF=_ble_highlight_layer_adapter_buff
if ((LAYER_UMIN<LAYER_UMAX)); then
((PREV_UMIN=LAYER_UMIN,PREV_UMAX=LAYER_UMAX))
else
((PREV_UMIN=-1,PREV_UMAX=-1))
fi
}
function ble/highlight/layer:adapter/getg {
# 描画属性がない時は _ble_region_highlight_table[i]
# には空文字列が入っているのでOK
g=${_ble_highlight_layer_adapter_table[$1]}
}
#------------------------------------------------------------------------------
## @fn _ble_region_highlight_table; ble/syntax-highlight/append triplets ; _ble_region_highlight_table
function ble/syntax-highlight/append {
while (($#)); do
local -a triplet
triplet=($1)
local ret; ble/color/gspec2g "${triplet[2]}"; local g=$ret
local i=${triplet[0]} iN=${triplet[1]}
for ((;i<iN;i++)); do
_ble_region_highlight_table[$i]=$g
done
shift
done
}
function ble/syntax-highlight+region {
if [[ $_ble_edit_mark_active ]]; then
if ((_ble_edit_mark>_ble_edit_ind)); then
ble/syntax-highlight/append "$_ble_edit_ind $_ble_edit_mark bg=60,fg=white"
elif ((_ble_edit_mark<_ble_edit_ind)); then
ble/syntax-highlight/append "$_ble_edit_mark $_ble_edit_ind bg=60,fg=white"
fi
fi
}
function ble/syntax-highlight+test {
local text=$1
local i iN=${#text} w
local mode=cmd
for ((i=0;i<iN;)); do
local tail=${text:i} rex
if [[ $mode == cmd ]]; then
if rex='^[_a-zA-Z][_a-zA-Z0-9]*=' && [[ $tail =~ $rex ]]; then
# 変数への代入
local var=${tail%%=*}
ble/syntax-highlight/append "$i $((i+${#var})) fg=orange"
((i+=${#var}+1))
mode=rhs
elif rex='^[_a-zA-Z][_a-zA-Z0-9]*\[[^]]+\]=' && [[ $tail =~ $rex ]]; then
# 配列変数への代入
local var="${tail%%\[*}"
ble/syntax-highlight/append "$i $((i+${#var})) fg=orange"
((i+=${#var}+1))
local tmp="${tail%%\]=*}"
local ind="${tmp#*\[}"
ble/syntax-highlight/append "$i $((i+${#ind})) fg=green"
((i+=${#var}+1))
mode=rhs
elif rex='^[^ "'\'']+([ ]|$)' && [[ $tail =~ $rex ]]; then
local cmd="${tail%%[ ]*}" cmd_type
ble/util/type cmd_type "$cmd"
case $cmd_type:$cmd in
builtin:*)
ble/syntax-highlight/append "$i $((i+${#cmd})) fg=red" ;;
alias:*)
ble/syntax-highlight/append "$i $((i+${#cmd})) fg=teal" ;;
function:*)
ble/syntax-highlight/append "$i $((i+${#cmd})) fg=navy" ;;
file:*)
ble/syntax-highlight/append "$i $((i+${#cmd})) fg=green" ;;
keyword:*)
ble/syntax-highlight/append "$i $((i+${#cmd})) fg=blue" ;;
*)
ble/syntax-highlight/append "$i $((i+${#cmd})) bg=224" ;;
esac
((i+=${#cmd}))
mode=arg
else
((i++))
fi
else
((i++))
fi
done
ble/syntax-highlight+region "$@"
# ble/syntax-highlight/append "${#text1} $((${#text1}+1)) standout"
}
function ble/syntax-highlight+default/type {
type=$1
local cmd=$2
case $type:$cmd in
(builtin::|builtin:.)
# 見にくいので太字にする
type=builtin_bold ;;
(builtin:*)
type=builtin ;;
(alias:*)
type=alias ;;
(function:*)
type=function ;;
(file:*)
type=file ;;
(keyword:*)
type=keyword ;;
(*:%*)
# jobs
ble/util/joblist.check
if jobs "$cmd" &>/dev/null; then
type=jobs
else
type=error
fi ;;
(*)
type=error ;;
esac
}
function ble/syntax-highlight+default {
local rex IFS=$_ble_term_IFS
local text=$1
local i iN=${#text} w
local mode=cmd
for ((i=0;i<iN;)); do
local tail=${text:i}
if [[ $mode == cmd ]]; then
if rex='^([_a-zA-Z][_a-zA-Z0-9]*)\+?=' && [[ $tail =~ $rex ]]; then
# for bash-3.1 ${#arr[n]} bug
local rematch1="${BASH_REMATCH[1]}"
# local var="${BASH_REMATCH[0]::-1}"
ble/syntax-highlight/append "$i $((i+$rematch1)) fg=orange"
((i+=${#BASH_REMATCH}))
mode=rhs
continue
elif rex='^([^'"$IFS"'|&;()<>'\''"\]|\\.)+' && [[ $tail =~ $rex ]]; then
# ■ time'hello' 等の場合に time だけが切り出されてしまう
local word=${BASH_REMATCH[0]}
builtin eval "local cmd=${word}"
# この部分の判定で fork を沢山する \if 等に対しては 4fork+2exec になる。
# ■キャッシュ(accept-line 時に clear)するなどした方が良いかもしれない。
local type; ble/util/type type "$cmd"
ble/syntax-highlight+default/type "$type" "$cmd" # -> type
if [[ $type = alias && $cmd != "$word" ]]; then
# alias を \ で無効化している場合
# → unalias して再度 check (2fork)
type=$(
builtin unalias "$cmd"
ble/util/type type "$cmd"
ble/syntax-highlight+default/type "$type" "$cmd" # -> type
ble/util/put "$type")
elif [[ "$type" = keyword && "$cmd" != "$word" ]]; then
# keyword (time do if function else elif fi の類) を \ で無効化している場合
# →file, function, builtin, jobs のどれかになる。以下 3fork+2exec
ble/util/joblist.check
if [[ ! ${cmd##%*} ]] && jobs "$cmd" &>/dev/null; then
# %() { :; } として 関数を定義できるが jobs の方が優先される。
# (% という名の関数を呼び出す方法はない?)
# でも % で始まる物が keyword になる事はそもそも無いような。
type=jobs
elif ble/is-function "$cmd"; then
type=function
elif enable -p | ble/bin/grep -q -F -x "enable $cmd" &>/dev/null; then
type=builtin
elif which "$cmd" &>/dev/null; then
type=file
else
type=error
fi
fi
case $type in
(file)
ble/syntax-highlight/append "$i $((i+${#word})) fg=green" ;;
(alias)
ble/syntax-highlight/append "$i $((i+${#word})) fg=teal" ;;
(function)
ble/syntax-highlight/append "$i $((i+${#word})) fg=navy" ;;
(builtin)
ble/syntax-highlight/append "$i $((i+${#word})) fg=red" ;;
(builtin_bold)
ble/syntax-highlight/append "$i $((i+${#word})) fg=red,bold" ;;
(keyword)
ble/syntax-highlight/append "$i $((i+${#word})) fg=blue" ;;
(jobs)
ble/syntax-highlight/append "$i $((i+1)) fg=red" ;;
(error|*)
ble/syntax-highlight/append "$i $((i+${#word})) bg=224" ;;
esac
((i+=${#BASH_REMATCH}))
if rex='^keyword:([!{]|time|do|if|then|else|while|until)$|^builtin:eval$' && [[ $type:$cmd =~ $rex ]]; then
mode=cmd
else
mode=arg
fi
continue
fi
elif [[ $mode == arg ]]; then
if rex='^([^"$'"$IFS"'|&;()<>'\''"`\]|\\.)+' && [[ $tail =~ $rex ]]; then
# ■ time'hello' 等の場合に time だけが切り出されてしまう
local arg=${BASH_REMATCH[0]}
local file=$arg
[[ ( $file == '~' || $file = '~/'* ) && ! ( -e $file || -h $file ) ]] && file=$HOME${file:1}
if [[ -d $file ]]; then
ble/syntax-highlight/append "$i $((i+${#arg})) fg=navy,underline"
elif [[ -h $file ]]; then
ble/syntax-highlight/append "$i $((i+${#arg})) fg=teal,underline"
elif [[ -x $file ]]; then
ble/syntax-highlight/append "$i $((i+${#arg})) fg=green,underline"
elif [[ -f $file ]]; then
ble/syntax-highlight/append "$i $((i+${#arg})) underline"
fi
((i+=${#arg}))
continue
fi
fi
# /^'([^'])*'|^\$'([^\']|\\.)*'|^`([^\`]|\\.)*`|^\\./
if rex='^'\''([^'\''])*'\''|^\$'\''([^\'\'']|\\.)*'\''|^`([^\`]|\\.)*`|^\\.' && [[ $tail =~ $rex ]]; then
ble/syntax-highlight/append "$i $((i+${#BASH_REMATCH})) fg=green"
((i+=${#BASH_REMATCH}))
mode=arg_
continue
elif rex='^['"$IFS"']+' && [[ $tail =~ $rex ]]; then
((i+=${#BASH_REMATCH}))
local spaces=${BASH_REMATCH[0]}
if [[ $spaces =~ $'\n' ]]; then
mode=cmd
else
[[ $mode = arg_ ]] && mode=arg
fi
continue
elif rex='^;;?|^;;&$|^&&?|^\|\|?' && [[ $tail =~ $rex ]]; then
if [[ $mode = cmd ]]; then
ble/syntax-highlight/append "$i $((i+${#BASH_REMATCH})) bg=224"
fi
((i+=${#BASH_REMATCH}))
mode=cmd
continue
elif rex='^(&?>>?|<>?|[<>]&)' && [[ $tail =~ $rex ]]; then
ble/syntax-highlight/append "$i $((i+${#BASH_REMATCH})) bold"
((i+=${#BASH_REMATCH}))
mode=arg
continue
elif rex='^(' && [[ $tail =~ $rex ]]; then
((i+=${#BASH_REMATCH}))
mode=cmd
continue
fi
# 他 "...", ${}, $... arg と共通
((i++))
# a[]=... の引数は、${} や "" を考慮に入れるだけでなく [] の数を数える。
done
ble/syntax-highlight+region "$@"
}