Compare commits

...

7 Commits

Author SHA1 Message Date
5c65e6a062 patch 8.1.1337: get empty text prop when splitting line just after text prop
Problem:    Get empty text prop when splitting line just after text prop.
Solution:   Do not create an empty text prop at the start of the line.
2019-05-17 11:08:56 +02:00
17aca707f9 patch 8.1.1336: some eval functionality is not covered by tests
Problem:    Some eval functionality is not covered by tests.
Solution:   Add a few more test cases. (Masato Nishihata, closes #4374)
2019-05-16 22:24:55 +02:00
dda4144d39 patch 8.1.1335: listener callback is called after inserting text
Problem:    Listener callback is called after inserting text.
Solution:   Flush the changes before inserting or deleting a line.  Store
            changes per buffer.
2019-05-16 22:11:47 +02:00
eda652215a patch 8.1.1334: when buffer is hidden "F" in 'shortmess' is not used
Problem:    When buffer is hidden "F" in 'shortmess' is not used.
Solution:   Check the "F" flag in 'shortmess' when the buffer is already
            loaded. (Jason Franklin)  Add test_getvalue() to be able to test
            this.
2019-05-16 20:29:44 +02:00
45dd07f10a patch 8.1.1333: text properties don't always move after changes
Problem:    Text properties don't always move after changes.
Solution:   Update properties before reporting changes to listeners. Move text
            property when splitting a line.
2019-05-15 22:45:37 +02:00
fe1ade0a78 patch 8.1.1332: cannot flush listeners without redrawing, mix of changes
Problem:    Cannot flush change listeners without also redrawing.  The line
            numbers in the list of changes may become invalid.
Solution:   Add listener_flush().  Invoke listeners before adding a change
            that makes line numbers invalid.
2019-05-14 21:20:36 +02:00
fb222df28d patch 8.1.1331: test 29 is old style
Problem:    Test 29 is old style.
Solution:   Turn it into a new style test. (Yegappan Lakshmanan, closes #4370)
2019-05-14 17:57:19 +02:00
30 changed files with 980 additions and 415 deletions

View File

@ -2459,6 +2459,7 @@ lispindent({lnum}) Number Lisp indent for line {lnum}
list2str({list} [, {utf8}]) String turn numbers in {list} into a String list2str({list} [, {utf8}]) String turn numbers in {list} into a String
listener_add({callback} [, {buf}]) listener_add({callback} [, {buf}])
Number add a callback to listen to changes Number add a callback to listen to changes
listener_flush([{buf}]) none invoke listener callbacks
listener_remove({id}) none remove a listener callback listener_remove({id}) none remove a listener callback
localtime() Number current time localtime() Number current time
log({expr}) Float natural logarithm (base e) of {expr} log({expr}) Float natural logarithm (base e) of {expr}
@ -2700,6 +2701,7 @@ test_alloc_fail({id}, {countdown}, {repeat})
test_autochdir() none enable 'autochdir' during startup test_autochdir() none enable 'autochdir' during startup
test_feedinput({string}) none add key sequence to input buffer test_feedinput({string}) none add key sequence to input buffer
test_garbagecollect_now() none free memory right now for testing test_garbagecollect_now() none free memory right now for testing
test_getvalue({string}) any get value of an internal variable
test_ignore_error({expr}) none ignore a specific error test_ignore_error({expr}) none ignore a specific error
test_null_blob() Blob null value for testing test_null_blob() Blob null value for testing
test_null_channel() Channel null value for testing test_null_channel() Channel null value for testing
@ -6322,8 +6324,21 @@ listener_add({callback} [, {buf}]) *listener_add()*
buffer is used. buffer is used.
Returns a unique ID that can be passed to |listener_remove()|. Returns a unique ID that can be passed to |listener_remove()|.
The {callback} is invoked with a list of items that indicate a The {callback} is invoked with four arguments:
change. The list cannot be changed. Each list item is a a:bufnr the buffer that was changed
a:start first changed line number
a:end first line number below the change
a:added total number of lines added, negative if lines
were deleted
a:changes a List of items with details about the changes
Example: >
func Listener(bufnr, start, end, added, changes)
echo 'lines ' .. a:start .. ' until ' .. a:end .. ' changed'
endfunc
call listener_add('Listener', bufnr)
< The List cannot be changed. Each item in a:changes is a
dictionary with these entries: dictionary with these entries:
lnum the first line number of the change lnum the first line number of the change
end the first line below the change end the first line below the change
@ -6337,35 +6352,32 @@ listener_add({callback} [, {buf}]) *listener_add()*
lnum line below which the new line is added lnum line below which the new line is added
end equal to "lnum" end equal to "lnum"
added number of lines inserted added number of lines inserted
col one col 1
When lines are deleted the values are: When lines are deleted the values are:
lnum the first deleted line lnum the first deleted line
end the line below the first deleted line, before end the line below the first deleted line, before
the deletion was done the deletion was done
added negative, number of lines deleted added negative, number of lines deleted
col one col 1
When lines are changed: When lines are changed:
lnum the first changed line lnum the first changed line
end the line below the last changed line end the line below the last changed line
added zero added 0
col first column with a change or one col first column with a change or 1
The entries are in the order the changes was made, thus the The entries are in the order the changes were made, thus the
most recent change is at the end. One has to go through the most recent change is at the end. The line numbers are valid
list from end to start to compute the line numbers in the when the callback is invoked, but later changes may make them
current state of the text. invalid, thus keeping a copy for later might not work.
When using the same function for multiple buffers, you can The {callback} is invoked just before the screen is updated,
pass the buffer to that function using a |Partial|. when |listener_flush()| is called or when a change is being
Example: > made that changes the line count in a way it causes a line
func Listener(bufnr, changes) number in the list of changes to become invalid.
" ...
endfunc
let bufnr = ...
call listener_add(function('Listener', [bufnr]), bufnr)
< The {callback} is invoked just before the screen is updated. The {callback} is invoked with the text locked, see
To trigger this in a script use the `:redraw` command. |textlock|. If you do need to make changes to the buffer, use
a timer to do this later |timer_start()|.
The {callback} is not invoked when the buffer is first loaded. The {callback} is not invoked when the buffer is first loaded.
Use the |BufReadPost| autocmd event to handle the initial text Use the |BufReadPost| autocmd event to handle the initial text
@ -6373,6 +6385,14 @@ listener_add({callback} [, {buf}]) *listener_add()*
The {callback} is also not invoked when the buffer is The {callback} is also not invoked when the buffer is
unloaded, use the |BufUnload| autocmd event for that. unloaded, use the |BufUnload| autocmd event for that.
listener_flush([{buf}]) *listener_flush()*
Invoke listener callbacks for buffer {buf}. If there are no
pending changes then no callbacks are invoked.
{buf} refers to a buffer name or number. For the accepted
values, see |bufname()|. When {buf} is omitted the current
buffer is used.
listener_remove({id}) *listener_remove()* listener_remove({id}) *listener_remove()*
Remove a listener previously added with listener_add(). Remove a listener previously added with listener_add().
@ -9875,6 +9895,11 @@ test_garbagecollect_now() *test_garbagecollect_now()*
internally, and |v:testing| must have been set before calling internally, and |v:testing| must have been set before calling
any function. any function.
test_getvalue({name}) *test_getvalue()*
Get the value of an internal variable. These values for
{name} are supported:
need_fileinfo
test_ignore_error({expr}) *test_ignore_error()* test_ignore_error({expr}) *test_ignore_error()*
Ignore any error containing {expr}. A normal message is given Ignore any error containing {expr}. A normal message is given
instead. instead.

View File

@ -2179,7 +2179,7 @@ test_libvterm:
test1 \ test1 \
test_eval \ test_eval \
test3 \ test3 \
test29 test30 test37 test39 \ test30 test37 test39 \
test42 test44 test48 test49 \ test42 test44 test48 test49 \
test52 test59 \ test52 test59 \
test64 test69 \ test64 test69 \

View File

@ -1742,9 +1742,12 @@ enter_buffer(buf_T *buf)
} }
else else
{ {
if (!msg_silent) if (!msg_silent && !shortmess(SHM_FILEINFO))
need_fileinfo = TRUE; /* display file info after redraw */ need_fileinfo = TRUE; // display file info after redraw
(void)buf_check_timestamp(curbuf, FALSE); /* check if file changed */
// check if file changed
(void)buf_check_timestamp(curbuf, FALSE);
curwin->w_topline = 1; curwin->w_topline = 1;
#ifdef FEAT_DIFF #ifdef FEAT_DIFF
curwin->w_topfill = 0; curwin->w_topfill = 0;

View File

@ -152,11 +152,72 @@ changed_internal(void)
} }
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
static list_T *recorded_changes = NULL;
static long next_listener_id = 0; static long next_listener_id = 0;
/*
* Check if the change at "lnum" / "col" is above or overlaps with an existing
* changed. If above then flush changes and invoke listeners.
* If "merge" is TRUE do the merge.
* Returns TRUE if the change was merged.
*/
static int
check_recorded_changes(
buf_T *buf,
linenr_T lnum,
colnr_T col,
linenr_T lnume,
long xtra,
int merge)
{
if (buf->b_recorded_changes != NULL && xtra != 0)
{
listitem_T *li;
linenr_T nr;
for (li = buf->b_recorded_changes->lv_first; li != NULL;
li = li->li_next)
{
nr = (linenr_T)dict_get_number(
li->li_tv.vval.v_dict, (char_u *)"lnum");
if (nr >= lnum || nr > lnume)
{
if (li->li_next == NULL && lnum == nr
&& col + 1 == (colnr_T)dict_get_number(
li->li_tv.vval.v_dict, (char_u *)"col"))
{
if (merge)
{
dictitem_T *di;
// Same start point and nothing is following, entries
// can be merged.
di = dict_find(li->li_tv.vval.v_dict,
(char_u *)"end", -1);
nr = tv_get_number(&di->di_tv);
if (lnume > nr)
di->di_tv.vval.v_number = lnume;
di = dict_find(li->li_tv.vval.v_dict,
(char_u *)"added", -1);
di->di_tv.vval.v_number += xtra;
return TRUE;
}
}
else
{
// the current change is going to make the line number in
// the older change invalid, flush now
invoke_listeners(curbuf);
break;
}
}
}
}
return FALSE;
}
/* /*
* Record a change for listeners added with listener_add(). * Record a change for listeners added with listener_add().
* Always for the current buffer.
*/ */
static void static void
may_record_change( may_record_change(
@ -169,13 +230,19 @@ may_record_change(
if (curbuf->b_listener == NULL) if (curbuf->b_listener == NULL)
return; return;
if (recorded_changes == NULL)
// If the new change is going to change the line numbers in already listed
// changes, then flush.
if (check_recorded_changes(curbuf, lnum, col, lnume, xtra, TRUE))
return;
if (curbuf->b_recorded_changes == NULL)
{ {
recorded_changes = list_alloc(); curbuf->b_recorded_changes = list_alloc();
if (recorded_changes == NULL) // out of memory if (curbuf->b_recorded_changes == NULL) // out of memory
return; return;
++recorded_changes->lv_refcount; ++curbuf->b_recorded_changes->lv_refcount;
recorded_changes->lv_lock = VAR_FIXED; curbuf->b_recorded_changes->lv_lock = VAR_FIXED;
} }
dict = dict_alloc(); dict = dict_alloc();
@ -186,7 +253,7 @@ may_record_change(
dict_add_number(dict, "added", (varnumber_T)xtra); dict_add_number(dict, "added", (varnumber_T)xtra);
dict_add_number(dict, "col", (varnumber_T)col + 1); dict_add_number(dict, "col", (varnumber_T)col + 1);
list_append_dict(recorded_changes, dict); list_append_dict(curbuf->b_recorded_changes, dict);
} }
/* /*
@ -230,6 +297,23 @@ f_listener_add(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = lnr->lr_id; rettv->vval.v_number = lnr->lr_id;
} }
/*
* listener_flush() function
*/
void
f_listener_flush(typval_T *argvars, typval_T *rettv UNUSED)
{
buf_T *buf = curbuf;
if (argvars[0].v_type != VAR_UNKNOWN)
{
buf = get_buf_arg(&argvars[0]);
if (buf == NULL)
return;
}
invoke_listeners(buf);
}
/* /*
* listener_remove() function * listener_remove() function
*/ */
@ -259,32 +343,73 @@ f_listener_remove(typval_T *argvars, typval_T *rettv UNUSED)
} }
} }
/*
* Called before inserting a line above "lnum"/"lnum3" or deleting line "lnum"
* to "lnume".
*/
void
may_invoke_listeners(buf_T *buf, linenr_T lnum, linenr_T lnume, int added)
{
check_recorded_changes(buf, lnum, 0, lnume, added, FALSE);
}
/* /*
* Called when a sequence of changes is done: invoke listeners added with * Called when a sequence of changes is done: invoke listeners added with
* listener_add(). * listener_add().
*/ */
void void
invoke_listeners(void) invoke_listeners(buf_T *buf)
{ {
listener_T *lnr; listener_T *lnr;
typval_T rettv; typval_T rettv;
int dummy; int dummy;
typval_T argv[2]; typval_T argv[6];
listitem_T *li;
linenr_T start = MAXLNUM;
linenr_T end = 0;
linenr_T added = 0;
if (recorded_changes == NULL) // nothing changed if (buf->b_recorded_changes == NULL // nothing changed
|| buf->b_listener == NULL) // no listeners
return; return;
argv[0].v_type = VAR_LIST;
argv[0].vval.v_list = recorded_changes;
for (lnr = curbuf->b_listener; lnr != NULL; lnr = lnr->lr_next) argv[0].v_type = VAR_NUMBER;
argv[0].vval.v_number = buf->b_fnum; // a:bufnr
for (li = buf->b_recorded_changes->lv_first; li != NULL; li = li->li_next)
{
varnumber_T lnum;
lnum = dict_get_number(li->li_tv.vval.v_dict, (char_u *)"lnum");
if (start > lnum)
start = lnum;
lnum = dict_get_number(li->li_tv.vval.v_dict, (char_u *)"end");
if (lnum > end)
end = lnum;
added = dict_get_number(li->li_tv.vval.v_dict, (char_u *)"added");
}
argv[1].v_type = VAR_NUMBER;
argv[1].vval.v_number = start;
argv[2].v_type = VAR_NUMBER;
argv[2].vval.v_number = end;
argv[3].v_type = VAR_NUMBER;
argv[3].vval.v_number = added;
argv[4].v_type = VAR_LIST;
argv[4].vval.v_list = buf->b_recorded_changes;
++textlock;
for (lnr = buf->b_listener; lnr != NULL; lnr = lnr->lr_next)
{ {
call_func(lnr->lr_callback, -1, &rettv, call_func(lnr->lr_callback, -1, &rettv,
1, argv, NULL, 0L, 0L, &dummy, TRUE, lnr->lr_partial, NULL); 5, argv, NULL, 0L, 0L, &dummy, TRUE, lnr->lr_partial, NULL);
clear_tv(&rettv); clear_tv(&rettv);
} }
list_unref(recorded_changes); --textlock;
recorded_changes = NULL; list_unref(buf->b_recorded_changes);
buf->b_recorded_changes = NULL;
} }
#endif #endif
@ -553,12 +678,12 @@ changed_bytes(linenr_T lnum, colnr_T col)
void void
inserted_bytes(linenr_T lnum, colnr_T col, int added UNUSED) inserted_bytes(linenr_T lnum, colnr_T col, int added UNUSED)
{ {
changed_bytes(lnum, col);
#ifdef FEAT_TEXT_PROP #ifdef FEAT_TEXT_PROP
if (curbuf->b_has_textprop && added != 0) if (curbuf->b_has_textprop && added != 0)
adjust_prop_columns(lnum, col, added); adjust_prop_columns(lnum, col, added);
#endif #endif
changed_bytes(lnum, col);
} }
/* /*
@ -2045,6 +2170,12 @@ open_line(
) )
mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1L, 0L); mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1L, 0L);
did_append = TRUE; did_append = TRUE;
#ifdef FEAT_TEXT_PROP
if ((State & INSERT) && !(State & VREPLACE_FLAG))
// properties after the split move to the next line
adjust_props_for_split(curwin->w_cursor.lnum, curwin->w_cursor.lnum,
curwin->w_cursor.col + 1, 0);
#endif
} }
else else
{ {

View File

@ -442,6 +442,7 @@ static void f_tempname(typval_T *argvars, typval_T *rettv);
static void f_test_alloc_fail(typval_T *argvars, typval_T *rettv); static void f_test_alloc_fail(typval_T *argvars, typval_T *rettv);
static void f_test_autochdir(typval_T *argvars, typval_T *rettv); static void f_test_autochdir(typval_T *argvars, typval_T *rettv);
static void f_test_feedinput(typval_T *argvars, typval_T *rettv); static void f_test_feedinput(typval_T *argvars, typval_T *rettv);
static void f_test_getvalue(typval_T *argvars, typval_T *rettv);
static void f_test_option_not_set(typval_T *argvars, typval_T *rettv); static void f_test_option_not_set(typval_T *argvars, typval_T *rettv);
static void f_test_override(typval_T *argvars, typval_T *rettv); static void f_test_override(typval_T *argvars, typval_T *rettv);
static void f_test_refcount(typval_T *argvars, typval_T *rettv); static void f_test_refcount(typval_T *argvars, typval_T *rettv);
@ -768,6 +769,7 @@ static struct fst
{"lispindent", 1, 1, f_lispindent}, {"lispindent", 1, 1, f_lispindent},
{"list2str", 1, 2, f_list2str}, {"list2str", 1, 2, f_list2str},
{"listener_add", 1, 2, f_listener_add}, {"listener_add", 1, 2, f_listener_add},
{"listener_flush", 0, 1, f_listener_flush},
{"listener_remove", 1, 1, f_listener_remove}, {"listener_remove", 1, 1, f_listener_remove},
{"localtime", 0, 0, f_localtime}, {"localtime", 0, 0, f_localtime},
#ifdef FEAT_FLOAT #ifdef FEAT_FLOAT
@ -990,6 +992,7 @@ static struct fst
{"test_autochdir", 0, 0, f_test_autochdir}, {"test_autochdir", 0, 0, f_test_autochdir},
{"test_feedinput", 1, 1, f_test_feedinput}, {"test_feedinput", 1, 1, f_test_feedinput},
{"test_garbagecollect_now", 0, 0, f_test_garbagecollect_now}, {"test_garbagecollect_now", 0, 0, f_test_garbagecollect_now},
{"test_getvalue", 1, 1, f_test_getvalue},
{"test_ignore_error", 1, 1, f_test_ignore_error}, {"test_ignore_error", 1, 1, f_test_ignore_error},
{"test_null_blob", 0, 0, f_test_null_blob}, {"test_null_blob", 0, 0, f_test_null_blob},
#ifdef FEAT_JOB_CHANNEL #ifdef FEAT_JOB_CHANNEL
@ -14411,6 +14414,25 @@ f_test_feedinput(typval_T *argvars, typval_T *rettv UNUSED)
#endif #endif
} }
/*
* "test_getvalue({name})" function
*/
static void
f_test_getvalue(typval_T *argvars, typval_T *rettv)
{
if (argvars[0].v_type != VAR_STRING)
emsg(_(e_invarg));
else
{
char_u *name = tv_get_string(&argvars[0]);
if (STRCMP(name, (char_u *)"need_fileinfo") == 0)
rettv->vval.v_number = need_fileinfo;
else
semsg(_(e_invarg2), name);
}
}
/* /*
* "test_option_not_set({name})" function * "test_option_not_set({name})" function
*/ */

View File

@ -5728,7 +5728,7 @@ do_sub(exarg_T *eap)
last_line = lnum + 1; last_line = lnum + 1;
} }
#ifdef FEAT_TEXT_PROP #ifdef FEAT_TEXT_PROP
adjust_props_for_split(lnum, plen, 1); adjust_props_for_split(lnum + 1, lnum, plen, 1);
#endif #endif
// all line numbers increase // all line numbers increase
++sub_firstlnum; ++sub_firstlnum;

View File

@ -2790,6 +2790,12 @@ ml_append_int(
if (len == 0) if (len == 0)
len = (colnr_T)STRLEN(line) + 1; // space needed for the text len = (colnr_T)STRLEN(line) + 1; // space needed for the text
#ifdef FEAT_EVAL
// When inserting above recorded changes: flush the changes before changing
// the text.
may_invoke_listeners(buf, lnum + 1, lnum + 1, 1);
#endif
#ifdef FEAT_TEXT_PROP #ifdef FEAT_TEXT_PROP
if (curbuf->b_has_textprop && lnum > 0) if (curbuf->b_has_textprop && lnum > 0)
// Add text properties that continue from the previous line. // Add text properties that continue from the previous line.
@ -3526,6 +3532,11 @@ ml_delete_int(buf_T *buf, linenr_T lnum, int message)
if (lnum < 1 || lnum > buf->b_ml.ml_line_count) if (lnum < 1 || lnum > buf->b_ml.ml_line_count)
return FAIL; return FAIL;
#ifdef FEAT_EVAL
// When inserting above recorded changes: flush the changes before changing
// the text.
may_invoke_listeners(buf, lnum, lnum + 1, -1);
#endif
if (lowest_marked && lowest_marked > lnum) if (lowest_marked && lowest_marked > lnum)
lowest_marked--; lowest_marked--;

View File

@ -3,8 +3,10 @@ void change_warning(int col);
void changed(void); void changed(void);
void changed_internal(void); void changed_internal(void);
void f_listener_add(typval_T *argvars, typval_T *rettv); void f_listener_add(typval_T *argvars, typval_T *rettv);
void f_listener_flush(typval_T *argvars, typval_T *rettv);
void f_listener_remove(typval_T *argvars, typval_T *rettv); void f_listener_remove(typval_T *argvars, typval_T *rettv);
void invoke_listeners(void); void may_invoke_listeners(buf_T *buf, linenr_T lnum, linenr_T lnume, int added);
void invoke_listeners(buf_T *buf);
void changed_bytes(linenr_T lnum, colnr_T col); void changed_bytes(linenr_T lnum, colnr_T col);
void inserted_bytes(linenr_T lnum, colnr_T col, int added); void inserted_bytes(linenr_T lnum, colnr_T col, int added);
void appended_lines(linenr_T lnum, long count); void appended_lines(linenr_T lnum, long count);

View File

@ -14,5 +14,5 @@ void f_prop_type_list(typval_T *argvars, typval_T *rettv);
void clear_global_prop_types(void); void clear_global_prop_types(void);
void clear_buf_prop_types(buf_T *buf); void clear_buf_prop_types(buf_T *buf);
void adjust_prop_columns(linenr_T lnum, colnr_T col, int bytes_added); 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); void adjust_props_for_split(linenr_T lnum_props, linenr_T lnum_top, int kept, int deleted);
/* vim: set ft=c : */ /* vim: set ft=c : */

View File

@ -565,8 +565,13 @@ update_screen(int type_arg)
} }
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
// Before updating the screen, notify any listeners of changed text. {
invoke_listeners(); buf_T *buf;
// Before updating the screen, notify any listeners of changed text.
FOR_ALL_BUFFERS(buf)
invoke_listeners(buf);
}
#endif #endif
if (must_redraw) if (must_redraw)

View File

@ -2439,6 +2439,7 @@ struct file_buffer
dict_T *b_vars; /* internal variables, local to buffer */ dict_T *b_vars; /* internal variables, local to buffer */
listener_T *b_listener; listener_T *b_listener;
list_T *b_recorded_changes;
#endif #endif
#ifdef FEAT_TEXT_PROP #ifdef FEAT_TEXT_PROP
int b_has_textprop; // TRUE when text props were added int b_has_textprop; // TRUE when text props were added

View File

@ -14,7 +14,6 @@ SCRIPTS_FIRST = \
# Tests that run on all systems. # Tests that run on all systems.
SCRIPTS_ALL = \ SCRIPTS_ALL = \
test3.out \ test3.out \
test29.out \
test37.out \ test37.out \
test39.out \ test39.out \
test42.out \ test42.out \

View File

@ -74,7 +74,6 @@ VIMPROG = <->vim.exe
.SUFFIXES : .out .in .SUFFIXES : .out .in
SCRIPT = test1.out test3.out \ SCRIPT = test1.out test3.out \
test29.out \
test30.out test37.out test39.out \ test30.out test37.out test39.out \
test42.out test44.out test48.out test49.out \ test42.out test44.out test48.out test49.out \
test64.out test69.out \ test64.out test69.out \

View File

@ -1,231 +0,0 @@
Test for joining lines and marks in them
in compatible and nocompatible modes
and with 'joinspaces' set or not
and with 'cpoptions' flag 'j' set or not
STARTTEST
:so small.vim
:set nocompatible viminfo+=nviminfo
:set nojoinspaces
:set cpoptions-=j
/firstline/
j"td/^STARTTEST/-1
PJjJjJjJjJjJjJjJjJjJjJjJjJjJj05lmx2j06lmy2k4Jy3l$p`xyl$p`yy2l$p:set cpoptions+=j
j05lmx2j06lmy2k4Jy3l$p`xyl$p`yy2l$p:set cpoptions-=j joinspaces
j"tpJjJjJjJjJjJjJjJjJjJjJjJjJjJj05lmx2j06lmy2k4Jy3l$p`xyl$p`yy2l$p:set cpoptions+=j
j05lmx2j06lmy2k4Jy3l$p`xyl$p`yy2l$p:set cpoptions-=j nojoinspaces compatible
j"tpJjJjJjJjJjJjJjJjJjJjJjJjJjJj4Jy3l$pjd/STARTTEST/-2
ENDTEST
firstline
asdfasdf.
asdf
asdfasdf.
asdf
asdfasdf.
asdf
asdfasdf.
asdf
asdfasdf.
asdf
asdfasdf.
asdf
asdfasdf.
asdf
asdfasdf
asdf
asdfasdf
asdf
asdfasdf
asdf
asdfasdf
asdf
asdfasdf
asdf
asdfasdf
asdf
asdfasdf
asdf
zx cvn.
as dfg?
hjkl iop!
ert
zx cvn.
as dfg?
hjkl iop!
ert
STARTTEST
/^{/+1
:set comments=s1:/*,mb:*,ex:*/,://
:set nojoinspaces fo=j
:set backspace=eol,start
:.,+3join
j4J
:.,+2join
j3J
:.,+2join
j3J
:.,+2join
jj3J
ENDTEST
{
/*
* Make sure the previous comment leader is not removed.
*/
/*
* Make sure the previous comment leader is not removed.
*/
// Should the next comment leader be left alone?
// Yes.
// Should the next comment leader be left alone?
// Yes.
/* Here the comment leader should be left intact. */
// And so should this one.
/* Here the comment leader should be left intact. */
// And so should this one.
if (condition) // Remove the next comment leader!
// OK, I will.
action();
if (condition) // Remove the next comment leader!
// OK, I will.
action();
}
STARTTEST
:" Test with backspace set to the non-compatible setting
:set belloff=all
/^\d\+ this
:set cp bs=2
Avim1
Avim2u
:set cpo-=<
:inoremap <c-u> <left><c-u>
Avim3
:iunmap <c-u>
Avim4
:" Test with backspace set to the compatible setting
:set backspace= visualbell
A vim5A
A vim6Azweiu
:inoremap <c-u> <left><c-u>
A vim7
:set compatible novisualbell
ENDTEST
1 this shouldn't be deleted
2 this shouldn't be deleted
3 this shouldn't be deleted
4 this should be deleted
5 this shouldn't be deleted
6 this shouldn't be deleted
7 this shouldn't be deleted
8 this shouldn't be deleted (not touched yet)
STARTTEST
/^{/+1
:set comments=sO:*\ -,mO:*\ \ ,exO:*/
:set comments+=s1:/*,mb:*,ex:*/,://
:set comments+=s1:>#,mb:#,ex:#<,:<
:set cpoptions-=j joinspaces fo=j
:set backspace=eol,start
:.,+3join
j4J
:.,+8join
j9J
:.,+2join
j3J
:.,+2join
j3J
:.,+2join
jj3J
j:.,+2join
jj3J
j:.,+5join
j6J
oSome code!
// Make sure backspacing does not remove this comment leader.0i
ENDTEST
{
/*
* Make sure the previous comment leader is not removed.
*/
/*
* Make sure the previous comment leader is not removed.
*/
/* List:
* - item1
* foo bar baz
* foo bar baz
* - item2
* foo bar baz
* foo bar baz
*/
/* List:
* - item1
* foo bar baz
* foo bar baz
* - item2
* foo bar baz
* foo bar baz
*/
// Should the next comment leader be left alone?
// Yes.
// Should the next comment leader be left alone?
// Yes.
/* Here the comment leader should be left intact. */
// And so should this one.
/* Here the comment leader should be left intact. */
// And so should this one.
if (condition) // Remove the next comment leader!
// OK, I will.
action();
if (condition) // Remove the next comment leader!
// OK, I will.
action();
int i = 7 /* foo *// 3
// comment
;
int i = 7 /* foo *// 3
// comment
;
># Note that the last character of the ending comment leader (left angle
# bracket) is a comment leader itself. Make sure that this comment leader is
# not removed from the next line #<
< On this line a new comment is opened which spans 2 lines. This comment should
< retain its comment leader.
># Note that the last character of the ending comment leader (left angle
# bracket) is a comment leader itself. Make sure that this comment leader is
# not removed from the next line #<
< On this line a new comment is opened which spans 2 lines. This comment should
< retain its comment leader.
}
STARTTEST
:g/^STARTTEST/.,/^ENDTEST/d
:?firstline?+1,$w! test.out
:qa!

View File

@ -1,97 +0,0 @@
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
zx cvn. as dfg? hjkl iop! ert ernop
zx cvn. as dfg? hjkl iop! ert ernop
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
zx cvn. as dfg? hjkl iop! ert enop
zx cvn. as dfg? hjkl iop! ert ernop
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
zx cvn. as dfg? hjkl iop! ert a
{
/* Make sure the previous comment leader is not removed. */
/* Make sure the previous comment leader is not removed. */
// Should the next comment leader be left alone? Yes.
// Should the next comment leader be left alone? Yes.
/* Here the comment leader should be left intact. */ // And so should this one.
/* Here the comment leader should be left intact. */ // And so should this one.
if (condition) // Remove the next comment leader! OK, I will.
action();
if (condition) // Remove the next comment leader! OK, I will.
action();
}
1 this shouldn't be deleted
2 this shouldn't be deleted
3 this shouldn't be deleted
4 this should be deleted3
6 this shouldn't be deleted vim5
7 this shouldn't be deleted vim6
8 this shouldn't be deleted (not touched yet) vim7
{
/* Make sure the previous comment leader is not removed. */
/* Make sure the previous comment leader is not removed. */
/* List: item1 foo bar baz foo bar baz item2 foo bar baz foo bar baz */
/* List: item1 foo bar baz foo bar baz item2 foo bar baz foo bar baz */
// Should the next comment leader be left alone? Yes.
// Should the next comment leader be left alone? Yes.
/* Here the comment leader should be left intact. */ // And so should this one.
/* Here the comment leader should be left intact. */ // And so should this one.
if (condition) // Remove the next comment leader! OK, I will.
action();
if (condition) // Remove the next comment leader! OK, I will.
action();
int i = 7 /* foo *// 3 // comment
;
int i = 7 /* foo *// 3 // comment
;
># Note that the last character of the ending comment leader (left angle bracket) is a comment leader itself. Make sure that this comment leader is not removed from the next line #< < On this line a new comment is opened which spans 2 lines. This comment should retain its comment leader.
># Note that the last character of the ending comment leader (left angle bracket) is a comment leader itself. Make sure that this comment leader is not removed from the next line #< < On this line a new comment is opened which spans 2 lines. This comment should retain its comment leader.
Some code!// Make sure backspacing does not remove this comment leader.
}

View File

@ -56,4 +56,55 @@ func Test_backspace_option()
set nocompatible viminfo+=nviminfo set nocompatible viminfo+=nviminfo
endfunc endfunc
" Test with backspace set to the non-compatible setting
func Test_backspace_ctrl_u()
new
call append(0, [
\ "1 this shouldn't be deleted",
\ "2 this shouldn't be deleted",
\ "3 this shouldn't be deleted",
\ "4 this should be deleted",
\ "5 this shouldn't be deleted",
\ "6 this shouldn't be deleted",
\ "7 this shouldn't be deleted",
\ "8 this shouldn't be deleted (not touched yet)"])
call cursor(2, 1)
set compatible
set backspace=2
exe "normal Avim1\<C-U>\<Esc>\<CR>"
exe "normal Avim2\<C-G>u\<C-U>\<Esc>\<CR>"
set cpo-=<
inoremap <c-u> <left><c-u>
exe "normal Avim3\<C-U>\<Esc>\<CR>"
iunmap <c-u>
exe "normal Avim4\<C-U>\<C-U>\<Esc>\<CR>"
" Test with backspace set to the compatible setting
set backspace= visualbell
exe "normal A vim5\<Esc>A\<C-U>\<C-U>\<Esc>\<CR>"
exe "normal A vim6\<Esc>Azwei\<C-G>u\<C-U>\<Esc>\<CR>"
inoremap <c-u> <left><c-u>
exe "normal A vim7\<C-U>\<C-U>\<Esc>\<CR>"
call assert_equal([
\ "1 this shouldn't be deleted",
\ "2 this shouldn't be deleted",
\ "3 this shouldn't be deleted",
\ "4 this should be deleted3",
\ "",
\ "6 this shouldn't be deleted vim5",
\ "7 this shouldn't be deleted vim6",
\ "8 this shouldn't be deleted (not touched yet) vim7",
\ ""], getline(1, '$'))
set compatible&vim
set visualbell&vim
set backspace&vim
close!
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -8,7 +8,7 @@ func Test_setbufline_getbufline()
hide hide
call assert_equal(0, setbufline(b, 1, ['foo', 'bar'])) call assert_equal(0, setbufline(b, 1, ['foo', 'bar']))
call assert_equal(['foo'], getbufline(b, 1)) call assert_equal(['foo'], getbufline(b, 1))
call assert_equal(['bar'], getbufline(b, 2)) call assert_equal(['bar'], getbufline(b, '$'))
call assert_equal(['foo', 'bar'], getbufline(b, 1, 2)) call assert_equal(['foo', 'bar'], getbufline(b, 1, 2))
exe "bd!" b exe "bd!" b
call assert_equal([], getbufline(b, 1, 2)) call assert_equal([], getbufline(b, 1, 2))
@ -81,6 +81,7 @@ func Test_appendbufline()
call setline(1, ['a', 'b', 'c']) call setline(1, ['a', 'b', 'c'])
let b = bufnr('%') let b = bufnr('%')
wincmd w wincmd w
call assert_equal(1, appendbufline(b, -1, ['x']))
call assert_equal(1, appendbufline(b, 4, ['x'])) call assert_equal(1, appendbufline(b, 4, ['x']))
call assert_equal(1, appendbufline(1234, 1, ['x'])) call assert_equal(1, appendbufline(1234, 1, ['x']))
call assert_equal(0, appendbufline(b, 3, ['d', 'e'])) call assert_equal(0, appendbufline(b, 3, ['d', 'e']))
@ -130,8 +131,11 @@ func Test_deletebufline()
exe "bd!" b exe "bd!" b
call assert_equal(1, deletebufline(b, 1)) call assert_equal(1, deletebufline(b, 1))
call assert_equal(1, deletebufline(-1, 1))
split Xtest split Xtest
call setline(1, ['a', 'b', 'c']) call setline(1, ['a', 'b', 'c'])
call cursor(line('$'), 1)
let b = bufnr('%') let b = bufnr('%')
wincmd w wincmd w
call assert_equal(1, deletebufline(b, 4)) call assert_equal(1, deletebufline(b, 4))

View File

@ -102,4 +102,14 @@ func Test_cindent_expr()
bw! bw!
endfunc endfunc
func Test_cindent_func()
new
setlocal cindent
call setline(1, ['int main(void)', '{', 'return 0;', '}'])
call assert_equal(cindent(0), -1)
call assert_equal(cindent(3), &sw)
call assert_equal(cindent(line('$')+1), -1)
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -25,6 +25,12 @@ func Test_move_cursor()
call cursor(9, 1) call cursor(9, 1)
call assert_equal([4, 1, 0, 1], getcurpos()[1:]) call assert_equal([4, 1, 0, 1], getcurpos()[1:])
call setline(1, ["\<TAB>"])
call cursor(1, 1, 1)
call assert_equal([1, 1, 1], getcurpos()[1:3])
call assert_equal(-1, cursor(-1, -1))
quit! quit!
endfunc endfunc

View File

@ -105,3 +105,8 @@ func Test_symlink_recursive_delete()
bwipe Xdir3/subdir/Xfile bwipe Xdir3/subdir/Xfile
bwipe Xdir4/Xfile bwipe Xdir4/Xfile
endfunc endfunc
func Test_delete_errors()
call assert_fails('call delete('''')', 'E474:')
call assert_fails('call delete(''foo'', 0)', 'E15:')
endfunc

View File

@ -64,3 +64,12 @@ func Test_expand_sflnum()
call assert_equal(64, str2nr(trim(execute('Flnum')))) call assert_equal(64, str2nr(trim(execute('Flnum'))))
delcommand Flnum delcommand Flnum
endfunc endfunc
func Test_expand()
new
call assert_equal("", expand('%:S'))
call assert_equal('3', expand('<slnum>'))
call assert_equal(['4'], expand('<slnum>', v:false, v:true))
" Don't add any line above this, otherwise <slnum> will change.
quit
endfunc

View File

@ -13,6 +13,7 @@ func Test_abs()
call assert_equal('inf', string(abs(1.0/0.0))) call assert_equal('inf', string(abs(1.0/0.0)))
call assert_equal('inf', string(abs(-1.0/0.0))) call assert_equal('inf', string(abs(-1.0/0.0)))
call assert_equal('nan', string(abs(0.0/0.0))) call assert_equal('nan', string(abs(0.0/0.0)))
call assert_equal('12', string(abs('12abc')))
call assert_equal('12', string(abs('-12abc'))) call assert_equal('12', string(abs('-12abc')))
call assert_fails("call abs([])", 'E745:') call assert_fails("call abs([])", 'E745:')
call assert_fails("call abs({})", 'E728:') call assert_fails("call abs({})", 'E728:')

View File

@ -45,9 +45,3 @@ func Test_fnamemodify()
let $HOME = save_home let $HOME = save_home
let &shell = save_shell let &shell = save_shell
endfunc endfunc
func Test_expand()
new
call assert_equal("", expand('%:S'))
quit
endfunc

View File

@ -52,6 +52,7 @@ func Test_empty()
endif endif
call assert_equal(0, empty(function('Test_empty'))) call assert_equal(0, empty(function('Test_empty')))
call assert_equal(0, empty(function('Test_empty', [0])))
endfunc endfunc
func Test_len() func Test_len()
@ -869,6 +870,7 @@ func Test_count()
call assert_equal(1, count(l, 'a', 0, 1)) call assert_equal(1, count(l, 'a', 0, 1))
call assert_equal(2, count(l, 'a', 1, 1)) call assert_equal(2, count(l, 'a', 1, 1))
call assert_fails('call count(l, "a", 0, 10)', 'E684:') call assert_fails('call count(l, "a", 0, 10)', 'E684:')
call assert_fails('call count(l, "a", [])', 'E745:')
let d = {1: 'a', 2: 'a', 3: 'A', 4: 'b'} let d = {1: 'a', 2: 'a', 3: 'A', 4: 'b'}
call assert_equal(2, count(d, 'a')) call assert_equal(2, count(d, 'a'))
@ -896,6 +898,8 @@ func Test_count()
call assert_equal(2, count("foo", "O", 1)) call assert_equal(2, count("foo", "O", 1))
call assert_equal(2, count("fooooo", "oo")) call assert_equal(2, count("fooooo", "oo"))
call assert_equal(0, count("foo", "")) call assert_equal(0, count("foo", ""))
call assert_fails('call count(0, 0)', 'E712:')
endfunc endfunc
func Test_changenr() func Test_changenr()
@ -1431,3 +1435,23 @@ func Test_readdir()
call delete('Xdir', 'rf') call delete('Xdir', 'rf')
endfunc endfunc
func Test_call()
call assert_equal(3, call('len', [123]))
call assert_fails("call call('len', 123)", 'E714:')
call assert_equal(0, call('', []))
function Mylen() dict
return len(self.data)
endfunction
let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")}
call assert_fails("call call('Mylen', [], 0)", 'E715:')
endfunc
func Test_char2nr()
call assert_equal(12354, char2nr('あ', 1))
endfunc
func Test_eventhandler()
call assert_equal(0, eventhandler())
endfunc

View File

@ -33,3 +33,391 @@ func Test_join_marks()
call assert_equal([0, 4, 67, 0], getpos("']")) call assert_equal([0, 4, 67, 0], getpos("']"))
enew! enew!
endfunc endfunc
" Test for joining lines and marks in them
" in compatible and nocompatible modes
" and with 'joinspaces' set or not
" and with 'cpoptions' flag 'j' set or not
func Test_join_spaces_marks()
new
" Text used for the test
insert
asdfasdf.
asdf
asdfasdf.
asdf
asdfasdf.
asdf
asdfasdf.
asdf
asdfasdf.
asdf
asdfasdf.
asdf
asdfasdf.
asdf
asdfasdf
asdf
asdfasdf
asdf
asdfasdf
asdf
asdfasdf
asdf
asdfasdf
asdf
asdfasdf
asdf
asdfasdf
asdf
zx cvn.
as dfg?
hjkl iop!
ert
zx cvn.
as dfg?
hjkl iop!
ert
.
let text = getline(1, '$')
normal gg
set nojoinspaces
set cpoptions-=j
normal JjJjJjJjJjJjJjJjJjJjJjJjJjJ
normal j05lmx
normal 2j06lmy
normal 2k4Jy3l$p
normal `xyl$p
normal `yy2l$p
set cpoptions+=j
normal j05lmx
normal 2j06lmy
normal 2k4Jy3l$p
normal `xyl$p
normal `yy2l$p
normal G
let last_line = line('$')
" Expected output
append
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
zx cvn. as dfg? hjkl iop! ert ernop
zx cvn. as dfg? hjkl iop! ert ernop
.
call assert_equal(getline(last_line + 1, '$'), getline(1, last_line))
enew!
call append(0, text)
normal gg
set cpoptions-=j
set joinspaces
normal JjJjJjJjJjJjJjJjJjJjJjJjJjJ
normal j05lmx
normal 2j06lmy
normal 2k4Jy3l$p
normal `xyl$p
normal `yy2l$p
set cpoptions+=j
normal j05lmx
normal 2j06lmy
normal 2k4Jy3l$p
normal `xyl$p
normal `yy2l$p
normal G
let last_line = line('$')
" Expected output
append
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
zx cvn. as dfg? hjkl iop! ert enop
zx cvn. as dfg? hjkl iop! ert ernop
.
call assert_equal(getline(last_line + 1, '$'), getline(1, last_line))
enew!
call append(0, text)
normal gg
set cpoptions-=j
set nojoinspaces
set compatible
normal JjJjJjJjJjJjJjJjJjJjJjJjJjJ
normal j4Jy3l$pjdG
normal G
let last_line = line('$')
" Expected output
append
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf. asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
asdfasdf asdf
zx cvn. as dfg? hjkl iop! ert a
.
call assert_equal(getline(last_line + 1, '$'), getline(1, last_line))
set nocompatible
set cpoptions&vim
set joinspaces&vim
close!
endfunc
" Test for joining lines with comments
func Test_join_lines_with_comments()
new
" Text used by the test
insert
{
/*
* Make sure the previous comment leader is not removed.
*/
/*
* Make sure the previous comment leader is not removed.
*/
// Should the next comment leader be left alone?
// Yes.
// Should the next comment leader be left alone?
// Yes.
/* Here the comment leader should be left intact. */
// And so should this one.
/* Here the comment leader should be left intact. */
// And so should this one.
if (condition) // Remove the next comment leader!
// OK, I will.
action();
if (condition) // Remove the next comment leader!
// OK, I will.
action();
}
.
call cursor(2, 1)
set comments=s1:/*,mb:*,ex:*/,://
set nojoinspaces fo=j
set backspace=eol,start
.,+3join
exe "normal j4J\<CR>"
.,+2join
exe "normal j3J\<CR>"
.,+2join
exe "normal j3J\<CR>"
.,+2join
exe "normal jj3J\<CR>"
normal G
let last_line = line('$')
" Expected output
append
{
/* Make sure the previous comment leader is not removed. */
/* Make sure the previous comment leader is not removed. */
// Should the next comment leader be left alone? Yes.
// Should the next comment leader be left alone? Yes.
/* Here the comment leader should be left intact. */ // And so should this one.
/* Here the comment leader should be left intact. */ // And so should this one.
if (condition) // Remove the next comment leader! OK, I will.
action();
if (condition) // Remove the next comment leader! OK, I will.
action();
}
.
call assert_equal(getline(last_line + 1, '$'), getline(1, last_line))
set comments&vim
set joinspaces&vim
set fo&vim
set backspace&vim
close!
endfunc
" Test for joining lines with different comment leaders
func Test_join_comments_2()
new
insert
{
/*
* Make sure the previous comment leader is not removed.
*/
/*
* Make sure the previous comment leader is not removed.
*/
/* List:
* - item1
* foo bar baz
* foo bar baz
* - item2
* foo bar baz
* foo bar baz
*/
/* List:
* - item1
* foo bar baz
* foo bar baz
* - item2
* foo bar baz
* foo bar baz
*/
// Should the next comment leader be left alone?
// Yes.
// Should the next comment leader be left alone?
// Yes.
/* Here the comment leader should be left intact. */
// And so should this one.
/* Here the comment leader should be left intact. */
// And so should this one.
if (condition) // Remove the next comment leader!
// OK, I will.
action();
if (condition) // Remove the next comment leader!
// OK, I will.
action();
int i = 7 /* foo *// 3
// comment
;
int i = 7 /* foo *// 3
// comment
;
># Note that the last character of the ending comment leader (left angle
# bracket) is a comment leader itself. Make sure that this comment leader is
# not removed from the next line #<
< On this line a new comment is opened which spans 2 lines. This comment should
< retain its comment leader.
># Note that the last character of the ending comment leader (left angle
# bracket) is a comment leader itself. Make sure that this comment leader is
# not removed from the next line #<
< On this line a new comment is opened which spans 2 lines. This comment should
< retain its comment leader.
}
.
call cursor(2, 1)
set comments=sO:*\ -,mO:*\ \ ,exO:*/
set comments+=s1:/*,mb:*,ex:*/,://
set comments+=s1:>#,mb:#,ex:#<,:<
set cpoptions-=j joinspaces fo=j
set backspace=eol,start
.,+3join
exe "normal j4J\<CR>"
.,+8join
exe "normal j9J\<CR>"
.,+2join
exe "normal j3J\<CR>"
.,+2join
exe "normal j3J\<CR>"
.,+2join
exe "normal jj3J\<CR>j"
.,+2join
exe "normal jj3J\<CR>j"
.,+5join
exe "normal j6J\<CR>"
exe "normal oSome code!\<CR>// Make sure backspacing does not remove this comment leader.\<Esc>0i\<C-H>\<Esc>"
normal G
let last_line = line('$')
" Expected output
append
{
/* Make sure the previous comment leader is not removed. */
/* Make sure the previous comment leader is not removed. */
/* List: item1 foo bar baz foo bar baz item2 foo bar baz foo bar baz */
/* List: item1 foo bar baz foo bar baz item2 foo bar baz foo bar baz */
// Should the next comment leader be left alone? Yes.
// Should the next comment leader be left alone? Yes.
/* Here the comment leader should be left intact. */ // And so should this one.
/* Here the comment leader should be left intact. */ // And so should this one.
if (condition) // Remove the next comment leader! OK, I will.
action();
if (condition) // Remove the next comment leader! OK, I will.
action();
int i = 7 /* foo *// 3 // comment
;
int i = 7 /* foo *// 3 // comment
;
># Note that the last character of the ending comment leader (left angle bracket) is a comment leader itself. Make sure that this comment leader is not removed from the next line #< < On this line a new comment is opened which spans 2 lines. This comment should retain its comment leader.
># Note that the last character of the ending comment leader (left angle bracket) is a comment leader itself. Make sure that this comment leader is not removed from the next line #< < On this line a new comment is opened which spans 2 lines. This comment should retain its comment leader.
Some code!// Make sure backspacing does not remove this comment leader.
}
.
call assert_equal(getline(last_line + 1, '$'), getline(1, last_line))
close!
endfunc

View File

@ -1,6 +1,8 @@
" tests for listener_add() and listener_remove() " tests for listener_add() and listener_remove()
func s:StoreList(l) func s:StoreList(s, l)
let s:start = a:s
let s:text = getline(a:s)
let s:list = a:l let s:list = a:l
endfunc endfunc
@ -16,9 +18,10 @@ endfunc
func Test_listening() func Test_listening()
new new
call setline(1, ['one', 'two']) call setline(1, ['one', 'two'])
let id = listener_add({l -> s:StoreList(l)}) let s:list = []
let id = listener_add({b, s, e, a, l -> s:StoreList(s, l)})
call setline(1, 'one one') call setline(1, 'one one')
redraw call listener_flush()
call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list) call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list)
" Undo is also a change " Undo is also a change
@ -26,12 +29,14 @@ func Test_listening()
call append(2, 'two two') call append(2, 'two two')
undo undo
redraw redraw
call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1}, " the two changes get merged
\ {'lnum': 3, 'end': 4, 'col': 1, 'added': -1}, ], s:list) call assert_equal([{'lnum': 3, 'end': 4, 'col': 1, 'added': 0}], s:list)
1 1
" Two listeners, both get called. " Two listeners, both get called. Also check column.
let id2 = listener_add({l -> s:AnotherStoreList(l)}) call setline(1, ['one one', 'two'])
call listener_flush()
let id2 = listener_add({b, s, e, a, l -> s:AnotherStoreList(l)})
let s:list = [] let s:list = []
let s:list2 = [] let s:list2 = []
exe "normal $asome\<Esc>" exe "normal $asome\<Esc>"
@ -39,7 +44,10 @@ func Test_listening()
call assert_equal([{'lnum': 1, 'end': 2, 'col': 8, 'added': 0}], s:list) call assert_equal([{'lnum': 1, 'end': 2, 'col': 8, 'added': 0}], s:list)
call assert_equal([{'lnum': 1, 'end': 2, 'col': 8, 'added': 0}], s:list2) call assert_equal([{'lnum': 1, 'end': 2, 'col': 8, 'added': 0}], s:list2)
" removing listener works
call listener_remove(id2) call listener_remove(id2)
call setline(1, ['one one', 'two'])
call listener_flush()
let s:list = [] let s:list = []
let s:list2 = [] let s:list2 = []
call setline(3, 'three') call setline(3, 'three')
@ -47,12 +55,68 @@ func Test_listening()
call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1}], s:list) call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1}], s:list)
call assert_equal([], s:list2) call assert_equal([], s:list2)
" a change above a previous change without a line number change is reported
" together
call setline(1, ['one one', 'two'])
call listener_flush()
call append(2, 'two two')
call setline(1, 'something')
call listener_flush()
call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1},
\ {'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list)
" an insert just above a previous change that was the last one gets merged
call setline(1, ['one one', 'two'])
call listener_flush()
let s:list = []
call setline(2, 'something')
call append(1, 'two two')
call assert_equal([], s:list)
call listener_flush()
call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 1}], s:list)
" an insert above a previous change causes a flush
call setline(1, ['one one', 'two'])
call listener_flush()
call setline(2, 'something')
call append(0, 'two two')
call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 0}], s:list)
call assert_equal('something', s:text)
call listener_flush()
call assert_equal([{'lnum': 1, 'end': 1, 'col': 1, 'added': 1}], s:list)
call assert_equal('two two', s:text)
" a delete at a previous change that was the last one gets merged
call setline(1, ['one one', 'two'])
call listener_flush()
let s:list = []
call setline(2, 'something')
2del
call assert_equal([], s:list)
call listener_flush()
call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': -1}], s:list)
" a delete above a previous change causes a flush
call setline(1, ['one one', 'two'])
call listener_flush()
call setline(2, 'another')
1del
call assert_equal([{'lnum': 2, 'end': 3, 'col': 1, 'added': 0}], s:list)
call assert_equal(2, s:start)
call assert_equal('another', s:text)
call listener_flush()
call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': -1}], s:list)
call assert_equal('another', s:text)
" the "o" command first adds an empty line and then changes it " the "o" command first adds an empty line and then changes it
%del
call setline(1, ['one one', 'two'])
call listener_flush()
let s:list = [] let s:list = []
exe "normal Gofour\<Esc>" exe "normal Gofour\<Esc>"
redraw redraw
call assert_equal([{'lnum': 4, 'end': 4, 'col': 1, 'added': 1}, call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1},
\ {'lnum': 4, 'end': 5, 'col': 1, 'added': 0}], s:list) \ {'lnum': 3, 'end': 4, 'col': 1, 'added': 0}], s:list)
" Remove last listener " Remove last listener
let s:list = [] let s:list = []
@ -62,7 +126,7 @@ func Test_listening()
call assert_equal([], s:list) call assert_equal([], s:list)
" Trying to change the list fails " Trying to change the list fails
let id = listener_add({l -> s:EvilStoreList(l)}) let id = listener_add({b, s, e, a, l -> s:EvilStoreList(l)})
let s:list3 = [] let s:list3 = []
call setline(1, 'asdfasdf') call setline(1, 'asdfasdf')
redraw redraw
@ -72,9 +136,64 @@ func Test_listening()
bwipe! bwipe!
endfunc endfunc
func s:StoreBufList(buf, l) func s:StoreListArgs(buf, start, end, added, list)
let s:buf = a:buf
let s:start = a:start
let s:end = a:end
let s:added = a:added
let s:list = a:list
endfunc
func Test_listener_args()
new
call setline(1, ['one', 'two'])
let s:list = []
let id = listener_add('s:StoreListArgs')
" just one change
call setline(1, 'one one')
call listener_flush()
call assert_equal(bufnr(''), s:buf)
call assert_equal(1, s:start)
call assert_equal(2, s:end)
call assert_equal(0, s:added)
call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0}], s:list)
" two disconnected changes
call setline(1, ['one', 'two', 'three', 'four'])
call listener_flush()
call setline(1, 'one one')
call setline(3, 'three three')
call listener_flush()
call assert_equal(bufnr(''), s:buf)
call assert_equal(1, s:start)
call assert_equal(4, s:end)
call assert_equal(0, s:added)
call assert_equal([{'lnum': 1, 'end': 2, 'col': 1, 'added': 0},
\ {'lnum': 3, 'end': 4, 'col': 1, 'added': 0}], s:list)
" add and remove lines
call setline(1, ['one', 'two', 'three', 'four', 'five', 'six'])
call listener_flush()
call append(2, 'two two')
4del
call append(5, 'five five')
call listener_flush()
call assert_equal(bufnr(''), s:buf)
call assert_equal(3, s:start)
call assert_equal(6, s:end)
call assert_equal(1, s:added)
call assert_equal([{'lnum': 3, 'end': 3, 'col': 1, 'added': 1},
\ {'lnum': 4, 'end': 5, 'col': 1, 'added': -1},
\ {'lnum': 6, 'end': 6, 'col': 1, 'added': 1}], s:list)
call listener_remove(id)
bwipe!
endfunc
func s:StoreBufList(buf, start, end, added, list)
let s:bufnr = a:buf let s:bufnr = a:buf
let s:list = a:l let s:list = a:list
endfunc endfunc
func Test_listening_other_buf() func Test_listening_other_buf()
@ -82,7 +201,7 @@ func Test_listening_other_buf()
call setline(1, ['one', 'two']) call setline(1, ['one', 'two'])
let bufnr = bufnr('') let bufnr = bufnr('')
normal ww normal ww
let id = listener_add(function('s:StoreBufList', [bufnr]), bufnr) let id = listener_add(function('s:StoreBufList'), bufnr)
let s:list = [] let s:list = []
call setbufline(bufnr, 1, 'hello') call setbufline(bufnr, 1, 'hello')
redraw redraw

View File

@ -470,13 +470,19 @@ func Test_shortmess_F2()
call assert_match('file2', execute('bn', '')) call assert_match('file2', execute('bn', ''))
set shortmess+=F set shortmess+=F
call assert_true(empty(execute('bn', ''))) call assert_true(empty(execute('bn', '')))
call assert_false(test_getvalue('need_fileinfo'))
call assert_true(empty(execute('bn', ''))) call assert_true(empty(execute('bn', '')))
call assert_false(test_getvalue('need_fileinfo'))
set hidden set hidden
call assert_true(empty(execute('bn', ''))) call assert_true(empty(execute('bn', '')))
call assert_false(test_getvalue('need_fileinfo'))
call assert_true(empty(execute('bn', ''))) call assert_true(empty(execute('bn', '')))
call assert_false(test_getvalue('need_fileinfo'))
set nohidden set nohidden
call assert_true(empty(execute('bn', ''))) call assert_true(empty(execute('bn', '')))
call assert_false(test_getvalue('need_fileinfo'))
call assert_true(empty(execute('bn', ''))) call assert_true(empty(execute('bn', '')))
call assert_false(test_getvalue('need_fileinfo'))
set shortmess& set shortmess&
call assert_match('file1', execute('bn', '')) call assert_match('file1', execute('bn', ''))
call assert_match('file2', execute('bn', '')) call assert_match('file2', execute('bn', ''))

View File

@ -151,6 +151,7 @@ endfunc
func SetupOneLine() func SetupOneLine()
call setline(1, 'xonex xtwoxx') call setline(1, 'xonex xtwoxx')
normal gg0
call AddPropTypes() call AddPropTypes()
call prop_add(1, 2, {'length': 3, 'id': 11, 'type': 'one'}) call prop_add(1, 2, {'length': 3, 'id': 11, 'type': 'one'})
call prop_add(1, 8, {'length': 3, 'id': 12, 'type': 'two'}) call prop_add(1, 8, {'length': 3, 'id': 12, 'type': 'two'})
@ -272,6 +273,65 @@ func Test_prop_replace()
set bs& set bs&
endfunc endfunc
func Test_prop_open_line()
new
" open new line, props stay in top line
let expected = SetupOneLine() " 'xonex xtwoxx'
exe "normal o\<Esc>"
call assert_equal('xonex xtwoxx', getline(1))
call assert_equal('', getline(2))
call assert_equal(expected, prop_list(1))
call DeletePropTypes()
" move all props to next line
let expected = SetupOneLine() " 'xonex xtwoxx'
exe "normal 0i\<CR>\<Esc>"
call assert_equal('', getline(1))
call assert_equal('xonex xtwoxx', getline(2))
call assert_equal(expected, prop_list(2))
call DeletePropTypes()
" split just before prop, move all props to next line
let expected = SetupOneLine() " 'xonex xtwoxx'
exe "normal 0li\<CR>\<Esc>"
call assert_equal('x', getline(1))
call assert_equal('onex xtwoxx', getline(2))
let expected[0].col -= 1
let expected[1].col -= 1
call assert_equal(expected, prop_list(2))
call DeletePropTypes()
" split inside prop, split first prop
let expected = SetupOneLine() " 'xonex xtwoxx'
exe "normal 0lli\<CR>\<Esc>"
call assert_equal('xo', getline(1))
call assert_equal('nex xtwoxx', getline(2))
let exp_first = [deepcopy(expected[0])]
let exp_first[0].length = 1
call assert_equal(exp_first, prop_list(1))
let expected[0].col = 1
let expected[0].length = 2
let expected[1].col -= 2
call assert_equal(expected, prop_list(2))
call DeletePropTypes()
" split just after first prop, second prop move to next line
let expected = SetupOneLine() " 'xonex xtwoxx'
exe "normal 0fea\<CR>\<Esc>"
call assert_equal('xone', getline(1))
call assert_equal('x xtwoxx', getline(2))
let exp_first = expected[0:0]
call assert_equal(exp_first, prop_list(1))
let expected = expected[1:1]
let expected[0].col -= 4
call assert_equal(expected, prop_list(2))
call DeletePropTypes()
bwipe!
set bs&
endfunc
func Test_prop_clear() func Test_prop_clear()
new new
call AddPropTypes() call AddPropTypes()

View File

@ -8,18 +8,15 @@
*/ */
/* /*
* Text properties implementation. * Text properties implementation. See ":help text-properties".
*
* Text properties are attached to the text. They move with the text when
* text is inserted/deleted.
*
* Text properties have a user specified ID number, which can be unique.
* Text properties have a type, which can be used to specify highlighting.
* *
* TODO: * TODO:
* - When using 'cursorline' attributes should be merged. (#3912) * - When using 'cursorline' attributes should be merged. (#3912)
* - Adjust text property column and length when text is inserted/deleted. * - Adjust text property column and length when text is inserted/deleted.
* -> splitting a line can create a zero-length property. Don't highlight it
* and extend it when inserting text.
* -> a :substitute with a multi-line match * -> a :substitute with a multi-line match
* -> join two lines, also with BS in Insert mode
* -> search for changed_bytes() from misc1.c * -> search for changed_bytes() from misc1.c
* - Perhaps we only need TP_FLAG_CONT_NEXT and can drop TP_FLAG_CONT_PREV? * - 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 global_proptypes, to quickly lookup a prop type by ID
@ -28,8 +25,6 @@
* the index, like DB_MARKED? * the index, like DB_MARKED?
* - Also test line2byte() with many lines, so that ml_updatechunk() is taken * - Also test line2byte() with many lines, so that ml_updatechunk() is taken
* into account. * into account.
* - Add mechanism to keep track of changed lines, so that plugin can update
* text properties in these.
* - Perhaps have a window-local option to disable highlighting from text * - Perhaps have a window-local option to disable highlighting from text
* properties? * properties?
*/ */
@ -1033,12 +1028,17 @@ adjust_prop_columns(
/* /*
* Adjust text properties for a line that was split in two. * 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 * "lnum_props" is the line that has the properties from before the split.
* below it. "kept" is the number of bytes kept in the first line, while * "lnum_top" is the top line.
* "kept" is the number of bytes kept in the first line, while
* "deleted" is the number of bytes deleted. * "deleted" is the number of bytes deleted.
*/ */
void void
adjust_props_for_split(linenr_T lnum, int kept, int deleted) adjust_props_for_split(
linenr_T lnum_props,
linenr_T lnum_top,
int kept,
int deleted)
{ {
char_u *props; char_u *props;
int count; int count;
@ -1049,11 +1049,12 @@ adjust_props_for_split(linenr_T lnum, int kept, int deleted)
if (!curbuf->b_has_textprop) if (!curbuf->b_has_textprop)
return; return;
count = get_text_props(curbuf, lnum + 1, &props, FALSE);
// Get the text properties from "lnum_props".
count = get_text_props(curbuf, lnum_props, &props, FALSE);
ga_init2(&prevprop, sizeof(textprop_T), 10); ga_init2(&prevprop, sizeof(textprop_T), 10);
ga_init2(&nextprop, 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. // Keep the relevant ones in the first line, reducing the length if needed.
// Copy the ones that include the split to the second line. // Copy the ones that include the split to the second line.
// Move the ones after the split to the second line. // Move the ones after the split to the second line.
@ -1074,7 +1075,9 @@ adjust_props_for_split(linenr_T lnum, int kept, int deleted)
++prevprop.ga_len; ++prevprop.ga_len;
} }
if (prop.tp_col + prop.tp_len >= skipped && ga_grow(&nextprop, 1) == OK) // Only add the property to the next line if the length is bigger than
// zero.
if (prop.tp_col + prop.tp_len > skipped && ga_grow(&nextprop, 1) == OK)
{ {
p = ((textprop_T *)nextprop.ga_data) + nextprop.ga_len; p = ((textprop_T *)nextprop.ga_data) + nextprop.ga_len;
*p = prop; *p = prop;
@ -1089,10 +1092,11 @@ adjust_props_for_split(linenr_T lnum, int kept, int deleted)
} }
} }
set_text_props(lnum, prevprop.ga_data, prevprop.ga_len * sizeof(textprop_T)); set_text_props(lnum_top, prevprop.ga_data,
prevprop.ga_len * sizeof(textprop_T));
ga_clear(&prevprop); ga_clear(&prevprop);
set_text_props(lnum_top + 1, nextprop.ga_data,
set_text_props(lnum + 1, nextprop.ga_data, nextprop.ga_len * sizeof(textprop_T)); nextprop.ga_len * sizeof(textprop_T));
ga_clear(&nextprop); ga_clear(&nextprop);
} }

View File

@ -767,6 +767,20 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1337,
/**/
1336,
/**/
1335,
/**/
1334,
/**/
1333,
/**/
1332,
/**/
1331,
/**/ /**/
1330, 1330,
/**/ /**/