diff options
author | Ron Yorston <rmy@pobox.com> | 2021-04-15 12:06:51 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2021-04-15 13:09:12 +0200 |
commit | f2277268384d47fbcaba081f19cebc68de819836 (patch) | |
tree | adee727cf5f81119c1d93bd1e1c9981d095fd985 /editors/vi.c | |
parent | 47f78913f7576596d44c602a015735cb9c49f4f0 (diff) |
vi: allow line addresses to have an offset
Line addresses in colon commands can be defined using an expression
that includes '+' or '-' operators. The implementation follows
traditional vi:
- The first term in the expression defines an address. It can be
an absolute line number, '.', '$', a search or a marker.
- The second and subsequent terms must be non-negative integers.
- If the first term is missing '.' is assumed. If the operator is
missing addition is assumed. If the final term in missing an
offset of 1 is assumed.
Thus the following are valid addresses:
.+1 .+ + .1
.-1 .- -
The following are not valid (though they are in vim):
.+$ .$ 2+.
function old new delta
colon 3701 3844 +143
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/0 up/down: 143/0) Total: 143 bytes
Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'editors/vi.c')
-rw-r--r-- | editors/vi.c | 118 |
1 files changed, 72 insertions, 46 deletions
diff --git a/editors/vi.c b/editors/vi.c index 0866e0fa9..6dd951421 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -2342,67 +2342,93 @@ static char *char_search(char *p, const char *pat, int dir_and_range) //----- The Colon commands ------------------------------------- #if ENABLE_FEATURE_VI_COLON -static char *get_one_address(char *p, int *addr) // get colon addr, if present +static char *get_one_address(char *p, int *result) // get colon addr, if present { - int st; + int st, num, sign, addr, new_addr; # if ENABLE_FEATURE_VI_YANKMARK || ENABLE_FEATURE_VI_SEARCH char *q, c; # endif IF_FEATURE_VI_SEARCH(int dir;) - *addr = -1; // assume no addr - if (*p == '.') { // the current line - p++; - *addr = count_lines(text, dot); - } + addr = -1; // assume no addr + sign = 0; + for (;;) { + new_addr = -1; + if (isblank(*p)) { + p++; + } else if (*p == '.') { // the current line + p++; + new_addr = count_lines(text, dot); + } # if ENABLE_FEATURE_VI_YANKMARK - else if (*p == '\'') { // is this a mark addr - p++; - c = tolower(*p); - p++; - q = NULL; - if (c >= 'a' && c <= 'z') { - // we have a mark - c = c - 'a'; - q = mark[(unsigned char) c]; + else if (*p == '\'') { // is this a mark addr + p++; + c = tolower(*p); + p++; + q = NULL; + if (c >= 'a' && c <= 'z') { + // we have a mark + c = c - 'a'; + q = mark[(unsigned char) c]; + } + if (q == NULL) // is mark valid + return NULL; + new_addr = count_lines(text, q); } - if (q == NULL) // is mark valid - return NULL; - *addr = count_lines(text, q); - } # endif # if ENABLE_FEATURE_VI_SEARCH - else if (*p == '/' || *p == '?') { // a search pattern - c = *p; - q = strchrnul(p + 1, c); - if (p + 1 != q) { - // save copy of new pattern - free(last_search_pattern); - last_search_pattern = xstrndup(p, q - p); + else if (*p == '/' || *p == '?') { // a search pattern + c = *p; + q = strchrnul(p + 1, c); + if (p + 1 != q) { + // save copy of new pattern + free(last_search_pattern); + last_search_pattern = xstrndup(p, q - p); + } + p = q; + if (*p == c) + p++; + if (c == '/') { + q = next_line(dot); + dir = (FORWARD << 1) | FULL; + } else { + q = begin_line(dot); + dir = ((unsigned)BACK << 1) | FULL; + } + q = char_search(q, last_search_pattern + 1, dir); + if (q == NULL) + return NULL; + new_addr = count_lines(text, q); } - p = q; - if (*p == c) +# endif + else if (*p == '$') { // the last line in file p++; - if (c == '/') { - q = next_line(dot); - dir = (FORWARD << 1) | FULL; + new_addr = count_lines(text, end - 1); + } else if (isdigit(*p)) { + sscanf(p, "%d%n", &num, &st); + p += st; + if (addr < 0) { // specific line number + addr = num; + } else { // offset from current addr + addr += sign >= 0 ? num : -num; + } + sign = 0; + } else if (*p == '-' || *p == '+') { + sign = *p++ == '-' ? -1 : 1; + if (addr < 0) { // default address is dot + addr = count_lines(text, dot); + } } else { - q = begin_line(dot); - dir = ((unsigned)BACK << 1) | FULL; + addr += sign; // consume unused trailing sign + break; + } + if (new_addr >= 0) { + if (addr >= 0) // only one new address per expression + return NULL; + addr = new_addr; } - q = char_search(q, last_search_pattern + 1, dir); - if (q == NULL) - return NULL; - *addr = count_lines(text, q); - } -# endif - else if (*p == '$') { // the last line in file - p++; - *addr = count_lines(text, end - 1); - } else if (isdigit(*p)) { // specific line number - sscanf(p, "%d%n", addr, &st); - p += st; } + *result = addr; return p; } |