From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 29083 invoked by alias); 20 Jun 2018 13:07:02 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: List-Unsubscribe: X-Seq: 43078 Received: (qmail 23794 invoked by uid 1010); 20 Jun 2018 13:07:02 -0000 X-Qmail-Scanner-Diagnostics: from park01.gkg.net by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.99.2/21882. spamassassin: 3.4.1. Clear:RC:0(205.235.26.22):SA:0(-1.4/5.0):. Processed in 1.453246 secs); 20 Jun 2018 13:07:02 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.4 required=5.0 tests=BAYES_00, FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS, RCVD_IN_DNSWL_NONE,SPF_PASS,T_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.1 X-Envelope-From: SRS0=dMS4=JG=yahoo.co.uk=okiddle@bounces.park01.gkg.net X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | X-Virus-Scanned: by amavisd-new at gkg.net Authentication-Results: amavisd4.gkg.net (amavisd-new); dkim=pass (2048-bit key) header.d=yahoo.co.uk X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 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== X-YMail-OSG: ALbY9ugVM1nOfTD_Y03FxYitNTFjxZA.VRwXjCyiUrwn87S100ClBDPpBO0whvh 0QiKekVyanJPBI091jIxdpJVis24Zp0a_QnwFP96hEG6Z.oQQ4PcNkmuiXKKtH_adop8fT1zGN3P Pr6Xtkweo8.gmIWZqhTafe3HekfR3QugdDO1TdkrGDz2zLwD4DTrGyRg_i0CI_hXTeBoDCTgNc_d kYsr1ZVaygJ2wN1rezX4Poo_ccV4KigbMO_zzzAAYdivni99pX21J1C858AjD6OAqY.SLAde6yD3 sffKFTZU3U6kXZ1BjE2M8mGXtL9ZbIFyL1s6I175dzXV0k3jm9WHI1yOP.mUwHYyBUtlNQIDt9FU XHhqTrqa8rSg1X_wYjMyuHFaNRc0VFqy45lRhj.uZad1DMowy_R5fsTjpHXVbMT83qjOqeEXVmcq ge0UA9Z0YplGbwz5JFxbJpVhcHGFOOKIX20qD8OaIrgkDUytXgeHxA4664FBfLzvaRvtTuVmFS6R _2e7fxaIrNpD_XASZxt0fVHut.kDK71jJRESfJ9PjkpDuFb04NSK.cXxBBYwczjZ9ZAt_xe4lxo5 gB6vAlgCVRgvp2iUho5NdrYUCaT9QiRs3eSIr8hOz86y3qrhYdZOu_gD4uQnkV2mDSyBNe1NH_ds OSoOPldvaGU7vQ0yD3xC3ORhJHOcpM2GtPchurqk_HG3gjEWgE4VQ8X67TpnIk.KNEopVFLo0CwO oA_JTaCPI08JjIHteaYMOXA5Y1qfyq0HgQyej9PyzSU1.gW5xhsUq_8sD.M4tozNSThnEkG8kAiD ytfXjFPcwc7SydvwAVbDIg09BNkP04_d_aCqNvaASfGOQk2PYGOrxK8g_tqT5j1UQA2eX7BaZrkw u6GZZVo2Wdx_KICHsSLjc1sdpzEVQDL3RHTS6GsGTiSX1vyNlnxOeBYrE.TkgEXvygMt8.rWIBS0 - cc: Zsh workers In-reply-to: <9F50D6F1-6438-4220-8413-401E6D1998A8@dana.is> From: Oliver Kiddle References: <9F50D6F1-6438-4220-8413-401E6D1998A8@dana.is> To: dana Subject: Re: [PATCH] Completion: Minor improvements to _comm and _sed MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-ID: <19969.1529499988.1@thecus> Date: Wed, 20 Jun 2018 15:06:28 +0200 Message-ID: <19970.1529499988@thecus> 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[@]"