To: vim_dev@googlegroups.com Subject: Patch 8.2.5019 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.5019 Problem: Cannot get the first screen column of a character. Solution: Let virtcol() optionally return a list. (closes #10482, closes #7964) Files: runtime/doc/builtin.txt, src/evalfunc.c, src/testdir/test_functions.vim, src/testdir/test_vim9_builtin.vim *** ../vim-8.2.5018/runtime/doc/builtin.txt 2022-05-24 11:40:07.514685757 +0100 --- runtime/doc/builtin.txt 2022-05-26 12:00:32.582468991 +0100 *************** *** 689,695 **** uniq({list} [, {func} [, {dict}]]) List remove adjacent duplicates from a list values({dict}) List values in {dict} ! virtcol({expr}) Number screen column of cursor or mark visualmode([expr]) String last visual mode used wildmenumode() Number whether 'wildmenu' mode is active win_execute({id}, {command} [, {silent}]) --- 689,696 ---- uniq({list} [, {func} [, {dict}]]) List remove adjacent duplicates from a list values({dict}) List values in {dict} ! virtcol({expr} [, {list}]) Number or List ! screen column of cursor or mark visualmode([expr]) String last visual mode used wildmenumode() Number whether 'wildmenu' mode is active win_execute({id}, {command} [, {silent}]) *************** *** 780,785 **** --- 781,787 ---- and({expr}, {expr}) *and()* Bitwise AND on the two arguments. The arguments are converted to a number. A List, Dict or Float argument causes an error. + Also see `or()` and `xor()`. Example: > :let flag = and(bits, 0x80) < Can also be used as a |method|: > *************** *** 936,948 **** item is ignored. cmd Ex command to execute for this autocmd event event autocmd event name. Refer to |autocmd-events|. group autocmd group name. Refer to |autocmd-groups|. If this group doesn't exist then it is created. If not specified or empty, then the default group is used. nested boolean flag, set to v:true to add a nested autocmd. Refer to |autocmd-nested|. ! once boolean flag, set to v:true to add a autocmd which executes only once. Refer to |autocmd-once|. pattern autocmd pattern string. Refer to --- 938,951 ---- item is ignored. cmd Ex command to execute for this autocmd event event autocmd event name. Refer to |autocmd-events|. + TODO: currently only accepts one event. group autocmd group name. Refer to |autocmd-groups|. If this group doesn't exist then it is created. If not specified or empty, then the default group is used. nested boolean flag, set to v:true to add a nested autocmd. Refer to |autocmd-nested|. ! once boolean flag, set to v:true to add an autocmd which executes only once. Refer to |autocmd-once|. pattern autocmd pattern string. Refer to *************** *** 952,958 **** commands associated with the specified autocmd event and group and add the {cmd}. This is useful to avoid adding the same command ! multiple times for a autocmd event in a group. Returns v:true on success and v:false on failure. Examples: > --- 955,961 ---- commands associated with the specified autocmd event and group and add the {cmd}. This is useful to avoid adding the same command ! multiple times for an autocmd event in a group. Returns v:true on success and v:false on failure. Examples: > *************** *** 9727,9733 **** Can also be used as a |method|: > mydict->values() ! virtcol({expr}) *virtcol()* The result is a Number, which is the screen column of the file position given with {expr}. That is, the last screen position occupied by the character at that position, when the screen --- 9736,9742 ---- Can also be used as a |method|: > mydict->values() ! virtcol({expr} [, {list}]) *virtcol()* The result is a Number, which is the screen column of the file position given with {expr}. That is, the last screen position occupied by the character at that position, when the screen *************** *** 9736,9748 **** the . For example, for a in column 1, with 'ts' set to 8, it returns 8. |conceal| is ignored. For the byte position use |col()|. For the use of {expr} see |col()|. ! When 'virtualedit' is used {expr} can be [lnum, col, off], where ! "off" is the offset in screen columns from the start of the ! character. E.g., a position within a or after the last ! character. When "off" is omitted zero is used. ! When Virtual editing is active in the current mode, a position ! beyond the end of the line can be returned. |'virtualedit'| The accepted positions are: . the cursor position $ the end of the cursor line (the result is the --- 9745,9761 ---- the . For example, for a in column 1, with 'ts' set to 8, it returns 8. |conceal| is ignored. For the byte position use |col()|. + For the use of {expr} see |col()|. ! ! When 'virtualedit' is used {expr} can be [lnum, col, off], ! where "off" is the offset in screen columns from the start of ! the character. E.g., a position within a or after the ! last character. When "off" is omitted zero is used. When ! Virtual editing is active in the current mode, a position ! beyond the end of the line can be returned. Also see ! |'virtualedit'| ! The accepted positions are: . the cursor position $ the end of the cursor line (the result is the *************** *** 9754,9764 **** cursor is the end). When not in Visual mode returns the cursor position. Differs from |'<| in that it's updated right away. Note that only marks in the current file can be used. Examples: > ! virtcol(".") with text "foo^Lbar", with cursor on the "^L", returns 5 ! virtcol("$") with text "foo^Lbar", returns 9 ! virtcol("'t") with text " there", with 't at 'h', returns 6 < The first column is 1. 0 is returned for an error. A more advanced example that echoes the maximum length of all lines: > --- 9767,9788 ---- cursor is the end). When not in Visual mode returns the cursor position. Differs from |'<| in that it's updated right away. + + If {list} is present and non-zero then virtcol() returns a List + with the first and last screen position occupied by the + character. + Note that only marks in the current file can be used. Examples: > ! " With text "foo^Lbar" and cursor on the "^L": ! ! virtcol(".") " returns 5 ! virtcol(".", 1) " returns [4, 5] ! virtcol("$") " returns 9 ! ! " With text " there", with 't at 'h': ! ! virtcol("'t") " returns 6 < The first column is 1. 0 is returned for an error. A more advanced example that echoes the maximum length of all lines: > *** ../vim-8.2.5018/src/evalfunc.c 2022-05-21 20:16:51.003567195 +0100 --- src/evalfunc.c 2022-05-26 11:52:49.938931235 +0100 *************** *** 994,999 **** --- 994,1000 ---- static argcheck_T arg2_string_list_number[] = {arg_string, arg_list_number}; static argcheck_T arg2_string_number[] = {arg_string, arg_number}; static argcheck_T arg2_string_or_list_dict[] = {arg_string_or_list_any, arg_dict_any}; + static argcheck_T arg2_string_or_list_bool[] = {arg_string_or_list_any, arg_bool}; static argcheck_T arg2_string_string_or_number[] = {arg_string, arg_string_or_nr}; static argcheck_T arg3_any_list_dict[] = {NULL, arg_list_any, arg_dict_any}; static argcheck_T arg3_buffer_lnum_lnum[] = {arg_buffer, arg_lnum, arg_lnum}; *************** *** 1458,1463 **** --- 1459,1478 ---- } static type_T * + ret_virtcol(int argcount, + type2_T *argtypes UNUSED, + type_T **decl_type) + { + // Assume that if the second argument is passed it's non-zero + if (argcount == 2) + { + *decl_type = &t_list_any; + return &t_list_number; + } + return &t_number; + } + + static type_T * ret_maparg(int argcount, type2_T *argtypes UNUSED, type_T **decl_type UNUSED) *************** *** 2665,2672 **** ret_first_arg, f_uniq}, {"values", 1, 1, FEARG_1, arg1_dict_any, ret_list_any, f_values}, ! {"virtcol", 1, 1, FEARG_1, arg1_string_or_list_any, ! ret_number, f_virtcol}, {"visualmode", 0, 1, 0, arg1_bool, ret_string, f_visualmode}, {"wildmenumode", 0, 0, 0, NULL, --- 2680,2687 ---- ret_first_arg, f_uniq}, {"values", 1, 1, FEARG_1, arg1_dict_any, ret_list_any, f_values}, ! {"virtcol", 1, 2, FEARG_1, arg2_string_or_list_bool, ! ret_virtcol, f_virtcol}, {"visualmode", 0, 1, 0, arg1_bool, ret_string, f_visualmode}, {"wildmenumode", 0, 0, 0, NULL, *************** *** 10380,10402 **** } /* ! * "virtcol(string)" function */ static void f_virtcol(typval_T *argvars, typval_T *rettv) { ! colnr_T vcol = 0; pos_T *fp; int fnum = curbuf->b_fnum; int len; if (in_vim9script() ! && check_for_string_or_list_arg(argvars, 0) == FAIL) return; fp = var2fpos(&argvars[0], FALSE, &fnum, FALSE); if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count ! && fnum == curbuf->b_fnum) { // Limit the column to a valid value, getvvcol() doesn't check. if (fp->col < 0) --- 10395,10420 ---- } /* ! * "virtcol(string, bool)" function */ static void f_virtcol(typval_T *argvars, typval_T *rettv) { ! colnr_T vcol_start = 0; ! colnr_T vcol_end = 0; pos_T *fp; int fnum = curbuf->b_fnum; int len; if (in_vim9script() ! && (check_for_string_or_list_arg(argvars, 0) == FAIL ! || (argvars[1].v_type != VAR_UNKNOWN ! && check_for_bool_arg(argvars, 1) == FAIL))) return; fp = var2fpos(&argvars[0], FALSE, &fnum, FALSE); if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count ! && fnum == curbuf->b_fnum) { // Limit the column to a valid value, getvvcol() doesn't check. if (fp->col < 0) *************** *** 10407,10417 **** if (fp->col > len) fp->col = len; } ! getvvcol(curwin, fp, NULL, NULL, &vcol); ! ++vcol; } ! rettv->vval.v_number = vcol; } /* --- 10425,10447 ---- if (fp->col > len) fp->col = len; } ! getvvcol(curwin, fp, &vcol_start, NULL, &vcol_end); ! ++vcol_start; ! ++vcol_end; } ! if (argvars[1].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[1])) ! { ! if (rettv_list_alloc(rettv) == OK) ! { ! list_append_number(rettv->vval.v_list, vcol_start); ! list_append_number(rettv->vval.v_list, vcol_end); ! } ! else ! rettv->vval.v_number = 0; ! } ! else ! rettv->vval.v_number = vcol_end; } /* *** ../vim-8.2.5018/src/testdir/test_functions.vim 2022-05-20 10:39:14.832585770 +0100 --- src/testdir/test_functions.vim 2022-05-26 11:52:49.938931235 +0100 *************** *** 2947,2950 **** --- 2947,2961 ---- endif endfunc + " Test for virtcol() + func Test_virtcol() + enew! + call setline(1, "the\tquick\tbrown\tfox") + norm! 4| + call assert_equal(8, virtcol('.')) + call assert_equal(8, virtcol('.', v:false)) + call assert_equal([4, 8], virtcol('.', v:true)) + bwipe! + endfunc + " vim: shiftwidth=2 sts=2 expandtab *** ../vim-8.2.5018/src/testdir/test_vim9_builtin.vim 2022-05-19 10:31:06.969630503 +0100 --- src/testdir/test_vim9_builtin.vim 2022-05-26 12:07:07.810073881 +0100 *************** *** 4494,4507 **** enddef def Test_virtcol() ! v9.CheckDefAndScriptFailure(['virtcol(1.1)'], ['E1013: Argument 1: type mismatch, expected string but got float', 'E1222: String or List required for argument 1']) ! v9.CheckDefExecAndScriptFailure(['virtcol("")'], 'E1209: Invalid value for a line number') new ! setline(1, ['abcdefgh']) cursor(1, 4) assert_equal(4, virtcol('.')) assert_equal(4, virtcol([1, 4])) ! assert_equal(9, virtcol([1, '$'])) assert_equal(0, virtcol([10, '$'])) bw! enddef --- 4494,4516 ---- enddef def Test_virtcol() ! v9.CheckDefAndScriptFailure(['virtcol(1.1)'], [ ! 'E1013: Argument 1: type mismatch, expected string but got float', ! 'E1222: String or List required for argument 1']) ! v9.CheckDefAndScriptFailure(['virtcol(".", "a")'], [ ! 'E1013: Argument 2: type mismatch, expected bool but got string', ! 'E1212: Bool required for argument 2']) ! v9.CheckDefExecAndScriptFailure(['virtcol("")'], ! 'E1209: Invalid value for a line number') new ! setline(1, ['abcde和平fgh']) cursor(1, 4) assert_equal(4, virtcol('.')) + assert_equal([4, 4], virtcol('.', 1)) + cursor(1, 6) + assert_equal([6, 7], virtcol('.', 1)) assert_equal(4, virtcol([1, 4])) ! assert_equal(13, virtcol([1, '$'])) assert_equal(0, virtcol([10, '$'])) bw! enddef *** ../vim-8.2.5018/src/version.c 2022-05-25 19:15:06.382288150 +0100 --- src/version.c 2022-05-26 11:56:00.990804039 +0100 *************** *** 736,737 **** --- 736,739 ---- { /* Add new patch number below this line */ + /**/ + 5019, /**/ -- I'd like to meet the man who invented sex and see what he's working on now. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///