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.

Reply via email to