Skip to content

Commit 9ee0994

Browse files
committed
main: Add arithmetic substitution highlighting
Closes zsh-users#607 zsh-users#649 zsh-users#704
1 parent d1802e3 commit 9ee0994

File tree

5 files changed

+121
-26
lines changed

5 files changed

+121
-26
lines changed

changelog.md

+3-7
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,6 @@
4444
- Fix `echo >&p` highlighting the `p` as a filename if a file by that name happened to exist
4545
[part of #645]
4646

47-
- Fix `: $((42))` being highlighted as a subshell.
48-
[part of #607]
49-
50-
- Regress highlighting of `: $((ls); (ls))`: is a subshell, but will now be
51-
incorrectly highlighted as an arithmetic expansion.
52-
[#704]
53-
5447
- Fix wrong highlighting of unquoted parameter expansions under zsh 5.2 and older
5548
[e165f18c758e]
5649

@@ -73,6 +66,9 @@
7366

7467
- Recognize `env` as a precommand (e.g., `env FOO=bar ls`)
7568

69+
- Highlight arithmetic substitutions (e.g., `$(( 42 ))`)
70+
[#607 #649 #704]
71+
7672
# Changes in version 0.7.1
7773

7874
- Remove out-of-date information from the 0.7.0 changelog.

docs/highlighters/main.md

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ This highlighter defines the following styles:
4242
* `command-substitution-delimiter-quoted` - a quoted command substitution delimiters (`"$(` and `)"`)
4343
* `process-substitution` - process substitutions (`<(echo foo)`)
4444
* `process-substitution-delimiter` - process substitution delimiters (`<(` and `)`)
45+
* `arithmetic-substitution` - arithmentic substitution (`$(( 42 ))`)
4546
* `single-hyphen-option` - single-hyphen options (`-o`)
4647
* `double-hyphen-option` - double-hyphen options (`--option`)
4748
* `back-quoted-argument` - backtick command substitution (`` `foo` ``)

highlighters/main/main-highlighter.zsh

+106-9
Original file line numberDiff line numberDiff line change
@@ -1300,8 +1300,13 @@ _zsh_highlight_main_highlighter_highlight_argument()
13001300
(( i = REPLY ))
13011301
highlights+=($reply)
13021302
continue
1303-
elif [[ $arg[i+1] == $'\x28' && ${arg:$i} != $'\x28\x28'*$'\x29\x29'* ]]; then
1304-
# command substitution that doesn't look like an arithmetic expansion
1303+
elif [[ $arg[i+1] == $'\x28' ]]; then
1304+
if [[ $arg[i+2] == $'\x28' ]] && _zsh_highlight_main_highlighter_highlight_arithmetic $i; then
1305+
# Arithmetic substitution
1306+
(( i = REPLY ))
1307+
highlights+=($reply)
1308+
continue
1309+
fi
13051310
start=$i
13061311
(( i += 2 ))
13071312
_zsh_highlight_main_highlighter_highlight_list $(( start_pos + i - 1 )) S $has_end $arg[i,-1]
@@ -1316,10 +1321,6 @@ _zsh_highlight_main_highlighter_highlight_argument()
13161321
highlights+=($(( start_pos + i - 1)) $(( start_pos + i )) command-substitution-delimiter-unquoted)
13171322
fi
13181323
continue
1319-
else
1320-
# TODO: if it's an arithmetic expansion, skip past it, to prevent
1321-
# multiplications from being highlighted as globbing (issue #607,
1322-
# test-data/arith1.zsh)
13231324
fi
13241325
while [[ $arg[i+1] == [=~#+'^'] ]]; do
13251326
(( i += 1 ))
@@ -1447,11 +1448,17 @@ _zsh_highlight_main_highlighter_highlight_double_quote()
14471448
# $#, $*, $@, $?, $- - like $$ above
14481449
(( k += 1 )) # highlight both dollar signs
14491450
(( i += 1 )) # don't consider the second one as introducing another parameter expansion
1450-
elif [[ $arg[i+1] == $'\x28' && ${arg:$i} != $'\x28\x28'*$'\x29\x29'* ]]; then
1451-
# command substitution that doesn't look like an arithmetic expansion
1451+
elif [[ $arg[i+1] == $'\x28' ]]; then
1452+
saved_reply=($reply)
1453+
if [[ $arg[i+2] == $'\x28' ]] && _zsh_highlight_main_highlighter_highlight_arithmetic $i; then
1454+
# Arithmetic substitution
1455+
(( i = REPLY ))
1456+
reply=($saved_reply $reply)
1457+
continue
1458+
fi
1459+
14521460
breaks+=( $last_break $(( start_pos + i - 1 )) )
14531461
(( i += 2 ))
1454-
saved_reply=($reply)
14551462
_zsh_highlight_main_highlighter_highlight_list $(( start_pos + i - 1 )) S $has_end $arg[i,-1]
14561463
ret=$?
14571464
(( i += REPLY ))
@@ -1632,6 +1639,96 @@ _zsh_highlight_main_highlighter_highlight_backtick()
16321639
REPLY=$i
16331640
}
16341641

1642+
# Highlight special chars inside arithmetic substitutions
1643+
_zsh_highlight_main_highlighter_highlight_arithmetic()
1644+
{
1645+
local -a saved_reply
1646+
local style
1647+
integer i j k paren_depth ret
1648+
reply=()
1649+
1650+
for (( i = $1 + 3 ; i <= end_pos - start_pos ; i += 1 )) ; do
1651+
(( j = i + start_pos - 1 ))
1652+
(( k = j + 1 ))
1653+
case "$arg[$i]" in
1654+
[\'\"\\@{}])
1655+
style=unknown-token
1656+
;;
1657+
'(')
1658+
(( paren_depth++ ))
1659+
continue
1660+
;;
1661+
')')
1662+
if (( paren_depth )); then
1663+
(( paren_depth-- ))
1664+
continue
1665+
fi
1666+
[[ $arg[i+1] == ')' ]] && { (( i++ )); break; }
1667+
# Special case ) at the end of the buffer to avoid flashing command substitution for a character
1668+
(( has_end && (len == k) )) && break
1669+
# This is a single paren and there are no open parens, so this isn't an arithmetic substitution
1670+
return 1
1671+
;;
1672+
'`')
1673+
saved_reply=($reply)
1674+
_zsh_highlight_main_highlighter_highlight_backtick $i
1675+
(( i = REPLY ))
1676+
reply=($saved_reply $reply)
1677+
continue
1678+
;;
1679+
'$' )
1680+
if [[ $arg[i+1] == $'\x28' ]]; then
1681+
saved_reply=($reply)
1682+
if [[ $arg[i+2] == $'\x28' ]] && _zsh_highlight_main_highlighter_highlight_arithmetic $i; then
1683+
# Arithmetic substitution
1684+
(( i = REPLY ))
1685+
reply=($saved_reply $reply)
1686+
continue
1687+
fi
1688+
1689+
(( i += 2 ))
1690+
_zsh_highlight_main_highlighter_highlight_list $(( start_pos + i - 1 )) S $has_end $arg[i,end_pos]
1691+
ret=$?
1692+
(( i += REPLY ))
1693+
reply=(
1694+
$saved_reply
1695+
$j $(( start_pos + i )) command-substitution-quoted
1696+
$j $(( j + 2 )) command-substitution-delimiter-quoted
1697+
$reply
1698+
)
1699+
if (( ret == 0 )); then
1700+
reply+=($(( start_pos + i - 1 )) $(( start_pos + i )) command-substitution-delimiter)
1701+
fi
1702+
continue
1703+
else
1704+
continue
1705+
fi
1706+
;;
1707+
($histchars[1]) # ! - may be a history expansion
1708+
if [[ $arg[i+1] != ('='|$'\x28'|$'\x7b'|[[:blank:]]) ]]; then
1709+
style=history-expansion
1710+
else
1711+
continue
1712+
fi
1713+
;;
1714+
*)
1715+
continue
1716+
;;
1717+
1718+
esac
1719+
reply+=($j $k $style)
1720+
done
1721+
1722+
if [[ $arg[i] != ')' ]]; then
1723+
# If unclosed, i points past the end
1724+
(( i-- ))
1725+
fi
1726+
style=arithmetic-expansion
1727+
reply=($(( start_pos + $1 - 1)) $(( start_pos + i )) arithmetic-expansion $reply)
1728+
REPLY=$i
1729+
}
1730+
1731+
16351732
# Called with a single positional argument.
16361733
# Perform filename expansion (tilde expansion) on the argument and set $REPLY to the expanded value.
16371734
#

highlighters/main/test-data/arith-cmdsubst-mess.zsh

+10-10
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ BUFFER=$': $((ls); (ls))'
3333
expected_region_highlight=(
3434
'1 1 builtin' # :
3535
'3 15 default' # $((ls); (ls))
36-
'3 15 command-substitution-unquoted "issue #704"' # $((ls); (ls))
37-
'3 4 command-substitution-delimiter-unquoted "issue #704"' # $(
38-
'5 5 reserved-word "issue #704"' # (
39-
'6 7 command "issue #704"' # ls
40-
'8 8 reserved-word "issue #704"' # )
41-
'9 9 commandseparator "issue #704"' # ;
42-
'11 11 reserved-word "issue #704"' # (
43-
'12 13 command "issue #704"' # ls
44-
'14 14 reserved-word "issue #704"' # )
45-
'15 15 command-substitution-delimiter-unquoted "issue #704"' # )
36+
'3 15 command-substitution-unquoted' # $((ls); (ls))
37+
'3 4 command-substitution-delimiter-unquoted' # $(
38+
'5 5 reserved-word' # (
39+
'6 7 command' # ls
40+
'8 8 reserved-word' # )
41+
'9 9 commandseparator' # ;
42+
'11 11 reserved-word' # (
43+
'12 13 command' # ls
44+
'14 14 reserved-word' # )
45+
'15 15 command-substitution-delimiter-unquoted' # )
4646
)

highlighters/main/test-data/arith2.zsh

+1
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ expected_region_highlight=(
3434
'1 1 builtin' # :
3535
'3 16 default' # "$(( 6 * 9 ))"
3636
'3 16 double-quoted-argument' # "$(( 6 * 9 ))"
37+
'4 15 arithmetic-expansion' # $(( 6 * 9 ))
3738
)

0 commit comments

Comments
 (0)