mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-01-01 05:16:05 +01:00
322 lines
8.4 KiB
C
322 lines
8.4 KiB
C
/*
|
|
* textobjects.c - ZLE widgets 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 (!region_active || zlecs == mark || mark == -1) {
|
|
/* 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 (!virangeflag) {
|
|
if (!invicmdmode())
|
|
region_active = 1;
|
|
else if (zlecs && zlecs > mark)
|
|
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;
|
|
zcontext_save();
|
|
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 &= ~ERRFLAG_ERROR;
|
|
noerrs = ne;
|
|
zcontext_restore();
|
|
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));
|
|
free(linein);
|
|
|
|
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 && invicmdmode())
|
|
DECCS();
|
|
|
|
return 0;
|
|
}
|