Compare commits

...

14 Commits

Author SHA1 Message Date
Bram Moolenaar
8ad16da729 patch 8.1.0696: when test_edit fails 'insertmode' may not be reset
Problem:    When test_edit fails 'insertmode' may not be reset and the next
            test may get stuck. (James McCoy)
Solution:   Always reset 'insertmode' after executing a test.  Avoid that an
            InsertCharPre autocommand or a 'complete' function can change the
            state. (closes #3768)
2019-01-06 15:29:57 +01:00
Bram Moolenaar
f42b45d719 patch 8.1.0695: internal error when using :popup
Problem:    Internal error when using :popup.
Solution:   When a menu only exists in Terminal mode give an error. (Naruhiko
            Nishino, closes #3765)
2019-01-06 13:11:05 +01:00
Bram Moolenaar
4614f53e0f patch 8.1.0694: when using text props may free memory that is not allocated
Problem:    When using text props may free memory that is not allocated.
            (Andy Massimino)
Solution:   Allocate the line when adjusting text props. (closes #3766)
2019-01-06 12:54:55 +01:00
Bram Moolenaar
f780b8a1c1 patch 8.1.0693: channel test fails sometimes
Problem:    Channel test fails sometimes.
Solution:   Avoid race condition.
2019-01-05 00:35:22 +01:00
Bram Moolenaar
8b62d87e42 patch 8.1.0692: if a buffer was deleted a channel can't write to it
Problem:    If a buffer was deleted a channel can't write to it.
Solution:   When the buffer exists but was unloaded, prepare it for writing.
            (closes #3764)
2019-01-05 00:02:57 +01:00
Bram Moolenaar
4164bb204e patch 8.1.0691: text properties are not adjusted for :substitute
Problem:    Text properties are not adjusted for :substitute.
Solution:   Adjust text properties as well as possible.
2019-01-04 23:09:49 +01:00
Bram Moolenaar
21b5038e02 patch 8.1.0690: setline() and setbufline() do not clear text properties
Problem:    setline() and setbufline() do not clear text properties.
Solution:   Clear text properties when setting the text.
2019-01-04 18:07:24 +01:00
Bram Moolenaar
7f1664e392 patch 8.1.0689: undo with text properties not tested
Problem:    Undo with text properties not tested.
Solution:   Add a test function.
2019-01-04 17:21:24 +01:00
Bram Moolenaar
ccae4672fd patch 8.1.0688: text properties are not restored by undo
Problem:    Text properties are not restored by undo.
Solution:   Also save text properties for undo.
2019-01-04 15:09:57 +01:00
Bram Moolenaar
6d3a1940be patch 8.1.0687: sentence text object in Visual mode is not tested
Problem:    Sentence text object in Visual mode is not tested.
Solution:   Add a test. (Dominique Pelle, closes #3758)
2019-01-03 23:10:32 +01:00
Bram Moolenaar
5823f84dd0 patch 8.1.0686: when 'y' is in 'cpoptions' yanking for the clipboard changes redo
Problem:    When 'y' is in 'cpoptions' yanking for the clipboard changes redo.
Solution:   Do not use the 'y' flag when "gui_yank" is TRUE. (Andy Massimino,
            closes #3760)
2019-01-03 22:58:08 +01:00
Bram Moolenaar
f2d79fa92d patch 8.1.0685: get_buf_tv() is named inconsistently
Problem:    get_buf_tv() is named inconsistently.
Solution:   Rename it to tv_get_buf(). (Yegappan Lakshmanan, closes #3759)
2019-01-03 22:19:27 +01:00
Bram Moolenaar
4b7214ea78 patch 8.1.0684: warnings from 64-bit compiler
Problem:    Warnings from 64-bit compiler.
Solution:   Add type casts. (Mike Williams)
2019-01-03 21:55:32 +01:00
Bram Moolenaar
637532b3c0 patch 8.1.0683: spell highlighting does not always end
Problem:    Spell highlighting does not always end. (Gary Johnson)
Solution:   Also reset char_attr when spell errors are highlighted.
2019-01-03 21:44:40 +01:00
24 changed files with 615 additions and 141 deletions

View File

@@ -1,4 +1,4 @@
*gui.txt* For Vim version 8.1. Last change: 2018 Mar 06
*gui.txt* For Vim version 8.1. Last change: 2019 Jan 06
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -914,7 +914,9 @@ may be used to complete the name of the menu item for the appropriate mode.
To remove all menus use: *:unmenu-all* >
:unmenu * " remove all menus in Normal and visual mode
:unmenu! * " remove all menus in Insert and Command-line mode
:aunmenu * " remove all menus in all modes
:aunmenu * " remove all menus in all modes, except for Terminal
" mode
:tlunmenu * " remove all menus in Terminal mode
If you want to get rid of the menu bar: >
:set guioptions-=m

View File

@@ -1098,6 +1098,25 @@ channel_set_job(channel_T *channel, job_T *job, jobopt_T *options)
}
}
/*
* Prepare buffer "buf" for writing channel output to.
*/
static void
prepare_buffer(buf_T *buf)
{
buf_T *save_curbuf = curbuf;
buf_copy_options(buf, BCO_ENTER);
curbuf = buf;
#ifdef FEAT_QUICKFIX
set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL);
set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL);
#endif
if (curbuf->b_ml.ml_mfp == NULL)
ml_open(curbuf);
curbuf = save_curbuf;
}
/*
* Find a buffer matching "name" or create a new one.
* Returns NULL if there is something very wrong (error already reported).
@@ -1120,14 +1139,9 @@ find_buffer(char_u *name, int err, int msg)
NULL, (linenr_T)0, BLN_LISTED | BLN_NEW);
if (buf == NULL)
return NULL;
buf_copy_options(buf, BCO_ENTER);
prepare_buffer(buf);
curbuf = buf;
#ifdef FEAT_QUICKFIX
set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL);
set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL);
#endif
if (curbuf->b_ml.ml_mfp == NULL)
ml_open(curbuf);
if (msg)
ml_replace(1, (char_u *)(err ? "Reading from channel error..."
: "Reading from channel output..."), TRUE);
@@ -1244,6 +1258,9 @@ channel_set_options(channel_T *channel, jobopt_T *opt)
ch_log(channel, "writing out to buffer '%s'",
(char *)buf->b_ffname);
set_bufref(&channel->ch_part[PART_OUT].ch_bufref, buf);
// if the buffer was deleted or unloaded resurrect it
if (buf->b_ml.ml_mfp == NULL)
prepare_buffer(buf);
}
}
}
@@ -1287,6 +1304,9 @@ channel_set_options(channel_T *channel, jobopt_T *opt)
ch_log(channel, "writing err to buffer '%s'",
(char *)buf->b_ffname);
set_bufref(&channel->ch_part[PART_ERR].ch_bufref, buf);
// if the buffer was deleted or unloaded resurrect it
if (buf->b_ml.ml_mfp == NULL)
prepare_buffer(buf);
}
}
}

View File

@@ -4219,6 +4219,7 @@ expand_by_function(
win_T *curwin_save;
buf_T *curbuf_save;
typval_T rettv;
int save_State = State;
funcname = (type == CTRL_X_FUNCTION) ? curbuf->b_p_cfu : curbuf->b_p_ofu;
if (*funcname == NUL)
@@ -4272,6 +4273,9 @@ expand_by_function(
ins_compl_add_dict(matchdict);
theend:
// Restore State, it might have been changed.
State = save_State;
if (matchdict != NULL)
dict_unref(matchdict);
if (matchlist != NULL)
@@ -5549,6 +5553,7 @@ ins_complete(int c, int enable_pum)
pos_T pos;
win_T *curwin_save;
buf_T *curbuf_save;
int save_State = State;
/* Call 'completefunc' or 'omnifunc' and get pattern length as a
* string */
@@ -5572,6 +5577,8 @@ ins_complete(int c, int enable_pum)
curwin_save = curwin;
curbuf_save = curbuf;
col = call_func_retnr(funcname, 2, args);
State = save_State;
if (curwin_save != curwin || curbuf_save != curbuf)
{
EMSG(_(e_complwin));
@@ -10730,6 +10737,7 @@ do_insert_char_pre(int c)
{
char_u *res;
char_u buf[MB_MAXBYTES + 1];
int save_State = State;
/* Return quickly when there is nothing to do. */
if (!has_insertcharpre())
@@ -10762,6 +10770,9 @@ do_insert_char_pre(int c)
set_vim_var_string(VV_CHAR, NULL, -1); /* clear v:char */
--textlock;
// Restore the State, it may have been changed.
State = save_State;
return res;
}
#endif

View File

@@ -1357,8 +1357,10 @@ set_buffer_lines(
if (!append && lnum <= curbuf->b_ml.ml_line_count)
{
/* existing line, replace it */
if (u_savesub(lnum) == OK && ml_replace(lnum, line, TRUE) == OK)
// Existing line, replace it.
// Removes any existing text properties.
if (u_savesub(lnum) == OK && ml_replace_len(
lnum, line, (colnr_T)STRLEN(line) + 1, TRUE, TRUE) == OK)
{
changed_bytes(lnum, 0);
if (is_curbuf && lnum == curwin->w_cursor.lnum)
@@ -1424,7 +1426,7 @@ f_appendbufline(typval_T *argvars, typval_T *rettv)
linenr_T lnum;
buf_T *buf;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
if (buf == NULL)
rettv->vval.v_number = 1; /* FAIL */
else
@@ -1892,7 +1894,7 @@ buflist_find_by_name(char_u *name, int curtab_only)
* Get buffer by number or pattern.
*/
buf_T *
get_buf_tv(typval_T *tv, int curtab_only)
tv_get_buf(typval_T *tv, int curtab_only)
{
char_u *name = tv->vval.v_string;
buf_T *buf;
@@ -1925,7 +1927,7 @@ f_bufname(typval_T *argvars, typval_T *rettv)
(void)tv_get_number(&argvars[0]); /* issue errmsg if type error */
++emsg_off;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
rettv->v_type = VAR_STRING;
if (buf != NULL && buf->b_fname != NULL)
rettv->vval.v_string = vim_strsave(buf->b_fname);
@@ -1946,7 +1948,7 @@ f_bufnr(typval_T *argvars, typval_T *rettv)
(void)tv_get_number(&argvars[0]); /* issue errmsg if type error */
++emsg_off;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
--emsg_off;
/* If the buffer isn't found and the second argument is not zero create a
@@ -1974,7 +1976,7 @@ buf_win_common(typval_T *argvars, typval_T *rettv, int get_nr)
(void)tv_get_number(&argvars[0]); /* issue errmsg if type error */
++emsg_off;
buf = get_buf_tv(&argvars[0], TRUE);
buf = tv_get_buf(&argvars[0], TRUE);
FOR_ALL_WINDOWS(wp)
{
++winnr;
@@ -2962,7 +2964,7 @@ f_deletebufline(typval_T *argvars, typval_T *rettv)
tabpage_T *tp;
win_T *wp;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
if (buf == NULL)
{
rettv->vval.v_number = 1; /* FAIL */
@@ -4525,7 +4527,7 @@ f_getbufinfo(typval_T *argvars, typval_T *rettv)
/* Information about one buffer. Argument specifies the buffer */
(void)tv_get_number(&argvars[0]); /* issue errmsg if type error */
++emsg_off;
argbuf = get_buf_tv(&argvars[0], FALSE);
argbuf = tv_get_buf(&argvars[0], FALSE);
--emsg_off;
if (argbuf == NULL)
return;
@@ -4609,7 +4611,7 @@ f_getbufline(typval_T *argvars, typval_T *rettv)
(void)tv_get_number(&argvars[0]); /* issue errmsg if type error */
++emsg_off;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
--emsg_off;
lnum = tv_get_lnum_buf(&argvars[1], buf);
@@ -4636,7 +4638,7 @@ f_getbufvar(typval_T *argvars, typval_T *rettv)
(void)tv_get_number(&argvars[0]); /* issue errmsg if type error */
varname = tv_get_string_chk(&argvars[1]);
++emsg_off;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -4707,7 +4709,7 @@ f_getchangelist(typval_T *argvars, typval_T *rettv)
#ifdef FEAT_JUMPLIST
(void)tv_get_number(&argvars[0]); /* issue errmsg if type error */
++emsg_off;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
--emsg_off;
if (buf == NULL)
return;
@@ -8721,7 +8723,7 @@ f_prompt_setcallback(typval_T *argvars, typval_T *rettv UNUSED)
if (check_secure())
return;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
if (buf == NULL)
return;
@@ -8750,7 +8752,7 @@ f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv UNUSED)
if (check_secure())
return;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
if (buf == NULL)
return;
@@ -8778,7 +8780,7 @@ f_prompt_setprompt(typval_T *argvars, typval_T *rettv UNUSED)
if (check_secure())
return;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
if (buf == NULL)
return;
@@ -10580,7 +10582,7 @@ f_setbufline(typval_T *argvars, typval_T *rettv)
linenr_T lnum;
buf_T *buf;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
if (buf == NULL)
rettv->vval.v_number = 1; /* FAIL */
else
@@ -10605,7 +10607,7 @@ f_setbufvar(typval_T *argvars, typval_T *rettv UNUSED)
return;
(void)tv_get_number(&argvars[0]); /* issue errmsg if type error */
varname = tv_get_string_chk(&argvars[1]);
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
varp = &argvars[2];
if (buf != NULL && varname != NULL && varp != NULL)
@@ -11365,7 +11367,7 @@ f_sign_getplaced(typval_T *argvars, typval_T *rettv)
if (argvars[0].v_type != VAR_UNKNOWN)
{
// get signs placed in this buffer
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
if (buf == NULL)
{
EMSG2(_("E158: Invalid buffer name: %s"),
@@ -11457,7 +11459,7 @@ f_sign_place(typval_T *argvars, typval_T *rettv)
goto cleanup;
// Buffer to place the sign
buf = get_buf_tv(&argvars[3], FALSE);
buf = tv_get_buf(&argvars[3], FALSE);
if (buf == NULL)
{
EMSG2(_("E158: Invalid buffer name: %s"), tv_get_string(&argvars[2]));
@@ -11566,7 +11568,7 @@ f_sign_unplace(typval_T *argvars, typval_T *rettv)
if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
{
buf = get_buf_tv(&di->di_tv, FALSE);
buf = tv_get_buf(&di->di_tv, FALSE);
if (buf == NULL)
{
EMSG2(_("E158: Invalid buffer name: %s"),
@@ -12812,7 +12814,7 @@ f_swapname(typval_T *argvars, typval_T *rettv)
buf_T *buf;
rettv->v_type = VAR_STRING;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
if (buf == NULL || buf->b_ml.ml_mfp == NULL
|| buf->b_ml.ml_mfp->mf_fname == NULL)
rettv->vval.v_string = NULL;

View File

@@ -5628,9 +5628,19 @@ do_sub(exarg_T *eap)
* - original text up to match
* - length of substituted part
* - original text after match
* Adjust text properties here, since we have all information
* needed.
*/
if (nmatch == 1)
{
p1 = sub_firstline;
#ifdef FEAT_TEXT_PROP
if (curbuf->b_has_textprop)
adjust_prop_columns(lnum, regmatch.startpos[0].col,
sublen - 1 - (regmatch.endpos[0].col
- regmatch.startpos[0].col));
#endif
}
else
{
p1 = ml_get(sub_firstlnum + nmatch - 1);
@@ -5732,11 +5742,12 @@ do_sub(exarg_T *eap)
STRMOVE(p1, p1 + 1);
else if (*p1 == CAR)
{
if (u_inssub(lnum) == OK) /* prepare for undo */
if (u_inssub(lnum) == OK) // prepare for undo
{
*p1 = NUL; /* truncate up to the CR */
ml_append(lnum - 1, new_start,
(colnr_T)(p1 - new_start + 1), FALSE);
colnr_T plen = (colnr_T)(p1 - new_start + 1);
*p1 = NUL; // truncate up to the CR
ml_append(lnum - 1, new_start, plen, FALSE);
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L);
if (subflags.do_ask)
appended_lines(lnum - 1, 1L);
@@ -5746,13 +5757,16 @@ do_sub(exarg_T *eap)
first_line = lnum;
last_line = lnum + 1;
}
/* All line numbers increase. */
#ifdef FEAT_TEXT_PROP
adjust_props_for_split(lnum, plen, 1);
#endif
// all line numbers increase
++sub_firstlnum;
++lnum;
++line2;
/* move the cursor to the new line, like Vi */
// move the cursor to the new line, like Vi
++curwin->w_cursor.lnum;
/* copy the rest */
// copy the rest
STRMOVE(new_start, p1 + 1);
p1 = new_start - 1;
}

View File

@@ -1583,6 +1583,9 @@ EXTERN char_u e_invalidreg[] INIT(= N_("E850: Invalid register name"));
#endif
EXTERN char_u e_dirnotf[] INIT(= N_("E919: Directory not found in '%s': \"%s\""));
EXTERN char_u e_au_recursive[] INIT(= N_("E952: Autocommand caused recursive behavior"));
#ifdef FEAT_MENU
EXTERN char_u e_menuothermode[] INIT(= N_("E328: Menu only exists in another mode"));
#endif
#ifdef FEAT_GUI_MAC
EXTERN short disallow_gui INIT(= FALSE);

View File

@@ -562,7 +562,7 @@ ml_set_crypt_key(
/* Skip data block with negative block number.
* Should not happen, because of the ml_preserve()
* above. Get same block again for next index. */
++idx;
++idx;
continue;
}
@@ -3217,11 +3217,22 @@ ml_replace(linenr_T lnum, char_u *line, int copy)
if (line != NULL)
len = (colnr_T)STRLEN(line);
return ml_replace_len(lnum, line, len, copy);
return ml_replace_len(lnum, line, len, FALSE, copy);
}
/*
* Replace a line for the current buffer. Like ml_replace() with:
* "len_arg" is the length of the text, excluding NUL.
* If "has_props" is TRUE then "line_arg" includes the text properties and
* "len_arg" includes the NUL of the text.
*/
int
ml_replace_len(linenr_T lnum, char_u *line_arg, colnr_T len_arg, int copy)
ml_replace_len(
linenr_T lnum,
char_u *line_arg,
colnr_T len_arg,
int has_props,
int copy)
{
char_u *line = line_arg;
colnr_T len = len_arg;
@@ -3233,8 +3244,21 @@ ml_replace_len(linenr_T lnum, char_u *line_arg, colnr_T len_arg, int copy)
if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
return FAIL;
if (copy && (line = vim_strnsave(line, len)) == NULL) /* allocate memory */
return FAIL;
if (!has_props)
++len; // include the NUL after the text
if (copy)
{
// copy the line to allocated memory
#ifdef FEAT_TEXT_PROP
if (has_props)
line = vim_memsave(line, len);
else
#endif
line = vim_strnsave(line, len - 1);
if (line == NULL)
return FAIL;
}
#ifdef FEAT_NETBEANS_INTG
if (netbeans_active())
{
@@ -3249,14 +3273,14 @@ ml_replace_len(linenr_T lnum, char_u *line_arg, colnr_T len_arg, int copy)
curbuf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
#ifdef FEAT_TEXT_PROP
if (curbuf->b_has_textprop)
if (curbuf->b_has_textprop && !has_props)
// Need to fetch the old line to copy over any text properties.
ml_get_buf(curbuf, lnum, TRUE);
#endif
}
#ifdef FEAT_TEXT_PROP
if (curbuf->b_has_textprop)
if (curbuf->b_has_textprop && !has_props)
{
size_t oldtextlen = STRLEN(curbuf->b_ml.ml_line_ptr) + 1;
@@ -3266,11 +3290,11 @@ ml_replace_len(linenr_T lnum, char_u *line_arg, colnr_T len_arg, int copy)
size_t textproplen = curbuf->b_ml.ml_line_len - oldtextlen;
// Need to copy over text properties, stored after the text.
newline = alloc(len + 1 + (int)textproplen);
newline = alloc(len + (int)textproplen);
if (newline != NULL)
{
mch_memmove(newline, line, len + 1);
mch_memmove(newline + len + 1, curbuf->b_ml.ml_line_ptr + oldtextlen, textproplen);
mch_memmove(newline, line, len);
mch_memmove(newline + len, curbuf->b_ml.ml_line_ptr + oldtextlen, textproplen);
vim_free(line);
line = newline;
len += (colnr_T)textproplen;
@@ -3279,11 +3303,11 @@ ml_replace_len(linenr_T lnum, char_u *line_arg, colnr_T len_arg, int copy)
}
#endif
if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY) /* same line allocated */
vim_free(curbuf->b_ml.ml_line_ptr); /* free it */
if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY) // same line allocated
vim_free(curbuf->b_ml.ml_line_ptr); // free it
curbuf->b_ml.ml_line_ptr = line;
curbuf->b_ml.ml_line_len = len + 1;
curbuf->b_ml.ml_line_len = len;
curbuf->b_ml.ml_line_lnum = lnum;
curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
@@ -3350,7 +3374,7 @@ adjust_text_props_for_delete(
internal_error("no text property below deleted line");
return;
}
this_props_len = line_size - textlen;
this_props_len = line_size - (int)textlen;
}
found = FALSE;
@@ -3489,7 +3513,7 @@ ml_delete_int(buf_T *buf, linenr_T lnum, int message)
if ((long)textlen < line_size)
{
textprop_save_len = line_size - textlen;
textprop_save_len = line_size - (int)textlen;
textprop_save = vim_memsave((char_u *)dp + line_start + textlen,
textprop_save_len);
}
@@ -5379,7 +5403,7 @@ ml_updatechunk(
// the text prop info would also be counted. Go over the
// lines.
for (i = end_idx; i < idx; ++i)
size += STRLEN((char_u *)dp + (dp->db_index[i] & DB_INDEX_MASK)) + 1;
size += (int)STRLEN((char_u *)dp + (dp->db_index[i] & DB_INDEX_MASK)) + 1;
}
else
#endif
@@ -5588,7 +5612,7 @@ ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp)
// lengths.
len = 0;
for (i = start_idx; i <= idx; ++i)
len += STRLEN((char_u *)dp + ((dp->db_index[i]) & DB_INDEX_MASK)) + 1;
len += (int)STRLEN((char_u *)dp + ((dp->db_index[i]) & DB_INDEX_MASK)) + 1;
}
else
#endif

View File

@@ -61,7 +61,6 @@ static char_u *menu_translate_tab_and_shift(char_u *arg_start);
static char *menu_mode_chars[] = {"n", "v", "s", "o", "i", "c", "tl", "t"};
static char_u e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu");
static char_u e_othermode[] = N_("E328: Menu only exists in another mode");
static char_u e_nomenu[] = N_("E329: No menu \"%s\"");
#ifdef FEAT_TOOLBAR
@@ -956,7 +955,7 @@ remove_menu(
else if (*name != NUL)
{
if (!silent)
EMSG(_(e_othermode));
EMSG(_(e_menuothermode));
return FAIL;
}
@@ -1130,7 +1129,7 @@ show_menus(char_u *path_name, int modes)
}
else if ((menu->modes & modes) == 0x0)
{
EMSG(_(e_othermode));
EMSG(_(e_menuothermode));
vim_free(path_name);
return FAIL;
}

View File

@@ -1326,7 +1326,8 @@ set_vcount_ca(cmdarg_T *cap, int *set_prevcount)
#endif
/*
* Handle an operator after visual mode or when the movement is finished
* Handle an operator after Visual mode or when the movement is finished.
* "gui_yank" is true when yanking text for the clipboard.
*/
void
do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
@@ -1372,6 +1373,10 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
*/
if ((finish_op || VIsual_active) && oap->op_type != OP_NOP)
{
// Yank can be redone when 'y' is in 'cpoptions', but not when yanking
// for the clipboard.
int redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank;
#ifdef FEAT_LINEBREAK
/* Avoid a problem with unwanted linebreaks in block mode. */
if (curwin->w_p_lbr)
@@ -1407,7 +1412,7 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
/* Only redo yank when 'y' flag is in 'cpoptions'. */
/* Never redo "zf" (define fold). */
if ((vim_strchr(p_cpo, CPO_YANK) != NULL || oap->op_type != OP_YANK)
if ((redo_yank || oap->op_type != OP_YANK)
&& ((!VIsual_active || oap->motion_force)
/* Also redo Operator-pending Visual mode mappings */
|| (VIsual_active && cap->cmdchar == ':'
@@ -1628,7 +1633,7 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
}
/* can't redo yank (unless 'y' is in 'cpoptions') and ":" */
if ((vim_strchr(p_cpo, CPO_YANK) != NULL || oap->op_type != OP_YANK)
if ((redo_yank || oap->op_type != OP_YANK)
&& oap->op_type != OP_COLON
#ifdef FEAT_FOLDING
&& oap->op_type != OP_FOLD

View File

@@ -1195,6 +1195,14 @@ pum_show_popupmenu(vimmenu_T *menu)
|| (mp->modes & mp->enabled & mode))
++pum_size;
// When there are only Terminal mode menus, using "popup Edit" results in
// pum_size being zero.
if (pum_size <= 0)
{
EMSG(e_menuothermode);
return;
}
array = (pumitem_T *)alloc_clear((unsigned)sizeof(pumitem_T) * pum_size);
if (array == NULL)
return;

View File

@@ -4,7 +4,7 @@ char_u *get_expr_name(expand_T *xp, int idx);
int find_internal_func(char_u *name);
int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv);
buf_T *buflist_find_by_name(char_u *name, int curtab_only);
buf_T *get_buf_tv(typval_T *tv, int curtab_only);
buf_T *tv_get_buf(typval_T *tv, int curtab_only);
void execute_redir_str(char_u *value, int value_len);
void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv);
float_T vim_round(float_T f);

View File

@@ -24,7 +24,7 @@ int ml_line_alloced(void);
int ml_append(linenr_T lnum, char_u *line, colnr_T len, int newfile);
int ml_append_buf(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, int newfile);
int ml_replace(linenr_T lnum, char_u *line, int copy);
int ml_replace_len(linenr_T lnum, char_u *line_arg, colnr_T len_arg, int copy);
int ml_replace_len(linenr_T lnum, char_u *line_arg, colnr_T len_arg, int has_props, int copy);
int ml_delete(linenr_T lnum, int message);
void ml_setmarked(linenr_T lnum);
linenr_T ml_firstmarked(void);

View File

@@ -14,4 +14,5 @@ void f_prop_type_list(typval_T *argvars, typval_T *rettv);
void clear_global_prop_types(void);
void clear_buf_prop_types(buf_T *buf);
void adjust_prop_columns(linenr_T lnum, colnr_T col, int bytes_added);
void adjust_props_for_split(linenr_T lnum, int kept, int deleted);
/* vim: set ft=c : */

View File

@@ -4161,7 +4161,11 @@ win_line(
break;
}
if (draw_state == WL_LINE && area_highlighting)
if (draw_state == WL_LINE && (area_highlighting
#ifdef FEAT_SPELL
|| has_spell
#endif
))
{
/* handle Visual or match highlighting in this line */
if (vcol == fromcol

View File

@@ -347,6 +347,14 @@ typedef struct
* structures used for undo
*/
// One line saved for undo. After the NUL terminated text there might be text
// properties, thus ul_len can be larger than STRLEN(ul_line) + 1.
typedef struct {
char_u *ul_line; // text of the line
long ul_len; // length of the line including NUL, plus text
// properties
} undoline_T;
typedef struct u_entry u_entry_T;
typedef struct u_header u_header_T;
struct u_entry
@@ -355,7 +363,7 @@ struct u_entry
linenr_T ue_top; /* number of line above undo block */
linenr_T ue_bot; /* number of line below undo block */
linenr_T ue_lcount; /* linecount when u_save called */
char_u **ue_array; /* array of lines in undo block */
undoline_T *ue_array; /* array of lines in undo block */
long ue_size; /* number of lines in ue_array */
#ifdef U_DEBUG
int ue_magic; /* magic number to check allocation */
@@ -2167,7 +2175,7 @@ struct file_buffer
/*
* variables for "U" command in undo.c
*/
char_u *b_u_line_ptr; /* saved line for "U" command */
undoline_T b_u_line_ptr; /* saved line for "U" command */
linenr_T b_u_line_lnum; /* line number of line in u_line */
colnr_T b_u_line_colnr; /* optional column number */

View File

@@ -3842,7 +3842,7 @@ term_get_buf(typval_T *argvars, char *where)
(void)tv_get_number(&argvars[0]); /* issue errmsg if type error */
++emsg_off;
buf = get_buf_tv(&argvars[0], FALSE);
buf = tv_get_buf(&argvars[0], FALSE);
--emsg_off;
if (buf == NULL || buf->b_term == NULL)
{

View File

@@ -26,7 +26,7 @@
" It will be called after each Test_ function.
"
" When debugging a test it can be useful to add messages to v:errors:
" call add(v:errors, "this happened")
" call add(v:errors, "this happened")
" Without the +eval feature we can't run these tests, bail out.
@@ -149,6 +149,10 @@ func RunTheTest(test)
endtry
endif
" In case 'insertmode' was set and something went wrong, make sure it is
" reset to avoid trouble with anything else.
set noinsertmode
if exists("*TearDown")
try
call TearDown()

View File

@@ -1645,6 +1645,27 @@ func Test_collapse_buffers()
bwipe!
endfunc
func Test_write_to_deleted_buffer()
if !executable('echo') || !has('job')
return
endif
let job = job_start('echo hello', {'out_io': 'buffer', 'out_name': 'test_buffer', 'out_msg': 0})
let bufnr = bufnr('test_buffer')
call WaitForAssert({-> assert_equal(['hello'], getbufline(bufnr, 1, '$'))})
call assert_equal('nofile', getbufvar(bufnr, '&buftype'))
call assert_equal('hide', getbufvar(bufnr, '&bufhidden'))
bdel test_buffer
call assert_equal([], getbufline(bufnr, 1, '$'))
let job = job_start('echo hello', {'out_io': 'buffer', 'out_name': 'test_buffer', 'out_msg': 0})
call WaitForAssert({-> assert_equal(['hello'], getbufline(bufnr, 1, '$'))})
call assert_equal('nofile', getbufvar(bufnr, '&buftype'))
call assert_equal('hide', getbufvar(bufnr, '&bufhidden'))
bwipe! test_buffer
endfunc
func Test_cmd_parsing()
if !has('unix')
return

View File

@@ -882,5 +882,18 @@ func Test_complete_o_tab()
delfunc s:act_on_text_changed
endfunc
func Test_menu_only_exists_in_terminal()
if !exists(':tlmenu') || has('gui_running')
return
endif
tlnoremenu &Edit.&Paste<Tab>"+gP <C-W>"+
aunmenu *
try
popup Edit
call assert_false(1, 'command should have failed')
catch
call assert_exception('E328:')
endtry
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -89,30 +89,34 @@ func SetupPropsInFirstLine()
call setline(1, 'one two three')
call prop_add(1, 1, {'length': 3, 'id': 11, 'type': 'one'})
call prop_add(1, 5, {'length': 3, 'id': 12, 'type': 'two'})
call prop_add(1, 8, {'length': 5, 'id': 13, 'type': 'three'})
call prop_add(1, 9, {'length': 5, 'id': 13, 'type': 'three'})
call prop_add(1, 1, {'length': 13, 'id': 14, 'type': 'whole'})
endfunc
let s:expected_props = [{'col': 1, 'length': 13, 'id': 14, 'type': 'whole', 'start': 1, 'end': 1},
func Get_expected_props()
return [
\ {'col': 1, 'length': 13, 'id': 14, 'type': 'whole', 'start': 1, 'end': 1},
\ {'col': 1, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1},
\ {'col': 5, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1},
\ {'col': 8, 'length': 5, 'id': 13, 'type': 'three', 'start': 1, 'end': 1},
\ {'col': 9, 'length': 5, 'id': 13, 'type': 'three', 'start': 1, 'end': 1},
\ ]
endfunc
func Test_prop_add()
new
call AddPropTypes()
call SetupPropsInFirstLine()
call assert_equal(s:expected_props, prop_list(1))
let expected_props = Get_expected_props()
call assert_equal(expected_props, prop_list(1))
call assert_fails("call prop_add(10, 1, {'length': 1, 'id': 14, 'type': 'whole'})", 'E966:')
call assert_fails("call prop_add(1, 22, {'length': 1, 'id': 14, 'type': 'whole'})", 'E964:')
" Insert a line above, text props must still be there.
call append(0, 'empty')
call assert_equal(s:expected_props, prop_list(2))
call assert_equal(expected_props, prop_list(2))
" Delete a line above, text props must still be there.
1del
call assert_equal(s:expected_props, prop_list(1))
call assert_equal(expected_props, prop_list(1))
" Prop without length or end column is zero length
call prop_clear(1)
@@ -128,7 +132,7 @@ func Test_prop_remove()
new
call AddPropTypes()
call SetupPropsInFirstLine()
let props = deepcopy(s:expected_props)
let props = Get_expected_props()
call assert_equal(props, prop_list(1))
" remove by id
@@ -236,7 +240,7 @@ func Test_prop_clear()
new
call AddPropTypes()
call SetupPropsInFirstLine()
call assert_equal(s:expected_props, prop_list(1))
call assert_equal(Get_expected_props(), prop_list(1))
call prop_clear(1)
call assert_equal([], prop_list(1))
@@ -251,7 +255,7 @@ func Test_prop_clear_buf()
call SetupPropsInFirstLine()
let bufnr = bufnr('')
wincmd w
call assert_equal(s:expected_props, prop_list(1, {'bufnr': bufnr}))
call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr}))
call prop_clear(1, 1, {'bufnr': bufnr})
call assert_equal([], prop_list(1, {'bufnr': bufnr}))
@@ -261,6 +265,83 @@ func Test_prop_clear_buf()
bwipe!
endfunc
func Test_prop_setline()
new
call AddPropTypes()
call SetupPropsInFirstLine()
call assert_equal(Get_expected_props(), prop_list(1))
call setline(1, 'foobar')
call assert_equal([], prop_list(1))
call DeletePropTypes()
bwipe!
endfunc
func Test_prop_setbufline()
new
call AddPropTypes()
call SetupPropsInFirstLine()
let bufnr = bufnr('')
wincmd w
call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr}))
call setbufline(bufnr, 1, 'foobar')
call assert_equal([], prop_list(1, {'bufnr': bufnr}))
wincmd w
call DeletePropTypes()
bwipe!
endfunc
func Test_prop_substitute()
new
" Set first line to 'one two three'
call AddPropTypes()
call SetupPropsInFirstLine()
let expected_props = Get_expected_props()
call assert_equal(expected_props, prop_list(1))
" Change "n" in "one" to XX: 'oXXe two three'
s/n/XX/
let expected_props[0].length += 1
let expected_props[1].length += 1
let expected_props[2].col += 1
let expected_props[3].col += 1
call assert_equal(expected_props, prop_list(1))
" Delete "t" in "two" and "three" to XX: 'oXXe wo hree'
s/t//g
let expected_props[0].length -= 2
let expected_props[2].length -= 1
let expected_props[3].length -= 1
let expected_props[3].col -= 1
call assert_equal(expected_props, prop_list(1))
" Split the line by changing w to line break: 'oXXe ', 'o hree'
" The long prop is split and spans both lines.
" The props on "two" and "three" move to the next line.
s/w/\r/
let new_props = [
\ copy(expected_props[0]),
\ copy(expected_props[2]),
\ copy(expected_props[3]),
\ ]
let expected_props[0].length = 5
unlet expected_props[3]
unlet expected_props[2]
call assert_equal(expected_props, prop_list(1))
let new_props[0].length = 6
let new_props[1].col = 1
let new_props[1].length = 1
let new_props[2].col = 3
call assert_equal(new_props, prop_list(2))
call DeletePropTypes()
bwipe!
endfunc
" Setup a three line prop in lines 2 - 4.
" Add short props in line 1 and 5.
func Setup_three_line_prop()
@@ -348,6 +429,59 @@ func Test_prop_byteoff()
call prop_type_delete('comment')
endfunc
func Test_prop_undo()
new
call prop_type_add('comment', {'highlight': 'Directory'})
call setline(1, ['oneone', 'twotwo', 'three'])
" Set 'undolevels' to break changes into undo-able pieces.
set ul&
call prop_add(1, 3, {'end_col': 5, 'type': 'comment'})
let expected = [{'col': 3, 'length': 2, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
call assert_equal(expected, prop_list(1))
" Insert a character, then undo.
exe "normal 0lllix\<Esc>"
set ul&
let expected[0].length = 3
call assert_equal(expected, prop_list(1))
undo
let expected[0].length = 2
call assert_equal(expected, prop_list(1))
" Delete a character, then undo
exe "normal 0lllx"
set ul&
let expected[0].length = 1
call assert_equal(expected, prop_list(1))
undo
let expected[0].length = 2
call assert_equal(expected, prop_list(1))
" Delete the line, then undo
1d
set ul&
call assert_equal([], prop_list(1))
undo
call assert_equal(expected, prop_list(1))
" Insert a character, delete two characters, then undo with "U"
exe "normal 0lllix\<Esc>"
set ul&
let expected[0].length = 3
call assert_equal(expected, prop_list(1))
exe "normal 0lllxx"
set ul&
let expected[0].length = 1
call assert_equal(expected, prop_list(1))
normal U
let expected[0].length = 2
call assert_equal(expected, prop_list(1))
bwipe!
call prop_type_delete('comment')
endfunc
" screenshot test with textprop highlighting
funct Test_textprop_screenshots()
if !CanRunVimInTerminal() || &encoding != 'utf-8'

View File

@@ -273,3 +273,44 @@ func Test_virtual_replace2()
%d_
set bs&vim
endfunc
func Test_Visual_sentence_textobject()
new
call setline(1, ['First sentence. Second sentence. Third', 'sentence. Fouth sentence'])
" When start and end of visual area are identical, 'as' or 'is' select
" the whole sentence.
norm! 1gofdvasy
call assert_equal('Second sentence. ', @")
norm! 1gofdvisy
call assert_equal('Second sentence.', @")
" When start and end of visual area are not identical, 'as' or 'is'
" extend the sentence in direction of the end of the visual area.
norm! 1gofdvlasy
call assert_equal('d sentence. ', @")
norm! gvasy
call assert_equal("d sentence. Third\nsentence. ", @")
norm! 1gofdvlisy
call assert_equal('d sentence.', @")
norm! gvisy
call assert_equal('d sentence. ', @")
norm! gvisy
call assert_equal("d sentence. Third\nsentence.", @")
" Extend visual area in opposite direction.
norm! 1gofdvhasy
call assert_equal(' Second', @")
norm! gvasy
call assert_equal("First sentence. Second", @")
norm! 1gofdvhisy
call assert_equal('Second', @")
norm! gvisy
call assert_equal(' Second', @")
norm! gvisy
call assert_equal('First sentence. Second', @")
bwipe!
endfunc

View File

@@ -18,6 +18,8 @@
*
* TODO:
* - Adjust text property column and length when text is inserted/deleted.
* -> a :substitute with a multi-line match
* -> search for changed_bytes() from ex_cmds.c
* - Perhaps we only need TP_FLAG_CONT_NEXT and can drop TP_FLAG_CONT_PREV?
* - Add an arrray for global_proptypes, to quickly lookup a prop type by ID
* - Add an arrray for b_proptypes, to quickly lookup a prop type by ID
@@ -130,7 +132,7 @@ get_bufnr_from_arg(typval_T *arg, buf_T **buf)
di = dict_find(arg->vval.v_dict, (char_u *)"bufnr", -1);
if (di != NULL)
{
*buf = get_buf_tv(&di->di_tv, FALSE);
*buf = tv_get_buf(&di->di_tv, FALSE);
if (*buf == NULL)
return FAIL;
}
@@ -262,9 +264,9 @@ f_prop_add(typval_T *argvars, typval_T *rettv UNUSED)
if (lnum == end_lnum)
length = end_col - col;
else
length = textlen - col + 1;
length = (int)textlen - col + 1;
if (length > (long)textlen)
length = textlen; // can include the end-of-line
length = (int)textlen; // can include the end-of-line
if (length < 0)
length = 0; // zero-width property
@@ -346,6 +348,34 @@ get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change)
return (int)(proplen / sizeof(textprop_T));
}
/*
* Set the text properties for line "lnum" to "props" with length "len".
* If "len" is zero text properties are removed, "props" is not used.
* Any existing text properties are dropped.
* Only works for the current buffer.
*/
static void
set_text_props(linenr_T lnum, char_u *props, int len)
{
char_u *text;
char_u *newtext;
size_t textlen;
text = ml_get(lnum);
textlen = STRLEN(text) + 1;
newtext = alloc(textlen + len);
if (newtext == NULL)
return;
mch_memmove(newtext, text, textlen);
if (len > 0)
mch_memmove(newtext + textlen, props, len);
if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY)
vim_free(curbuf->b_ml.ml_line_ptr);
curbuf->b_ml.ml_line_ptr = newtext;
curbuf->b_ml.ml_line_len = textlen + len;
curbuf->b_ml.ml_flags |= ML_LINE_DIRTY;
}
static proptype_T *
find_type_by_id(hashtab_T *ht, int id)
{
@@ -533,7 +563,7 @@ f_prop_remove(typval_T *argvars, typval_T *rettv)
di = dict_find(dict, (char_u *)"bufnr", -1);
if (di != NULL)
{
buf = get_buf_tv(&di->di_tv, FALSE);
buf = tv_get_buf(&di->di_tv, FALSE);
if (buf == NULL)
return;
}
@@ -949,7 +979,9 @@ adjust_prop_columns(
pt = text_prop_type_by_id(curbuf, tmp_prop.tp_type);
if (bytes_added > 0
? (tmp_prop.tp_col >= col + (pt != NULL && (pt->pt_flags & PT_FLAG_INS_START_INCL) ? 2 : 1))
? (tmp_prop.tp_col >= col
+ (pt != NULL && (pt->pt_flags & PT_FLAG_INS_START_INCL)
? 2 : 1))
: (tmp_prop.tp_col > col + 1))
{
tmp_prop.tp_col += bytes_added;
@@ -957,7 +989,7 @@ adjust_prop_columns(
}
else if (tmp_prop.tp_len > 0
&& tmp_prop.tp_col + tmp_prop.tp_len > col
+ ((pt != NULL && (pt->pt_flags & PT_FLAG_INS_END_INCL))
+ ((pt != NULL && (pt->pt_flags & PT_FLAG_INS_END_INCL))
? 0 : 1))
{
tmp_prop.tp_len += bytes_added;
@@ -971,9 +1003,79 @@ adjust_prop_columns(
}
if (dirty)
{
colnr_T newlen = (int)textlen + wi * (colnr_T)sizeof(textprop_T);
if ((curbuf->b_ml.ml_flags & ML_LINE_DIRTY) == 0)
curbuf->b_ml.ml_line_ptr =
vim_memsave(curbuf->b_ml.ml_line_ptr, newlen);
curbuf->b_ml.ml_flags |= ML_LINE_DIRTY;
curbuf->b_ml.ml_line_len = textlen + wi * sizeof(textprop_T);
curbuf->b_ml.ml_line_len = newlen;
}
}
/*
* Adjust text properties for a line that was split in two.
* "lnum" is the newly inserted line. The text properties are now on the line
* below it. "kept" is the number of bytes kept in the first line, while
* "deleted" is the number of bytes deleted.
*/
void
adjust_props_for_split(linenr_T lnum, int kept, int deleted)
{
char_u *props;
int count;
garray_T prevprop;
garray_T nextprop;
int i;
int skipped = kept + deleted;
if (!curbuf->b_has_textprop)
return;
count = get_text_props(curbuf, lnum + 1, &props, FALSE);
ga_init2(&prevprop, sizeof(textprop_T), 10);
ga_init2(&nextprop, sizeof(textprop_T), 10);
// Get the text properties, which are at "lnum + 1".
// Keep the relevant ones in the first line, reducing the length if needed.
// Copy the ones that include the split to the second line.
// Move the ones after the split to the second line.
for (i = 0; i < count; ++i)
{
textprop_T prop;
textprop_T *p;
// copy the prop to an aligned structure
mch_memmove(&prop, props + i * sizeof(textprop_T), sizeof(textprop_T));
if (prop.tp_col < kept && ga_grow(&prevprop, 1) == OK)
{
p = ((textprop_T *)prevprop.ga_data) + prevprop.ga_len;
*p = prop;
if (p->tp_col + p->tp_len >= kept)
p->tp_len = kept - p->tp_col;
++prevprop.ga_len;
}
if (prop.tp_col + prop.tp_len >= skipped && ga_grow(&nextprop, 1) == OK)
{
p = ((textprop_T *)nextprop.ga_data) + nextprop.ga_len;
*p = prop;
if (p->tp_col > skipped)
p->tp_col -= skipped - 1;
else
{
p->tp_len -= skipped - p->tp_col;
p->tp_col = 1;
}
++nextprop.ga_len;
}
}
set_text_props(lnum, prevprop.ga_data, prevprop.ga_len * sizeof(textprop_T));
ga_clear(&prevprop);
set_text_props(lnum + 1, nextprop.ga_data, nextprop.ga_len * sizeof(textprop_T));
ga_clear(&nextprop);
}
#endif // FEAT_TEXT_PROP

View File

@@ -125,7 +125,6 @@ static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);
#endif
#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)
static char_u *u_save_line(linenr_T);
/* used in undo_end() to report number of added and deleted lines */
static long u_newcount, u_oldcount;
@@ -352,6 +351,28 @@ get_undolevel(void)
return curbuf->b_p_ul;
}
/*
* u_save_line(): save an allocated copy of line "lnum" into "ul".
* Returns FAIL when out of memory.
*/
static int
u_save_line(undoline_T *ul, linenr_T lnum)
{
char_u *line = ml_get(lnum);
if (curbuf->b_ml.ml_line_len == 0)
{
ul->ul_len = 1;
ul->ul_line = vim_strsave((char_u *)"");
}
else
{
ul->ul_len = curbuf->b_ml.ml_line_len;
ul->ul_line = vim_memsave(line, ul->ul_len);
}
return ul->ul_line == NULL ? FAIL : OK;
}
/*
* Common code for various ways to save text before a change.
* "top" is the line above the first changed line.
@@ -664,8 +685,8 @@ u_savecommon(
if (size > 0)
{
if ((uep->ue_array = (char_u **)U_ALLOC_LINE(
sizeof(char_u *) * size)) == NULL)
if ((uep->ue_array = (undoline_T *)U_ALLOC_LINE(
sizeof(undoline_T) * size)) == NULL)
{
u_freeentry(uep, 0L);
goto nomem;
@@ -678,7 +699,7 @@ u_savecommon(
u_freeentry(uep, i);
return FAIL;
}
if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
if (u_save_line(&uep->ue_array[i], lnum++) == FAIL)
{
u_freeentry(uep, i);
goto nomem;
@@ -1111,6 +1132,8 @@ read_string_decrypt(bufinfo_T *bi, int len)
vim_free(ptr);
return NULL;
}
// In case there are text properties there already is a NUL, but
// checking for that is more expensive than just adding a dummy byte.
ptr[len] = NUL;
#ifdef FEAT_CRYPT
if (bi->bi_state != NULL && bi->bi_buffer == NULL)
@@ -1126,7 +1149,7 @@ read_string_decrypt(bufinfo_T *bi, int len)
static int
serialize_header(bufinfo_T *bi, char_u *hash)
{
int len;
long len;
buf_T *buf = bi->bi_buf;
FILE *fp = bi->bi_fp;
char_u time_buf[8];
@@ -1148,7 +1171,7 @@ serialize_header(bufinfo_T *bi, char_u *hash)
buf->b_p_key, &header, &header_len);
if (bi->bi_state == NULL)
return FAIL;
len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp);
len = (long)fwrite(header, (size_t)header_len, (size_t)1, fp);
vim_free(header);
if (len != 1)
{
@@ -1181,9 +1204,10 @@ serialize_header(bufinfo_T *bi, char_u *hash)
/* buffer-specific data */
undo_write_bytes(bi, (long_u)buf->b_ml.ml_line_count, 4);
len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
len = buf->b_u_line_ptr.ul_line == NULL
? 0 : STRLEN(buf->b_u_line_ptr.ul_line);
undo_write_bytes(bi, (long_u)len, 4);
if (len > 0 && fwrite_crypt(bi, buf->b_u_line_ptr, (size_t)len) == FAIL)
if (len > 0 && fwrite_crypt(bi, buf->b_u_line_ptr.ul_line, (size_t)len) == FAIL)
return FAIL;
undo_write_bytes(bi, (long_u)buf->b_u_line_lnum, 4);
undo_write_bytes(bi, (long_u)buf->b_u_line_colnr, 4);
@@ -1360,10 +1384,12 @@ serialize_uep(
undo_write_bytes(bi, (long_u)uep->ue_size, 4);
for (i = 0; i < uep->ue_size; ++i)
{
len = STRLEN(uep->ue_array[i]);
// Text is written without the text properties, since we cannot restore
// the text property types.
len = STRLEN(uep->ue_array[i].ul_line);
if (undo_write_bytes(bi, (long_u)len, 4) == FAIL)
return FAIL;
if (len > 0 && fwrite_crypt(bi, uep->ue_array[i], len) == FAIL)
if (len > 0 && fwrite_crypt(bi, uep->ue_array[i].ul_line, len) == FAIL)
return FAIL;
}
return OK;
@@ -1374,7 +1400,7 @@ unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name)
{
int i;
u_entry_T *uep;
char_u **array = NULL;
undoline_T *array = NULL;
char_u *line;
int line_len;
@@ -1392,13 +1418,13 @@ unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name)
if (uep->ue_size > 0)
{
if (uep->ue_size < LONG_MAX / (int)sizeof(char_u *))
array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
array = (undoline_T *)U_ALLOC_LINE(sizeof(undoline_T) * uep->ue_size);
if (array == NULL)
{
*error = TRUE;
return uep;
}
vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
vim_memset(array, 0, sizeof(undoline_T) * uep->ue_size);
}
uep->ue_array = array;
@@ -1417,7 +1443,8 @@ unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name)
*error = TRUE;
return uep;
}
array[i] = line;
array[i].ul_line = line;
array[i].ul_len = line_len + 1;
}
return uep;
}
@@ -1610,7 +1637,7 @@ u_write_undo(
/* If there is no undo information at all, quit here after deleting any
* existing undo file. */
if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
if (buf->b_u_numhead == 0 && buf->b_u_line_ptr.ul_line == NULL)
{
if (p_verbose > 0)
verb_msg((char_u *)_("Skipping undo file write, nothing to undo"));
@@ -1771,7 +1798,7 @@ u_read_undo(char_u *name, char_u *hash, char_u *orig_name)
char_u *file_name;
FILE *fp;
long version, str_len;
char_u *line_ptr = NULL;
undoline_T line_ptr;
linenr_T line_lnum;
colnr_T line_colnr;
linenr_T line_count;
@@ -1798,6 +1825,9 @@ u_read_undo(char_u *name, char_u *hash, char_u *orig_name)
bufinfo_T bi;
vim_memset(&bi, 0, sizeof(bi));
line_ptr.ul_len = 0;
line_ptr.ul_line = NULL;
if (name == NULL)
{
file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
@@ -1917,7 +1947,10 @@ u_read_undo(char_u *name, char_u *hash, char_u *orig_name)
if (str_len < 0)
goto error;
if (str_len > 0)
line_ptr = read_string_decrypt(&bi, str_len);
{
line_ptr.ul_line = read_string_decrypt(&bi, str_len);
line_ptr.ul_len = str_len + 1;
}
line_lnum = (linenr_T)undo_read_4c(&bi);
line_colnr = (colnr_T)undo_read_4c(&bi);
if (line_lnum < 0 || line_colnr < 0)
@@ -2098,7 +2131,7 @@ u_read_undo(char_u *name, char_u *hash, char_u *orig_name)
goto theend;
error:
vim_free(line_ptr);
vim_free(line_ptr.ul_line);
if (uhp_table != NULL)
{
for (i = 0; i < num_read_uhps; i++)
@@ -2596,7 +2629,7 @@ target_zero:
static void
u_undoredo(int undo)
{
char_u **newarray = NULL;
undoline_T *newarray = NULL;
linenr_T oldsize;
linenr_T newsize;
linenr_T top, bot;
@@ -2669,8 +2702,13 @@ u_undoredo(int undo)
* undoing auto-formatting puts the cursor in the previous
* line. */
for (i = 0; i < newsize && i < oldsize; ++i)
if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
{
char_u *p = ml_get(top + 1 + i);
if (curbuf->b_ml.ml_line_len != uep->ue_array[i].ul_len
|| memcmp(uep->ue_array[i].ul_line, p, curbuf->b_ml.ml_line_len) != 0)
break;
}
if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
{
newlnum = top;
@@ -2689,10 +2727,10 @@ u_undoredo(int undo)
/* delete the lines between top and bot and save them in newarray */
if (oldsize > 0)
{
if ((newarray = (char_u **)U_ALLOC_LINE(
sizeof(char_u *) * oldsize)) == NULL)
if ((newarray = (undoline_T *)U_ALLOC_LINE(
sizeof(undoline_T) * oldsize)) == NULL)
{
do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
do_outofmem_msg((long_u)(sizeof(undoline_T) * oldsize));
/*
* We have messed up the entry list, repair is impossible.
* we have to free the rest of the list.
@@ -2709,7 +2747,7 @@ u_undoredo(int undo)
for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
{
/* what can we do when we run out of memory? */
if ((newarray[i] = u_save_line(lnum)) == NULL)
if (u_save_line(&newarray[i], lnum) == FAIL)
do_outofmem_msg((long_u)0);
/* remember we deleted the last line in the buffer, and a
* dummy empty line will be inserted */
@@ -2726,15 +2764,13 @@ u_undoredo(int undo)
{
for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
{
/*
* If the file is empty, there is an empty line 1 that we
* should get rid of, by replacing it with the new line
*/
// If the file is empty, there is an empty line 1 that we
// should get rid of, by replacing it with the new line.
if (empty_buffer && lnum == 0)
ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
ml_replace_len((linenr_T)1, uep->ue_array[i].ul_line, uep->ue_array[i].ul_len, TRUE, TRUE);
else
ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
vim_free(uep->ue_array[i]);
ml_append(lnum, uep->ue_array[i].ul_line, (colnr_T)uep->ue_array[i].ul_len, FALSE);
vim_free(uep->ue_array[i].ul_line);
}
vim_free((char_u *)uep->ue_array);
}
@@ -3172,13 +3208,17 @@ u_find_first_changed(void)
for (lnum = 1; lnum < curbuf->b_ml.ml_line_count
&& lnum <= uep->ue_size; ++lnum)
if (STRCMP(ml_get_buf(curbuf, lnum, FALSE),
uep->ue_array[lnum - 1]) != 0)
{
char_u *p = ml_get_buf(curbuf, lnum, FALSE);
if (uep->ue_array[lnum - 1].ul_len != curbuf->b_ml.ml_line_len
|| memcmp(p, uep->ue_array[lnum - 1].ul_line, uep->ue_array[lnum - 1].ul_len) != 0)
{
CLEAR_POS(&(uhp->uh_cursor));
uhp->uh_cursor.lnum = lnum;
return;
}
}
if (curbuf->b_ml.ml_line_count != uep->ue_size)
{
/* lines added or deleted at the end, put the cursor there */
@@ -3383,7 +3423,7 @@ u_freeentries(
u_freeentry(u_entry_T *uep, long n)
{
while (n > 0)
vim_free(uep->ue_array[--n]);
vim_free(uep->ue_array[--n].ul_line);
vim_free((char_u *)uep->ue_array);
#ifdef U_DEBUG
uep->ue_magic = 0;
@@ -3400,12 +3440,13 @@ u_clearall(buf_T *buf)
buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
buf->b_u_synced = TRUE;
buf->b_u_numhead = 0;
buf->b_u_line_ptr = NULL;
buf->b_u_line_ptr.ul_line = NULL;
buf->b_u_line_ptr.ul_len = 0;
buf->b_u_line_lnum = 0;
}
/*
* save the line "lnum" for the "U" command
* Save the line "lnum" for the "U" command.
*/
void
u_saveline(linenr_T lnum)
@@ -3420,7 +3461,7 @@ u_saveline(linenr_T lnum)
curbuf->b_u_line_colnr = curwin->w_cursor.col;
else
curbuf->b_u_line_colnr = 0;
if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
if (u_save_line(&curbuf->b_u_line_ptr, lnum) == FAIL)
do_outofmem_msg((long_u)0);
}
@@ -3431,9 +3472,10 @@ u_saveline(linenr_T lnum)
void
u_clearline(void)
{
if (curbuf->b_u_line_ptr != NULL)
if (curbuf->b_u_line_ptr.ul_line != NULL)
{
VIM_CLEAR(curbuf->b_u_line_ptr);
VIM_CLEAR(curbuf->b_u_line_ptr.ul_line);
curbuf->b_u_line_ptr.ul_len = 0;
curbuf->b_u_line_lnum = 0;
}
}
@@ -3447,32 +3489,30 @@ u_clearline(void)
void
u_undoline(void)
{
colnr_T t;
char_u *oldp;
colnr_T t;
undoline_T oldp;
if (undo_off)
return;
if (curbuf->b_u_line_ptr == NULL
if (curbuf->b_u_line_ptr.ul_line == NULL
|| curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
{
beep_flush();
return;
}
/* first save the line for the 'u' command */
// first save the line for the 'u' command
if (u_savecommon(curbuf->b_u_line_lnum - 1,
curbuf->b_u_line_lnum + 1, (linenr_T)0, FALSE) == FAIL)
return;
oldp = u_save_line(curbuf->b_u_line_lnum);
if (oldp == NULL)
if (u_save_line(&oldp, curbuf->b_u_line_lnum) == FAIL)
{
do_outofmem_msg((long_u)0);
return;
}
ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
ml_replace_len(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr.ul_line, curbuf->b_u_line_ptr.ul_len, TRUE, FALSE);
changed_bytes(curbuf->b_u_line_lnum, 0);
vim_free(curbuf->b_u_line_ptr);
curbuf->b_u_line_ptr = oldp;
t = curbuf->b_u_line_colnr;
@@ -3491,17 +3531,7 @@ u_blockfree(buf_T *buf)
{
while (buf->b_u_oldhead != NULL)
u_freeheader(buf, buf->b_u_oldhead, NULL);
vim_free(buf->b_u_line_ptr);
}
/*
* u_save_line(): allocate memory and copy line 'lnum' into it.
* Returns NULL when out of memory.
*/
static char_u *
u_save_line(linenr_T lnum)
{
return vim_strsave(ml_get(lnum));
vim_free(buf->b_u_line_ptr.ul_line);
}
/*

View File

@@ -799,6 +799,34 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
696,
/**/
695,
/**/
694,
/**/
693,
/**/
692,
/**/
691,
/**/
690,
/**/
689,
/**/
688,
/**/
687,
/**/
686,
/**/
685,
/**/
684,
/**/
683,
/**/
682,
/**/