aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/eval.txt26
-rw-r--r--src/nvim/eval.lua2
-rw-r--r--src/nvim/eval/funcs.c10
-rw-r--r--src/nvim/testdir/runtest.vim86
-rw-r--r--src/nvim/testdir/test_functions.vim4
-rw-r--r--src/nvim/testdir/test_mksession.vim45
6 files changed, 122 insertions, 51 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 448885296d..ca03ee0374 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2409,7 +2409,8 @@ str2list({expr} [, {utf8}]) List convert each character of {expr} to
str2nr({expr} [, {base}]) Number convert String to Number
strchars({expr} [, {skipcc}]) Number character length of the String {expr}
strcharpart({str}, {start} [, {len}])
- String {len} characters of {str} at {start}
+ String {len} characters of {str} at
+ character {start}
strdisplaywidth({expr} [, {col}]) Number display length of the String {expr}
strftime({format} [, {time}]) String time in specified format
strgetchar({str}, {index}) Number get char {index} from {str}
@@ -2417,8 +2418,9 @@ stridx({haystack}, {needle} [, {start}])
Number index of {needle} in {haystack}
string({expr}) String String representation of {expr} value
strlen({expr}) Number length of the String {expr}
-strpart({str}, {start} [, {len}])
- String {len} characters of {str} at {start}
+strpart({str}, {start} [, {len} [, {chars}]])
+ String {len} bytes/chars of {str} at
+ byte {start}
strridx({haystack}, {needle} [, {start}])
Number last index of {needle} in {haystack}
strtrans({expr}) String translate string to make it printable
@@ -2906,7 +2908,8 @@ byte2line({byte}) *byte2line()*
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.
+ {expr}. Use zero for the first character, it then 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
@@ -8438,14 +8441,19 @@ strlen({expr}) The result is a Number, which is the length of the String
{expr} in bytes.
If the argument is a Number it is first converted to a String.
For other types an error is given.
- If you want to count the number of multi-byte characters use
+ If you want to count the number of multibyte characters use
|strchars()|.
Also see |len()|, |strdisplaywidth()| and |strwidth()|.
-strpart({src}, {start} [, {len}]) *strpart()*
+strpart({src}, {start} [, {len} [, {chars}]]) *strpart()*
The result is a String, which is part of {src}, starting from
byte {start}, with the byte length {len}.
- To count characters instead of bytes use |strcharpart()|.
+ When {chars} is present and TRUE then {len} is the number of
+ characters positions (composing characters are not counted
+ separately, thus "1" means one base character and any
+ following composing characters).
+ To count {start} as characters instead of bytes use
+ |strcharpart()|.
When bytes are selected which do not exist, this doesn't
result in an error, the bytes are simply omitted.
@@ -8457,8 +8465,8 @@ strpart({src}, {start} [, {len}]) *strpart()*
strpart("abcdefg", 3) == "defg"
< Note: To get the first character, {start} must be 0. For
- example, to get three bytes under and after the cursor: >
- strpart(getline("."), col(".") - 1, 3)
+ example, to get the character under the cursor: >
+ strpart(getline("."), col(".") - 1, 1, v:true)
<
strridx({haystack}, {needle} [, {start}]) *strridx()*
The result is a Number, which gives the byte index in
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index a5544435d2..be16ddd7f6 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -336,7 +336,7 @@ return {
stridx={args={2, 3}},
string={args=1},
strlen={args=1},
- strpart={args={2, 3}},
+ strpart={args={2, 4}},
strridx={args={2, 3}},
strtrans={args=1},
strwidth={args=1},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 18e514b9a0..8e0e420eb0 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -9946,6 +9946,16 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
len = slen - n;
}
+ if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN) {
+ int off;
+
+ // length in characters
+ for (off = n; off < (int)slen && len > 0; len--) {
+ off += utfc_ptr2len((char_u *)p + off);
+ }
+ len = off - n;
+ }
+
rettv->v_type = VAR_STRING;
rettv->vval.v_string = (char_u *)xmemdupz(p + n, (size_t)len);
}
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index 2bf61b0719..4f16aa807c 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -7,6 +7,19 @@
" ../vim -u NONE -S runtest.vim test_channel.vim open_delay
" The output can be found in the "messages" file.
"
+" If the environment variable $TEST_FILTER is set then only test functions
+" matching this pattern are executed. E.g. for sh/bash:
+" export TEST_FILTER=Test_channel
+" For csh:
+" setenv TEST_FILTER Test_channel
+"
+" To ignore failure for tests that are known to fail in a certain environment,
+" set $TEST_MAY_FAIL to a comma separated list of function names. E.g. for
+" sh/bash:
+" export TEST_MAY_FAIL=Test_channel_one,Test_channel_other
+" The failure report will then not be included in the test.log file and
+" "make test" will not fail.
+"
" The test script may contain anything, only functions that start with
" "Test_" are special. These will be invoked and should contain assert
" functions. See test_assert.vim for an example.
@@ -65,10 +78,14 @@ set encoding=utf-8
let s:test_script_fname = expand('%')
au! SwapExists * call HandleSwapExists()
func HandleSwapExists()
- " Only ignore finding a swap file for the test script (the user might be
+ " Ignore finding a swap file for the test script (the user might be
" editing it and do ":make test_name") and the output file.
+ " Report finding another swap file and chose 'q' to avoid getting stuck.
if expand('<afile>') == 'messages' || expand('<afile>') =~ s:test_script_fname
let v:swapchoice = 'e'
+ else
+ call assert_report('Unexpected swap file: ' .. v:swapname)
+ let v:swapchoice = 'q'
endif
endfunc
@@ -136,13 +153,6 @@ func RunTheTest(test)
endtry
endif
- let message = 'Executed ' . a:test
- if has('reltime')
- let message ..= ' in ' .. reltimestr(reltime(func_start)) .. ' seconds'
- endif
- call add(s:messages, message)
- let s:done += 1
-
if a:test =~ 'Test_nocatch_'
" Function handles errors itself. This avoids skipping commands after the
" error.
@@ -196,13 +206,26 @@ func RunTheTest(test)
endwhile
exe 'cd ' . save_cwd
+
+ let message = 'Executed ' . a:test
+ if has('reltime')
+ let message ..= ' in ' .. reltimestr(reltime(func_start)) .. ' seconds'
+ endif
+ call add(s:messages, message)
+ let s:done += 1
endfunc
-func AfterTheTest()
+func AfterTheTest(func_name)
if len(v:errors) > 0
- let s:fail += 1
- call add(s:errors, 'Found errors in ' . s:test . ':')
- call extend(s:errors, v:errors)
+ if match(s:may_fail_list, '^' .. a:func_name) >= 0
+ let s:fail_expected += 1
+ call add(s:errors_expected, 'Found errors in ' . s:test . ':')
+ call extend(s:errors_expected, v:errors)
+ else
+ let s:fail += 1
+ call add(s:errors, 'Found errors in ' . s:test . ':')
+ call extend(s:errors, v:errors)
+ endif
let v:errors = []
endif
endfunc
@@ -218,7 +241,7 @@ endfunc
" This function can be called by a test if it wants to abort testing.
func FinishTesting()
- call AfterTheTest()
+ call AfterTheTest('')
" Don't write viminfo on exit.
set viminfo=
@@ -226,7 +249,7 @@ func FinishTesting()
" Clean up files created by setup.vim
call delete('XfakeHOME', 'rf')
- if s:fail == 0
+ if s:fail == 0 && s:fail_expected == 0
" Success, create the .res file so that make knows it's done.
exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
write
@@ -242,11 +265,18 @@ func FinishTesting()
endif
if s:done == 0
- let message = 'NO tests executed'
+ if s:filtered > 0
+ let message = "NO tests match $TEST_FILTER: '" .. $TEST_FILTER .. "'"
+ else
+ let message = 'NO tests executed'
+ endif
else
+ if s:filtered > 0
+ call add(s:messages, "Filtered " .. s:filtered .. " tests with $TEST_FILTER")
+ endif
let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
endif
- if has('reltime')
+ if s:done > 0 && has('reltime')
let message ..= ' in ' .. reltimestr(reltime(s:start_time)) .. ' seconds'
endif
echo message
@@ -257,6 +287,12 @@ func FinishTesting()
call add(s:messages, message)
call extend(s:messages, s:errors)
endif
+ if s:fail_expected > 0
+ let message = s:fail_expected . ' FAILED (matching $TEST_MAY_FAIL):'
+ echo message
+ call add(s:messages, message)
+ call extend(s:messages, s:errors_expected)
+ endif
" Add SKIPPED messages
call extend(s:messages, s:skipped)
@@ -276,11 +312,13 @@ endfunc
let g:testname = expand('%')
let s:done = 0
let s:fail = 0
+let s:fail_expected = 0
let s:errors = []
+let s:errors_expected = []
let s:messages = []
let s:skipped = []
if expand('%') =~ 'test_vimscript.vim'
- " this test has intentional s:errors, don't use try/catch.
+ " this test has intentional errors, don't use try/catch.
source %
else
try
@@ -311,11 +349,12 @@ let s:flaky_tests = [
\ 'Test_repeat_three()',
\ 'Test_state()',
\ 'Test_stop_all_in_callback()',
- \ 'Test_term_mouse_double_click_to_create_tab',
+ \ 'Test_term_mouse_double_click_to_create_tab()',
\ 'Test_term_mouse_multiple_clicks_to_visually_select()',
\ 'Test_terminal_composing_unicode()',
\ 'Test_terminal_redir_file()',
\ 'Test_terminal_tmap()',
+ \ 'Test_termwinscroll()',
\ 'Test_with_partial_callback()',
\ ]
@@ -335,8 +374,17 @@ endif
" If the environment variable $TEST_FILTER is set then filter the function
" names against it.
+let s:filtered = 0
if $TEST_FILTER != ''
+ let s:filtered = len(s:tests)
let s:tests = filter(s:tests, 'v:val =~ $TEST_FILTER')
+ let s:filtered -= len(s:tests)
+endif
+
+let s:may_fail_list = []
+if $TEST_MAY_FAIL != ''
+ " Split the list at commas and add () to make it match s:test.
+ let s:may_fail_list = split($TEST_MAY_FAIL, ',')->map({i, v -> v .. '()'})
endif
" Execute the tests in alphabetical order.
@@ -388,7 +436,7 @@ for s:test in sort(s:tests)
endwhile
endif
- call AfterTheTest()
+ call AfterTheTest(s:test)
endfor
call FinishTesting()
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 51689db9c4..6b45ac61d1 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -334,6 +334,10 @@ func Test_strpart()
call assert_equal('lép', strpart('éléphant', 2, 4))
call assert_equal('léphant', strpart('éléphant', 2))
+
+ call assert_equal('é', strpart('éléphant', 0, 1, 1))
+ call assert_equal('ép', strpart('éléphant', 3, 2, v:true))
+ call assert_equal('ó', strpart('cómposed', 1, 1, 1))
endfunc
func Test_tolower()
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index 1e22f7e9c9..3243edbf55 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -9,6 +9,29 @@ endif
source shared.vim
source term_util.vim
+" Test for storing global and local argument list in a session file
+" This one must be done first.
+func Test__mksession_arglocal()
+ enew | only
+ n a b c
+ new
+ arglocal
+ mksession! Xtest_mks.out
+
+ %bwipe!
+ %argdelete
+ argglobal
+ source Xtest_mks.out
+ call assert_equal(2, winnr('$'))
+ call assert_equal(2, arglistid(1))
+ call assert_equal(0, arglistid(2))
+
+ %bwipe!
+ %argdelete
+ argglobal
+ call delete('Xtest_mks.out')
+endfunc
+
func Test_mksession()
tabnew
let wrap_save = &wrap
@@ -359,28 +382,6 @@ func Test_mksession_slash()
set sessionoptions&
endfunc
-" Test for storing global and local argument list in a session file
-func Test_mkseesion_arglocal()
- enew | only
- n a b c
- new
- arglocal
- mksession! Xtest_mks.out
-
- %bwipe!
- %argdelete
- argglobal
- source Xtest_mks.out
- call assert_equal(2, winnr('$'))
- call assert_equal(2, arglistid(1))
- call assert_equal(0, arglistid(2))
-
- %bwipe!
- %argdelete
- argglobal
- call delete('Xtest_mks.out')
-endfunc
-
" Test for changing directory to the session file directory
func Test_mksession_sesdir()
call mkdir('Xproj')