From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 9638 invoked by alias); 21 Nov 2014 00:01:31 -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: X-Seq: 33730 Received: (qmail 2077 invoked from network); 21 Nov 2014 00:01:15 -0000 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.co.uk; s=s2048; t=1416528070; bh=f61zf0SqO49IB0NNt5Xd3xAzrLdZ9TzzNYN+cCP/Gps=; h=In-reply-to:From:References:To:Subject:Date:From:Subject; b=SWME7agMkRxwZVvwh/blLjFR9P+rTruVI4XM/GDsYevksR9e7ZtnHvyc1nT5BZwt2j2NgiuK/oZieEhMmcCoISM0Cn8dxa/aHqIQEBvzQQTE9Mt9z0Gjnk9q4kGE+Ig5mMcLDxgQcVyq9tZkbrQph55VF49UCN+5jJRijezT9UrCzFDjoRcC3f6bb1/N73v877mSICGy7M6ZPNxUbu6fJcxK4azZy5ycTF3IbLdIsjdwtnU7oIFjhC+oqesCsWtjPu693lCBDYUmqQLw9ZcZ7U/+O/G0OE7Z2KrhGlC6xP09c02tAg/P77CByNPbEUJgp6CgidxFsuUSJW4FGTaTyA== DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=s2048; d=yahoo.co.uk; b=qJxMLGNM74DA7AhS5QLufXIE8X00eFfvqx/G4glyowJsQYf8/QHEa0iu6xfIVejXpoHuEqxcGUkY4unjZv77t/H7659CAm9wtUkzRF4QGtB5KEKklMdgc2Ka7pq3w3GkgAoJQXfKu0jv3XWKy7ctwik6F99Vq+YkrhqTu2e9rx5/wLm0ftaauVApucDz28tpmqk+H6lVoMZTWBXb92S7SqgJsm6JPxEtQYOZXmb0stwQwlmxexZ28RfISyT+eMXKIJ6AMxD3Jx8edg0cZ8ILVJtKoF7H3ni6BkV0b1T89ASfBQHL0A5w1pKU0aOLFEvVDutSzpuikJMtqN+vBJpfVw==; X-Yahoo-Newman-Id: 788075.93151.bm@smtp103.mail.ir2.yahoo.com X-Yahoo-Newman-Property: ymail-3 X-YMail-OSG: 1XKipMgVM1lSeSi4yNrzpcgzPyFrd2OwMe8ss9P.sVSLDMu ENJQg7KcDiOaDkcjrTj.hCTHFSKYBbOqhanz4b1eOyR72fX9DYeZUXptKB6P GnlWq0duxRP4.6rl1W5EXTP1hhFomjf.f3WlfF_Vr7jBtdiHY1rclqSnwiTZ D_QC6PkdhHhb2feTw7uP0ih7J_18rXJT5n.lTo8.4.srPH9gABH3pBftyFRR v6uCEuLgPV3fnfqVz6lrPSlotK2sWIjoCGZ76t8T4pim1Po6TOzGWHsg5ucb cackIaf.S.l5g6dr7ipUduHxQksg_sibq1XXzH7QicD6G9S9DDqpcLF84dk_ BSFXFcj0GqOX_aoG8fcQb.bGTto1sp85Gg.I1YwMVriq3He3dnEx6RHbKSWR TJxmWS7_hk2PNEbvqHnzNBFMX18ZqdC4dxB8hho_Gw0fLOk9ybSjAC2tro3B eC2nTvoICYqwSN.tAZmts0PVJFmaKYtVr4fHQ_WII.2myL6DcN2alW9FowgN VLWsWIO6UUnKqRDhYVrGwmnI.dEheFQ-- X-Yahoo-SMTP: opAkk_CswBAce_kJ3nIPlH80cJI- In-reply-to: <42F4E5F2-97E7-4803-87CB-B1C67CD943B2@kba.biglobe.ne.jp> From: Oliver Kiddle References: <930.1416260160@thecus.kiddle.eu> <42F4E5F2-97E7-4803-87CB-B1C67CD943B2@kba.biglobe.ne.jp> To: "zsh-workers@zsh.org" Subject: Re: PATCH: key bindings, fixes, docs, tests for vi stuff MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-ID: <15510.1416528068.1@thecus.kiddle.eu> Date: Fri, 21 Nov 2014 01:01:08 +0100 Message-ID: <15511.1416528068@thecus.kiddle.eu> On 18 Nov, "Jun T." wrote: > Apparently 'daw' is not working. With this patch, that test failure should now be fixed. I'd recommend Emacs mode users at least look at the new select-in-shell-word widget. I can imagine it might be useful. It is bound to "ia" in the viopp and visual keymaps along with aa which is similar. This also adds the usual vim aw aW iw and iW bindings that can be used together with vi operators (c d y p etc) or visual selection mode. Vim has many more, most not useful from zsh (sentences, paragraphs etc). The code for select-in-shell-word is based on that for the (z) expansion. I was hoping it would shed some light on the lexer but, it didn't really. Oliver diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo index aa7ff4b..f9dcd80 100644 --- a/Doc/Zsh/zle.yo +++ b/Doc/Zsh/zle.yo @@ -1975,7 +1975,7 @@ When a previous completion displayed a list below the prompt, this widget can be used to move the prompt below the list. ) enditem() -texinode(Miscellaneous)()(Completion)(Zle Widgets) +texinode(Miscellaneous)(Text Objects)(Completion)(Zle Widgets) subsect(Miscellaneous) startitem() tindex(accept-and-hold) @@ -2333,6 +2333,50 @@ If the last command executed was a digit as part of an argument, continue the argument. Otherwise, execute vi-beginning-of-line. ) enditem() +texinode(Text Objects)()(Miscellaneous)(Zle Widgets) +subsect(Text Objects) +cindex(text objects) +Text objects are commands that can be used to select a block of text +according to some criteria. They are a feature of the vim text editor +and so are primarily intended for use with vi operators or from visual +selection mode. However, they can also be used from vi-insert or emacs +mode. Key bindings listed below apply to the tt(viopp) and tt(visual) +keymaps. + +startitem() +tindex(select-a-blank-word) +item(tt(select-a-blank-word) (aW))( +Select a word including adjacent blanks, where a word is defined as a +series of non-blank characters. With a numeric argument, multiple words +will be selected. +) +tindex(select-a-shell-word) +item(tt(select-a-shell-word) (aa))( +Select the current command argument applying the normal rules for +quoting. +) +tindex(select-a-word) +item(tt(select-a-word) (aw))( +Select a word including adjacent blanks, using the normal vi-style word +definition. With a numeric argument, multiple words will be selected. +) +tindex(select-in-blank-word) +item(tt(select-in-blank-word) (iW))( +Select a word, where a word is defined as a series of non-blank +characters. With a numeric argument, multiple words will be selected. +) +tindex(select-in-shell-word) +item(tt(select-in-shell-word) (ia))( +Select the current command argument applying the normal rules for +quoting. If the argument begins and ends with matching quote characters, +these are not included in the selection. +) +tindex(select-in-word) +item(tt(select-in-word) (iw))( +Select a word, using the normal vi-style word definition. With a numeric +argument, multiple words will be selected. +) +enditem() texinode(Character Highlighting)()(Zle Widgets)(Zsh Line Editor) sect(Character Highlighting) diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list index 2618297..1a664e5 100644 --- a/Src/Zle/iwidgets.list +++ b/Src/Zle/iwidgets.list @@ -100,6 +100,12 @@ "reset-prompt", resetprompt, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "run-help", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"select-a-word", selectword, ZLE_KEEPSUFFIX +"select-in-word", selectword, ZLE_KEEPSUFFIX +"select-a-blank-word", selectword, ZLE_KEEPSUFFIX +"select-in-blank-word", selectword, ZLE_KEEPSUFFIX +"select-a-shell-word", selectargument, ZLE_KEEPSUFFIX +"select-in-shell-word", selectargument, ZLE_KEEPSUFFIX "self-insert", selfinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX "self-insert-unmeta", selfinsertunmeta, ZLE_MENUCMP | ZLE_KEEPSUFFIX "send-break", sendbreak, 0 diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c new file mode 100644 index 0000000..7f049c5 --- /dev/null +++ b/Src/Zle/textobjects.c @@ -0,0 +1,321 @@ +/* + * textobjects.c - ZLE module implementing Vim style text objects + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 2014 Oliver Kiddle + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Oliver Kiddle or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Oliver Kiddle and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Oliver Kiddle and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Oliver Kiddle and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zle.mdh" +#include "textobjects.pro" + +/* class of character: 0 is whitespace, 1 is word character, 2 is other */ +static int +wordclass(ZLE_CHAR_T x) +{ + return (ZC_iblank(x) ? 0 : ((ZC_ialnum(x) || (ZWC('_') == x)) ? 1 : 2)); +} + +static int +blankwordclass(ZLE_CHAR_T x) +{ + return (ZC_iblank(x) ? 0 : 1); +} + +/**/ +int +selectword(UNUSED(char **args)) +{ + int n = zmult; + int all = (bindk == t_selectaword || bindk == t_selectablankword); + int (*viclass)(ZLE_CHAR_T) = (bindk == t_selectaword || + bindk == t_selectinword) ? wordclass : blankwordclass; + int sclass = viclass(zleline[zlecs]); + int doblanks = all && sclass; + + if (!invicmdmode()) { + region_active = 1; + mark = zlecs; + } + if (!region_active || zlecs == mark) { + /* search back to first character of same class as the start position + * also stop at the beginning of the line */ + mark = zlecs; + while (mark) { + int pos = mark; + DECPOS(pos); + if (zleline[pos] == ZWC('\n') || viclass(zleline[pos]) != sclass) + break; + mark = pos; + } + /* similarly scan forward over characters of the same class */ + while (zlecs < zlell) { + INCCS(); + int pos = zlecs; + /* single newlines within blanks are included */ + if (all && !sclass && pos < zlell && zleline[pos] == ZWC('\n')) + INCPOS(pos); + + if (zleline[pos] == ZWC('\n') || viclass(zleline[pos]) != sclass) + break; + } + + if (all) { + int nclass = viclass(zleline[zlecs]); + /* if either start or new position is blank advance over + * a new block of characters of a common type */ + if (!nclass || !sclass) { + while (zlecs < zlell) { + INCCS(); + if (zleline[zlecs] == ZWC('\n') || + viclass(zleline[zlecs]) != nclass) + break; + } + if (n < 2) + doblanks = 0; + } + } + } else { + /* For visual mode, advance one char so repeated + * invocations select subsequent words */ + if (zlecs > mark) { + if (zlecs < zlell) + INCCS(); + } else if (zlecs) + DECCS(); + if (zlecs < mark) { + /* visual mode with the cursor before the mark: move cursor back */ + while (n-- > 0) { + int pos = zlecs; + /* first over blanks */ + if (all && (!viclass(zleline[pos]) || + zleline[pos] == ZWC('\n'))) { + all = 0; + while (pos) { + DECPOS(pos); + if (zleline[pos] == ZWC('\n')) + break; + zlecs = pos; + if (viclass(zleline[pos])) + break; + } + } else if (zlecs && zleline[zlecs] == ZWC('\n')) { + /* for in widgets pass over one newline */ + DECPOS(pos); + if (zleline[pos] != ZWC('\n')) + zlecs = pos; + } + pos = zlecs; + sclass = viclass(zleline[zlecs]); + /* now retreat over non-blanks */ + while (zleline[pos] != ZWC('\n') && + viclass(zleline[pos]) == sclass) { + zlecs = pos; + if (!pos) { + zlecs = 0; + break; + } + DECPOS(pos); + } + /* blanks again but only if there were none first time */ + if (all && zlecs) { + pos = zlecs; + DECPOS(pos); + if (!viclass(zleline[pos])) { + while (pos) { + DECPOS(pos); + if (zleline[pos] == ZWC('\n') || + viclass(zleline[pos])) + break; + zlecs = pos; + } + } + } + } + return 0; + } + n++; + doblanks = 0; + } + region_active = !!region_active; /* force to character wise */ + + /* for each digit argument, advance over further block of one class */ + while (--n > 0) { + if (zlecs < zlell && zleline[zlecs] == ZWC('\n')) + INCCS(); + sclass = viclass(zleline[zlecs]); + while (zlecs < zlell) { + INCCS(); + if (zleline[zlecs] == ZWC('\n') || + viclass(zleline[zlecs]) != sclass) + break; + } + /* for 'a' widgets, advance extra block if either consists of blanks */ + if (all) { + if (zlecs < zlell && zleline[zlecs] == ZWC('\n')) + INCCS(); + if (!sclass || !viclass(zleline[zlecs]) ) { + sclass = viclass(zleline[zlecs]); + if (n == 1 && !sclass) + doblanks = 0; + while (zlecs < zlell) { + INCCS(); + if (zleline[zlecs] == ZWC('\n') || + viclass(zleline[zlecs]) != sclass) + break; + } + } + } + } + + /* if we didn't remove blanks at either end we remove some at the start */ + if (doblanks) { + int pos = mark; + while (pos) { + DECPOS(pos); + /* don't remove blanks at the start of the line, i.e indentation */ + if (zleline[pos] == ZWC('\n')) + break; + if (!ZC_iblank(zleline[pos])) { + INCPOS(pos); + mark = pos; + break; + } + } + } + /* Adjustment: vi operators don't include the cursor position, in insert + * or emacs mode the region also doesn't but for vi visual mode it is + * included. */ + if (zlecs && zlecs > mark && !virangeflag) + DECCS(); + + return 0; +} + +/**/ +int +selectargument(UNUSED(char **args)) +{ + int ne = noerrs, ocs = zlemetacs; + int owb = wb, owe= we, oadx = addedx, ona = noaliases; + char *p; + int ll, cs; + char *linein; + int wend = 0, wcur = 0; + int n = zmult; + int *wstarts; + int tmpsz; + + if (n < 1 || 2*n > zlell + 1) + return 1; + + /* if used from emacs mode enable the region */ + if (!invicmdmode()) { + region_active = 1; + mark = zlecs; + } + + wstarts = (int *) zhalloc(n * sizeof(int)); + memset(wstarts, 0, n * sizeof(int)); + + addedx = 0; + noerrs = 1; + lexsave(); + lexflags = LEXFLAGS_ACTIVE; + linein = zlegetline(&ll, &cs); + zlemetall = ll; + zlemetacs = cs; + + if (!isfirstln && chline) { + p = (char *) zhalloc(hptr - chline + zlemetall + 2); + memcpy(p, chline, hptr - chline); + memcpy(p + (hptr - chline), linein, ll); + p[(hptr - chline) + ll] = '\0'; + inpush(p, 0, NULL); + zlemetacs += hptr - chline; + } else { + p = (char *) zhalloc(ll + 1); + memcpy(p, linein, ll); + p[ll] = '\0'; + inpush(p, 0, NULL); + } + if (zlemetacs) + zlemetacs--; + strinbeg(0); + noaliases = 1; + do { + wstarts[wcur++] = wend; + wcur %= n; + ctxtlex(); + if (tok == ENDINPUT || tok == LEXERR) + break; + wend = zlemetall - inbufct; + } while (tok != ENDINPUT && tok != LEXERR && wend <= zlemetacs); + noaliases = ona; + strinend(); + inpop(); + errflag = 0; + noerrs = ne; + lexrestore(); + zlemetacs = ocs; + wb = owb; + we = owe; + addedx = oadx; + + /* convert offsets for mark and zlecs back to ZLE internal format */ + linein[wend] = '\0'; /* a bit of a hack to get two offsets */ + free(stringaszleline(linein, wstarts[wcur], &zlecs, &tmpsz, &mark)); + + if (bindk == t_selectinshellword) { + ZLE_CHAR_T *match = ZWS("`\'\""); + ZLE_CHAR_T *lmatch = ZWS("\'({"), *rmatch = ZWS("\')}"); + ZLE_CHAR_T *ematch = match, *found; + int start, end = zlecs; + /* for 'in' widget, don't include initial blanks ... */ + while (mark < zlecs && ZC_iblank(zleline[mark])) + INCPOS(mark); + /* ... or a matching pair of quotes */ + start = mark; + if (zleline[start] == ZWC('$')) { + match = lmatch; + ematch = rmatch; + INCPOS(start); + } + found = ZS_strchr(match, zleline[start]); + if (found) { + DECPOS(end); + if (zleline[end] == ematch[found-match]) { + zlecs = end; + INCPOS(start); + mark = start; + } + } + } + + /* Adjustment: vi operators don't include the cursor position */ + if (!virangeflag) + DECCS(); + + return 0; +} diff --git a/Src/Zle/zle.mdd b/Src/Zle/zle.mdd index c6e4d11..dd69eff 100644 --- a/Src/Zle/zle.mdd +++ b/Src/Zle/zle.mdd @@ -7,7 +7,8 @@ autofeatures="b:bindkey b:vared b:zle" objects="zle_bindings.o zle_hist.o zle_keymap.o zle_main.o \ zle_misc.o zle_move.o zle_params.o zle_refresh.o \ -zle_thingy.o zle_tricky.o zle_utils.o zle_vi.o zle_word.o" +zle_thingy.o zle_tricky.o zle_utils.o zle_vi.o zle_word.o \ +textobjects.o" headers="zle.h zle_things.h" diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index 216e302..30d25eb 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -1343,6 +1343,12 @@ default_bindings(void) add_cursor_key(kptr, TCDOWNCURSOR, t_downline, 'B'); bindkey(kptr, "k", refthingy(t_upline), NULL); bindkey(kptr, "j", refthingy(t_downline), NULL); + bindkey(kptr, "aa", refthingy(t_selectashellword), NULL); + bindkey(kptr, "ia", refthingy(t_selectinshellword), NULL); + bindkey(kptr, "aw", refthingy(t_selectaword), NULL); + bindkey(kptr, "iw", refthingy(t_selectinword), NULL); + bindkey(kptr, "aW", refthingy(t_selectablankword), NULL); + bindkey(kptr, "iW", refthingy(t_selectinblankword), NULL); } /* escape in operator pending cancels the operation */ bindkey(oppmap, "\33", refthingy(t_vicmdmode), NULL); diff --git a/Test/X02zlevi.ztst b/Test/X02zlevi.ztst index 94afb60..6b7ca56 100644 --- a/Test/X02zlevi.ztst +++ b/Test/X02zlevi.ztst @@ -382,6 +382,45 @@ >BUFFER: - >CURSOR: 0 + zletest $'---- word ----word\eo \eo----\eodone\eh' \ + 'vhawmaawmbawmcawmdawmeawmfawmgv`ara`brb`crc$r$`drd`ere`frf`grg' +0:all word with existing selection and cursor before mark +>BUFFER: g---f worde ----dord +>c $ +>b--- +>aone +>CURSOR: 0 + + zletest $'---- word word----\e0lvlawmaawmbawmcawvrd`ara`brb`crc' +0:all word with existing selection and mark before cursor +>BUFFER: ---- aword bworc---d +>CURSOR: 19 + + zletest $' --ww ww---\eo\eoww\evhiwiw' m{a,b,c,d,e}iw vrE \`{a,b,c,d,e}r. +0:in word with existing selection and cursor before mark +>BUFFER: E.-.w. .w.-- +> +>ww +>CURSOR: 1 + + zletest $' --ww ww--\eO \ev0o' m{a,b,c,d,e}iw vrE \`{a,b,c,d,e}r. +0:in word with existing selection and mark before cursor +>BUFFER: . +> .-.w. .wE-- +>CURSOR: 10 + + zletest $' `one` $(echo two) " three " $\'four\'\C-v\tfive ${six:-6}\e' \ + vaaom{a,b,c,d,e,f}v \`{a,b,c,d,e,f}rX +0:all argument for different arguments +>BUFFER: X `one`X $(echo two)X" three "X$'four'XfiveX${six:-6} +>CURSOR: 0 + + zletest $'{ls `echo x` $((3+4)) "a b" $\'\\t\\n\' ${d%/}\e' \ + cia{6,5,4,3,2,1}$'\eBB' +0:in argument for different arguments +>BUFFER: 1ls `2` $(3) "4" $'5' ${6} +>CURSOR: 0 + %clean zmodload -ui zsh/zpty