diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 9d4c972e64..57d4d2c935 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -47,6 +47,28 @@ effects. Be careful not to destroy your text. It's a good idea to use the same autocommands for the File* and Buf* events when possible. +Recommended use: +- Always use a group, so that it's easy to delete the autocommand. +- Keep the command itself short, call a function to do more work. +- Make it so that the script it is defined it can be sourced several times + without the autocommand being repeated. + +Example in Vim9 script: > + autocmd_add({replace: true, + group: 'DemoGroup', + event: 'BufEnter', + pattern: '*.txt', + cmd: 'call DemoBufEnter()' + }) + +In legacy script: > + call autocmd_add(#{replace: v:true, + \ group: 'DemoGroup', + \ event: 'BufEnter', + \ pattern: '*.txt', + \ cmd: 'call DemoBufEnter()' + \ }) + ============================================================================== 2. Defining autocommands *autocmd-define* @@ -83,7 +105,8 @@ triggered. } The |autocmd_add()| function can be used to add a list of autocmds and autocmd -groups from a Vim script. +groups from a Vim script. It is preferred if you have anything that would +require using `:execute` with `:autocmd`. Note: The ":autocmd" command can only be followed by another command when the '|' appears where the pattern is expected. This works: > diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 29cc506273..5b20ea3322 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -940,13 +940,19 @@ autocmd_add({acmds}) *autocmd_add()* If this group doesn't exist then it is created. If not specified or empty, then the default group is used. - nested set to v:true to add a nested autocmd. - Refer to |autocmd-nested|. - once set to v:true to add a autocmd which executes - only once. Refer to |autocmd-once|. + 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 |autocmd-patterns|. If "bufnr" item is present, then this item is ignored. + replace boolean flag, set to v:true to remove all the + 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: > @@ -1037,10 +1043,10 @@ autocmd_get([{opts}]) *autocmd_get()* cmd Command executed for this autocmd. event Autocmd event name. group Autocmd group name. - nested Set to v:true for a nested autocmd. See - |autocmd-nested|. - once Set to v:true, if the autocmd will be executed - only once. See |autocmd-once|. + nested Boolean flag, set to v:true for a nested + autocmd. See |autocmd-nested|. + once Boolean flag, set to v:true, if the autocmd + will be executed only once. See |autocmd-once|. pattern Autocmd pattern. For a buffer-local autocmd, this will be of the form "". If there are multiple commands for an autocmd event in a diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 9994f8aac0..da68ff4f72 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -409,7 +409,7 @@ au BufNewFile,BufRead *.cu,*.cuh setf cuda " Dockerfile; Podman uses the same syntax with name Containerfile " Also see Dockerfile.* below. -au BufNewFile,BufRead Containerfile,Dockerfile,*.Dockerfile setf dockerfile +au BufNewFile,BufRead Containerfile,Dockerfile,dockerfile,*.[dD]ockerfile setf dockerfile " WildPackets EtherPeek Decoder au BufNewFile,BufRead *.dcd setf dcd @@ -819,6 +819,9 @@ au BufNewFile,BufRead *.hjson setf hjson " Hollywood au BufRead,BufNewFile *.hws setf hollywood +" Hoon +au BufRead,BufNewFile *.hoon setf hoon + " Tilde (must be before HTML) au BufNewFile,BufRead *.t.html setf tilde @@ -1172,6 +1175,9 @@ au BufNewFile,BufRead *.isc,*.monk,*.ssc,*.tsc setf monk " MOO au BufNewFile,BufRead *.moo setf moo +" Moonscript +au BufNewFile,BufRead *.moon setf moonscript + " Modconf au BufNewFile,BufRead */etc/modules.conf,*/etc/modules,*/etc/conf.modules setf modconf diff --git a/src/autocmd.c b/src/autocmd.c index 246be8fca1..eff9b2da63 100644 --- a/src/autocmd.c +++ b/src/autocmd.c @@ -2767,6 +2767,7 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete) char_u *end; int once; int nested; + int replace; // replace the cmd for a group/event int retval = VVAL_TRUE; int save_augroup = current_augroup; @@ -2878,6 +2879,9 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete) once = dict_get_bool(event_dict, (char_u *)"once", FALSE); nested = dict_get_bool(event_dict, (char_u *)"nested", FALSE); + // if 'replace' is true, then remove all the commands associated with + // this autocmd event/group and add the new command. + replace = dict_get_bool(event_dict, (char_u *)"replace", FALSE); cmd = dict_get_string(event_dict, (char_u *)"cmd", TRUE); if (cmd == NULL) @@ -2904,8 +2908,8 @@ autocmd_add_or_delete(typval_T *argvars, typval_T *rettv, int delete) } else { - if (do_autocmd_event(event, pat, once, nested, cmd, delete, group, - 0) == FAIL) + if (do_autocmd_event(event, pat, once, nested, cmd, + delete | replace, group, 0) == FAIL) { retval = VVAL_FALSE; break; diff --git a/src/evalvars.c b/src/evalvars.c index a20dfd386b..c6dc531513 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -2025,7 +2025,7 @@ do_unlet(char_u *name, int forceit) { hashtab_T *ht; hashitem_T *hi; - char_u *varname; + char_u *varname = NULL; // init to shut up gcc dict_T *d; dictitem_T *di; diff --git a/src/memline.c b/src/memline.c index 83344bcda5..83aa2c68d5 100644 --- a/src/memline.c +++ b/src/memline.c @@ -4004,6 +4004,8 @@ ml_flush_line(buf_T *buf) { #if defined(FEAT_BYTEOFF) && defined(FEAT_PROP_POPUP) int old_prop_len = 0; + if (buf->b_has_textprop) + old_prop_len = old_len - (int)STRLEN(old_line) - 1; #endif // if the length changes and there are following lines count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1; @@ -4023,10 +4025,6 @@ ml_flush_line(buf_T *buf) // adjust free space dp->db_free -= extra; dp->db_txt_start -= extra; -#if defined(FEAT_BYTEOFF) && defined(FEAT_PROP_POPUP) - if (buf->b_has_textprop) - old_prop_len = old_len - (int)STRLEN(new_line) - 1; -#endif // copy new line into the data block mch_memmove(old_line - extra, new_line, (size_t)new_len); diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro index f8d60ad9c6..8a3254b9ee 100644 --- a/src/proto/vim9instr.pro +++ b/src/proto/vim9instr.pro @@ -32,7 +32,7 @@ int generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name); int generate_STORENR(cctx_T *cctx, int idx, varnumber_T value); int generate_LOAD(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name, type_T *type); int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, type_T *type); -int generate_LOADV(cctx_T *cctx, char_u *name, int error); +int generate_LOADV(cctx_T *cctx, char_u *name); int generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit); int generate_LOCKCONST(cctx_T *cctx); int generate_OLDSCRIPT(cctx_T *cctx, isntype_T isn_type, char_u *name, int sid, type_T *type); diff --git a/src/register.c b/src/register.c index 5ebaa05dbf..d5a56ae0c6 100644 --- a/src/register.c +++ b/src/register.c @@ -2219,9 +2219,12 @@ error: len = STRLEN(y_array[y_size - 1]); col = (colnr_T)len - lendiff; if (col > 1) - curbuf->b_op_end.col = col - 1 - - mb_head_off(y_array[y_size - 1], + { + curbuf->b_op_end.col = col - 1; + if (len > 0) + curbuf->b_op_end.col -= mb_head_off(y_array[y_size - 1], y_array[y_size - 1] + len - 1); + } else curbuf->b_op_end.col = 0; diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index 58325633e7..0cd94ab843 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -3413,6 +3413,18 @@ func Test_autocmd_add() \ nested: v:false, once: v:false, event: 'BufHidden'}], \ autocmd_get(#{group: 'TestAcSet'})) + " Test for replacing a cmd for an event in a group + call autocmd_delete([#{group: 'TestAcSet'}]) + call autocmd_add([#{replace: v:true, group: 'TestAcSet', event: 'BufEnter', + \ pattern: '*.py', cmd: 'echo "bufenter"'}]) + call autocmd_add([#{replace: v:true, group: 'TestAcSet', event: 'BufEnter', + \ pattern: '*.py', cmd: 'echo "bufenter"'}]) + call assert_equal([ + \ #{cmd: 'echo "bufenter"', group: 'TestAcSet', pattern: '*.py', + \ nested: v:false, once: v:false, event: 'BufEnter'}], + \ autocmd_get(#{group: 'TestAcSet'})) + + " Test for adding a command for an unsupported autocmd event let l = [#{group: 'TestAcSet', event: 'abc', pattern: '*.sh', \ cmd: 'echo "bufadd"'}] call assert_fails('call autocmd_add(l)', 'E216:') diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index d0837fde0d..81ad0a6ec9 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -151,7 +151,7 @@ let s:filename_checks = { \ 'diff': ['file.diff', 'file.rej'], \ 'dircolors': ['.dir_colors', '.dircolors', '/etc/DIR_COLORS', 'any/etc/DIR_COLORS'], \ 'dnsmasq': ['/etc/dnsmasq.conf', '/etc/dnsmasq.d/file', 'any/etc/dnsmasq.conf', 'any/etc/dnsmasq.d/file'], - \ 'dockerfile': ['Containerfile', 'Dockerfile', 'file.Dockerfile', 'Dockerfile.debian', 'Containerfile.something'], + \ 'dockerfile': ['Containerfile', 'Dockerfile', 'dockerfile', 'file.Dockerfile', 'file.dockerfile', 'Dockerfile.debian', 'Containerfile.something'], \ 'dosbatch': ['file.bat'], \ 'dosini': ['.editorconfig', '/etc/yum.conf', 'file.ini', 'npmrc', '.npmrc', 'php.ini', 'php.ini-5', 'php.ini-file', '/etc/yum.repos.d/file', 'any/etc/yum.conf', 'any/etc/yum.repos.d/file', 'file.wrap'], \ 'dot': ['file.dot', 'file.gv'], @@ -245,6 +245,7 @@ let s:filename_checks = { \ 'hjson': ['file.hjson'], \ 'hog': ['file.hog', 'snort.conf', 'vision.conf'], \ 'hollywood': ['file.hws'], + \ 'hoon': ['file.hoon'], \ 'hostconf': ['/etc/host.conf', 'any/etc/host.conf'], \ 'hostsaccess': ['/etc/hosts.allow', '/etc/hosts.deny', 'any/etc/hosts.allow', 'any/etc/hosts.deny'], \ 'html': ['file.html', 'file.htm', 'file.cshtml'], @@ -354,6 +355,7 @@ let s:filename_checks = { \ 'modula3': ['file.m3', 'file.mg', 'file.i3', 'file.ig'], \ 'monk': ['file.isc', 'file.monk', 'file.ssc', 'file.tsc'], \ 'moo': ['file.moo'], + \ 'moonscript': ['file.moon'], \ 'mp': ['file.mp'], \ 'mplayerconf': ['mplayer.conf', '/.mplayer/config', 'any/.mplayer/config'], \ 'mrxvtrc': ['mrxvtrc', '.mrxvtrc'], diff --git a/src/testdir/test_put.vim b/src/testdir/test_put.vim index 0fde026401..aa5aa2b990 100644 --- a/src/testdir/test_put.vim +++ b/src/testdir/test_put.vim @@ -210,5 +210,14 @@ func Test_multibyte_op_end_mark() bwipe! endfunc +" this was putting a mark before the start of a line +func Test_put_empty_register() + new + norm yy + norm [Pi00ggv)s0 + sil! norm [P + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_textformat.vim b/src/testdir/test_textformat.vim index d33e2508d2..c5f5975801 100644 --- a/src/testdir/test_textformat.vim +++ b/src/testdir/test_textformat.vim @@ -1291,4 +1291,16 @@ func Test_fo_2() close! endfunc +" This was leaving the cursor after the end of a line. Complicated way to +" have the problem show up with valgrind. +func Test_correct_cursor_position() + set encoding=iso8859 + new + norm a000“0 + sil! norm gggg0i0gw0gg + + bwipe! + set encoding=utf8 +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_textobjects.vim b/src/testdir/test_textobjects.vim index 34894d068a..b605d76f2f 100644 --- a/src/testdir/test_textobjects.vim +++ b/src/testdir/test_textobjects.vim @@ -41,6 +41,24 @@ func Test_inner_block_with_cpo_M_right_backslash() call CpoM('(red (blue\) green)', 1, ['red (blue\) green', 'blue\', 'red (blue\) green']) endfunc +func Test_inner_block_single_char() + new + call setline(1, "(a)") + + set selection=inclusive + let @" = '' + call assert_nobeep('norm! 0faviby') + call assert_equal('a', @") + + set selection=exclusive + let @" = '' + call assert_nobeep('norm! 0faviby') + call assert_equal('a', @") + + set selection& + bwipe! +endfunc + func Test_quote_selection_selection_exclusive() new call setline(1, "a 'bcde' f") @@ -49,11 +67,11 @@ func Test_quote_selection_selection_exclusive() exe "norm! fdvhi'y" call assert_equal('bcde', @") - let @"='dummy' + let @" = 'dummy' exe "norm! $gevi'y" call assert_equal('bcde', @") - let @"='dummy' + let @" = 'dummy' exe "norm! 0fbhvi'y" call assert_equal('bcde', @") diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index c29c10b27f..1345045e6c 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -2094,4 +2094,97 @@ func Test_prop_blockwise_change() bwipe! endfunc +func Do_test_props_do_not_affect_byte_offsets(ff, increment) + new + let lcount = 410 + + " File format affects byte-offset calculations, so make sure it is known. + exec 'setlocal fileformat=' . a:ff + + " Fill the buffer with varying length lines. We need a suitably large number + " to force Vim code through paths wehere previous error have occurred. This + " is more 'art' than 'science'. + let text = 'a' + call setline(1, text) + let offsets = [1] + for idx in range(lcount) + call add(offsets, offsets[idx] + len(text) + a:increment) + if (idx % 6) == 0 + let text = text . 'a' + endif + call append(line('$'), text) + endfor + + " Set a property that spans a few lines to cause Vim's internal buffer code + " to perform a reasonable amount of rearrangement. + call prop_type_add('one', {'highlight': 'ErrorMsg'}) + call prop_add(1, 1, {'type': 'one', 'end_lnum': 6, 'end_col': 2}) + + for idx in range(lcount) + let boff = line2byte(idx + 1) + call assert_equal(offsets[idx], boff, 'Bad byte offset at line ' . (idx + 1)) + endfor + + call prop_type_delete('one') + bwipe! +endfunc + +func Test_props_do_not_affect_byte_offsets() + call Do_test_props_do_not_affect_byte_offsets('unix', 1) +endfunc + +func Test_props_do_not_affect_byte_offsets_dos() + call Do_test_props_do_not_affect_byte_offsets('dos', 2) +endfunc + +func Test_props_do_not_affect_byte_offsets_editline() + new + let lcount = 410 + + " File format affects byte-offset calculations, so make sure it is known. + setlocal fileformat=unix + + " Fill the buffer with varying length lines. We need a suitably large number + " to force Vim code through paths wehere previous error have occurred. This + " is more 'art' than 'science'. + let text = 'aa' + call setline(1, text) + let offsets = [1] + for idx in range(lcount) + call add(offsets, offsets[idx] + len(text) + 1) + if (idx % 6) == 0 + let text = text . 'a' + endif + call append(line('$'), text) + endfor + + " Set a property that just covers the first line. When this test was + " developed, this did not trigger a byte-offset error. + call prop_type_add('one', {'highlight': 'ErrorMsg'}) + call prop_add(1, 1, {'type': 'one', 'end_lnum': 1, 'end_col': 3}) + + for idx in range(lcount) + let boff = line2byte(idx + 1) + call assert_equal(offsets[idx], boff, + \ 'Confounding bad byte offset at line ' . (idx + 1)) + endfor + + " Insert text in the middle of the first line, keeping the property + " unchanged. + :1 + normal aHello + for idx in range(1, lcount) + let offsets[idx] = offsets[idx] + 5 + endfor + + for idx in range(lcount) + let boff = line2byte(idx + 1) + call assert_equal(offsets[idx], boff, + \ 'Bad byte offset at line ' . (idx + 1)) + endfor + + call prop_type_delete('one') + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/textformat.c b/src/textformat.c index 7e32c1ccc3..5ba2f1798d 100644 --- a/src/textformat.c +++ b/src/textformat.c @@ -870,6 +870,9 @@ op_format( { curwin->w_cursor = saved_cursor; saved_cursor.lnum = 0; + + // formatting may have made the cursor position invalid + check_cursor(); } if (oap->is_VIsual) diff --git a/src/textobject.c b/src/textobject.c index edaa64c51c..9a8a916260 100644 --- a/src/textobject.c +++ b/src/textobject.c @@ -1133,8 +1133,10 @@ current_block( /* * In Visual mode, when the resulting area is not bigger than what we * started with, extend it to the next block, and then exclude again. + * Don't try to expand the area if the area is empty. */ if (!LT_POS(start_pos, old_start) && !LT_POS(old_end, curwin->w_cursor) + && !EQUAL_POS(start_pos, curwin->w_cursor) && VIsual_active) { curwin->w_cursor = old_start; diff --git a/src/textprop.c b/src/textprop.c index 6fc628a272..9d3487fde2 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -344,6 +344,10 @@ f_prop_add_list(typval_T *argvars, typval_T *rettv UNUSED) if (get_bufnr_from_arg(&argvars[0], &buf) == FAIL) return; + // This must be done _before_ we start adding properties because property + // changes trigger buffer (memline) reorganisation, which needs this flag + // to be correctly set. + buf->b_has_textprop = TRUE; // this is never reset FOR_ALL_LIST_ITEMS(argvars[1].vval.v_list, li) { if (li->li_tv.v_type != VAR_LIST || li->li_tv.vval.v_list == NULL) @@ -368,7 +372,6 @@ f_prop_add_list(typval_T *argvars, typval_T *rettv UNUSED) return; } - buf->b_has_textprop = TRUE; // this is never reset redraw_buf_later(buf, VALID); } @@ -441,9 +444,13 @@ prop_add_common( if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL) return; + // This must be done _before_ we add the property because property changes + // trigger buffer (memline) reorganisation, which needs this flag to be + // correctly set. + buf->b_has_textprop = TRUE; // this is never reset + prop_add_one(buf, type_name, id, start_lnum, end_lnum, start_col, end_col); - buf->b_has_textprop = TRUE; // this is never reset redraw_buf_later(buf, VALID); } diff --git a/src/version.c b/src/version.c index e01b7ded0f..2c424428f8 100644 --- a/src/version.c +++ b/src/version.c @@ -749,6 +749,22 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 5018, +/**/ + 5017, +/**/ + 5016, +/**/ + 5015, +/**/ + 5014, +/**/ + 5013, +/**/ + 5012, +/**/ + 5011, /**/ 5010, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 48a913a4b1..496f4cde21 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1151,7 +1151,7 @@ generate_loadvar( generate_LOAD(cctx, ISN_LOADREG, name[1], NULL, &t_string); break; case dest_vimvar: - generate_LOADV(cctx, name + 2, TRUE); + generate_LOADV(cctx, name + 2); break; case dest_local: if (lvar->lv_from_outer > 0) diff --git a/src/vim9expr.c b/src/vim9expr.c index a52f2c7e28..b2bb405f6f 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -442,7 +442,7 @@ compile_load( switch (**arg) { - case 'v': res = generate_LOADV(cctx, name, error); + case 'v': res = generate_LOADV(cctx, name); break; case 's': if (current_script_is_vim9()) { diff --git a/src/vim9instr.c b/src/vim9instr.c index 3a8d695dad..f20d993728 100644 --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -355,11 +355,6 @@ get_compare_isn( vartype_T vartype1 = tv1 != NULL ? tv1->v_type : type1->tt_type; vartype_T vartype2 = tv2 != NULL ? tv2->v_type : type2->tt_type; - if (vartype1 == VAR_UNKNOWN) - vartype1 = VAR_ANY; - if (vartype2 == VAR_UNKNOWN) - vartype2 = VAR_ANY; - if (vartype1 == vartype2) { switch (vartype1) @@ -462,11 +457,8 @@ generate_COMPARE(cctx_T *cctx, exprtype_T exprtype, int ic) isn->isn_arg.op.op_ic = ic; // takes two arguments, puts one bool back - if (stack->ga_len >= 2) - { - --stack->ga_len; - set_type_on_stack(cctx, &t_bool, 0); - } + --stack->ga_len; + set_type_on_stack(cctx, &t_bool, 0); return OK; } @@ -485,9 +477,6 @@ generate_CONCAT(cctx_T *cctx, int count) RETURN_OK_IF_SKIP(cctx); - if (count < 1) - return FAIL; - if ((isn = generate_instr(cctx, ISN_CONCAT)) == NULL) return FAIL; isn->isn_arg.number = count; @@ -578,80 +567,74 @@ generate_SETTYPE( /* * Generate a PUSH instruction for "tv". * "tv" will be consumed or cleared. - * Nothing happens if "tv" is NULL or of type VAR_UNKNOWN; */ int generate_tv_PUSH(cctx_T *cctx, typval_T *tv) { - if (tv != NULL) + switch (tv->v_type) { - switch (tv->v_type) - { - case VAR_UNKNOWN: - break; - case VAR_BOOL: - generate_PUSHBOOL(cctx, tv->vval.v_number); - break; - case VAR_SPECIAL: - generate_PUSHSPEC(cctx, tv->vval.v_number); - break; - case VAR_NUMBER: - generate_PUSHNR(cctx, tv->vval.v_number); - break; + case VAR_BOOL: + generate_PUSHBOOL(cctx, tv->vval.v_number); + break; + case VAR_SPECIAL: + generate_PUSHSPEC(cctx, tv->vval.v_number); + break; + case VAR_NUMBER: + generate_PUSHNR(cctx, tv->vval.v_number); + break; #ifdef FEAT_FLOAT - case VAR_FLOAT: - generate_PUSHF(cctx, tv->vval.v_float); - break; + case VAR_FLOAT: + generate_PUSHF(cctx, tv->vval.v_float); + break; #endif - case VAR_BLOB: - generate_PUSHBLOB(cctx, tv->vval.v_blob); - tv->vval.v_blob = NULL; - break; - case VAR_LIST: - if (tv->vval.v_list != NULL) - iemsg("non-empty list constant not supported"); - generate_NEWLIST(cctx, 0, TRUE); - break; - case VAR_DICT: - if (tv->vval.v_dict != NULL) - iemsg("non-empty dict constant not supported"); - generate_NEWDICT(cctx, 0, TRUE); - break; + case VAR_BLOB: + generate_PUSHBLOB(cctx, tv->vval.v_blob); + tv->vval.v_blob = NULL; + break; + case VAR_LIST: + if (tv->vval.v_list != NULL) + iemsg("non-empty list constant not supported"); + generate_NEWLIST(cctx, 0, TRUE); + break; + case VAR_DICT: + if (tv->vval.v_dict != NULL) + iemsg("non-empty dict constant not supported"); + generate_NEWDICT(cctx, 0, TRUE); + break; #ifdef FEAT_JOB_CHANNEL - case VAR_JOB: - if (tv->vval.v_job != NULL) - iemsg("non-null job constant not supported"); - generate_PUSHJOB(cctx); - break; - case VAR_CHANNEL: - if (tv->vval.v_channel != NULL) - iemsg("non-null channel constant not supported"); - generate_PUSHCHANNEL(cctx); - break; + case VAR_JOB: + if (tv->vval.v_job != NULL) + iemsg("non-null job constant not supported"); + generate_PUSHJOB(cctx); + break; + case VAR_CHANNEL: + if (tv->vval.v_channel != NULL) + iemsg("non-null channel constant not supported"); + generate_PUSHCHANNEL(cctx); + break; #endif - case VAR_FUNC: - if (tv->vval.v_string != NULL) - iemsg("non-null function constant not supported"); - generate_PUSHFUNC(cctx, NULL, &t_func_unknown); - break; - case VAR_PARTIAL: - if (tv->vval.v_partial != NULL) - iemsg("non-null partial constant not supported"); - if (generate_instr_type(cctx, ISN_NEWPARTIAL, &t_func_unknown) - == NULL) - return FAIL; - break; - case VAR_STRING: - generate_PUSHS(cctx, &tv->vval.v_string); - tv->vval.v_string = NULL; - break; - default: - iemsg("constant type not supported"); - clear_tv(tv); + case VAR_FUNC: + if (tv->vval.v_string != NULL) + iemsg("non-null function constant not supported"); + generate_PUSHFUNC(cctx, NULL, &t_func_unknown); + break; + case VAR_PARTIAL: + if (tv->vval.v_partial != NULL) + iemsg("non-null partial constant not supported"); + if (generate_instr_type(cctx, ISN_NEWPARTIAL, &t_func_unknown) + == NULL) return FAIL; - } - tv->v_type = VAR_UNKNOWN; + break; + case VAR_STRING: + generate_PUSHS(cctx, &tv->vval.v_string); + tv->vval.v_string = NULL; + break; + default: + siemsg("constant type %d not supported", tv->v_type); + clear_tv(tv); + return FAIL; } + tv->v_type = VAR_UNKNOWN; return OK; } @@ -735,22 +718,21 @@ generate_PUSHF(cctx_T *cctx, float_T fnumber) generate_PUSHS(cctx_T *cctx, char_u **str) { isn_T *isn; + int ret = OK; - if (cctx->ctx_skip == SKIP_YES) + if (cctx->ctx_skip != SKIP_YES) { - if (str != NULL) - VIM_CLEAR(*str); - return OK; + if ((isn = generate_instr_type(cctx, ISN_PUSHS, &t_string)) == NULL) + ret = FAIL; + else + { + isn->isn_arg.string = str == NULL ? NULL : *str; + return OK; + } } - if ((isn = generate_instr_type(cctx, ISN_PUSHS, &t_string)) == NULL) - { - if (str != NULL) - VIM_CLEAR(*str); - return FAIL; - } - isn->isn_arg.string = str == NULL ? NULL : *str; - - return OK; + if (str != NULL) + VIM_CLEAR(*str); + return ret; } /* @@ -864,6 +846,7 @@ generate_AUTOLOAD(cctx_T *cctx, char_u *name, type_T *type) * Generate an ISN_GETITEM instruction with "index". * "with_op" is TRUE for "+=" and other operators, the stack has the current * value below the list with values. + * Caller must check the type is a list. */ int generate_GETITEM(cctx_T *cctx, int index, int with_op) @@ -874,12 +857,6 @@ generate_GETITEM(cctx_T *cctx, int index, int with_op) RETURN_OK_IF_SKIP(cctx); - if (type->tt_type != VAR_LIST) - { - // cannot happen, caller has checked the type - emsg(_(e_list_required)); - return FAIL; - } item_type = type->tt_member; if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL) return FAIL; @@ -1048,8 +1025,7 @@ generate_LOADOUTER( int generate_LOADV( cctx_T *cctx, - char_u *name, - int error) + char_u *name) { int di_flags; int vidx = find_vim_var(name, &di_flags); @@ -1058,8 +1034,7 @@ generate_LOADV( RETURN_OK_IF_SKIP(cctx); if (vidx < 0) { - if (error) - semsg(_(e_variable_not_found_str), name); + semsg(_(e_variable_not_found_str), name); return FAIL; } type = get_vim_var_type(vidx, cctx->ctx_type_list); @@ -1258,23 +1233,22 @@ generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp) generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name) { isn_T *isn; + int ret = OK; - if (cctx->ctx_skip == SKIP_YES) + if (cctx->ctx_skip != SKIP_YES) { - vim_free(lambda_name); - vim_free(func_name); - return OK; + if ((isn = generate_instr(cctx, ISN_NEWFUNC)) == NULL) + ret = FAIL; + else + { + isn->isn_arg.newfunc.nf_lambda = lambda_name; + isn->isn_arg.newfunc.nf_global = func_name; + return OK; + } } - if ((isn = generate_instr(cctx, ISN_NEWFUNC)) == NULL) - { - vim_free(lambda_name); - vim_free(func_name); - return FAIL; - } - isn->isn_arg.newfunc.nf_lambda = lambda_name; - isn->isn_arg.newfunc.nf_global = func_name; - - return OK; + vim_free(lambda_name); + vim_free(func_name); + return ret; } /* @@ -1827,19 +1801,20 @@ generate_EXEC_copy(cctx_T *cctx, isntype_T isntype, char_u *line) generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *str) { isn_T *isn; + int ret = OK; - if (cctx->ctx_skip == SKIP_YES) + if (cctx->ctx_skip != SKIP_YES) { - vim_free(str); - return OK; + if ((isn = generate_instr(cctx, isntype)) == NULL) + ret = FAIL; + else + { + isn->isn_arg.string = str; + return OK; + } } - if ((isn = generate_instr(cctx, isntype)) == NULL) - { - vim_free(str); - return FAIL; - } - isn->isn_arg.string = str; - return OK; + vim_free(str); + return ret; } int