]> granicus.if.org Git - vim/commitdiff
patch 8.2.2324: not easy to get mark en cursor posotion by character count v8.2.2324
authorBram Moolenaar <Bram@vim.org>
Sun, 10 Jan 2021 19:22:54 +0000 (20:22 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 10 Jan 2021 19:22:54 +0000 (20:22 +0100)
Problem:    Not easy to get mark en cursor posotion by character count.
Solution:   Add functions that use character index. (Yegappan Lakshmanan,
            closes #7648)

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
src/version.c

index 750448dfc6c409bd257d763382422ccd2773dea4..7848534b5da795bfa6f9ec023404c467c8f00292 100644 (file)
@@ -1,4 +1,4 @@
-*eval.txt*     For Vim version 8.2.  Last change: 2020 Dec 29
+*eval.txt*     For Vim version 8.2.  Last change: 2021 Jan 10
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -49,7 +49,7 @@ There are ten types of variables:
                                                        *Number* *Integer*
 Number         A 32 or 64 bit signed number.  |expr-number|
                The number of bits is available in |v:numbersize|.
-               Examples:  -123  0x10  0177  0b1011
+               Examples:  -123  0x10  0177  0o177 0b1011
 
 Float          A floating point number. |floating-point-format| *Float*
                {only when compiled with the |+float| feature}
@@ -97,9 +97,10 @@ the Number.  Examples:
 Conversion from a String to a Number only happens in legacy Vim script, not in
 Vim9 script.  It is done by converting the first digits to a number.
 Hexadecimal "0xf9", Octal "017" or "0o17", and Binary "0b10"
-numbers are recognized (NOTE: when using |scriptversion-4| octal with a
-leading "0" is not recognized).  If the String doesn't start with digits, the
-result is zero.
+numbers are recognized
+NOTE: when using |scriptversion-4| octal with a leading "0" is not recognized.
+The 0o notation requires patch 8.2.0886.
+If the String doesn't start with digits, the result is zero.
 Examples:
        String "456"    -->     Number 456 ~
        String "6bar"   -->     Number 6 ~
@@ -1150,7 +1151,7 @@ expr7                                                     *expr7*
 
 For '!' |TRUE| becomes |FALSE|, |FALSE| becomes |TRUE| (one).
 For '-' the sign of the number is changed.
-For '+' the number is unchanged.
+For '+' the number is unchanged.  Note: "++" has no effect.
 
 A String will be converted to a Number first.
 
@@ -1191,6 +1192,7 @@ start with one!
 If the length of the String is less than the index, the result is an empty
 String.  A negative index always results in an empty string (reason: backward
 compatibility).  Use [-1:] to get the last byte or character.
+In Vim9 script a negative index is used like with a list: count from the end.
 
 If expr8 is a |List| then it results the item at index expr1.  See |list-index|
 for possible index values.  If the index is out of range this results in an
@@ -1318,8 +1320,8 @@ When using the lambda form there must be no white space between the } and the
 number
 ------
 number                 number constant                 *expr-number*
-                               *hex-number* *octal-number* *binary-number*
 
+                       *0x* *hex-number* *0o* *octal-number* *binary-number*
 Decimal, Hexadecimal (starting with 0x or 0X), Binary (starting with 0b or 0B)
 and Octal (starting with 0, 0o or 0O).
 
@@ -1572,7 +1574,7 @@ Note how execute() is used to execute an Ex command.  That's ugly though.
 
 Lambda expressions have internal names like '<lambda>42'.  If you get an error
 for a lambda expression, you can find what it is with the following command: >
-       :function {'<lambda>42'}
+       :function <lambda>42
 See also: |numbered-function|
 
 ==============================================================================
@@ -2475,12 +2477,13 @@ ch_status({handle} [, {options}])
 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 nr of cursor or mark
+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
@@ -2558,6 +2561,7 @@ getbufvar({expr}, {varname} [, {def}])
 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
@@ -2566,6 +2570,7 @@ getcmdwintype()                   String  return current command-line window type
 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
@@ -2828,8 +2833,10 @@ setbufline({expr}, {lnum}, {text})
 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}
@@ -3513,8 +3520,8 @@ byteidxcomp({expr}, {nr})                                 *byteidxcomp()*
 <              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.
+               Only works differently from byteidx() when 'encoding' is set
+               to a Unicode encoding.
 
                Can also be used as a |method|: >
                        GetName()->byteidxcomp(idx)
@@ -3590,6 +3597,18 @@ charclass({string})                                      *charclass()*
                        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}.
@@ -3680,7 +3699,8 @@ col({expr})       The result is a Number, which is the byte index of the column
                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 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
@@ -3981,6 +4001,9 @@ cursor({list})
                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.
@@ -5220,6 +5243,20 @@ getcharmod()                                             *getcharmod()*
                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:
@@ -5345,8 +5382,11 @@ getcurpos([{winid}])
                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.
+               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
@@ -5360,7 +5400,24 @@ getcurpos([{winid}])
                        call setpos('.', save_cursor)
 <              Note that this only works within the window.  See
                |winrestview()| for restoring more state.
-                                                       *getcwd()*
+
+               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.
@@ -5667,16 +5724,18 @@ getpos({expr})  Get the position for {expr}.  For possible values of {expr}
                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 |getcurpos()| and |setpos()|.
+<              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:
@@ -7542,8 +7601,10 @@ matchstrpos({expr}, {pat} [, {start} [, {count}]])               *matchstrpos()*
 <
 
                                                        *max()*
-max({expr})    Return the maximum value of all items in {expr}.
-               {expr} can be a |List| or a |Dictionary|.  For a Dictionary,
+max({expr})    Return the maximum value of all items in {expr}. Example: >
+                       echo max([apples, pears, oranges])
+
+<              {expr} can be a |List| or a |Dictionary|.  For a Dictionary,
                it returns the maximum of all values in the Dictionary.
                If {expr} is neither a List nor a Dictionary, or one of the
                items in {expr} cannot be used as a Number this results in
@@ -7613,8 +7674,10 @@ menu_info({name} [, {mode}])                             *menu_info()*
 
 
 <                                                      *min()*
-min({expr})    Return the minimum value of all items in {expr}.
-               {expr} can be a |List| or a |Dictionary|.  For a Dictionary,
+min({expr})    Return the minimum value of all items in {expr}. Example:  >
+                       echo min([apples, pears, oranges])
+
+<              {expr} can be a |List| or a |Dictionary|.  For a Dictionary,
                it returns the minimum of all values in the Dictionary.
                If {expr} is neither a List nor a Dictionary, or one of the
                items in {expr} cannot be used as a Number this results in
@@ -7631,13 +7694,13 @@ mkdir({name} [, {path} [, {prot}]])
                necessary.  Otherwise it must be "".
 
                If {prot} is given it is used to set the protection bits of
-               the new directory.  The default is 0755 (rwxr-xr-x: r/w for
-               the user readable for others).  Use 0700 to make it unreadable
-               for others.  This is only used for the last part of {name}.
-               Thus if you create /tmp/foo/bar then /tmp/foo will be created
-               with 0755.
+               the new directory.  The default is 0o755 (rwxr-xr-x: r/w for
+               the user, readable for others).  Use 0o700 to make it
+               unreadable for others.  This is only used for the last part of
+               {name}.  Thus if you create /tmp/foo/bar then /tmp/foo will be
+               created with 0o755.
                Example: >
-                       :call mkdir($HOME . "/tmp/foo/bar", "p", 0700)
+                       :call mkdir($HOME . "/tmp/foo/bar", "p", 0o700)
 
 <              This function is not available in the |sandbox|.
 
@@ -9200,6 +9263,19 @@ setcellwidths({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},
@@ -9242,6 +9318,21 @@ setcmdpos({pos})                                 *setcmdpos()*
                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.
@@ -9353,7 +9444,8 @@ setpos({expr}, {list})
 
                "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.
+               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
@@ -9373,7 +9465,7 @@ setpos({expr}, {list})
                Returns 0 when the position could be set, -1 otherwise.
                An error message is given if {expr} is invalid.
 
-               Also see |getpos()| and |getcurpos()|.
+               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
index a035038e4799cbb0c19bf3b56f76265f60a94108..da095e13f4665cceec5dea0dddd62c45fe3c4ffc 100644 (file)
@@ -753,6 +753,11 @@ Cursor and mark position:          *cursor-functions* *mark-functions*
        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
index 9c91e526d34b55c69ffd3036e17fe0253ef8629e..a0877f649bb3d0a65a3a28a91283122a3be0e66f 100644 (file)
@@ -5053,6 +5053,61 @@ string2float(
 }
 #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,7 +5116,8 @@ string2float(
 var2fpos(
     typval_T   *varp,
     int                dollar_lnum,    // TRUE when $ is last line
-    int                *fnum)          // set to fnum for '0, 'A, etc.
+    int                *fnum,          // set to fnum for '0, 'A, etc.
+    int                charcol)        // return character column
 {
     char_u             *name;
     static pos_T       pos;
@@ -5083,7 +5139,10 @@ var2fpos(
        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));
+       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,18 +5177,29 @@ var2fpos(
     if (name == NULL)
        return NULL;
     if (name[0] == '.')                                // cursor
-       return &curwin->w_cursor;
+    {
+       pos = curwin->w_cursor;
+       if (charcol)
+           pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1;
+       return &pos;
+    }
     if (name[0] == 'v' && name[1] == NUL)      // Visual start
     {
        if (VIsual_active)
-           return &VIsual;
-       return &curwin->w_cursor;
+           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,7 +5234,10 @@ var2fpos(
        else
        {
            pos.lnum = curwin->w_cursor.lnum;
-           pos.col = (colnr_T)STRLEN(ml_get_curline());
+           if (charcol)
+               pos.col = (colnr_T)mb_charlen(ml_get_curline());
+           else
+               pos.col = (colnr_T)STRLEN(ml_get_curline());
        }
        return &pos;
     }
@@ -5184,7 +5257,8 @@ list2fpos(
     typval_T   *arg,
     pos_T      *posp,
     int                *fnump,
-    colnr_T    *curswantp)
+    colnr_T    *curswantp,
+    int                charcol)
 {
     list_T     *l = arg->vval.v_list;
     long       i = 0;
@@ -5216,6 +5290,18 @@ list2fpos(
     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
index 098d906ecf3c3e847c838917123c6df05607f49c..85f8befff61bcb4d5bf8b0d4452f52a900f9279c 100644 (file)
@@ -47,6 +47,7 @@ static void f_ceil(typval_T *argvars, typval_T *rettv);
 #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,12 +88,14 @@ static void f_function(typval_T *argvars, typval_T *rettv);
 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,7 +193,9 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv);
 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,6 +795,8 @@ static funcentry_T global_functions[] =
                        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,6 +935,8 @@ static funcentry_T global_functions[] =
                        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,6 +951,8 @@ static funcentry_T global_functions[] =
                        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,10 +1405,14 @@ static funcentry_T global_functions[] =
                        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,
@@ -2423,6 +2438,61 @@ f_char2nr(typval_T *argvars, typval_T *rettv)
        rettv->vval.v_number = tv_get_string(&argvars[0])[0];
 }
 
+/*
+ * Get the current cursor column and store it in 'rettv'. If 'charcol' is TRUE,
+ * returns the character index of the column. Otherwise, returns the byte index
+ * of the column.
+ */
+    static void
+get_col(typval_T *argvars, typval_T *rettv, 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
  */
@@ -2497,42 +2567,7 @@ get_optional_window(typval_T *argvars, int idx)
     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;
+    get_col(argvars, rettv, FALSE);
 }
 
 /*
@@ -2633,26 +2668,24 @@ f_cosh(typval_T *argvars, typval_T *rettv)
 #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.
+ * 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
-f_cursor(typval_T *argvars, typval_T *rettv)
+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[1].v_type == VAR_UNKNOWN)
+    if (argvars[0].v_type == VAR_LIST)
     {
        pos_T       pos;
        colnr_T     curswant = -1;
 
-       if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL)
+       if (list2fpos(argvars, &pos, NULL, &curswant, charcol) == FAIL)
        {
            emsg(_(e_invarg));
            return;
@@ -2666,15 +2699,24 @@ f_cursor(typval_T *argvars, typval_T *rettv)
            set_curswant = FALSE;
        }
     }
-    else
+    else if ((argvars[0].v_type == VAR_NUMBER ||
+                                       argvars[0].v_type == VAR_STRING)
+           && argvars[1].v_type == VAR_NUMBER)
     {
        line = tv_get_lnum(argvars);
        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,6 +2735,19 @@ f_cursor(typval_T *argvars, typval_T *rettv)
     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,6 +3942,88 @@ f_getchangelist(typval_T *argvars, typval_T *rettv)
 #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,77 +4156,19 @@ f_getpid(typval_T *argvars UNUSED, typval_T *rettv)
     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);
+    getpos_both(argvars, rettv, TRUE, FALSE);
+}
+
+    static void
+f_getcursorcharpos(typval_T *argvars, typval_T *rettv)
+{
+    getpos_both(argvars, rettv, TRUE, TRUE);
 }
 
 /*
@@ -4098,7 +4177,7 @@ f_getcurpos(typval_T *argvars, typval_T *rettv)
     static void
 f_getpos(typval_T *argvars, typval_T *rettv)
 {
-    getpos_both(argvars, rettv, FALSE);
+    getpos_both(argvars, rettv, FALSE, FALSE);
 }
 
 /*
@@ -6183,14 +6262,14 @@ f_line(typval_T *argvars, typval_T *rettv)
                                                                         == OK)
            {
                check_cursor();
-               fp = var2fpos(&argvars[0], TRUE, &fnum);
+               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);
+       fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
 
     if (fp != NULL)
        lnum = fp->lnum;
@@ -8065,6 +8144,60 @@ f_searchpos(typval_T *argvars, typval_T *rettv)
        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)
 {
@@ -8106,6 +8239,15 @@ f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED)
     }
 }
 
+/*
+ * "setcursorcharpos" function
+ */
+    static void
+f_setcursorcharpos(typval_T *argvars, typval_T *rettv UNUSED)
+{
+    set_cursorpos(argvars, rettv, TRUE);
+}
+
 /*
  * "setenv()" function
  */
@@ -8165,41 +8307,7 @@ f_setfperm(typval_T *argvars, typval_T *rettv)
     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));
-       }
-    }
+    set_position(argvars, rettv, FALSE);
 }
 
 /*
@@ -9947,7 +10055,7 @@ f_virtcol(typval_T *argvars, typval_T *rettv)
     int                fnum = curbuf->b_fnum;
     int                len;
 
-    fp = var2fpos(&argvars[0], FALSE, &fnum);
+    fp = var2fpos(&argvars[0], FALSE, &fnum, FALSE);
     if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count
                                                    && fnum == curbuf->b_fnum)
     {
index cc86a5d26321a727a23121f2763bef1793a82854..66aa430334af9bee26dcb5891b26b228e2702aa7 100644 (file)
@@ -55,8 +55,10 @@ char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int copy
 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 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);
index 3dfb8fee5dd7253e938c01ed540da50074968727..e3cd87677c78b844015ebea85295a5fbb6814409 100644 (file)
--- a/src/tag.c
+++ b/src/tag.c
@@ -4201,7 +4201,7 @@ tagstack_push_items(win_T *wp, list_T *l)
        // 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)
+       if (list2fpos(&di->di_tv, &mark, &fnum, NULL, FALSE) != OK)
            continue;
        if ((tagname =
                dict_get_string(itemdict, (char_u *)"tagname", TRUE)) == NULL)
index cbaad24187bae8f062b82b5dcc0c683a0bec2442..19b3416b797c37266a74c7d562009b639982b370 100644 (file)
@@ -1,4 +1,4 @@
-" Tests for cursor().
+" Tests for cursor() and other functions that get/set the cursor position
 
 func Test_wrong_arguments()
   call assert_fails('call cursor(1. 3)', 'E474:')
@@ -123,4 +123,187 @@ func Test_screenpos_number()
   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
index 06276b473d96526e22155d07fd6156d36d88d2b5..7e4e63d17dd8305b393971c9ef2a634cb83d3983 100644 (file)
@@ -1579,7 +1579,7 @@ tv_get_lnum(typval_T *argvars)
     if (lnum <= 0)  // no valid number, try using arg like line()
     {
        int     fnum;
-       pos_T   *fp = var2fpos(&argvars[0], TRUE, &fnum);
+       pos_T   *fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
 
        if (fp != NULL)
            lnum = fp->lnum;
index a21122aa6071e8ec5e191dbd1290146b5b4c18f0..07c784098882ceb3211fa821a58a29353e72df74 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2324,
 /**/
     2323,
 /**/