aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval/funcs.c
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2024-02-22 20:32:52 +0800
committerzeertzjq <zeertzjq@outlook.com>2024-02-23 06:39:03 +0800
commit20e4001eeedc80b1f2857fcaca81f7a211a09b40 (patch)
treeaf478c0dc95acf36eaf48d6be9d71539313fd457 /src/nvim/eval/funcs.c
parentbb15fa035610bb9765ca16900703804a88faa3bb (diff)
downloadrneovim-20e4001eeedc80b1f2857fcaca81f7a211a09b40.tar.gz
rneovim-20e4001eeedc80b1f2857fcaca81f7a211a09b40.tar.bz2
rneovim-20e4001eeedc80b1f2857fcaca81f7a211a09b40.zip
vim-patch:9.1.0120: hard to get visual region using Vim script
Problem: hard to get visual region using Vim script Solution: Add getregion() Vim script function (Shougo Matsushita, Jakub Łuczyński) closes: vim/vim#13998 closes: vim/vim#11579 https://github.com/vim/vim/commit/3f905ab3c4f66562f4a224bf00f49d98a0b0da91 Cherry-pick changes from patch 9.1.0122, with :echom instead of :echow. Co-authored-by: Shougo Matsushita <Shougo.Matsu@gmail.com> Co-authored-by: Jakub Łuczyński <doubleloop@o2.pl>
Diffstat (limited to 'src/nvim/eval/funcs.c')
-rw-r--r--src/nvim/eval/funcs.c144
1 files changed, 144 insertions, 0 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 01e666887a..b62ed557e4 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -130,6 +130,7 @@
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
+#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/version.h"
#include "nvim/vim_defs.h"
@@ -2801,6 +2802,149 @@ static void f_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
getpos_both(argvars, rettv, false, false);
}
+/// Convert from block_def to string
+static char *block_def2str(struct block_def *bd)
+{
+ size_t size = (size_t)bd->startspaces + (size_t)bd->endspaces + (size_t)bd->textlen;
+ char *ret = xmalloc(size + 1);
+ char *p = ret;
+ memset(p, ' ', (size_t)bd->startspaces);
+ p += bd->startspaces;
+ memmove(p, bd->textstart, (size_t)bd->textlen);
+ p += bd->textlen;
+ memset(p, ' ', (size_t)bd->endspaces);
+ *(p + bd->endspaces) = NUL;
+ return ret;
+}
+
+/// "getregion()" function
+static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+
+ if (tv_check_for_string_arg(argvars, 0) == FAIL
+ || tv_check_for_string_arg(argvars, 1) == FAIL
+ || tv_check_for_string_arg(argvars, 2) == FAIL) {
+ return;
+ }
+
+ int fnum = -1;
+ // NOTE: var2fpos() returns static pointer.
+ pos_T *fp = var2fpos(&argvars[0], true, &fnum, false);
+ if (fp == NULL) {
+ return;
+ }
+ pos_T p1 = *fp;
+
+ fp = var2fpos(&argvars[1], true, &fnum, false);
+ if (fp == NULL) {
+ return;
+ }
+ pos_T p2 = *fp;
+
+ const char *pos1 = tv_get_string(&argvars[0]);
+ const char *pos2 = tv_get_string(&argvars[1]);
+ const char *type = tv_get_string(&argvars[2]);
+
+ const bool is_visual
+ = (pos1[0] == 'v' && pos1[1] == NUL) || (pos2[0] == 'v' && pos2[1] == NUL);
+
+ if (is_visual && !VIsual_active) {
+ return;
+ }
+
+ MotionType region_type = kMTUnknown;
+ if (type[0] == 'v' && type[1] == NUL) {
+ region_type = kMTCharWise;
+ } else if (type[0] == 'V' && type[1] == NUL) {
+ region_type = kMTLineWise;
+ } else if (type[0] == Ctrl_V && type[1] == NUL) {
+ region_type = kMTBlockWise;
+ } else {
+ return;
+ }
+
+ const TriState save_virtual = virtual_op;
+ virtual_op = virtual_active();
+
+ if (!lt(p1, p2)) {
+ // swap position
+ pos_T p = p1;
+ p1 = p2;
+ p2 = p;
+ }
+
+ oparg_T oap;
+ bool inclusive = true;
+
+ if (region_type == kMTCharWise) {
+ // handle 'selection' == "exclusive"
+ if (*p_sel == 'e' && !equalpos(p1, p2)) {
+ if (p2.coladd > 0) {
+ p2.coladd--;
+ } else if (p2.col > 0) {
+ p2.col--;
+ mark_mb_adjustpos(curbuf, &p2);
+ } else if (p2.lnum > 1) {
+ p2.lnum--;
+ p2.col = (colnr_T)strlen(ml_get(p2.lnum));
+ if (p2.col > 0) {
+ p2.col--;
+ mark_mb_adjustpos(curbuf, &p2);
+ }
+ }
+ }
+ // if fp2 is on NUL (empty line) inclusive becomes false
+ if (*ml_get_pos(&p2) == NUL && !virtual_op) {
+ inclusive = false;
+ }
+ } else if (region_type == kMTBlockWise) {
+ colnr_T sc1, ec1, sc2, ec2;
+ getvvcol(curwin, &p1, &sc1, NULL, &ec1);
+ getvvcol(curwin, &p2, &sc2, NULL, &ec2);
+ oap.motion_type = kMTBlockWise;
+ oap.inclusive = true;
+ oap.op_type = OP_NOP;
+ oap.start = p1;
+ oap.end = p2;
+ oap.start_vcol = MIN(sc1, sc2);
+ if (*p_sel == 'e' && ec1 < sc2 && 0 < sc2 && ec2 > ec1) {
+ oap.end_vcol = sc2 - 1;
+ } else {
+ oap.end_vcol = MAX(ec1, ec2);
+ }
+ }
+
+ // Include the trailing byte of a multi-byte char.
+ int l = utfc_ptr2len(ml_get_pos(&p2));
+ if (l > 1) {
+ p2.col += l - 1;
+ }
+
+ for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) {
+ char *akt = NULL;
+
+ if (region_type == kMTLineWise) {
+ akt = xstrdup(ml_get(lnum));
+ } else if (region_type == kMTBlockWise) {
+ struct block_def bd;
+ block_prep(&oap, &bd, lnum, false);
+ akt = block_def2str(&bd);
+ } else if (p1.lnum < lnum && lnum < p2.lnum) {
+ akt = xstrdup(ml_get(lnum));
+ } else {
+ struct block_def bd;
+ charwise_block_prep(p1, p2, &bd, lnum, inclusive);
+ akt = block_def2str(&bd);
+ }
+
+ assert(akt != NULL);
+ tv_list_append_allocated_string(rettv->vval.v_list, akt);
+ }
+
+ virtual_op = save_virtual;
+}
+
/// Common between getreg(), getreginfo() and getregtype(): get the register
/// name from the first argument.
/// Returns zero on error.