diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 3448ed5855..fb8b116010 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -325,6 +325,8 @@ js_encode({expr}) String encode JS style JSON json_decode({string}) any decode JSON json_encode({expr}) String encode JSON keys({dict}) List keys in {dict} +keytrans({string}) String translate internal keycodes to a form + that can be used by |:map| len({expr}) Number the length of {expr} libcall({lib}, {func}, {arg}) String call {func} in library {lib} with {arg} libcallnr({lib}, {func}, {arg}) Number idem, but return a Number @@ -5205,6 +5207,16 @@ keys({dict}) *keys()* Can also be used as a |method|: > mydict->keys() +keytrans({string}) *keytrans()* + Turn the internal byte representation of keys into a form that + can be used for |:map|. E.g. > + :let xx = "\" + :echo keytrans(xx) +< + + Can also be used as a |method|: > + "\"->keytrans() + < *len()* *E701* len({expr}) The result is a Number, which is the length of the argument. When {expr} is a String or a Number the length in bytes is diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index e2d32f94fc..e2d5cfe68d 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -737,6 +737,8 @@ String manipulation: *string-functions* fnameescape() escape a file name for use with a Vim command tr() translate characters from one set to another strtrans() translate a string to make it printable + keytrans() translate internal keycodes to a form that + can be used by |:map| tolower() turn a string to lowercase toupper() turn a string to uppercase charclass() class of a character diff --git a/runtime/filetype.vim b/runtime/filetype.vim index deca7dc71a..a27c3c71cb 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1723,6 +1723,9 @@ au BufNewFile,BufRead *.sdl,*.pr setf sdl " sed au BufNewFile,BufRead *.sed setf sed +" SubRip +au BufNewFile,BufRead *.srt setf srt + " svelte au BufNewFile,BufRead *.svelte setf svelte diff --git a/src/debugger.c b/src/debugger.c index e2f73cca03..235d493d58 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -135,9 +135,14 @@ do_debug(char_u *cmd) ignore_script = TRUE; } + // don't debug any function call, e.g. from an expresion mapping + n = debug_break_level; + debug_break_level = -1; + vim_free(cmdline); cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL); + debug_break_level = n; if (typeahead_saved) { restore_typeahead(&typeaheadbuf, TRUE); diff --git a/src/drawline.c b/src/drawline.c index 9b1d3530dc..e10bbaf4b9 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -666,9 +666,11 @@ win_line( // syntax_attr int text_prop_id = 0; // active property ID int text_prop_flags = 0; + int text_prop_above = FALSE; // first doing virtual text above int text_prop_follows = FALSE; // another text prop to display int saved_search_attr = 0; // search_attr to be used when n_extra // goes to zero + int saved_area_attr = 0; // idem for area_attr #endif #ifdef FEAT_SPELL int has_spell = FALSE; // this buffer has spell checking @@ -1784,6 +1786,7 @@ win_line( // Sort the properties on priority and/or starting last. // Then combine the attributes, highest priority last. + text_prop_above = FALSE; text_prop_follows = FALSE; sort_text_props(wp->w_buffer, text_props, text_prop_idxs, text_props_active); @@ -1817,6 +1820,8 @@ win_line( char_u *p = ((char_u **)wp->w_buffer ->b_textprop_text.ga_data)[ -text_prop_id - 1]; + int above = (tp->tp_flags + & TP_FLAG_ALIGN_ABOVE); // reset the ID in the copy to avoid it being used // again @@ -1826,8 +1831,6 @@ win_line( { int right = (tp->tp_flags & TP_FLAG_ALIGN_RIGHT); - int above = (tp->tp_flags - & TP_FLAG_ALIGN_ABOVE); int below = (tp->tp_flags & TP_FLAG_ALIGN_BELOW); int wrap = (tp->tp_flags & TP_FLAG_WRAP); @@ -1844,8 +1847,12 @@ win_line( extra_for_textprop = TRUE; extra_attr = used_attr; n_attr = mb_charlen(p); + // restore search_attr and area_attr when n_extra + // is down to zero saved_search_attr = search_attr; - search_attr = 0; // restore when n_extra is zero + saved_area_attr = area_attr; + search_attr = 0; + area_attr = 0; text_prop_attr = 0; text_prop_attr_comb = 0; if (*ptr == NUL) @@ -1902,6 +1909,9 @@ win_line( // If another text prop follows the condition below at // the last window column must know. + // If this is an "above" text prop and 'nowrap' the we + // must wrap anyway. + text_prop_above = above; text_prop_follows = other_tpi != -1; } } @@ -2198,6 +2208,8 @@ win_line( in_linebreak = FALSE; if (search_attr == 0) search_attr = saved_search_attr; + if (area_attr == 0 && *ptr != NUL) + area_attr = saved_area_attr; } #endif } @@ -3585,7 +3597,7 @@ win_line( || filler_todo > 0 #endif #ifdef FEAT_PROP_POPUP - || text_prop_follows + || text_prop_above || text_prop_follows #endif || (wp->w_p_list && wp->w_lcs_chars.eol != NUL && wlv.p_extra != at_end_str) @@ -3612,12 +3624,12 @@ win_line( && filler_todo <= 0 #endif #ifdef FEAT_PROP_POPUP - && !text_prop_follows + && !text_prop_above && !text_prop_follows #endif ) || lcs_eol_one == -1) break; #ifdef FEAT_PROP_POPUP - if (!wp->w_p_wrap && text_prop_follows) + if (!wp->w_p_wrap && text_prop_follows && !text_prop_above) { // do not output more of the line, only the "below" prop ptr += STRLEN(ptr); @@ -3651,7 +3663,7 @@ win_line( && filler_todo <= 0 #endif #ifdef FEAT_PROP_POPUP - && !text_prop_follows + && !text_prop_above && !text_prop_follows #endif && wp->w_width == Columns) { diff --git a/src/eval.c b/src/eval.c index 5d93b7e50c..d0957a145b 100644 --- a/src/eval.c +++ b/src/eval.c @@ -29,22 +29,6 @@ */ static int current_copyID = 0; -/* - * Info used by a ":for" loop. - */ -typedef struct -{ - int fi_semicolon; // TRUE if ending in '; var]' - int fi_varcount; // nr of variables in the list - int fi_break_count; // nr of line breaks encountered - listwatch_T fi_lw; // keep an eye on the item 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; - static int eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg); static int eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg); static int eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg); @@ -1914,7 +1898,8 @@ next_for_item(void *fi_void, char_u *arg) ? (ASSIGN_FINAL // first round: error if variable exists | (fi->fi_bi == 0 ? 0 : ASSIGN_DECL) - | ASSIGN_NO_MEMBER_TYPE) + | ASSIGN_NO_MEMBER_TYPE + | ASSIGN_UPDATE_BLOCK_ID) : 0); listitem_T *item; int skip_assign = in_vim9script() && arg[0] == '_' diff --git a/src/evalfunc.c b/src/evalfunc.c index 1c8f57088c..d0aa36cb9c 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -89,6 +89,7 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv); static void f_interrupt(typval_T *argvars, typval_T *rettv); static void f_invert(typval_T *argvars, typval_T *rettv); static void f_islocked(typval_T *argvars, typval_T *rettv); +static void f_keytrans(typval_T *argvars, typval_T *rettv); static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv); static void f_libcall(typval_T *argvars, typval_T *rettv); static void f_libcallnr(typval_T *argvars, typval_T *rettv); @@ -2058,6 +2059,8 @@ static funcentry_T global_functions[] = ret_string, f_json_encode}, {"keys", 1, 1, FEARG_1, arg1_dict_any, ret_list_string, f_keys}, + {"keytrans", 1, 1, FEARG_1, arg1_string, + ret_string, f_keytrans}, {"last_buffer_nr", 0, 0, 0, NULL, // obsolete ret_number, f_last_buffer_nr}, {"len", 1, 1, FEARG_1, arg1_len, @@ -7170,6 +7173,24 @@ f_islocked(typval_T *argvars, typval_T *rettv) clear_lval(&lv); } +/* + * "keytrans()" function + */ + static void +f_keytrans(typval_T *argvars, typval_T *rettv) +{ + char_u *escaped; + + rettv->v_type = VAR_STRING; + if (check_for_string_arg(argvars, 0) == FAIL + || argvars[0].vval.v_string == NULL) + return; + // Need to escape K_SPECIAL and CSI for mb_unescape(). + escaped = vim_strsave_escape_csi(argvars[0].vval.v_string); + rettv->vval.v_string = str2special_save(escaped, TRUE, TRUE); + vim_free(escaped); +} + /* * "last_buffer_nr()" function. */ diff --git a/src/evalvars.c b/src/evalvars.c index db563a8a0e..f1eeb12a8a 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -3853,6 +3853,14 @@ set_var_const( } clear_tv(&di->di_tv); + + if ((flags & ASSIGN_UPDATE_BLOCK_ID) + && SCRIPT_ID_VALID(current_sctx.sc_sid)) + { + scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); + + update_script_var_block_id(name, si->sn_current_block_id); + } } else { diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 311b9b7c2f..4938c6f485 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -4308,6 +4308,10 @@ ex_substitute(exarg_T *eap) - regmatch.startpos[0].lnum; search_match_endcol = regmatch.endpos[0].col + len_change; + if (search_match_lines == 0 + && search_match_endcol == 0) + // highlight at least one character for /^/ + search_match_endcol = 1; highlight_match = TRUE; update_topline(); diff --git a/src/ex_eval.c b/src/ex_eval.c index 2f31b3e1c5..9afcb56940 100644 --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -1208,6 +1208,7 @@ ex_while(exarg_T *eap) int skip; int result; cstack_T *cstack = eap->cstack; + int prev_cs_flags = 0; if (cstack->cs_idx == CSTACK_LEN - 1) eap->errmsg = _(e_while_for_nesting_too_deep); @@ -1230,15 +1231,18 @@ ex_while(exarg_T *eap) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); int i; + int first; int func_defined = cstack->cs_flags[cstack->cs_idx] & CSF_FUNC_DEF; // Any variables defined in the previous round are no longer // visible. Keep the first one for ":for", it is the loop // variable that we reuse every time around. - for (i = cstack->cs_script_var_len[cstack->cs_idx] + // Do this backwards, so that vars defined in a later round are + // found first. + first = cstack->cs_script_var_len[cstack->cs_idx] + (eap->cmdidx == CMD_while ? 0 : 1); - i < si->sn_var_vals.ga_len; ++i) + for (i = si->sn_var_vals.ga_len - 1; i >= first; --i) { svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + i; @@ -1250,8 +1254,15 @@ ex_while(exarg_T *eap) // still exists, from sn_vars. hide_script_var(si, i, func_defined); } + + // Start a new block ID, so that variables defined inside the + // loop are created new and not shared with the previous loop. + // Matters when used in a closure. + cstack->cs_block_id[cstack->cs_idx] = ++si->sn_last_block_id; + si->sn_current_block_id = si->sn_last_block_id; } } + prev_cs_flags = cstack->cs_flags[cstack->cs_idx]; cstack->cs_flags[cstack->cs_idx] = eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR; @@ -1270,7 +1281,7 @@ ex_while(exarg_T *eap) } else { - void *fi; + forinfo_T *fi; evalarg_T evalarg; /* @@ -1304,9 +1315,18 @@ ex_while(exarg_T *eap) result = next_for_item(fi, eap->arg); else result = FALSE; + if (fi != NULL) + // OR all the cs_flags together, if a function was defined in + // any round then the loop variable may have been used. + fi->fi_cs_flags |= prev_cs_flags; if (!result) { + // If a function was defined in any round then set the + // CSF_FUNC_DEF flag now, so that it's seen by leave_block(). + if (fi != NULL && (fi->fi_cs_flags & CSF_FUNC_DEF)) + cstack->cs_flags[cstack->cs_idx] |= CSF_FUNC_DEF; + free_for_info(fi); cstack->cs_forinfo[cstack->cs_idx] = NULL; } diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index d0d946cc0e..21a6ed1090 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -6261,7 +6261,7 @@ gui_mch_invert_rectangle(int r, int c, int nr, int nc) }; cairo_t * const cr = cairo_create(gui.surface); - set_cairo_source_rgba_from_color(cr, gui.norm_pixel ^ gui.back_pixel); + cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); # if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,9,2) cairo_set_operator(cr, CAIRO_OPERATOR_DIFFERENCE); # else @@ -6281,13 +6281,9 @@ gui_mch_invert_rectangle(int r, int c, int nr, int nc) if (gui.drawarea->window == NULL) return; - values.foreground.pixel = gui.norm_pixel ^ gui.back_pixel; - values.background.pixel = gui.norm_pixel ^ gui.back_pixel; - values.function = GDK_XOR; + values.function = GDK_INVERT; invert_gc = gdk_gc_new_with_values(gui.drawarea->window, &values, - GDK_GC_FOREGROUND | - GDK_GC_BACKGROUND | GDK_GC_FUNCTION); gdk_gc_set_exposures(invert_gc, gui.visibility != GDK_VISIBILITY_UNOBSCURED); diff --git a/src/main.c b/src/main.c index 77ce6d816c..f7a57c9cbe 100644 --- a/src/main.c +++ b/src/main.c @@ -563,6 +563,7 @@ vim_main2(void) // don't have them. if (!gui.in_use && params.evim_mode) mch_exit(1); + firstwin->w_prev_height = firstwin->w_height; // may have changed } #endif diff --git a/src/map.c b/src/map.c index e6d3195a47..371d08e8c1 100644 --- a/src/map.c +++ b/src/map.c @@ -2317,7 +2317,7 @@ mapblock2dict( int buffer_local, // false if not buffer local mapping int abbr) // true if abbreviation { - char_u *lhs = str2special_save(mp->m_keys, TRUE); + char_u *lhs = str2special_save(mp->m_keys, TRUE, FALSE); char_u *mapmode = map_mode_to_chars(mp->m_mode); dict_add_string(dict, "lhs", lhs); @@ -2409,7 +2409,7 @@ get_maparg(typval_T *argvars, typval_T *rettv, int exact) if (*rhs == NUL) rettv->vval.v_string = vim_strsave((char_u *)""); else - rettv->vval.v_string = str2special_save(rhs, FALSE); + rettv->vval.v_string = str2special_save(rhs, FALSE, FALSE); } } @@ -2478,7 +2478,7 @@ f_maplist(typval_T *argvars UNUSED, typval_T *rettv) keys_buf = NULL; did_simplify = FALSE; - lhs = str2special_save(mp->m_keys, TRUE); + lhs = str2special_save(mp->m_keys, TRUE, FALSE); (void)replace_termcodes(lhs, &keys_buf, flags, &did_simplify); vim_free(lhs); diff --git a/src/menu.c b/src/menu.c index 72aede5b44..d57352c92d 100644 --- a/src/menu.c +++ b/src/menu.c @@ -437,6 +437,7 @@ ex_menu( --curwin->w_height; curwin->w_winbar_height = h; } + curwin->w_prev_height = curwin->w_height; } theend: @@ -3272,7 +3273,7 @@ menuitem_getinfo(char_u *menu_name, vimmenu_T *menu, int modes, dict_T *dict) *menu->strings[bit] == NUL ? (char_u *)"" : (tofree = str2special_save( - menu->strings[bit], FALSE))); + menu->strings[bit], FALSE, FALSE))); vim_free(tofree); } if (status == OK) diff --git a/src/message.c b/src/message.c index e8f55c6552..3a31811252 100644 --- a/src/message.c +++ b/src/message.c @@ -1764,7 +1764,7 @@ msg_outtrans_special( ++str; } else - text = (char *)str2special(&str, from); + text = (char *)str2special(&str, from, FALSE); if (text[0] != NUL && text[1] == NUL) // single-byte character or illegal byte text = (char *)transchar_byte((char_u)text[0]); @@ -1787,14 +1787,16 @@ msg_outtrans_special( char_u * str2special_save( char_u *str, - int is_lhs) // TRUE for lhs, FALSE for rhs + int replace_spaces, // TRUE to replace " " with "". + // used for the lhs of mapping and keytrans(). + int replace_lt) // TRUE to replace "<" with "". { garray_T ga; char_u *p = str; ga_init2(&ga, 1, 40); while (*p != NUL) - ga_concat(&ga, str2special(&p, is_lhs)); + ga_concat(&ga, str2special(&p, replace_spaces, replace_lt)); ga_append(&ga, NUL); return (char_u *)ga.ga_data; } @@ -1809,7 +1811,9 @@ str2special_save( char_u * str2special( char_u **sp, - int from) // TRUE for lhs of mapping + int replace_spaces, // TRUE to replace " " with "". + // used for the lhs of mapping and keytrans(). + int replace_lt) // TRUE to replace "<" with "". { int c; static char_u buf[7]; @@ -1866,8 +1870,10 @@ str2special( *sp = str + (*str == NUL ? 0 : 1); // Make special keys and C0 control characters in <> form, also . - // Use only for lhs of a mapping. - if (special || c < ' ' || (from && c == ' ')) + if (special + || c < ' ' + || (replace_spaces && c == ' ') + || (replace_lt && c == '<')) return get_special_key_name(c, modifiers); buf[0] = c; buf[1] = NUL; @@ -1885,7 +1891,7 @@ str2specialbuf(char_u *sp, char_u *buf, int len) *buf = NUL; while (*sp) { - s = str2special(&sp, FALSE); + s = str2special(&sp, FALSE, FALSE); if ((int)(STRLEN(s) + STRLEN(buf)) < len) STRCAT(buf, s); } diff --git a/src/misc1.c b/src/misc1.c index d51092f9d5..6befce08ba 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -377,8 +377,8 @@ plines_win_nofill( if (!wp->w_p_wrap) lines = 1 #ifdef FEAT_PROP_POPUP - // add a line for each "below" aligned text property - + prop_count_below(wp->w_buffer, lnum) + // add a line for each "above" and "below" aligned text property + + prop_count_above_below(wp->w_buffer, lnum) #endif ; else diff --git a/src/misc2.c b/src/misc2.c index e5a0c355fe..ff5d5bec32 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -1547,7 +1547,8 @@ may_adjust_key_for_ctrl(int modifiers, int key) * When Ctrl is also used and are different, but should * be . Same for and . * Also for and . - * This includes all printable ASCII characters except numbers and a-z. + * This includes all printable ASCII characters except a-z. + * Digits are included because with AZERTY the Shift key is used to get them. */ int may_remove_shift_modifier(int modifiers, int key) @@ -1557,6 +1558,7 @@ may_remove_shift_modifier(int modifiers, int key) || modifiers == (MOD_MASK_SHIFT | MOD_MASK_META)) && ((key >= '!' && key <= '/') || (key >= ':' && key <= 'Z') + || vim_isdigit(key) || (key >= '[' && key <= '`') || (key >= '{' && key <= '~'))) return modifiers & ~MOD_MASK_SHIFT; diff --git a/src/move.c b/src/move.c index 8660d89055..4b823ba574 100644 --- a/src/move.c +++ b/src/move.c @@ -1068,6 +1068,19 @@ curs_columns( #endif ) { +#ifdef FEAT_PROP_POPUP + if (curwin->w_virtcol_first_char > 0) + { + int cols = (curwin->w_width - extra); + int rows = cols > 0 ? curwin->w_virtcol_first_char / cols : 1; + + // each "above" text prop shifts the text one row down + curwin->w_wrow += rows; + curwin->w_wcol -= rows * cols; + endcol -= rows * cols; + curwin->w_cline_height = rows + 1; + } +#endif /* * If Cursor is left of the screen, scroll rightwards. * If Cursor is right of the screen, scroll leftwards diff --git a/src/option.c b/src/option.c index acc7f59461..cd97f0974f 100644 --- a/src/option.c +++ b/src/option.c @@ -4102,7 +4102,8 @@ get_option_value( if (stringval != NULL) { if ((char_u **)varp == &p_pt) // 'pastetoggle' - *stringval = str2special_save(*(char_u **)(varp), FALSE); + *stringval = str2special_save(*(char_u **)(varp), FALSE, + FALSE); #ifdef FEAT_CRYPT // never return the value of the crypt key else if ((char_u **)varp == &curbuf->b_p_key @@ -4987,7 +4988,7 @@ put_setstring( { s = *valuep; while (*s != NUL) - if (put_escstr(fd, str2special(&s, FALSE), 2) == FAIL) + if (put_escstr(fd, str2special(&s, FALSE, FALSE), 2) == FAIL) return FAIL; } // expand the option value, replace $HOME by ~ diff --git a/src/proto/message.pro b/src/proto/message.pro index 3f8a8fe2e3..6657a08ec3 100644 --- a/src/proto/message.pro +++ b/src/proto/message.pro @@ -37,8 +37,8 @@ char_u *msg_outtrans_one(char_u *p, int attr); int msg_outtrans_len_attr(char_u *msgstr, int len, int attr); void msg_make(char_u *arg); int msg_outtrans_special(char_u *strstart, int from, int maxlen); -char_u *str2special_save(char_u *str, int is_lhs); -char_u *str2special(char_u **sp, int from); +char_u *str2special_save(char_u *str, int replace_spaces, int replace_lt); +char_u *str2special(char_u **sp, int replace_spaces, int replace_lt); void str2specialbuf(char_u *sp, char_u *buf, int len); void msg_prt_line(char_u *s, int list); void msg_puts(char *s); diff --git a/src/proto/textprop.pro b/src/proto/textprop.pro index 2b239673d0..b3912c16ae 100644 --- a/src/proto/textprop.pro +++ b/src/proto/textprop.pro @@ -4,7 +4,7 @@ void f_prop_add(typval_T *argvars, typval_T *rettv); void f_prop_add_list(typval_T *argvars, typval_T *rettv); int prop_add_common(linenr_T start_lnum, colnr_T start_col, dict_T *dict, buf_T *default_buf, typval_T *dict_arg); int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change); -int prop_count_below(buf_T *buf, linenr_T lnum); +int prop_count_above_below(buf_T *buf, linenr_T lnum); int count_props(linenr_T lnum, int only_starting, int last_line); void sort_text_props(buf_T *buf, textprop_T *props, int *idxs, int count); int find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum); diff --git a/src/proto/vim9compile.pro b/src/proto/vim9compile.pro index e6c6f3eff2..13b96c2240 100644 --- a/src/proto/vim9compile.pro +++ b/src/proto/vim9compile.pro @@ -1,6 +1,7 @@ /* vim9compile.c */ int lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx); int arg_exists(char_u *name, size_t len, int *idxp, type_T **type, int *gen_load_outer, cctx_T *cctx); +void update_script_var_block_id(char_u *name, int block_id); int script_is_vim9(void); int script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack); int check_defined(char_u *p, size_t len, cctx_T *cctx, cstack_T *cstack, int is_arg); diff --git a/src/structs.h b/src/structs.h index 0616220eb1..659d92434e 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1626,6 +1626,23 @@ typedef enum { typedef struct svar_S svar_T; #if defined(FEAT_EVAL) || defined(PROTO) +/* + * Info used by a ":for" loop. + */ +typedef struct +{ + int fi_semicolon; // TRUE if ending in '; var]' + int fi_varcount; // nr of variables in the list + int fi_break_count; // nr of line breaks encountered + listwatch_T fi_lw; // keep an eye on the item 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 + int fi_cs_flags; // cs_flags or'ed together +} forinfo_T; + typedef struct funccall_S funccall_T; // values used for "uf_def_status" diff --git a/src/testdir/dumps/Test_prop_with_text_above_5.dump b/src/testdir/dumps/Test_prop_with_text_above_5.dump new file mode 100644 index 0000000000..1b91e2483a --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_above_5.dump @@ -0,0 +1,9 @@ +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|1| |f+0#0000000#ffff4012|i|r|s|t| |t|h|i|n|g| |a|b|o|v|e| @36 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|s+0#0000000#ffd7ff255|e|c|o|n|d| |t|h|i|n|g| |a|b|o|v|e| @35 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|i+0#0000000&|n|s|e|r|t|e|d| |o|n|e| |t|w|o| @37 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|2| |t+0#0000000&|h|r|e@1| |f|o|u>r| @43 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|3| | +0#0000000&@2|a+0&#ffff4012|n|o|t|h|e|r| |t|h|i|n|g| @37 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|f+0#0000000&|i|v|e| |s|i|x| @45 +|~+0#4040ff13&| @58 +|~| @58 +|:+0#0000000&|s|e|t| |n|o|w|r|a|p| @30|2|,|1|0| @9|A|l@1| diff --git a/src/testdir/dumps/Test_prop_with_text_above_6.dump b/src/testdir/dumps/Test_prop_with_text_above_6.dump new file mode 100644 index 0000000000..faf9af6b7b --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_above_6.dump @@ -0,0 +1,9 @@ +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|1| |f+0#0000000#ffff4012|i|r|s|t| |t|h|i|n|g| |a|b|o|v|e| @36 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|s+0#0000000#ffd7ff255|e|c|o|n|d| |t|h|i|n|g| |a|b|o|v|e| @35 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|i+0#0000000#e0e0e08|n|s|e|r|t|e>d+0&#ffffff0| |o|n|e| |t|w|o| @37 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|b+0#0000000#5fd7ff255|e|l|o|w| +0&#ffffff0@48 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|2| |t+0#0000000&|h|r|e@1| |f|o|u|r| @43 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|3| | +0#0000000&@2|a+0&#ffff4012|n|o|t|h|e|r| |t|h|i|n|g| @37 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|f+0#0000000&|i|v|e| |s|i|x| @45 +|~+0#4040ff13&| @58 +|-+2#0000000&@1| |V|I|S|U|A|L| |-@1| +0&&@19|8| @8|1|,|8|-|1@1|6| @6|A|l@1| diff --git a/src/testdir/dumps/Test_prop_with_text_above_7.dump b/src/testdir/dumps/Test_prop_with_text_above_7.dump new file mode 100644 index 0000000000..9caa3c303c --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_above_7.dump @@ -0,0 +1,9 @@ +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|1| |f+0#0000000#ffff4012|i|r|s|t| |t|h|i|n|g| |a|b|o|v|e| @36 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|s+0#0000000#ffd7ff255|e|c|o|n|d| |t|h|i|n|g| |a|b|o|v|e| @35 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|i+0#0000000#e0e0e08|n|s|e|r|t|e>d+0&#ffffff0| +0&#e0e0e08|o|n|e| |t|w|o| +0&#ffffff0@37 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|b+0#0000000#5fd7ff255|e|l|o|w| +0#4040ff13#ffffff0| +0#0000000&@47 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|2| |t+0#0000000&|h|r|e@1| |f|o|u|r| @43 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|3| | +0#0000000&@2|a+0&#ffff4012|n|o|t|h|e|r| |t|h|i|n|g| @37 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|f+0#0000000&|i|v|e| |s|i|x| @45 +|~+0#4040ff13&| @58 +|-+2#0000000&@1| |V|I|S|U|A|L| |L|I|N|E| |-@1| +0&&@14|1| @8|1|,|8|-|1@1|6| @6|A|l@1| diff --git a/src/testdir/dumps/Test_prop_with_text_above_8.dump b/src/testdir/dumps/Test_prop_with_text_above_8.dump new file mode 100644 index 0000000000..f4c72fb14c --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_above_8.dump @@ -0,0 +1,9 @@ +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|1| |f+0#0000000#ffff4012|i|r|s|t| |t|h|i|n|g| |a|b|o|v|e| @36 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|s+0#0000000#ffd7ff255|e|c|o|n|d| |t|h|i|n|g| |a|b|o|v|e| @35 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|i+0#0000000&|n|s|e|r|t|e|d| @45 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|2| >o+0#0000000&|n|e| |t|w|o| @46 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|b+0#0000000#5fd7ff255|e|l|o|w| +0&#ffffff0@48 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|3| |t+0#0000000&|h|r|e@1| |f|o|u|r| @43 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|4| | +0#0000000&@2|a+0&#ffff4012|n|o|t|h|e|r| |t|h|i|n|g| @37 +| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|f+0#0000000&|i|v|e| |s|i|x| @45 +@42|2|,|1| @10|A|l@1| diff --git a/src/testdir/dumps/Test_sub_highlight_zer_match_1.dump b/src/testdir/dumps/Test_sub_highlight_zer_match_1.dump new file mode 100644 index 0000000000..481d0bb4e4 --- /dev/null +++ b/src/testdir/dumps/Test_sub_highlight_zer_match_1.dump @@ -0,0 +1,8 @@ +|o+1&#ffffff0|n+0&&|e| @56 +|t|w|o| @56 +|t|h|r|e@1| @54 +|~+0#4040ff13&| @58 +|~| @58 +|~| @58 +|~| @58 +|r+0#00e0003&|e|p|l|a|c|e| |w|i|t|h| @1|.@2|/|a|/|q|/|l|/|^|E|/|^|Y|)|?> +0#0000000&@10|1|,|1| @10|A|l@1| diff --git a/src/testdir/dumps/Test_vim9_closure_fails.dump b/src/testdir/dumps/Test_vim9_closure_fails.dump index 1189a3a919..dd0103cada 100644 --- a/src/testdir/dumps/Test_vim9_closure_fails.dump +++ b/src/testdir/dumps/Test_vim9_closure_fails.dump @@ -1,6 +1,6 @@ -|~+0#4040ff13#ffffff0| @73 +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 |~| @73 -|E+0#ffffff16#e000002|r@1|o|r| |d|e|t|e|c|t|e|d| |w|h|i|l|e| |p|r|o|c|e|s@1|i|n|g| |f|u|n|c|t|i|o|n| |<|l|a|m|b|d|a|>|1|:| +0#0000000#ffffff0@23 -|l+0#af5f00255&|i|n|e| @3|1|:| +0#0000000&@64 -|E+0#ffffff16#e000002|1|3|0|2|:| |S|c|r|i|p|t| |v|a|r|i|a|b|l|e| |w|a|s| |d|e|l|e|t|e|d| +0#0000000#ffffff0@40 -|P+0#00e0003&|r|e|s@1| |E|N|T|E|R| |o|r| |t|y|p|e| |c|o|m@1|a|n|d| |t|o| |c|o|n|t|i|n|u|e> +0#0000000&@35 +|~| @73 +|~| @73 +|0+0#0000000&| @55|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index b8fccb2b89..62867e3f33 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -531,6 +531,7 @@ let s:filename_checks = { \ 'squid': ['squid.conf'], \ 'squirrel': ['file.nut'], \ 'srec': ['file.s19', 'file.s28', 'file.s37', 'file.mot', 'file.srec'], + \ 'srt': ['file.srt'], \ 'sshconfig': ['ssh_config', '/.ssh/config', '/etc/ssh/ssh_config.d/file.conf', 'any/etc/ssh/ssh_config.d/file.conf', 'any/.ssh/config', 'any/.ssh/file.conf'], \ 'sshdconfig': ['sshd_config', '/etc/ssh/sshd_config.d/file.conf', 'any/etc/ssh/sshd_config.d/file.conf'], \ 'st': ['file.st'], diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index cad1d4b435..4c71e227ae 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -2764,6 +2764,32 @@ func Test_eval() call assert_fails("call eval('5 a')", 'E488:') endfunc +" Test for the keytrans() function +func Test_keytrans() + call assert_equal('', keytrans(' ')) + call assert_equal('', keytrans('<')) + call assert_equal('Tab>', keytrans('')) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\<*M-Space>")) + call assert_equal('', "\<*M-x>"->keytrans()) + call assert_equal('', "\<*C-I>"->keytrans()) + call assert_equal('', "\<*S-3>"->keytrans()) + call assert_equal('π', 'π'->keytrans()) + call assert_equal('', "\"->keytrans()) + call assert_equal('ě', 'ě'->keytrans()) + call assert_equal('', "\"->keytrans()) + call assert_equal('', ''->keytrans()) + call assert_equal('', test_null_string()->keytrans()) + call assert_fails('call keytrans(1)', 'E1174:') + call assert_fails('call keytrans()', 'E119:') +endfunc + " Test for the nr2char() function func Test_nr2char() set encoding=latin1 diff --git a/src/testdir/test_substitute.vim b/src/testdir/test_substitute.vim index df01e06f3a..92e86a9a10 100644 --- a/src/testdir/test_substitute.vim +++ b/src/testdir/test_substitute.vim @@ -2,6 +2,7 @@ source shared.vim source check.vim +source screendump.vim func Test_multiline_subst() enew! @@ -684,6 +685,21 @@ func Test_sub_cmd_9() bw! endfunc +func Test_sub_highlight_zero_match() + CheckRunVimInTerminal + + let lines =<< trim END + call setline(1, ['one', 'two', 'three']) + END + call writefile(lines, 'XscriptSubHighlight', 'D') + let buf = RunVimInTerminal('-S XscriptSubHighlight', #{rows: 8, cols: 60}) + call term_sendkeys(buf, ":%s/^/ /c\") + call VerifyScreenDump(buf, 'Test_sub_highlight_zer_match_1', {}) + + call term_sendkeys(buf, "\") + call StopVimInTerminal(buf) +endfunc + func Test_nocatch_sub_failure_handling() " normal error results in all replacements func Foo() diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index 6e3ca3b1d1..5167e8e3b3 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -2855,11 +2855,15 @@ func Test_props_with_text_above() call setline(1, ['one two', 'three four', 'five six']) call prop_type_add('above1', #{highlight: 'Search'}) call prop_type_add('above2', #{highlight: 'DiffChange'}) + call prop_type_add('below', #{highlight: 'DiffAdd'}) call prop_add(1, 0, #{type: 'above1', text: 'first thing above', text_align: 'above'}) call prop_add(1, 0, #{type: 'above2', text: 'second thing above', text_align: 'above'}) call prop_add(3, 0, #{type: 'above1', text: 'another thing', text_align: 'above', text_padding_left: 3}) normal gglllj + func AddPropBelow() + call prop_add(1, 0, #{type: 'below', text: 'below', text_align: 'below'}) + endfunc END call writefile(lines, 'XscriptPropsWithTextAbove', 'D') let buf = RunVimInTerminal('-S XscriptPropsWithTextAbove', #{rows: 9, cols: 60}) @@ -2873,6 +2877,18 @@ func Test_props_with_text_above() call term_sendkeys(buf, ":set number signcolumn=yes\") call VerifyScreenDump(buf, 'Test_prop_with_text_above_4', {}) + call term_sendkeys(buf, ":set nowrap\gg$j") + call VerifyScreenDump(buf, 'Test_prop_with_text_above_5', {}) + + call term_sendkeys(buf, ":call AddPropBelow()\") + call term_sendkeys(buf, "ggve") + call VerifyScreenDump(buf, 'Test_prop_with_text_above_6', {}) + call term_sendkeys(buf, "V") + call VerifyScreenDump(buf, 'Test_prop_with_text_above_7', {}) + + call term_sendkeys(buf, "\ls\\") + call VerifyScreenDump(buf, 'Test_prop_with_text_above_8', {}) + call StopVimInTerminal(buf) endfunc diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index a1c58a2048..4a5546b8eb 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -2930,8 +2930,10 @@ enddef def Run_Test_closure_in_for_loop_fails() var lines =<< trim END vim9script + redraw for n in [0] - timer_start(10, (_) => { + # time should be enough for startup to finish + timer_start(200, (_) => { echo n }) endfor @@ -2940,7 +2942,7 @@ def Run_Test_closure_in_for_loop_fails() # Check that an error shows var buf = g:RunVimInTerminal('-S XTest_closure_fails', {rows: 6, wait_for_ruler: 0}) - g:VerifyScreenDump(buf, 'Test_vim9_closure_fails', {}) + g:VerifyScreenDump(buf, 'Test_vim9_closure_fails', {wait: 3000}) # clean up g:StopVimInTerminal(buf) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index f746ca1782..0f462e3243 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -2259,11 +2259,11 @@ def Test_for_loop() enddef def Test_for_loop_with_closure() + # using the loop variable in a closure results in the last used value var lines =<< trim END var flist: list for i in range(5) - var inloop = i - flist[i] = () => inloop + flist[i] = () => i endfor for i in range(5) assert_equal(4, flist[i]()) @@ -2271,6 +2271,22 @@ def Test_for_loop_with_closure() END v9.CheckDefAndScriptSuccess(lines) + # using a local variable set to the loop variable in a closure results in the + # value at that moment + lines =<< trim END + var flist: list + for i in range(5) + var inloop = i + flist[i] = () => inloop + endfor + for i in range(5) + assert_equal(i, flist[i]()) + endfor + END + # FIXME + # v9.CheckDefAndScriptSuccess(lines) + v9.CheckScriptSuccess(['vim9script'] + lines) + lines =<< trim END var flist: list for i in range(5) @@ -2280,10 +2296,12 @@ def Test_for_loop_with_closure() } endfor for i in range(5) - assert_equal(4, flist[i]()) + assert_equal(i, flist[i]()) endfor END - v9.CheckDefAndScriptSuccess(lines) + # FIXME + # v9.CheckDefAndScriptSuccess(lines) + v9.CheckScriptSuccess(['vim9script'] + lines) enddef def Test_for_loop_fails() diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim index a75286fae3..c9f9caa347 100644 --- a/src/testdir/test_window_cmd.vim +++ b/src/testdir/test_window_cmd.vim @@ -1646,6 +1646,7 @@ func Test_splitscroll_with_splits() for so in [0, 5] for ls in range(0, 2) for pos in ["H", "M", "L"] + tabnew | tabonly! | redraw let tabline = (gui ? 0 : (tab ? 1 : 0)) let winbar_sb = (sb ? winbar : 0) execute 'set scrolloff=' . so @@ -1655,17 +1656,24 @@ func Test_splitscroll_with_splits() execute tab ? 'tabnew' : '' execute winbar ? 'nnoremenu 1.10 WinBar.Test :echo' : '' call setline(1, range(1, 256)) - execute 'norm gg' . pos - " No scroll for vertical split and quit - vsplit | quit - call assert_equal(1, line("w0")) + " No scroll for restore_snapshot + norm G + try + copen | close | colder + catch /E380/ + endtry + call assert_equal(257 - winheight(0), line("w0")) - " No scroll for horizontal split - split | redraw! | wincmd k + " No scroll for firstwin horizontal split + execute 'norm gg' . pos + split | redraw | wincmd k call assert_equal(1, line("w0")) + call assert_equal(&scroll, winheight(0) / 2) + wincmd j + call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0")) " No scroll when resizing windows - resize +2 + wincmd k | resize +2 call assert_equal(1, line("w0")) wincmd j call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0")) @@ -1715,7 +1723,7 @@ func Test_splitscroll_with_splits() call assert_equal(1, line("w0")) " No scroll in windows split and quit multiple times - quit | split | split | quit + quit | redraw | split | redraw | split | redraw | quit | redraw call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0")) " No scroll for new buffer @@ -1740,11 +1748,9 @@ func Test_splitscroll_with_splits() call assert_equal(6, line("w0")) wincmd j call assert_equal(5 + win_screenpos(0)[0] - tabline - winbar_sb, line("w0")) - only endfor endfor endfor - tabonly! endfor endfor endfor diff --git a/src/textprop.c b/src/textprop.c index ff96833f6c..d2536ce776 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -232,8 +232,9 @@ prop_add_one( for (lnum = start_lnum; lnum <= end_lnum; ++lnum) { - colnr_T col; // start column - long length; // in bytes + colnr_T col; // start column use in tp_col + colnr_T sort_col; // column where it appears + long length; // in bytes // Fetch the line to get the ml_line_len field updated. proplen = get_text_props(buf, lnum, &props, TRUE); @@ -248,6 +249,7 @@ prop_add_one( semsg(_(e_invalid_column_number_nr), (long)start_col); goto theend; } + sort_col = col; if (lnum == end_lnum) length = end_col - col; @@ -263,7 +265,9 @@ prop_add_one( length = 1; // text is placed on one character if (col == 0) { - col = MAXCOL; // after the line + col = MAXCOL; // before or after the line + if ((text_flags & TP_FLAG_ALIGN_ABOVE) == 0) + sort_col = MAXCOL; length += text_padding_left; } } @@ -280,9 +284,15 @@ prop_add_one( // the text, we need to copy them as bytes before using it as a struct. for (i = 0; i < proplen; ++i) { + colnr_T prop_col; + mch_memmove(&tmp_prop, props + i * sizeof(textprop_T), sizeof(textprop_T)); - if (tmp_prop.tp_col >= col) + // col is MAXCOL when the text goes above or after the line, when + // above we should use column zero for sorting + prop_col = (tmp_prop.tp_flags & TP_FLAG_ALIGN_ABOVE) + ? 0 : tmp_prop.tp_col; + if (prop_col >= sort_col) break; } newprops = newtext + textlen; @@ -608,12 +618,12 @@ get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change) } /* - * Return the number of text properties with "below" alignment in line "lnum". - * A "right" aligned property also goes below after a "below" or other "right" - * aligned property. + * Return the number of text properties with "above" or "below" alignment in + * line "lnum". A "right" aligned property also goes below after a "below" or + * other "right" aligned property. */ int -prop_count_below(buf_T *buf, linenr_T lnum) +prop_count_above_below(buf_T *buf, linenr_T lnum) { char_u *props; int count = get_text_props(buf, lnum, &props, FALSE); @@ -636,6 +646,11 @@ prop_count_below(buf_T *buf, linenr_T lnum) next_right_goes_below = TRUE; ++result; } + else if (prop.tp_flags & TP_FLAG_ALIGN_ABOVE) + { + next_right_goes_below = FALSE; + ++result; + } else if (prop.tp_flags & TP_FLAG_ALIGN_RIGHT) next_right_goes_below = TRUE; } @@ -2249,6 +2264,7 @@ adjust_props_for_split( proptype_T *pt; int start_incl, end_incl; int cont_prev, cont_next; + int prop_col; // copy the prop to an aligned structure mch_memmove(&prop, props + i * sizeof(textprop_T), sizeof(textprop_T)); @@ -2256,9 +2272,13 @@ adjust_props_for_split( pt = text_prop_type_by_id(curbuf, prop.tp_type); start_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_START_INCL)); end_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_END_INCL)); - cont_prev = prop.tp_col != MAXCOL && prop.tp_col + !start_incl <= kept; - cont_next = prop.tp_col != MAXCOL - && skipped <= prop.tp_col + prop.tp_len - !end_incl; + + // a text prop "above" behaves like it is on the first text column + prop_col = (prop.tp_flags & TP_FLAG_ALIGN_ABOVE) ? 1 : prop.tp_col; + + cont_prev = prop_col != MAXCOL && prop_col + !start_incl <= kept; + cont_next = prop_col != MAXCOL + && skipped <= prop_col + prop.tp_len - !end_incl; // when a prop has text it is never copied if (prop.tp_id < 0 && cont_next) cont_prev = FALSE; @@ -2277,8 +2297,7 @@ adjust_props_for_split( // Only add the property to the next line if the length is bigger than // zero. - if ((cont_next || prop.tp_col == MAXCOL) - && ga_grow(&nextprop, 1) == OK) + if ((cont_next || prop_col == MAXCOL) && ga_grow(&nextprop, 1) == OK) { textprop_T *p = ((textprop_T *)nextprop.ga_data) + nextprop.ga_len; diff --git a/src/typval.c b/src/typval.c index 95abe212e3..9bcc781884 100644 --- a/src/typval.c +++ b/src/typval.c @@ -410,7 +410,7 @@ check_for_nonempty_string_arg(typval_T *args, int idx) check_for_opt_string_arg(typval_T *args, int idx) { return (args[idx].v_type == VAR_UNKNOWN - || check_for_string_arg(args, idx) != FAIL); + || check_for_string_arg(args, idx) != FAIL) ? OK : FAIL; } /* @@ -434,7 +434,7 @@ check_for_number_arg(typval_T *args, int idx) check_for_opt_number_arg(typval_T *args, int idx) { return (args[idx].v_type == VAR_UNKNOWN - || check_for_number_arg(args, idx) != FAIL); + || check_for_number_arg(args, idx) != FAIL) ? OK : FAIL; } /* @@ -532,7 +532,7 @@ check_for_nonnull_list_arg(typval_T *args, int idx) check_for_opt_list_arg(typval_T *args, int idx) { return (args[idx].v_type == VAR_UNKNOWN - || check_for_list_arg(args, idx) != FAIL); + || check_for_list_arg(args, idx) != FAIL) ? OK : FAIL; } /* @@ -573,7 +573,7 @@ check_for_nonnull_dict_arg(typval_T *args, int idx) check_for_opt_dict_arg(typval_T *args, int idx) { return (args[idx].v_type == VAR_UNKNOWN - || check_for_dict_arg(args, idx) != FAIL); + || check_for_dict_arg(args, idx) != FAIL) ? OK : FAIL; } #if defined(FEAT_JOB_CHANNEL) || defined(PROTO) @@ -599,7 +599,7 @@ check_for_chan_or_job_arg(typval_T *args, int idx) check_for_opt_chan_or_job_arg(typval_T *args, int idx) { return (args[idx].v_type == VAR_UNKNOWN - || check_for_chan_or_job_arg(args, idx) != FAIL); + || check_for_chan_or_job_arg(args, idx) != FAIL) ? OK : FAIL; } /* @@ -623,7 +623,7 @@ check_for_job_arg(typval_T *args, int idx) check_for_opt_job_arg(typval_T *args, int idx) { return (args[idx].v_type == VAR_UNKNOWN - || check_for_job_arg(args, idx) != FAIL); + || check_for_job_arg(args, idx) != FAIL) ? OK : FAIL; } #endif @@ -649,7 +649,7 @@ check_for_string_or_number_arg(typval_T *args, int idx) check_for_opt_string_or_number_arg(typval_T *args, int idx) { return (args[idx].v_type == VAR_UNKNOWN - || check_for_string_or_number_arg(args, idx) != FAIL); + || check_for_string_or_number_arg(args, idx) != FAIL) ? OK : FAIL; } /* @@ -669,7 +669,7 @@ check_for_buffer_arg(typval_T *args, int idx) check_for_opt_buffer_arg(typval_T *args, int idx) { return (args[idx].v_type == VAR_UNKNOWN - || check_for_buffer_arg(args, idx)); + || check_for_buffer_arg(args, idx) != FAIL) ? OK : FAIL; } /* @@ -689,7 +689,7 @@ check_for_lnum_arg(typval_T *args, int idx) check_for_opt_lnum_arg(typval_T *args, int idx) { return (args[idx].v_type == VAR_UNKNOWN - || check_for_lnum_arg(args, idx)); + || check_for_lnum_arg(args, idx) != FAIL) ? OK : FAIL; } #if defined(FEAT_JOB_CHANNEL) || defined(PROTO) @@ -746,7 +746,7 @@ check_for_string_or_list_or_blob_arg(typval_T *args, int idx) check_for_opt_string_or_list_arg(typval_T *args, int idx) { return (args[idx].v_type == VAR_UNKNOWN - || check_for_string_or_list_arg(args, idx)); + || check_for_string_or_list_arg(args, idx) != FAIL) ? OK : FAIL; } /* @@ -788,7 +788,8 @@ check_for_string_or_number_or_list_arg(typval_T *args, int idx) check_for_opt_string_or_number_or_list_arg(typval_T *args, int idx) { return (args[idx].v_type == VAR_UNKNOWN - || check_for_string_or_number_or_list_arg(args, idx) != FAIL); + || check_for_string_or_number_or_list_arg(args, idx) + != FAIL) ? OK : FAIL; } /* diff --git a/src/version.c b/src/version.c index 217636952b..702941c414 100644 --- a/src/version.c +++ b/src/version.c @@ -718,6 +718,34 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 461, +/**/ + 460, +/**/ + 459, +/**/ + 458, +/**/ + 457, +/**/ + 456, +/**/ + 455, +/**/ + 454, +/**/ + 453, +/**/ + 452, +/**/ + 451, +/**/ + 450, +/**/ + 449, +/**/ + 448, /**/ 447, /**/ diff --git a/src/vim.h b/src/vim.h index c887596d9e..8ae87caa37 100644 --- a/src/vim.h +++ b/src/vim.h @@ -2269,6 +2269,7 @@ typedef enum { #define ASSIGN_NO_MEMBER_TYPE 0x20 // use "any" for list and dict member type #define ASSIGN_FOR_LOOP 0x40 // assigning to loop variable #define ASSIGN_INIT 0x80 // not assigning a value, just a declaration +#define ASSIGN_UPDATE_BLOCK_ID 0x100 // update sav_block_id #include "ex_cmds.h" // Ex command defines #include "spell.h" // spell checking stuff diff --git a/src/vim9compile.c b/src/vim9compile.c index 02cbb25df5..9755d97089 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -183,6 +183,9 @@ find_script_var(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack) if (cctx == NULL) { + if (cstack == NULL) + return NULL; + // Not in a function scope, find variable with block ID equal to or // smaller than the current block id. Use "cstack" to go up the block // scopes. @@ -219,6 +222,23 @@ find_script_var(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack) return NULL; } +/* + * If "name" can be found in the current script set it's "block_id". + */ + void +update_script_var_block_id(char_u *name, int block_id) +{ + scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); + hashitem_T *hi; + sallvar_T *sav; + + hi = hash_find(&si->sn_all_vars.dv_hashtab, name); + if (HASHITEM_EMPTY(hi)) + return; + sav = HI2SAV(hi); + sav->sav_block_id = block_id; +} + /* * Return TRUE if the script context is Vim9 script. */ diff --git a/src/window.c b/src/window.c index 62f904ce61..14c26af69f 100644 --- a/src/window.c +++ b/src/window.c @@ -3929,6 +3929,7 @@ new_frame(win_T *wp) win_init_size(void) { firstwin->w_height = ROWS_AVAIL; + firstwin->w_prev_height = ROWS_AVAIL; topframe->fr_height = ROWS_AVAIL; firstwin->w_width = Columns; topframe->fr_width = Columns; @@ -4075,6 +4076,7 @@ win_new_tabpage(int after) win_init_size(); firstwin->w_winrow = tabline_height(); + firstwin->w_prev_winrow = firstwin->w_winrow; win_comp_scroll(curwin); newtp->tp_topframe = topframe; @@ -6378,10 +6380,8 @@ win_fix_scroll(int resize) { lnum = wp->w_cursor.lnum; wp->w_cursor.lnum = MIN(wp->w_buffer->b_ml.ml_line_count, - wp->w_botline - 1 + (wp->w_prev_height - ? (wp->w_winrow - wp->w_prev_winrow) - + (wp->w_height - wp->w_prev_height) - : -WINBAR_HEIGHT(wp))); + wp->w_botline - 1 + (wp->w_winrow - wp->w_prev_winrow) + + (wp->w_height - wp->w_prev_height)); // Bring the new cursor position to the bottom of the screen. wp->w_fraction = FRACTION_MULT; scroll_to_fraction(wp, wp->w_prev_height); @@ -6390,6 +6390,7 @@ win_fix_scroll(int resize) invalidate_botline_win(wp); validate_botline_win(wp); } + win_comp_scroll(wp); wp->w_prev_height = wp->w_height; wp->w_prev_winrow = wp->w_winrow; } @@ -6818,6 +6819,9 @@ last_status_rec(frame_T *fr, int statusline) comp_col(); redraw_all_later(UPD_SOME_VALID); } + // Set prev_height when difference is due to 'laststatus'. + if (abs(wp->w_height - wp->w_prev_height) == 1) + wp->w_prev_height = wp->w_height; } else if (fr->fr_layout == FR_ROW) { @@ -7107,6 +7111,8 @@ restore_snapshot( win_comp_pos(); if (wp != NULL && close_curwin) win_goto(wp); + if (!p_spsc) + win_fix_scroll(FALSE); redraw_all_later(UPD_NOT_VALID); } clear_snapshot(curtab, idx);