vim-patch:8.2.2324: not easy to get mark en cursor posotion by character count

Problem:    Not easy to get mark en cursor posotion by character count.
Solution:   Add functions that use character index. (Yegappan Lakshmanan,
            closes vim/vim#7648)
6f02b00bb0
This commit is contained in:
zeertzjq
2022-02-06 04:46:16 +08:00
parent 8ba9f19961
commit 6ab71683d1
9 changed files with 589 additions and 178 deletions

View File

@@ -8151,6 +8151,52 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
return ret;
}
/// Convert the specified byte index of line 'lnum' in buffer 'buf' to a
/// character index. Works only for loaded buffers. Returns -1 on failure.
/// The index of the first character is one.
int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx)
{
if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
return -1;
}
if (lnum > buf->b_ml.ml_line_count) {
lnum = buf->b_ml.ml_line_count;
}
char_u *str = ml_get_buf(buf, lnum, false);
if (*str == NUL) {
return 1;
}
return mb_charlen_len(str, byteidx + 1);
}
/// Convert the specified character index of line 'lnum' in buffer 'buf' to a
/// byte index. Works only for loaded buffers. Returns -1 on failure. The index
/// of the first byte and the first character is one.
int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx)
{
if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
return -1;
}
if (lnum > buf->b_ml.ml_line_count) {
lnum = buf->b_ml.ml_line_count;
}
char_u *str = ml_get_buf(buf, lnum, false);
// Convert the character offset to a byte offset
char_u *t = str;
while (*t != NUL && --charidx > 0) {
t += utfc_ptr2len(t);
}
return t - str + 1;
}
/// Translate a VimL object into a position
///
/// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid
@@ -8159,9 +8205,11 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
/// @param[in] tv Object to translate.
/// @param[in] dollar_lnum True when "$" is last line.
/// @param[out] ret_fnum Set to fnum for marks.
/// @param[in] charcol True to return character column.
///
/// @return Pointer to position or NULL in case of error (e.g. invalid type).
pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret_fnum)
pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret_fnum,
const bool charcol)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
static pos_T pos;
@@ -8191,7 +8239,11 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
if (error) {
return NULL;
}
len = (long)STRLEN(ml_get(pos.lnum));
if (charcol) {
len = mb_charlen(ml_get(pos.lnum));
} else {
len = STRLEN(ml_get(pos.lnum));
}
// We accept "$" for the column number: last column.
li = tv_list_find(l, 1L);
@@ -8222,19 +8274,31 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
return NULL;
}
if (name[0] == '.') { // Cursor.
return &curwin->w_cursor;
pos = curwin->w_cursor;
if (charcol) {
pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1;
}
return &pos;
}
if (name[0] == 'v' && name[1] == NUL) { // Visual start.
if (VIsual_active) {
return &VIsual;
pos = VIsual;
} else {
pos = curwin->w_cursor;
}
return &curwin->w_cursor;
if (charcol) {
pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1;
}
return &pos;
}
if (name[0] == '\'') { // Mark.
pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum);
if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) {
return NULL;
}
if (charcol) {
pp->col = buf_byteidx_to_charidx(curbuf, pp->lnum, pp->col) - 1;
}
return pp;
}
@@ -8260,22 +8324,24 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
pos.col = 0;
} else {
pos.lnum = curwin->w_cursor.lnum;
pos.col = (colnr_T)STRLEN(get_cursor_line_ptr());
if (charcol) {
pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr());
} else {
pos.col = (colnr_T)STRLEN(get_cursor_line_ptr());
}
}
return &pos;
}
return NULL;
}
/*
* Convert list in "arg" into a position and optional file number.
* When "fnump" is NULL there is no file number, only 3 items.
* Note that the column is passed on as-is, the caller may want to decrement
* it to use 1 for the first column.
* Return FAIL when conversion is not possible, doesn't check the position for
* validity.
*/
int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
/// Convert list in "arg" into a position and optional file number.
/// When "fnump" is NULL there is no file number, only 3 items.
/// Note that the column is passed on as-is, the caller may want to decrement
/// it to use 1 for the first column.
/// Return FAIL when conversion is not possible, doesn't check the position for
/// validity.
int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool charcol)
{
list_T *l;
long i = 0;
@@ -8311,6 +8377,15 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
if (n < 0) {
return FAIL;
}
// If character position is specified, then convert to byte position
if (charcol) {
// Get the text for the specified line in a loaded buffer
buf_T *buf = buflist_findnr(fnump == NULL ? curbuf->b_fnum : *fnump);
if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
return FAIL;
}
n = buf_charidx_to_byteidx(buf, posp->lnum, n);
}
posp->col = n;
n = tv_list_find_nr(l, i, NULL); // off

View File

@@ -71,6 +71,7 @@ return {
chanclose={args={1, 2}},
chansend={args=2},
char2nr={args={1, 2}, base=1},
charcol={args=1, base=1},
charidx={args={2, 3}, base=1},
chdir={args=1, base=1},
cindent={args=1, base=1},
@@ -144,6 +145,7 @@ return {
getchangelist={args={0, 1}, base=1},
getchar={args={0, 1}},
getcharmod={},
getcharpos={args=1, base=1},
getcharsearch={},
getcharstr={args={0, 1}},
getcmdline={},
@@ -152,6 +154,7 @@ return {
getcmdwintype={},
getcompletion={args={2, 3}, base=1},
getcurpos={args={0, 1}, base=1},
getcursorcharpos={args={0, 1}, base=1},
getcwd={args={0, 2}, base=1},
getenv={args=1, base=1},
getfontname={args={0, 1}},
@@ -312,8 +315,10 @@ return {
serverstop={args=1},
setbufline={args=3, base=3},
setbufvar={args=3, base=3},
setcharpos={args=2, base=2},
setcharsearch={args=1, base=1},
setcmdpos={args=1, base=1},
setcursorcharpos={args={1, 3}, base=1},
setenv={args=2, base=2},
setfperm={args=2, base=1},
setline={args=2, base=2},

View File

@@ -1020,6 +1020,49 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = utf_ptr2char((const char_u *)tv_get_string(&argvars[0]));
}
/// Get the current cursor column and store it in 'rettv'. If 'charcol' is true,
/// returns the character index of the column. Otherwise, returns the byte index
/// of the column.
static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
{
colnr_T col = 0;
pos_T *fp;
int fnum = curbuf->b_fnum;
fp = var2fpos(&argvars[0], false, &fnum, charcol);
if (fp != NULL && fnum == curbuf->b_fnum) {
if (fp->col == MAXCOL) {
// '> can be MAXCOL, get the length of the line then
if (fp->lnum <= curbuf->b_ml.ml_line_count) {
col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1;
} else {
col = MAXCOL;
}
} else {
col = fp->col + 1;
// col(".") when the cursor is on the NUL at the end of the line
// because of "coladd" can be seen as an extra column.
if (virtual_active() && fp == &curwin->w_cursor) {
char_u *p = get_cursor_pos_ptr();
if (curwin->w_cursor.coladd >=
(colnr_T)win_chartabsize(curwin, p, curwin->w_virtcol - curwin->w_cursor.coladd)) {
int l;
if (*p != NUL && p[(l = utfc_ptr2len(p))] == NUL) {
col += l;
}
}
}
}
}
rettv->vval.v_number = col;
}
/// "charcol()" function
static void f_charcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
get_col(argvars, rettv, true);
}
// "charidx()" function
static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -1148,45 +1191,10 @@ static void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
/*
* "col(string)" function
*/
/// "col(string)" function
static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
colnr_T col = 0;
pos_T *fp;
int fnum = curbuf->b_fnum;
fp = var2fpos(&argvars[0], FALSE, &fnum);
if (fp != NULL && fnum == curbuf->b_fnum) {
if (fp->col == MAXCOL) {
// '> can be MAXCOL, get the length of the line then
if (fp->lnum <= curbuf->b_ml.ml_line_count) {
col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1;
} else {
col = MAXCOL;
}
} else {
col = fp->col + 1;
// col(".") when the cursor is on the NUL at the end of the line
// because of "coladd" can be seen as an extra column.
if (virtual_active() && fp == &curwin->w_cursor) {
char_u *p = get_cursor_pos_ptr();
if (curwin->w_cursor.coladd
>= (colnr_T)win_chartabsize(curwin, p,
(curwin->w_virtcol
- curwin->w_cursor.coladd))) {
int l;
if (*p != NUL && p[(l = utfc_ptr2len(p))] == NUL) {
col += l;
}
}
}
}
}
rettv->vval.v_number = col;
get_col(argvars, rettv, false);
}
/*
@@ -1549,24 +1557,21 @@ static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = ctx_size();
}
/// "cursor(lnum, col)" function, or
/// "cursor(list)"
///
/// Moves the cursor to the specified line and column.
///
/// @returns 0 when the position could be set, -1 otherwise.
static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// Set the cursor position.
/// If 'charcol' is true, then use the column number as a character offet.
/// Otherwise use the column number as a byte offset.
static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
{
long line, col;
long coladd = 0;
bool set_curswant = true;
rettv->vval.v_number = -1;
if (argvars[1].v_type == VAR_UNKNOWN) {
if (argvars[0].v_type == VAR_LIST) {
pos_T pos;
colnr_T curswant = -1;
if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL) {
if (list2fpos(argvars, &pos, NULL, &curswant, charcol) == FAIL) {
emsg(_(e_invarg));
return;
}
@@ -1578,16 +1583,22 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
curwin->w_curswant = curswant - 1;
set_curswant = false;
}
} else {
} else if ((argvars[0].v_type == VAR_NUMBER || argvars[0].v_type == VAR_STRING)
&& argvars[1].v_type == VAR_NUMBER) {
line = tv_get_lnum(argvars);
col = (long)tv_get_number_chk(&argvars[1], NULL);
if (charcol) {
col = buf_charidx_to_byteidx(curbuf, line, col);
}
if (argvars[2].v_type != VAR_UNKNOWN) {
coladd = (long)tv_get_number_chk(&argvars[2], NULL);
}
} else {
emsg(_(e_invarg));
return;
}
if (line < 0 || col < 0
|| coladd < 0) {
return; // type error; errmsg already given
if (line < 0 || col < 0 || coladd < 0) {
return; // type error; errmsg already given
}
if (line > 0) {
curwin->w_cursor.lnum = line;
@@ -1606,6 +1617,16 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = 0;
}
/// "cursor(lnum, col)" function, or
/// "cursor(list)"
///
/// Moves the cursor to the specified line and column.
/// Returns 0 when the position could be set, -1 otherwise.
static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
set_cursorpos(argvars, rettv, false);
}
// "debugbreak()" function
static void f_debugbreak(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -3288,6 +3309,67 @@ static void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = mod_mask;
}
static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool charcol)
{
pos_T *fp = NULL;
pos_T pos;
win_T *wp = curwin;
int fnum = -1;
if (getcurpos) {
if (argvars[0].v_type != VAR_UNKNOWN) {
wp = find_win_by_nr_or_id(&argvars[0]);
if (wp != NULL) {
fp = &wp->w_cursor;
}
} else {
fp = &curwin->w_cursor;
}
if (fp != NULL && charcol) {
pos = *fp;
pos.col = buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, pos.col) - 1;
fp = &pos;
}
} else {
fp = var2fpos(&argvars[0], true, &fnum, charcol);
}
list_T *const l = tv_list_alloc_ret(rettv, 4 + getcurpos);
tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0);
tv_list_append_number(l, ((fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0));
tv_list_append_number(l, ((fp != NULL)
? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1)
: (varnumber_T)0));
tv_list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0);
if (getcurpos) {
const int save_set_curswant = curwin->w_set_curswant;
const colnr_T save_curswant = curwin->w_curswant;
const colnr_T save_virtcol = curwin->w_virtcol;
if (wp == curwin) {
update_curswant();
}
tv_list_append_number(l, (wp == NULL) ? 0 : ((wp->w_curswant == MAXCOL)
? (varnumber_T)MAXCOL
: (varnumber_T)wp->w_curswant + 1));
// Do not change "curswant", as it is unexpected that a get
// function has a side effect.
if (wp == curwin && save_set_curswant) {
curwin->w_set_curswant = save_set_curswant;
curwin->w_curswant = save_curswant;
curwin->w_virtcol = save_virtcol;
curwin->w_valid &= ~VALID_VIRTCOL;
}
}
}
/// "getcharpos()" function
static void f_getcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
getpos_both(argvars, rettv, false, true);
}
/*
* "getcharsearch()" function
*/
@@ -3843,69 +3925,21 @@ static void f_getpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = os_get_pid();
}
static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos)
{
pos_T *fp = NULL;
win_T *wp = curwin;
int fnum = -1;
if (getcurpos) {
if (argvars[0].v_type != VAR_UNKNOWN) {
wp = find_win_by_nr_or_id(&argvars[0]);
if (wp != NULL) {
fp = &wp->w_cursor;
}
} else {
fp = &curwin->w_cursor;
}
} else {
fp = var2fpos(&argvars[0], true, &fnum);
}
list_T *const l = tv_list_alloc_ret(rettv, 4 + (!!getcurpos));
tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0);
tv_list_append_number(l, ((fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0));
tv_list_append_number(l, ((fp != NULL)
? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1)
: (varnumber_T)0));
tv_list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0);
if (getcurpos) {
const int save_set_curswant = curwin->w_set_curswant;
const colnr_T save_curswant = curwin->w_curswant;
const colnr_T save_virtcol = curwin->w_virtcol;
if (wp == curwin) {
update_curswant();
}
tv_list_append_number(l, (wp == NULL) ? 0 : (wp->w_curswant == MAXCOL)
? (varnumber_T)MAXCOL
: (varnumber_T)wp->w_curswant + 1);
// Do not change "curswant", as it is unexpected that a get
// function has a side effect.
if (wp == curwin && save_set_curswant) {
curwin->w_set_curswant = save_set_curswant;
curwin->w_curswant = save_curswant;
curwin->w_virtcol = save_virtcol;
curwin->w_valid &= ~VALID_VIRTCOL;
}
}
}
/*
* "getcurpos(string)" function
*/
/// "getcurpos(string)" function
static void f_getcurpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
getpos_both(argvars, rettv, true);
getpos_both(argvars, rettv, true, false);
}
/*
* "getpos(string)" function
*/
static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
getpos_both(argvars, rettv, true, true);
}
/// "getpos(string)" function
static void f_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
getpos_both(argvars, rettv, false);
getpos_both(argvars, rettv, false, false);
}
/// "getqflist()" functions
@@ -5889,13 +5923,13 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
switchwin_T switchwin;
if (switch_win_noblock(&switchwin, wp, tp, true) == OK) {
check_cursor();
fp = var2fpos(&argvars[0], true, &fnum);
fp = var2fpos(&argvars[0], true, &fnum, false);
}
restore_win_noblock(&switchwin, true);
}
} else {
// use current window
fp = var2fpos(&argvars[0], true, &fnum);
fp = var2fpos(&argvars[0], true, &fnum, false);
}
if (fp != NULL) {
@@ -9000,6 +9034,49 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
/// Set the cursor or mark position.
/// If 'charpos' is TRUE, then use the column number as a character offet.
/// Otherwise use the column number as a byte offset.
static void set_position(typval_T *argvars, typval_T *rettv, bool charpos)
{
pos_T pos;
int fnum;
colnr_T curswant = -1;
rettv->vval.v_number = -1;
const char *const name = tv_get_string_chk(argvars);
if (name != NULL) {
if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) == OK) {
if (pos.col != MAXCOL && --pos.col < 0) {
pos.col = 0;
}
if (name[0] == '.' && name[1] == NUL) {
// set cursor; "fnum" is ignored
curwin->w_cursor = pos;
if (curswant >= 0) {
curwin->w_curswant = curswant - 1;
curwin->w_set_curswant = false;
}
check_cursor();
rettv->vval.v_number = 0;
} else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) {
// set mark
if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) {
rettv->vval.v_number = 0;
}
} else {
emsg(_(e_invarg));
}
}
}
}
/// "setcharpos()" function
static void f_setcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
set_position(argvars, rettv, true);
}
static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
dict_T *d;
@@ -9042,6 +9119,12 @@ static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
/// "setcursorcharpos" function
static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
set_cursorpos(argvars, rettv, true);
}
/// "setenv()" function
static void f_setenv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -9298,41 +9381,10 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
/*
* "setpos()" function
*/
/// "setpos()" function
static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
pos_T pos;
int fnum;
colnr_T curswant = -1;
rettv->vval.v_number = -1;
const char *const name = tv_get_string_chk(argvars);
if (name != NULL) {
if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) {
if (pos.col != MAXCOL && --pos.col < 0) {
pos.col = 0;
}
if (name[0] == '.' && name[1] == NUL) {
// set cursor; "fnum" is ignored
curwin->w_cursor = pos;
if (curswant >= 0) {
curwin->w_curswant = curswant - 1;
curwin->w_set_curswant = false;
}
check_cursor();
rettv->vval.v_number = 0;
} else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) {
// set mark
if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) {
rettv->vval.v_number = 0;
}
} else {
emsg(_(e_invarg));
}
}
}
set_position(argvars, rettv, false);
}
/*
@@ -12015,7 +12067,7 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
pos_T *fp;
int fnum = curbuf->b_fnum;
fp = var2fpos(&argvars[0], FALSE, &fnum);
fp = var2fpos(&argvars[0], false, &fnum, false);
if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count
&& fnum == curbuf->b_fnum) {
// Limit the column to a valid value, getvvcol() doesn't check.

View File

@@ -3091,7 +3091,7 @@ linenr_T tv_get_lnum(const typval_T *const tv)
linenr_T lnum = (linenr_T)tv_get_number_chk(tv, NULL);
if (lnum == 0) { // No valid number, try using same function as line() does.
int fnum;
pos_T *const fp = var2fpos(tv, true, &fnum);
pos_T *const fp = var2fpos(tv, true, &fnum, false);
if (fp != NULL) {
lnum = fp->lnum;
}

View File

@@ -3408,7 +3408,7 @@ static void tagstack_push_items(win_T *wp, list_T *l)
if ((di = tv_dict_find(itemdict, "from", -1)) == NULL) {
continue;
}
if (list2fpos(&di->di_tv, &mark, &fnum, NULL) != OK) {
if (list2fpos(&di->di_tv, &mark, &fnum, NULL, false) != OK) {
continue;
}
if ((tagname = (char_u *)tv_dict_get_string(itemdict, "tagname", true))

View File

@@ -1,4 +1,4 @@
" Tests for cursor().
" Tests for cursor() and other functions that get/set the cursor position
func Test_wrong_arguments()
call assert_fails('call cursor(1. 3)', 'E474:')
@@ -119,3 +119,188 @@ func Test_screenpos_number()
close
bwipe!
endfunc
func SaveVisualStartCharPos()
call add(g:VisualStartPos, getcharpos('v'))
return ''
endfunc
" Test for the getcharpos() function
func Test_getcharpos()
call assert_fails('call getcharpos({})', 'E731:')
call assert_equal([0, 0, 0, 0], getcharpos(0))
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
" Test for '.' and '$'
normal 1G
call assert_equal([0, 1, 1, 0], getcharpos('.'))
call assert_equal([0, 4, 1, 0], getcharpos('$'))
normal 2G6l
call assert_equal([0, 2, 7, 0], getcharpos('.'))
normal 3G$
call assert_equal([0, 3, 1, 0], getcharpos('.'))
normal 4G$
call assert_equal([0, 4, 9, 0], getcharpos('.'))
" Test for a mark
normal 2G7lmmgg
call assert_equal([0, 2, 8, 0], getcharpos("'m"))
delmarks m
call assert_equal([0, 0, 0, 0], getcharpos("'m"))
" Test for the visual start column
vnoremap <expr> <F3> SaveVisualStartCharPos()
let g:VisualStartPos = []
exe "normal 2G6lv$\<F3>ohh\<F3>o\<F3>"
call assert_equal([[0, 2, 7, 0], [0, 2, 9, 0], [0, 2, 5, 0]], g:VisualStartPos)
call assert_equal([0, 2, 9, 0], getcharpos('v'))
let g:VisualStartPos = []
exe "normal 3Gv$\<F3>o\<F3>"
call assert_equal([[0, 3, 1, 0], [0, 3, 1, 0]], g:VisualStartPos)
let g:VisualStartPos = []
exe "normal 1Gv$\<F3>o\<F3>"
call assert_equal([[0, 1, 1, 0], [0, 1, 1, 0]], g:VisualStartPos)
vunmap <F3>
%bw!
endfunc
" Test for the setcharpos() function
func Test_setcharpos()
call assert_equal(-1, setcharpos('.', v:_null_list))
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
call setcharpos('.', [0, 1, 1, 0])
call assert_equal([1, 1], [line('.'), col('.')])
call setcharpos('.', [0, 2, 7, 0])
call assert_equal([2, 9], [line('.'), col('.')])
call setcharpos('.', [0, 3, 4, 0])
call assert_equal([3, 1], [line('.'), col('.')])
call setcharpos('.', [0, 3, 1, 0])
call assert_equal([3, 1], [line('.'), col('.')])
call setcharpos('.', [0, 4, 0, 0])
call assert_equal([4, 1], [line('.'), col('.')])
call setcharpos('.', [0, 4, 20, 0])
call assert_equal([4, 9], [line('.'), col('.')])
" Test for mark
delmarks m
call setcharpos("'m", [0, 2, 9, 0])
normal `m
call assert_equal([2, 11], [line('.'), col('.')])
%bw!
call assert_equal(-1, setcharpos('.', [10, 3, 1, 0]))
endfunc
func SaveVisualStartCharCol()
call add(g:VisualStartCol, charcol('v'))
return ''
endfunc
" Test for the charcol() function
func Test_charcol()
call assert_fails('call charcol({})', 'E731:')
call assert_equal(0, charcol(0))
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
" Test for '.' and '$'
normal 1G
call assert_equal(1, charcol('.'))
call assert_equal(1, charcol('$'))
normal 2G6l
call assert_equal(7, charcol('.'))
call assert_equal(10, charcol('$'))
normal 3G$
call assert_equal(1, charcol('.'))
call assert_equal(2, charcol('$'))
normal 4G$
call assert_equal(9, charcol('.'))
call assert_equal(10, charcol('$'))
" Test for [lnum, '$']
call assert_equal(1, charcol([1, '$']))
call assert_equal(10, charcol([2, '$']))
call assert_equal(2, charcol([3, '$']))
call assert_equal(0, charcol([5, '$']))
" Test for a mark
normal 2G7lmmgg
call assert_equal(8, charcol("'m"))
delmarks m
call assert_equal(0, charcol("'m"))
" Test for the visual start column
vnoremap <expr> <F3> SaveVisualStartCharCol()
let g:VisualStartCol = []
exe "normal 2G6lv$\<F3>ohh\<F3>o\<F3>"
call assert_equal([7, 9, 5], g:VisualStartCol)
call assert_equal(9, charcol('v'))
let g:VisualStartCol = []
exe "normal 3Gv$\<F3>o\<F3>"
call assert_equal([1, 1], g:VisualStartCol)
let g:VisualStartCol = []
exe "normal 1Gv$\<F3>o\<F3>"
call assert_equal([1, 1], g:VisualStartCol)
vunmap <F3>
%bw!
endfunc
" Test for getcursorcharpos()
func Test_getcursorcharpos()
call assert_equal(getcursorcharpos(), getcursorcharpos(0))
call assert_equal([0, 0, 0, 0, 0], getcursorcharpos(-1))
call assert_equal([0, 0, 0, 0, 0], getcursorcharpos(1999))
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
normal 1G9l
call assert_equal([0, 1, 1, 0, 1], getcursorcharpos())
normal 2G9l
call assert_equal([0, 2, 9, 0, 14], getcursorcharpos())
normal 3G9l
call assert_equal([0, 3, 1, 0, 1], getcursorcharpos())
normal 4G9l
call assert_equal([0, 4, 9, 0, 9], getcursorcharpos())
let winid = win_getid()
normal 2G5l
wincmd w
call assert_equal([0, 2, 6, 0, 11], getcursorcharpos(winid))
%bw!
endfunc
" Test for setcursorcharpos()
func Test_setcursorcharpos()
call assert_fails('call setcursorcharpos(v:_null_list)', 'E474:')
call assert_fails('call setcursorcharpos([1])', 'E474:')
call assert_fails('call setcursorcharpos([1, 1, 1, 1, 1])', 'E474:')
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
normal G
call setcursorcharpos([1, 1])
call assert_equal([1, 1], [line('.'), col('.')])
call setcursorcharpos([2, 7, 0])
call assert_equal([2, 9], [line('.'), col('.')])
call setcursorcharpos(3, 4)
call assert_equal([3, 1], [line('.'), col('.')])
call setcursorcharpos([3, 1])
call assert_equal([3, 1], [line('.'), col('.')])
call setcursorcharpos([4, 0, 0, 0])
call assert_equal([4, 1], [line('.'), col('.')])
call setcursorcharpos([4, 20])
call assert_equal([4, 9], [line('.'), col('.')])
normal 1G
call setcursorcharpos([100, 100, 100, 100])
call assert_equal([4, 9], [line('.'), col('.')])
normal 1G
call setcursorcharpos('$', 1)
call assert_equal([4, 1], [line('.'), col('.')])
%bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab