aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Dewar <seandewar@users.noreply.github.com>2022-01-01 07:27:46 +0000
committerSean Dewar <seandewar@users.noreply.github.com>2022-02-05 19:55:11 +0000
commit7002a3433bed7600ec02d64927ae0e77d077f34e (patch)
treeea9d586f0c4c56924991dd84d434880926bf1371
parent92e92f02e7106fcad1597bb8f0edf1e43186a07f (diff)
downloadrneovim-7002a3433bed7600ec02d64927ae0e77d077f34e.tar.gz
rneovim-7002a3433bed7600ec02d64927ae0e77d077f34e.tar.bz2
rneovim-7002a3433bed7600ec02d64927ae0e77d077f34e.zip
vim-patch:8.2.2658: :for cannot loop over a string
Problem: :for cannot loop over a string. Solution: Accept a string argument and iterate over its characters. https://github.com/vim/vim/commit/74e54fcb447e5db32f9c2df34c0554bbecdccca2 v8.2.2659 is already ported. N/A patches for version.c: vim-patch:8.2.2736: Vim9: for loop over string is a bit slow Problem: Vim9: for loop over string is a bit slow. Solution: Avoid using strlen(). https://github.com/vim/vim/commit/175a41c13f3e27e30c662f2f418c5a347dbc645d
-rw-r--r--runtime/doc/eval.txt12
-rw-r--r--src/nvim/eval.c34
-rw-r--r--src/nvim/testdir/test_vimscript.vim20
3 files changed, 59 insertions, 7 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index d6486073cf..aa6d994857 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -372,8 +372,8 @@ Changing the order of items in a list: >
For loop ~
-The |:for| loop executes commands for each item in a |List| or |Blob|.
-A variable is set to each item in the sequence. Example with a List: >
+The |:for| loop executes commands for each item in a |List|, |String| or |Blob|.
+A variable is set to each item in sequence. Example with a List: >
:for item in mylist
: call Doit(item)
:endfor
@@ -390,7 +390,7 @@ If all you want to do is modify each item in the list then the |map()|
function will be a simpler method than a for loop.
Just like the |:let| command, |:for| also accepts a list of variables. This
-requires the argument to be a list of lists. >
+requires the argument to be a List of Lists. >
:for [lnum, col] in [[1, 3], [2, 8], [3, 0]]
: call Doit(lnum, col)
:endfor
@@ -408,6 +408,12 @@ It is also possible to put remaining items in a List variable: >
For a Blob one byte at a time is used.
+For a String one character, including any composing characters, is used as a
+String. Example: >
+ for c in text
+ echo 'This character is ' .. c
+ endfor
+
List functions ~
*E714*
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 5d9326eb24..aecb23f251 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -112,9 +112,11 @@ typedef struct {
int fi_semicolon; // TRUE if ending in '; var]'
int fi_varcount; // nr of variables in the list
listwatch_T fi_lw; // keep an eye on the item used.
- list_T *fi_list; // list being used
+ list_T *fi_list; // list being used
int fi_bi; // index of blob
blob_T *fi_blob; // blob being used
+ char_u *fi_string; // copy of string being used
+ int fi_byte_idx; // byte index in fi_string
} forinfo_T;
// values for vv_flags:
@@ -2641,6 +2643,13 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip)
fi->fi_blob = btv.vval.v_blob;
}
tv_clear(&tv);
+ } else if (tv.v_type == VAR_STRING) {
+ fi->fi_byte_idx = 0;
+ fi->fi_string = tv.vval.v_string;
+ tv.vval.v_string = NULL;
+ if (fi->fi_string == NULL) {
+ fi->fi_string = vim_strsave((char_u *)"");
+ }
} else {
emsg(_(e_listblobreq));
tv_clear(&tv);
@@ -2679,6 +2688,19 @@ bool next_for_item(void *fi_void, char_u *arg)
fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK;
}
+ if (fi->fi_string != NULL) {
+ const int len = utfc_ptr2len(fi->fi_string + fi->fi_byte_idx);
+ if (len == 0) {
+ return false;
+ }
+ typval_T tv;
+ tv.v_type = VAR_STRING;
+ tv.v_lock = VAR_FIXED;
+ tv.vval.v_string = vim_strnsave(fi->fi_string + fi->fi_byte_idx, len);
+ fi->fi_byte_idx += len;
+ return ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK;
+ }
+
listitem_T *item = fi->fi_lw.lw_item;
if (item == NULL) {
return false;
@@ -2698,12 +2720,16 @@ void free_for_info(void *fi_void)
{
forinfo_T *fi = (forinfo_T *)fi_void;
- if (fi != NULL && fi->fi_list != NULL) {
+ if (fi == NULL) {
+ return;
+ }
+ if (fi->fi_list != NULL) {
tv_list_watch_remove(fi->fi_list, &fi->fi_lw);
tv_list_unref(fi->fi_list);
- }
- if (fi != NULL && fi->fi_blob != NULL) {
+ } else if (fi->fi_blob != NULL) {
tv_blob_unref(fi->fi_blob);
+ } else {
+ xfree(fi->fi_string);
}
xfree(fi);
}
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index 34733f127f..c59cab5f36 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -1692,6 +1692,26 @@ func Test_function_defined_line()
call delete('Xtest.vim')
endfunc
+func Test_for_over_string()
+ let res = ''
+ for c in 'aéc̀d'
+ let res ..= c .. '-'
+ endfor
+ call assert_equal('a-é-c̀-d-', res)
+
+ let res = ''
+ for c in ''
+ let res ..= c .. '-'
+ endfor
+ call assert_equal('', res)
+
+ let res = ''
+ for c in v:_null_string
+ let res ..= c .. '-'
+ endfor
+ call assert_equal('', res)
+endfunc
+
"-------------------------------------------------------------------------------
" Modelines {{{1
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker