Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: [PATCH] Completion: Minor improvements to _comm and _sed
- X-seq: zsh-workers 43078
- From: Oliver Kiddle <okiddle@xxxxxxxxxxx>
- To: dana <dana@xxxxxxx>
- Subject: Re: [PATCH] Completion: Minor improvements to _comm and _sed
- Date: Wed, 20 Jun 2018 15:06:28 +0200
- Authentication-results: amavisd4.gkg.net (amavisd-new); dkim=pass (2048-bit key) header.d=yahoo.co.uk
- Cc: Zsh workers <zsh-workers@xxxxxxx>
- Dkim-signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.co.uk; s=s2048; t=1529499993; bh=Bd85iauROyLn/qXbZJmAZKi5CSI/gDvBLalG7PDq6IE=; h=From:References:To:Subject:Date:From:Subject; b=VoVThC1L1E9W1oKApOr1vbSC5UHIz4jOdtl68xn25tBoTz+0goKdgArn73CwzWdcRScxNJUh0rqdJ/+91wEWBCN00qY/mQhJNHWCG/mbzjTdd0GVCGCs2cHxqv4BOnViXb0P/TnjTqBGqp/QEomuG/fTgq6vA3Tg8ierjZ8bXU5BTaYOxDc4ntdC7d/n9Xy1NW7bvBJCaMMp5FeEKL41qKhH903G5iDcUh28s/yjl32ZokNpLxnzaYhFGrBF0mLNF6lQ03tsugElRSLgZgcuSa3Dv3PbscIgARsD9PCY2Jdeq6MysSF9LGAbFWp1eCL0erjQ2JnAsqf0T/ysb/L+2Q==
- In-reply-to: <9F50D6F1-6438-4220-8413-401E6D1998A8@dana.is>
- List-help: <mailto:zsh-workers-help@zsh.org>
- List-id: Zsh Workers List <zsh-workers.zsh.org>
- List-post: <mailto:zsh-workers@zsh.org>
- List-unsubscribe: <mailto:zsh-workers-unsubscribe@zsh.org>
- Mailing-list: contact zsh-workers-help@xxxxxxx; run by ezmlm
- References: <9F50D6F1-6438-4220-8413-401E6D1998A8@dana.is>
On 17 Jun, dana wrote:
>
> * Change all option specs in _sed to use -o+/--opt= instead of -o-/--opt=- (i'm
> not aware of any platform where the latter is appropriate)
GNU sed appears to require that for the -i option as far as I can tell:
% sed -i .bak 's/d/f/'
sed: -e expression #1, char 1: unknown command: `.'
Same with --in-place: you need the =.
-i is the only one where the argument is optional. Not allowing space
separators often goes along with optional arguments.
It also appears that -i implies -s for GNU sed.
The following _sed patch, does quite a bit more besides by completing
the sed functions in the sed expression itself. This covers all the GNU
sed extension commands but I didn't check through sed on various other
systems for variations in the commands. I've tried to make the
patterns reasonably forgiving of odd syntax but _regex_arguments tends
to lead to somewhat unforgiving completions.
_expand mangles some things like single-quoted newlines which doesn't
especially help.
Oliver
diff --git a/Completion/Unix/Command/_sed b/Completion/Unix/Command/_sed
index 80218051b..f03278364 100644
--- a/Completion/Unix/Command/_sed
+++ b/Completion/Unix/Command/_sed
@@ -1,16 +1,60 @@
#compdef sed gsed psed s2p
-local inplace extended
-local -a args aopts=( -A '-*' )
+local variant inplace extended ign sep separator
+local -i nest=0
+local -a args aopts sedexpr cmds_none cmds_slash cmds_end substflags expl bsnl nl labels excl dedup
+local -a step range negate mods
+aopts=( -A '-*' )
+bsnl=( $'\\\n' )
+nl=$'\n'
+compquote nl
+cmds_none=(
+ '{:start group'
+ 'q:quit after printing pattern space'
+ 'h:copy pattern space to hold space'
+ '\::place label'
+ '#:comment'
+ '=:print current line number'
+ 'a:append text'
+ 'i:insert text'
+ 'r:append contents of file'
+ 'b:branch'
+ 't:branch if s command has been successful'
+ 'c:replace line with text'
+ 'l:list current line in visually unambiguous form'
+ 'w:write pattern space to file'
+)
+cmds_slash=(
+ 's:substitute regex'
+ 'y:transliterate characters'
+)
+cmds_end=(
+ 'd:delete pattern space'
+ 'D:delete up to the first newline in the pattern space'
+ 'g:copy hold space to pattern space'
+ 'G:append hold space to pattern space'
+ 'H:append pattern space to hold space'
+ 'n:read the next line of input into pattern space'
+ 'N:append the next line of input to the pattern space'
+ 'p:print the current pattern space'
+ 'P:print upto the first newline of the current pattern space'
+ 'x:exchange hold and pattern spaces'
+ '}:end group'
+)
+substflags=(
+ 'g:replace all matches to the regular expression'
+ 'p:print new pattern space if substitution made'
+ 'w:write result to named file if substitution made'
+)
args=(
'(-n --quiet --silent)'{-n,--quiet,--silent}'[suppress automatic printing of pattern space]'
- '(1)*'{-e+,--expression=}'[specify sed commands to run]:sed script'
- '(1)*'{-f+,--file=}'[add contents of file to commands to run]: :_files'
- '(-e)1: :_guard "^-*" sed script'
+ '(1)*'{-e+,--expression=}'[specify sed commands to run]:sed script:_sed_expressions'
+ '(1)*'{-f+,--file=}'[add contents of file to commands to run]:file:_files'
+ '(-e)1:sed script:_sed_expressions'
'*:input file:_files'
)
-inplace='[edit files in-place, running scripts separately for each file]::suffix for backup'
+inplace='[edit files in-place, running scripts separately for each file]:: :_guard "^(*[@/; ]*|?(#c6,)|-*)" "suffix for backup"'
extended='[use extended regular expressions]'
if [[ $service = (psed|s2p) ]]; then
@@ -18,11 +62,12 @@ if [[ $service = (psed|s2p) ]]; then
"${(@)args:#(|\(*\))(|\*)--*}"
'-a[delay opening files listed with w function]'
)
-elif _pick_variant gnu=GNU unix --version; then
+elif _pick_variant -r variant gnu=GNU unix --version; then
aopts=( )
+ (( $#words > 2 )) && ign='!'
args+=(
'--follow-symlinks[follow symlinks when processing in place]'
- '(-i --in-place)'{-i+,--in-place=}$inplace
+ '(-i --in-place -s --separate)'{-i-,--in-place=-}$inplace
'(-c --copy)'{-c,--copy}'[copy instead of rename when shuffling files in in-place mode]'
'(-l --line-length)'{-l+,--line-length=}'[specify line-wrap length for the l command]'
'(-r)--posix[disable GNU extensions]'
@@ -31,9 +76,28 @@ elif _pick_variant gnu=GNU unix --version; then
'--sandbox[block commands that can affect the system (r/w/W/e)]'
'(-u --unbuffered)'{-u,--unbuffered}'[disable data buffering]'
'(-z --null-data)'{-z,--null-data}'[separate lines by NUL characters]'
- '(- 1 :)--help[print program usage]'
- '(- 1 :)--version[print program version]'
+ "${ign}(- 1 :)--help[print program usage]"
+ "${ign}(- 1 :)--version[print program version]"
)
+ if [[ -z ${words[(r)--posix]} ]]; then
+ cmds_none+=(
+ 'R:append a line from file'
+ 'T:branch if no s command has been successful'
+ 'W:write the first line of pattern space to file'
+ 'v:fail if GNU extensions not supported or older than specified version'
+ )
+ cmds_end+=(
+ "e:execute a command and include it's output"
+ 'F:print the filename of the current input file'
+ 'Q:quit'
+ 'z:empty the pattern space'
+ )
+ substflags+=(
+ 'e:execute pattern space as a command and replace with result'
+ {i,I}':case-insensitive regular expression matching'
+ {m,M}':multi-line matching'
+ )
+ fi
else
args=( "${(@)args:#(|\(*\))(|\*)--*}" )
case $OSTYPE in
@@ -49,11 +113,142 @@ else
freebsd*) args+=( '-u[disable data buffering]' ) ;|
freebsd*|netbsd*)
args+=(
- '-I+[edit files in-place, treating all files as a single input stream]::suffix for backup'
+ '-I+[edit files in-place, treating all files as a single input stream]:: :_guard "^(*[@/; \\\]*|?(#c6,)|-*)" "suffix for backup"'
)
;;
openbsd*) args+=( '-u[make output line buffered]' ) ;;
esac
fi
+zstyle -s ":completion:${curcontext}:address-forms" list-separator separator || separator=--
+step=( "~ $separator step" )
+negate=( "! $separator negated" )
+range=( ", $separator range" )
+mods=( "I $separator case-insensitive" "M $separator multi-line" )
+
+sedexpr=(
+ \( /$'*\0[ \t\n]#'/ \) # strip off any preceding arguments - handled by _arguments
+ \(
+ # Handle an optional address range
+ \(
+ \(
+ \(
+ '///' '/[^/]#//' ':regexes:regex:' # skip /pattern/
+ \|
+ '/\\(?)/' -'sep=${match#?}' # handle \xpatternx
+ \( '/\?/' \| '/?/' -'[[ $match != $sep ]]' \) \# '/?/' -'[[ $match = $sep ]]' ':regexes:regex:'
+ \)
+ $'/[ \t]#/'
+ \( \| '/[IM]##/' -'dedup=( ${(s..)match} )' ':address-forms:address form:compadd -S "" -d mods -F dedup I M' \) \#
+ \|
+ '/([0-9]##|$)[ \t]#/' # line number
+ \(
+ '/\~[ \t]#/' # addr1~N
+ '/[0-9]##[ \t]#/' ': _message -e steps "number - match where line number is a multiple"'
+ \| '//' ':address-forms:address form:compadd -S "" -d step \~' \)
+ \|
+ '/[]/' ': _guard "^([sy]|[^0-9$/\\\]*)" "address - line number or /pattern/"'
+ \)
+ \( # range end, also optional
+ '/[ \t]#,[ \t]#/' -'excl=( \\\# : )' # exclude comments and labels after ranges
+ \(
+ '///' '/[^/]#//' ':regexes:regex:' # handle /pattern/
+ \|
+ '/\\(?)/' -'sep=${match#?}' # handle \xpatternx
+ \( '/\?/' \| '/?/' -'[[ $match != $sep ]]' \) \# '/?/' -'[[ $match = $sep ]]' ':regexes:regex - 2:'
+ \|
+ '/+[ \t]#/' # addr1,+N
+ '/[0-9]##/' ': _message -e number "number of following lines"'
+ \|
+ '/\~[ \t]#/' # addr1,~N
+ '/[0-9]##/' ': _message -e number "following lines until line number is a multiple of specified number"'
+ \|
+ '/([0-9]##|$)/' # line number
+ \|
+ '/[]/' ': _message -e ranges "ending line - [+~]number, $ or /pattern/"'
+ \)
+ \|
+ '//' -'excl=( \\\# : )' ':address-forms:address form:compadd -S "" -d range ,'
+ \)
+ \(
+ '/!/' ':address-forms:address form:compadd -S "" -d negate !'
+ \| \)
+ \| // -'excl=( \{ )' \) # { ... } is only useful following a range so exclude {
+
+ $'/[ \t]#/' -'(( nest )) || excl+=( \} )' # whitespace + exclude } without preceding {
+ \( # First commands, for which the pattern fully terminates them
+ '/e[ \t]#/' $'/((\\\n|\\[^\n]|[^\\\n])##\n|[\n;])/' ':commands:command:_cmdstring' # GNU extension
+ \|
+ $'/{[ ;\t\n]#/' -'((++nest,1))' # opening brace
+ \|
+ '/\#/' # comments
+ $'/[^\n]##\n[\n; \t]#/' ':comments:comment:'
+ \|
+ $'/[aci]/' # a, c and i commands
+ \(
+ $'/[ \t]#/' -'[[ $variant = gnu && $+opt_args[--posix] = 0 ]]' # GNU allows, e.g. 'c string'
+ \|
+ $'/[ \t]#/' $'/\\\n/' ':newlines:newline:compadd -Q -S "" "$bsnl"'
+ \)
+ $'/(\\\n|\\[^\n]|[^\\\n])##\n[\n; \t]#/' ':strings:string:'
+ \|
+ $'/[RrwW][ \t]#/' $'/[^\n]##\n[\n; \t]#/' ':files:file:_files -S ""'
+ \| # Now commands with shared termination handling
+ \(
+ # branches/labels, GNU sed allows an empty label
+ $'/[:btT][ \t]#/' $'/[^ \t\n;]#/' $'%[ \t\n;]%' -'labels+=( $match )'
+ ':labels:label: _wanted -x labels expl label compadd -S "" -a labels'
+ \|
+ '/l/' $'/[ \t]#<->/' ':width:width:'
+ \|
+ '/s(?)/' -'sep=${match#s}' # Substitutions
+ \( '/\\?/' \| '/?/' -'[[ $match != $sep ]]' \) \#
+ '/?/' -'[[ $match = $sep ]]' ':regexes:source regex:'
+ \( '/\\?/' \| '/?/' -'[[ $match != $sep ]]' \) \#
+ '/?/' -'[[ $match = $sep ]]' ':regexes:substitute string (back-references with & and \1 .. \9):'
+ \( # Substitution flags
+ $'/w[ \t]#/' $'/[^\n]##/' $'%\n%' ':files:file:_files -S ""'
+ \|
+ # pass existing flags, building exclusion list from them
+ $'/[gpiImM0-9]#/' -'excl=( ${(s..)${${${match/[iI]/iI}/[mM]/mM}}/e/ew} )'
+ \(
+ '//' -'[[ -z ${excl[(r)[0-9]]} ]]' # exclude if numbers already there
+ '//' '%[^egpiImM0-9]%' ': _message -e numbers "number - substitute nth match"'
+ \|
+ '//' '%[^egpiImM0-9]%' $':flags:flag: _describe -t flags flag substflags -S "" -F excl'
+ \)
+ \)
+ \|
+ '/y(?)/' -'sep=${match#y}' # Character transliterations
+ \( '/\?/' \| '/?/' -'[[ $match != $sep ]]' \) \# '/?/' -'[[ $match = $sep ]]' ':source:source:'
+ \( '/\?/' \| '/?/' -'[[ $match != $sep ]]' \) \# '/?/' -'[[ $match = $sep ]]' ':dest:dest:'
+ \|
+ '/[qQ]/' -'[[ $variant = gnu && $+opt_args[--posix] = 0 ]]'
+ $'/[\t ]#<->/' '%[^0-9]%' ':exit-codes:exit code:'
+ \|
+ '/[=dDFhHgGnNpPqQxz]/' # stand-alone commands that take no argument
+ \( $'/[ \t]#/' $'%[#\n;}]%' \| $'/[ \t]/' '/[]/' ': _message "no arguments"' \| \)
+ \|
+ $'/v[ \t]#/' $'/[^\n;}]#/' $'%[\n;}]%' ':versions:version:'
+ \|
+ $'/}[ \t]#/' -'((--nest,1))' # closing }
+ \|
+ /'[]'/ ':commands:command: _describe -t sed-commands "sed command" cmds_none -S "" -F excl -- cmds_slash -S / -- cmds_end -F excl -r \; -S $nl'
+ \)
+ $'/[ \t]#/'
+ \( $'/}[ \t]#/' -'((--nest,1))' \| \) # closing } is allowed by GNU sed without preceding ; or newline
+ \(
+ '/\#/' $'/[^\n]##\n[\n; \t]#/' ':comments:comment:' # line end comments
+ \|
+ # add in and auto-removable newline if command is terminated
+ $'/[;\n][ ;\t\n]#/' $':separators:separator:compadd -r ";" -S $nl ""'
+ \|
+ $'/{[ \t]#/' -'((++nest,1))' # opening {, keep count of nesting level
+ \)
+ \)
+ \) \#
+)
+
+_regex_arguments _sed_expressions "$sedexpr[@]"
+
_arguments -s -S $aopts : "$args[@]"
Messages sorted by:
Reverse Date,
Date,
Thread,
Author