Patch 8.2.2324 Problem: Not easy to get mark en cursor posotion by character count. Solution: Add functions that use character index. (Yegappan Lakshmanan, closes #7648) Files: runtime/doc/eval.txt, runtime/doc/usr_41.txt, src/eval.c, src/evalfunc.c, src/proto/eval.pro, src/tag.c, src/testdir/test_cursor_func.vim, src/typval.c
*** ../vim-8.2.2323/runtime/doc/eval.txt 2021-01-02 18:31:10.887220293 +0100 --- runtime/doc/eval.txt 2021-01-10 20:11:39.916682240 +0100 *************** *** 2441,2452 **** changenr() Number current change number char2nr({expr} [, {utf8}]) Number ASCII/UTF8 value of first char in {expr} charclass({string}) Number character class of {string} charidx({string}, {idx} [, {countcc}]) Number char index of byte {idx} in {string} chdir({dir}) String change current working directory cindent({lnum}) Number C indent for line {lnum} clearmatches([{win}]) none clear all matches ! col({expr}) Number column nr of cursor or mark complete({startcol}, {matches}) none set Insert mode completion complete_add({expr}) Number add completion match complete_check() Number check for key typed during completion --- 2477,2489 ---- changenr() Number current change number char2nr({expr} [, {utf8}]) Number ASCII/UTF8 value of first char in {expr} charclass({string}) Number character class of {string} + charcol({expr}) Number column number of cursor or mark charidx({string}, {idx} [, {countcc}]) Number char index of byte {idx} in {string} chdir({dir}) String change current working directory cindent({lnum}) Number C indent for line {lnum} clearmatches([{win}]) none clear all matches ! col({expr}) Number column byte index of cursor or mark complete({startcol}, {matches}) none set Insert mode completion complete_add({expr}) Number add completion match complete_check() Number check for key typed during completion *************** *** 2524,2529 **** --- 2561,2567 ---- getchangelist([{expr}]) List list of change list items getchar([expr]) Number get one character from the user getcharmod() Number modifiers for the last typed character + getcharpos({expr}) List position of cursor, mark, etc. getcharsearch() Dict last character search getcmdline() String return the current command-line getcmdpos() Number return cursor position in command-line *************** *** 2532,2537 **** --- 2570,2576 ---- getcompletion({pat}, {type} [, {filtered}]) List list of cmdline completion matches getcurpos([{winnr}]) List position of the cursor + getcursorcharpos([{winnr}]) List character position of the cursor getcwd([{winnr} [, {tabnr}]]) String get the current working directory getenv({name}) String return environment variable getfontname([{name}]) String name of font being used *************** *** 2722,2728 **** none change an existing property type prop_type_delete({name} [, {props}]) none delete a property type ! prop_type_get([{name} [, {props}]]) Dict get property type values prop_type_list([{props}]) List get list of property types pum_getpos() Dict position and size of pum if visible --- 2761,2767 ---- none change an existing property type prop_type_delete({name} [, {props}]) none delete a property type ! prop_type_get({name} [, {props}]) Dict get property type values prop_type_list([{props}]) List get list of property types pum_getpos() Dict position and size of pum if visible *************** *** 2794,2801 **** --- 2833,2842 ---- setbufvar({expr}, {varname}, {val}) none set {varname} in buffer {expr} to {val} setcellwidths({list}) none set character cell width overrides + setcharpos({expr}, {list}) Number set the {expr} position to {list} setcharsearch({dict}) Dict set character search from {dict} setcmdpos({pos}) Number set cursor position in command-line + setcursorcharpos({list}) Number move cursor to position in {list} setenv({name}, {val}) none set environment variable setfperm({fname}, {mode}) Number set {fname} file permissions to {mode} setline({lnum}, {line}) Number set line {lnum} to {line} *************** *** 2862,2868 **** str2nr({expr} [, {base} [, {quoted}]]) Number convert String to Number strcharpart({str}, {start} [, {len}]) ! String {len} characters of {str} at {start} strchars({expr} [, {skipcc}]) Number character length of the String {expr} strdisplaywidth({expr} [, {col}]) Number display length of the String {expr} strftime({format} [, {time}]) String format time with a specified format --- 2903,2910 ---- str2nr({expr} [, {base} [, {quoted}]]) Number convert String to Number strcharpart({str}, {start} [, {len}]) ! String {len} characters of {str} at ! character {start} strchars({expr} [, {skipcc}]) Number character length of the String {expr} strdisplaywidth({expr} [, {col}]) Number display length of the String {expr} strftime({format} [, {time}]) String format time with a specified format *************** *** 3443,3451 **** byteidx({expr}, {nr}) *byteidx()* Return byte index of the {nr}'th character in the string ! {expr}. Use zero for the first character, it returns zero. ! This function is only useful when there are multibyte ! characters, otherwise the returned value is equal to {nr}. Composing characters are not counted separately, their byte length is added to the preceding base character. See |byteidxcomp()| below for counting composing characters --- 3487,3496 ---- byteidx({expr}, {nr}) *byteidx()* Return byte index of the {nr}'th character in the string ! {expr}. Use zero for the first character, it then returns ! zero. ! If there are no multibyte characters the returned value is ! equal to {nr}. Composing characters are not counted separately, their byte length is added to the preceding base character. See |byteidxcomp()| below for counting composing characters *************** *** 3475,3482 **** < The first and third echo result in 3 ('e' plus composing character is 3 bytes), the second echo results in 1 ('e' is one byte). ! Only works different from byteidx() when 'encoding' is set to ! a Unicode encoding. Can also be used as a |method|: > GetName()->byteidxcomp(idx) --- 3520,3527 ---- < The first and third echo result in 3 ('e' plus composing character is 3 bytes), the second echo results in 1 ('e' is one byte). ! Only works differently from byteidx() when 'encoding' is set ! to a Unicode encoding. Can also be used as a |method|: > GetName()->byteidxcomp(idx) *************** *** 3552,3557 **** --- 3597,3614 ---- other specific Unicode class The class is used in patterns and word motions. + *charcol()* + charcol({expr}) Same as |col()| but returns the character index of the column + position given with {expr} instead of the byte position. + + Example: + With the cursor on '세' in line 5 with text "여보세요": > + charcol('.') returns 3 + col('.') returns 7 + + < Can also be used as a |method|: > + GetPos()->col() + < *charidx()* charidx({string}, {idx} [, {countcc}]) Return the character index of the byte at {idx} in {string}. *************** *** 3642,3648 **** out of range then col() returns zero. To get the line number use |line()|. To get both use |getpos()|. ! For the screen column position use |virtcol()|. Note that only marks in the current file can be used. Examples: > col(".") column of cursor --- 3699,3706 ---- out of range then col() returns zero. To get the line number use |line()|. To get both use |getpos()|. ! For the screen column position use |virtcol()|. For the ! character position use |charcol()|. Note that only marks in the current file can be used. Examples: > col(".") column of cursor *************** *** 3943,3948 **** --- 4001,4009 ---- This is like the return value of |getpos()| or |getcurpos()|, but without the first item. + To position the cursor using the character count, use + |setcursorcharpos()|. + Does not change the jumplist. If {lnum} is greater than the number of lines in the buffer, the cursor will be positioned at the last line in the buffer. *************** *** 5164,5169 **** --- 5243,5262 ---- character itself are obtained. Thus Shift-a results in "A" without a modifier. + *getcharpos()* + getcharpos({expr}) + Get the position for {expr}. Same as |getpos()| but the column + number in the returned List is a character index instead of + a byte index. + + Example: + With the cursor on '세' in line 5 with text "여보세요": > + getcharpos('.') returns [0, 5, 3, 0] + getpos('.') returns [0, 5, 7, 0] + < + Can also be used as a |method|: > + GetMark()->getcharpos() + getcharsearch() *getcharsearch()* Return the current character search information as a {dict} with the following entries: *************** *** 5289,5296 **** includes an extra "curswant" item in the list: [0, lnum, col, off, curswant] ~ The "curswant" number is the preferred column when moving the ! cursor vertically. Also see |getpos()|. ! The first "bufnum" item is always zero. The optional {winid} argument can specify the window. It can be the window number or the |window-ID|. The last known --- 5382,5392 ---- includes an extra "curswant" item in the list: [0, lnum, col, off, curswant] ~ The "curswant" number is the preferred column when moving the ! cursor vertically. Also see |getcursorcharpos()| and ! |getpos()|. ! The first "bufnum" item is always zero. The byte position of ! the cursor is returned in 'col'. To get the character ! position, use |getcursorcharpos()|. The optional {winid} argument can specify the window. It can be the window number or the |window-ID|. The last known *************** *** 5304,5310 **** call setpos('.', save_cursor) < Note that this only works within the window. See |winrestview()| for restoring more state. ! *getcwd()* getcwd([{winnr} [, {tabnr}]]) The result is a String, which is the name of the current working directory. --- 5400,5423 ---- call setpos('.', save_cursor) < Note that this only works within the window. See |winrestview()| for restoring more state. ! ! Can also be used as a |method|: > ! GetWinid()->getcurpos() ! ! < *getcursorcharpos()* ! getcursorcharpos([{winid}]) ! Same as |getcurpos()| but the column number in the returned ! List is a character index instead of a byte index. ! ! Example: ! With the cursor on '보' in line 3 with text "여보세요": > ! getcursorcharpos() returns [0, 3, 2, 0, 3] ! getcurpos() returns [0, 3, 4, 0, 3] ! ! < Can also be used as a |method|: > ! GetWinid()->getcursorcharpos() ! ! < *getcwd()* getcwd([{winnr} [, {tabnr}]]) The result is a String, which is the name of the current working directory. *************** *** 5602,5619 **** Note that for '< and '> Visual mode matters: when it is "V" (visual line mode) the column of '< is zero and the column of '> is a large number. This can be used to save and restore the position of a mark: > let save_a_mark = getpos("'a") ... call setpos("'a", save_a_mark) ! < Also see |getcurpos()| and |setpos()|. Can also be used as a |method|: > GetMark()->getpos() - getqflist([{what}]) *getqflist()* ! Returns a list with all the current quickfix errors. Each list item is a dictionary with these entries: bufnr number of buffer that has the file name, use bufname() to get the name --- 5724,5743 ---- Note that for '< and '> Visual mode matters: when it is "V" (visual line mode) the column of '< is zero and the column of '> is a large number. + The column number in the returned List is the byte position + within the line. To get the character position in the line, + use |getcharpos()| This can be used to save and restore the position of a mark: > let save_a_mark = getpos("'a") ... call setpos("'a", save_a_mark) ! < Also see |getcharpos()|, |getcurpos()| and |setpos()|. Can also be used as a |method|: > GetMark()->getpos() getqflist([{what}]) *getqflist()* ! Returns a |List| with all the current quickfix errors. Each list item is a dictionary with these entries: bufnr number of buffer that has the file name, use bufname() to get the name *************** *** 9116,9122 **** --- 9260,9281 ---- To clear the overrides pass an empty list: > setcellwidths([]); + < You can use the script $VIMRUNTIME/tools/emoji_list.vim to see + the effect for known emoji characters. + + setcharpos({expr}, {list}) *setcharpos()* + Same as |setpos()| but uses the specified column number as the + character index instead of the byte index in the line. + + Example: + With the text "여보세요" in line 8: > + call setcharpos('.', [0, 8, 4, 0]) + < positions the cursor on the fourth character '요'. > + call setpos('.', [0, 8, 4, 0]) + < positions the cursor on the second character '보'. + Can also be used as a |method|: > + GetPosition()->setcharpos('.') setcharsearch({dict}) *setcharsearch()* Set the current character search information to {dict}, *************** *** 9159,9164 **** --- 9318,9338 ---- Can also be used as a |method|: > GetPos()->setcmdpos() + setcursorcharpos({lnum}, {col} [, {off}]) *setcursorcharpos()* + setcursorcharpos({list}) + Same as |cursor()| but uses the specified column number as the + character index instead of the byte index in the line. + + Example: + With the text "여보세요" in line 4: > + call setcursorcharpos(4, 3) + < positions the cursor on the third character '세'. > + call cursor(4, 3) + < positions the cursor on the first character '여'. + + Can also be used as a |method|: > + GetCursorPos()->setcursorcharpos() + setenv({name}, {val}) *setenv()* Set environment variable {name} to {val}. When {val} is |v:null| the environment variable is deleted. *************** *** 9268,9274 **** "lnum" and "col" are the position in the buffer. The first column is 1. Use a zero "lnum" to delete a mark. If "col" is ! smaller than 1 then 1 is used. The "off" number is only used when 'virtualedit' is set. Then it is the offset in screen columns from the start of the --- 9444,9451 ---- "lnum" and "col" are the position in the buffer. The first column is 1. Use a zero "lnum" to delete a mark. If "col" is ! smaller than 1 then 1 is used. To use the character count ! instead of the byte count, use |setcharpos()|. The "off" number is only used when 'virtualedit' is set. Then it is the offset in screen columns from the start of the *************** *** 9288,9294 **** Returns 0 when the position could be set, -1 otherwise. An error message is given if {expr} is invalid. ! Also see |getpos()| and |getcurpos()|. This does not restore the preferred column for moving vertically; if you set the cursor position with this, |j| and --- 9465,9471 ---- Returns 0 when the position could be set, -1 otherwise. An error message is given if {expr} is invalid. ! Also see |setcharpos()|, |getpos()| and |getcurpos()|. This does not restore the preferred column for moving vertically; if you set the cursor position with this, |j| and *** ../vim-8.2.2323/runtime/doc/usr_41.txt 2020-12-28 12:56:54.179617297 +0100 --- runtime/doc/usr_41.txt 2021-01-10 19:55:52.679431025 +0100 *************** *** 745,750 **** --- 753,763 ---- screenchar() get character code at a screen line/row screenchars() get character codes at a screen line/row screenstring() get string of characters at a screen line/row + charcol() character number of the cursor or a mark + getcharpos() get character position of cursor, mark, etc. + setcharpos() set character position of cursor, mark, etc. + getcursorcharpos() get character position of the cursor + setcursorcharpos() set character position of the cursor Working with text in the current buffer: *text-functions* getline() get a line or list of lines from the buffer *** ../vim-8.2.2323/src/eval.c 2021-01-09 15:45:20.353451132 +0100 --- src/eval.c 2021-01-10 20:13:44.516542119 +0100 *************** *** 5054,5059 **** --- 5054,5114 ---- #endif /* + * 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) + { + char_u *str; + + 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; + + str = ml_get_buf(buf, lnum, FALSE); + if (str == NULL) + return -1; + + 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) + { + char_u *str; + char_u *t; + + 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; + + str = ml_get_buf(buf, lnum, FALSE); + if (str == NULL) + return -1; + + // Convert the character offset to a byte offset + t = str; + while (*t != NUL && --charidx > 0) + t += mb_ptr2len(t); + + return t - str + 1; + } + + /* * Translate a String variable into a position. * Returns NULL when there is an error. */ *************** *** 5061,5067 **** var2fpos( typval_T *varp, int dollar_lnum, // TRUE when $ is last line ! int *fnum) // set to fnum for '0, 'A, etc. { char_u *name; static pos_T pos; --- 5116,5123 ---- var2fpos( typval_T *varp, int dollar_lnum, // TRUE when $ is last line ! int *fnum, // set to fnum for '0, 'A, etc. ! int charcol) // return character column { char_u *name; static pos_T pos; *************** *** 5083,5089 **** pos.lnum = list_find_nr(l, 0L, &error); if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) return NULL; // invalid line number ! len = (long)STRLEN(ml_get(pos.lnum)); // Get the column number // We accept "$" for the column number: last column. --- 5139,5148 ---- pos.lnum = list_find_nr(l, 0L, &error); if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) return NULL; // invalid line number ! if (charcol) ! len = (long)mb_charlen(ml_get(pos.lnum)); ! else ! len = (long)STRLEN(ml_get(pos.lnum)); // Get the column number // We accept "$" for the column number: last column. *************** *** 5118,5135 **** if (name == NULL) return NULL; if (name[0] == '.') // cursor ! return &curwin->w_cursor; if (name[0] == 'v' && name[1] == NUL) // Visual start { if (VIsual_active) ! return &VIsual; ! return &curwin->w_cursor; } if (name[0] == '\'') // mark { pp = getmark_buf_fnum(curbuf, name[1], FALSE, fnum); if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) return NULL; return pp; } --- 5177,5205 ---- if (name == NULL) return NULL; if (name[0] == '.') // 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) ! pos = VIsual; ! else ! pos = 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, name[1], FALSE, 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; } *************** *** 5164,5170 **** else { pos.lnum = curwin->w_cursor.lnum; ! pos.col = (colnr_T)STRLEN(ml_get_curline()); } return &pos; } --- 5234,5243 ---- else { pos.lnum = curwin->w_cursor.lnum; ! if (charcol) ! pos.col = (colnr_T)mb_charlen(ml_get_curline()); ! else ! pos.col = (colnr_T)STRLEN(ml_get_curline()); } return &pos; } *************** *** 5184,5190 **** typval_T *arg, pos_T *posp, int *fnump, ! colnr_T *curswantp) { list_T *l = arg->vval.v_list; long i = 0; --- 5257,5264 ---- typval_T *arg, pos_T *posp, int *fnump, ! colnr_T *curswantp, ! int charcol) { list_T *l = arg->vval.v_list; long i = 0; *************** *** 5216,5221 **** --- 5290,5307 ---- n = list_find_nr(l, i++, NULL); // col if (n < 0) return FAIL; + // If character position is specified, then convert to byte position + if (charcol) + { + buf_T *buf; + + // Get the text for the specified line in a loaded buffer + 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 = list_find_nr(l, i, NULL); // off *** ../vim-8.2.2323/src/evalfunc.c 2021-01-07 20:23:29.842515296 +0100 --- src/evalfunc.c 2021-01-10 19:55:52.679431025 +0100 *************** *** 47,52 **** --- 47,53 ---- #endif static void f_changenr(typval_T *argvars, typval_T *rettv); static void f_char2nr(typval_T *argvars, typval_T *rettv); + static void f_charcol(typval_T *argvars, typval_T *rettv); static void f_charidx(typval_T *argvars, typval_T *rettv); static void f_col(typval_T *argvars, typval_T *rettv); static void f_confirm(typval_T *argvars, typval_T *rettv); *************** *** 87,98 **** --- 88,101 ---- static void f_garbagecollect(typval_T *argvars, typval_T *rettv); static void f_get(typval_T *argvars, typval_T *rettv); static void f_getchangelist(typval_T *argvars, typval_T *rettv); + static void f_getcharpos(typval_T *argvars, typval_T *rettv); static void f_getcharsearch(typval_T *argvars, typval_T *rettv); static void f_getenv(typval_T *argvars, typval_T *rettv); static void f_getfontname(typval_T *argvars, typval_T *rettv); static void f_getjumplist(typval_T *argvars, typval_T *rettv); static void f_getpid(typval_T *argvars, typval_T *rettv); static void f_getcurpos(typval_T *argvars, typval_T *rettv); + static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv); static void f_getpos(typval_T *argvars, typval_T *rettv); static void f_getreg(typval_T *argvars, typval_T *rettv); static void f_getreginfo(typval_T *argvars, typval_T *rettv); *************** *** 190,196 **** --- 193,201 ---- static void f_searchpair(typval_T *argvars, typval_T *rettv); static void f_searchpairpos(typval_T *argvars, typval_T *rettv); static void f_searchpos(typval_T *argvars, typval_T *rettv); + static void f_setcharpos(typval_T *argvars, typval_T *rettv); static void f_setcharsearch(typval_T *argvars, typval_T *rettv); + static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv); static void f_setenv(typval_T *argvars, typval_T *rettv); static void f_setfperm(typval_T *argvars, typval_T *rettv); static void f_setpos(typval_T *argvars, typval_T *rettv); *************** *** 790,795 **** --- 795,802 ---- ret_number, f_char2nr}, {"charclass", 1, 1, FEARG_1, NULL, ret_number, f_charclass}, + {"charcol", 1, 1, FEARG_1, NULL, + ret_number, f_charcol}, {"charidx", 2, 3, FEARG_1, NULL, ret_number, f_charidx}, {"chdir", 1, 1, FEARG_1, NULL, *************** *** 928,933 **** --- 935,942 ---- ret_number, f_getchar}, {"getcharmod", 0, 0, 0, NULL, ret_number, f_getcharmod}, + {"getcharpos", 1, 1, FEARG_1, NULL, + ret_list_number, f_getcharpos}, {"getcharsearch", 0, 0, 0, NULL, ret_dict_any, f_getcharsearch}, {"getcmdline", 0, 0, 0, NULL, *************** *** 942,947 **** --- 951,958 ---- ret_list_string, f_getcompletion}, {"getcurpos", 0, 1, FEARG_1, NULL, ret_list_number, f_getcurpos}, + {"getcursorcharpos", 0, 1, FEARG_1, NULL, + ret_list_number, f_getcursorcharpos}, {"getcwd", 0, 2, FEARG_1, NULL, ret_string, f_getcwd}, {"getenv", 1, 1, FEARG_1, NULL, *************** *** 1394,1403 **** --- 1405,1418 ---- ret_void, f_setbufvar}, {"setcellwidths", 1, 1, FEARG_1, NULL, ret_void, f_setcellwidths}, + {"setcharpos", 2, 2, FEARG_2, NULL, + ret_number, f_setcharpos}, {"setcharsearch", 1, 1, FEARG_1, NULL, ret_void, f_setcharsearch}, {"setcmdpos", 1, 1, FEARG_1, NULL, ret_number, f_setcmdpos}, + {"setcursorcharpos", 1, 3, FEARG_1, NULL, + ret_number, f_setcursorcharpos}, {"setenv", 2, 2, FEARG_2, NULL, ret_void, f_setenv}, {"setfperm", 2, 2, FEARG_1, NULL, *************** *** 2424,2429 **** --- 2439,2499 ---- } /* + * 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, int 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 = ml_get_cursor(); + + if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p, + curwin->w_virtcol - curwin->w_cursor.coladd)) + { + int l; + + if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL) + col += l; + } + } + } + } + rettv->vval.v_number = col; + } + + /* + * "charcol()" function + */ + static void + f_charcol(typval_T *argvars, typval_T *rettv) + { + get_col(argvars, rettv, TRUE); + } + + /* * "charidx()" function */ static void *************** *** 2497,2538 **** static void f_col(typval_T *argvars, typval_T *rettv) { ! 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 = ml_get_cursor(); ! ! if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p, ! curwin->w_virtcol - curwin->w_cursor.coladd)) ! { ! int l; ! ! if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL) ! col += l; ! } ! } ! } ! } ! rettv->vval.v_number = col; } /* --- 2567,2573 ---- static void f_col(typval_T *argvars, typval_T *rettv) { ! get_col(argvars, rettv, FALSE); } /* *************** *** 2633,2658 **** #endif /* ! * "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) { long line, col; long coladd = 0; int set_curswant = TRUE; rettv->vval.v_number = -1; ! if (argvars[1].v_type == VAR_UNKNOWN) { pos_T pos; colnr_T curswant = -1; ! if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL) { emsg(_(e_invarg)); return; --- 2668,2691 ---- #endif /* ! * 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, int charcol) { long line, col; long coladd = 0; int set_curswant = TRUE; rettv->vval.v_number = -1; ! if (argvars[0].v_type == VAR_LIST) { pos_T pos; colnr_T curswant = -1; ! if (list2fpos(argvars, &pos, NULL, &curswant, charcol) == FAIL) { emsg(_(e_invarg)); return; *************** *** 2666,2680 **** set_curswant = FALSE; } } ! else { line = tv_get_lnum(argvars); if (line < 0) semsg(_(e_invarg2), tv_get_string(&argvars[0])); col = (long)tv_get_number_chk(&argvars[1], NULL); if (argvars[2].v_type != VAR_UNKNOWN) coladd = (long)tv_get_number_chk(&argvars[2], NULL); } if (line < 0 || col < 0 || coladd < 0) return; // type error; errmsg already given if (line > 0) --- 2699,2722 ---- set_curswant = FALSE; } } ! 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); if (line < 0) semsg(_(e_invarg2), tv_get_string(&argvars[0])); 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) *************** *** 2693,2698 **** --- 2735,2753 ---- 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) + { + set_cursorpos(argvars, rettv, FALSE); + } + #ifdef MSWIN /* * "debugbreak()" function *************** *** 3887,3892 **** --- 3942,4029 ---- #endif } + static void + getpos_both( + typval_T *argvars, + typval_T *rettv, + int getcurpos, + int charcol) + { + pos_T *fp = NULL; + pos_T pos; + win_T *wp = curwin; + list_T *l; + int fnum = -1; + + if (rettv_list_alloc(rettv) == OK) + { + l = rettv->vval.v_list; + 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); + if (fnum != -1) + list_append_number(l, (varnumber_T)fnum); + else + list_append_number(l, (varnumber_T)0); + list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum + : (varnumber_T)0); + list_append_number(l, (fp != NULL) + ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) + : (varnumber_T)0); + list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : + (varnumber_T)0); + if (getcurpos) + { + int save_set_curswant = curwin->w_set_curswant; + colnr_T save_curswant = curwin->w_curswant; + colnr_T save_virtcol = curwin->w_virtcol; + + if (wp == curwin) + update_curswant(); + 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; + } + } + } + else + rettv->vval.v_number = FALSE; + } + + /* + * "getcharpos()" function + */ + static void + f_getcharpos(typval_T *argvars UNUSED, typval_T *rettv) + { + getpos_both(argvars, rettv, FALSE, TRUE); + } + /* * "getcharsearch()" function */ *************** *** 4019,4095 **** rettv->vval.v_number = mch_get_pid(); } - static void - getpos_both( - typval_T *argvars, - typval_T *rettv, - int getcurpos) - { - pos_T *fp = NULL; - win_T *wp = curwin; - list_T *l; - int fnum = -1; - - if (rettv_list_alloc(rettv) == OK) - { - l = rettv->vval.v_list; - 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); - if (fnum != -1) - list_append_number(l, (varnumber_T)fnum); - else - list_append_number(l, (varnumber_T)0); - list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum - : (varnumber_T)0); - list_append_number(l, (fp != NULL) - ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) - : (varnumber_T)0); - list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : - (varnumber_T)0); - if (getcurpos) - { - int save_set_curswant = curwin->w_set_curswant; - colnr_T save_curswant = curwin->w_curswant; - colnr_T save_virtcol = curwin->w_virtcol; - - if (wp == curwin) - update_curswant(); - 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; - } - } - } - else - rettv->vval.v_number = FALSE; - } - /* * "getcurpos()" function */ static void f_getcurpos(typval_T *argvars, typval_T *rettv) { ! getpos_both(argvars, rettv, TRUE); } /* --- 4156,4174 ---- rettv->vval.v_number = mch_get_pid(); } /* * "getcurpos()" function */ static void f_getcurpos(typval_T *argvars, typval_T *rettv) { ! getpos_both(argvars, rettv, TRUE, FALSE); ! } ! ! static void ! f_getcursorcharpos(typval_T *argvars, typval_T *rettv) ! { ! getpos_both(argvars, rettv, TRUE, TRUE); } /* *************** *** 4098,4104 **** static void f_getpos(typval_T *argvars, typval_T *rettv) { ! getpos_both(argvars, rettv, FALSE); } /* --- 4177,4183 ---- static void f_getpos(typval_T *argvars, typval_T *rettv) { ! getpos_both(argvars, rettv, FALSE, FALSE); } /* *************** *** 6183,6196 **** == OK) { check_cursor(); ! fp = var2fpos(&argvars[0], TRUE, &fnum); } restore_win_noblock(save_curwin, save_curtab, TRUE); } } else // use current window ! fp = var2fpos(&argvars[0], TRUE, &fnum); if (fp != NULL) lnum = fp->lnum; --- 6262,6275 ---- == OK) { check_cursor(); ! fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE); } restore_win_noblock(save_curwin, save_curtab, TRUE); } } else // use current window ! fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE); if (fp != NULL) lnum = fp->lnum; *************** *** 8065,8070 **** --- 8144,8203 ---- list_append_number(rettv->vval.v_list, (varnumber_T)n); } + /* + * 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, int charpos) + { + pos_T pos; + int fnum; + char_u *name; + colnr_T curswant = -1; + + rettv->vval.v_number = -1; + + 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(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) + { + set_position(argvars, rettv, TRUE); + } + static void f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED) { *************** *** 8107,8112 **** --- 8240,8254 ---- } /* + * "setcursorcharpos" function + */ + static void + f_setcursorcharpos(typval_T *argvars, typval_T *rettv UNUSED) + { + set_cursorpos(argvars, rettv, TRUE); + } + + /* * "setenv()" function */ static void *************** *** 8165,8205 **** static void f_setpos(typval_T *argvars, typval_T *rettv) { ! pos_T pos; ! int fnum; ! char_u *name; ! colnr_T curswant = -1; ! ! rettv->vval.v_number = -1; ! 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(name[1], &pos, fnum) == OK) ! rettv->vval.v_number = 0; ! } ! else ! emsg(_(e_invarg)); ! } ! } } /* --- 8307,8313 ---- static void f_setpos(typval_T *argvars, typval_T *rettv) { ! set_position(argvars, rettv, FALSE); } /* *************** *** 9947,9953 **** int fnum = curbuf->b_fnum; int len; ! fp = var2fpos(&argvars[0], FALSE, &fnum); if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count && fnum == curbuf->b_fnum) { --- 10055,10061 ---- int fnum = curbuf->b_fnum; int len; ! fp = var2fpos(&argvars[0], FALSE, &fnum, FALSE); if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count && fnum == curbuf->b_fnum) { *** ../vim-8.2.2323/src/proto/eval.pro 2021-01-09 13:20:32.200472552 +0100 --- src/proto/eval.pro 2021-01-10 19:55:52.679431025 +0100 *************** *** 55,62 **** char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); char_u *string_quote(char_u *str, int function); int string2float(char_u *text, float_T *value); ! pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum); ! int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp); int get_env_len(char_u **arg); int get_id_len(char_u **arg); int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose); --- 55,64 ---- char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); char_u *string_quote(char_u *str, int function); int string2float(char_u *text, float_T *value); ! int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx); ! int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx); ! pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum, int charcol); ! int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, int char_col); int get_env_len(char_u **arg); int get_id_len(char_u **arg); int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose); *** ../vim-8.2.2323/src/tag.c 2021-01-04 12:41:49.507891351 +0100 --- src/tag.c 2021-01-10 19:55:52.679431025 +0100 *************** *** 4201,4207 **** // parse 'from' for the cursor position before the tag jump if ((di = dict_find(itemdict, (char_u *)"from", -1)) == NULL) continue; ! if (list2fpos(&di->di_tv, &mark, &fnum, NULL) != OK) continue; if ((tagname = dict_get_string(itemdict, (char_u *)"tagname", TRUE)) == NULL) --- 4201,4207 ---- // parse 'from' for the cursor position before the tag jump if ((di = dict_find(itemdict, (char_u *)"from", -1)) == NULL) continue; ! if (list2fpos(&di->di_tv, &mark, &fnum, NULL, FALSE) != OK) continue; if ((tagname = dict_get_string(itemdict, (char_u *)"tagname", TRUE)) == NULL) *** ../vim-8.2.2323/src/testdir/test_cursor_func.vim 2020-12-21 21:58:42.607687803 +0100 --- src/testdir/test_cursor_func.vim 2021-01-10 19:55:52.683431015 +0100 *************** *** 1,4 **** ! " Tests for cursor(). func Test_wrong_arguments() call assert_fails('call cursor(1. 3)', 'E474:') --- 1,4 ---- ! " Tests for cursor() and other functions that get/set the cursor position func Test_wrong_arguments() call assert_fails('call cursor(1. 3)', 'E474:') *************** *** 123,126 **** --- 123,309 ---- 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('.', test_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(test_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 *** ../vim-8.2.2323/src/typval.c 2021-01-09 16:21:33.996353408 +0100 --- src/typval.c 2021-01-10 19:55:52.683431015 +0100 *************** *** 1579,1585 **** if (lnum <= 0) // no valid number, try using arg like line() { int fnum; ! pos_T *fp = var2fpos(&argvars[0], TRUE, &fnum); if (fp != NULL) lnum = fp->lnum; --- 1579,1585 ---- if (lnum <= 0) // no valid number, try using arg like line() { int fnum; ! pos_T *fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE); if (fp != NULL) lnum = fp->lnum; *** ../vim-8.2.2323/src/version.c 2021-01-10 19:23:23.352958354 +0100 --- src/version.c 2021-01-10 19:57:13.771202312 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2324, /**/ -- ~ ~ ~ ".signature" 4 lines, 50 characters written /// Bram Moolenaar -- b...@moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org /// -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/vim_dev/202101101923.10AJNM5O419873%40masaka.moolenaar.net.