aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ex_cmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/ex_cmds.c')
-rw-r--r--src/nvim/ex_cmds.c297
1 files changed, 286 insertions, 11 deletions
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 1b6d9b50e9..fb97c46117 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -39,6 +39,7 @@
#include "nvim/buffer_updates.h"
#include "nvim/main.h"
#include "nvim/mark.h"
+#include "nvim/mark_extended.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/message.h"
@@ -105,6 +106,8 @@ typedef struct {
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_cmds.c.generated.h"
+#include "mark_extended.h"
+
#endif
/// ":ascii" and "ga" implementation
@@ -658,10 +661,10 @@ void ex_sort(exarg_T *eap)
deleted = (long)(count - (lnum - eap->line2));
if (deleted > 0) {
mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted,
- false);
+ false, kExtmarkUndo);
msgmore(-deleted);
} else if (deleted < 0) {
- mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, false);
+ mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, false, kExtmarkUndo);
}
if (change_occurred || deleted != 0) {
changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true);
@@ -874,10 +877,12 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
* their final destination at the new text position -- webb
*/
last_line = curbuf->b_ml.ml_line_count;
- mark_adjust_nofold(line1, line2, last_line - line2, 0L, true);
+ mark_adjust_nofold(line1, line2, last_line - line2, 0L, true, kExtmarkNoUndo);
+ extmark_adjust(curbuf, line1, line2, last_line - line2, 0L, kExtmarkNoUndo,
+ true);
changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines, false);
if (dest >= line2) {
- mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L, false);
+ mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L, false, kExtmarkNoUndo);
FOR_ALL_TAB_WINDOWS(tab, win) {
if (win->w_buffer == curbuf) {
foldMoveRange(&win->w_folds, line1, line2, dest);
@@ -886,7 +891,8 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
curbuf->b_op_start.lnum = dest - num_lines + 1;
curbuf->b_op_end.lnum = dest;
} else {
- mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L, false);
+ mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L, false,
+ kExtmarkNoUndo);
FOR_ALL_TAB_WINDOWS(tab, win) {
if (win->w_buffer == curbuf) {
foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2);
@@ -897,7 +903,9 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
}
curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
mark_adjust_nofold(last_line - num_lines + 1, last_line,
- -(last_line - dest - extra), 0L, true);
+ -(last_line - dest - extra), 0L, true, kExtmarkNoUndo);
+
+ u_extmark_move(curbuf, line1, line2, last_line, dest, num_lines, extra);
changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra, false);
// send update regarding the new lines that were added
@@ -1281,12 +1289,14 @@ static void do_filter(
if (cmdmod.keepmarks || vim_strchr(p_cpo, CPO_REMMARK) == NULL) {
if (read_linecount >= linecount) {
// move all marks from old lines to new lines
- mark_adjust(line1, line2, linecount, 0L, false);
+ mark_adjust(line1, line2, linecount, 0L, false, kExtmarkUndo);
} else {
// move marks from old lines to new lines, delete marks
// that are in deleted lines
- mark_adjust(line1, line1 + read_linecount - 1, linecount, 0L, false);
- mark_adjust(line1 + read_linecount, line2, MAXLNUM, 0L, false);
+ mark_adjust(line1, line1 + read_linecount - 1, linecount, 0L, false,
+ kExtmarkUndo);
+ mark_adjust(line1 + read_linecount, line2, MAXLNUM, 0L, false,
+ kExtmarkUndo);
}
}
@@ -3214,6 +3224,189 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags,
return cmd;
}
+static void extmark_move_regmatch_single(lpos_T startpos,
+ lpos_T endpos,
+ linenr_T lnum,
+ int sublen)
+{
+ colnr_T mincol;
+ colnr_T endcol;
+ colnr_T col_amount;
+
+ mincol = startpos.col + 1;
+ endcol = endpos.col + 1;
+
+ // There are cases such as :s/^/x/ where this happens
+ // a delete is simply not required.
+ if (mincol + 1 <= endcol) {
+ extmark_col_adjust_delete(curbuf,
+ lnum, mincol + 1, endcol, kExtmarkUndo, 0);
+ }
+
+ // Insert, sublen seems to be the value we need but + 1...
+ col_amount = sublen - 1;
+ extmark_col_adjust(curbuf, lnum, mincol, 0, col_amount, kExtmarkUndo);
+}
+
+static void extmark_move_regmatch_multi(ExtmarkSubMulti s, long i)
+{
+ colnr_T mincol;
+ linenr_T u_lnum;
+ mincol = s.startpos.col + 1;
+
+ linenr_T n_u_lnum = s.lnum + s.endpos.lnum - s.startpos.lnum;
+ colnr_T n_after_newline_in_pat = s.endpos.col;
+ colnr_T n_before_newline_in_pat = mincol - s.cm_start.col;
+ long n_after_newline_in_sub;
+ if (!s.newline_in_sub) {
+ n_after_newline_in_sub = s.cm_end.col - s.cm_start.col;
+ } else {
+ n_after_newline_in_sub = s.cm_end.col;
+ }
+
+ if (s.newline_in_pat && !s.newline_in_sub) {
+ // -- Delete Pattern --
+ // 1. Move marks in the pattern
+ mincol = s.startpos.col + 1;
+ u_lnum = n_u_lnum;
+ assert(n_u_lnum == u_lnum);
+ extmark_copy_and_place(curbuf,
+ s.lnum, mincol,
+ u_lnum, n_after_newline_in_pat,
+ s.lnum, mincol,
+ kExtmarkUndo, true, NULL);
+ // 2. Move marks on last newline
+ mincol = mincol - n_before_newline_in_pat;
+ extmark_col_adjust(curbuf,
+ u_lnum,
+ n_after_newline_in_pat + 1,
+ -s.newline_in_pat,
+ mincol - n_after_newline_in_pat,
+ kExtmarkUndo);
+ // Take care of the lines after
+ extmark_adjust(curbuf,
+ u_lnum,
+ u_lnum,
+ MAXLNUM,
+ -s.newline_in_pat,
+ kExtmarkUndo,
+ false);
+ // 1. first insert the text in the substitutaion
+ extmark_col_adjust(curbuf,
+ s.lnum,
+ mincol + 1,
+ s.newline_in_sub,
+ n_after_newline_in_sub,
+ kExtmarkUndo);
+
+ } else {
+ // The data in sub_obj is as if the substituons above had already taken
+ // place. For our extmarks they haven't as we work from the bottom of the
+ // buffer up. Readjust the data.
+ n_u_lnum = s.lnum + s.endpos.lnum - s.startpos.lnum;
+ n_u_lnum = n_u_lnum - s.lnum_added;
+
+ // adjusted = L - (i-1)N
+ // where L = lnum value, N= lnum_added and i = iteration
+ linenr_T a_l_lnum = s.cm_start.lnum - ((i -1) * s.lnum_added);
+ linenr_T a_u_lnum = a_l_lnum + s.endpos.lnum;
+ assert(s.startpos.lnum == 0);
+
+ mincol = s.startpos.col + 1;
+ u_lnum = n_u_lnum;
+
+ if (!s.newline_in_pat && s.newline_in_sub) {
+ // -- Delete Pattern --
+ // 1. Move marks in the pattern
+ extmark_col_adjust_delete(curbuf,
+ a_l_lnum,
+ mincol + 1,
+ s.endpos.col + 1,
+ kExtmarkUndo,
+ s.eol);
+
+ extmark_adjust(curbuf,
+ a_u_lnum + 1,
+ MAXLNUM,
+ (long)s.newline_in_sub,
+ 0,
+ kExtmarkUndo,
+ false);
+ // 3. Insert
+ extmark_col_adjust(curbuf,
+ a_l_lnum,
+ mincol,
+ s.newline_in_sub,
+ (long)-mincol + 1 + n_after_newline_in_sub,
+ kExtmarkUndo);
+ } else if (s.newline_in_pat && s.newline_in_sub) {
+ if (s.lnum_added >= 0) {
+ linenr_T u_col = n_after_newline_in_pat == 0
+ ? 1 : n_after_newline_in_pat;
+ extmark_copy_and_place(curbuf,
+ a_l_lnum, mincol,
+ a_u_lnum, u_col,
+ a_l_lnum, mincol,
+ kExtmarkUndo, true, NULL);
+ // 2. Move marks on last newline
+ mincol = mincol - (colnr_T)n_before_newline_in_pat;
+ extmark_col_adjust(curbuf,
+ a_u_lnum,
+ (colnr_T)(n_after_newline_in_pat + 1),
+ -s.newline_in_pat,
+ mincol - n_after_newline_in_pat,
+ kExtmarkUndo);
+ // TODO(timeyyy): nothing to do here if lnum_added = 0
+ extmark_adjust(curbuf,
+ a_u_lnum + 1,
+ MAXLNUM,
+ (long)s.lnum_added,
+ 0,
+ kExtmarkUndo,
+ false);
+
+ extmark_col_adjust(curbuf,
+ a_l_lnum,
+ mincol + 1,
+ s.newline_in_sub,
+ (long)-mincol + n_after_newline_in_sub,
+ kExtmarkUndo);
+ } else {
+ mincol = s.startpos.col + 1;
+ a_l_lnum = s.startpos.lnum + 1;
+ a_u_lnum = s.endpos.lnum + 1;
+ extmark_copy_and_place(curbuf,
+ a_l_lnum, mincol,
+ a_u_lnum, n_after_newline_in_pat,
+ a_l_lnum, mincol,
+ kExtmarkUndo, true, NULL);
+ // 2. Move marks on last newline
+ mincol = mincol - (colnr_T)n_before_newline_in_pat;
+ extmark_col_adjust(curbuf,
+ a_u_lnum,
+ (colnr_T)(n_after_newline_in_pat + 1),
+ -s.newline_in_pat,
+ mincol - n_after_newline_in_pat,
+ kExtmarkUndo);
+ extmark_adjust(curbuf,
+ a_u_lnum,
+ a_u_lnum,
+ MAXLNUM,
+ s.lnum_added,
+ kExtmarkUndo,
+ false);
+ // 3. Insert
+ extmark_col_adjust(curbuf,
+ a_l_lnum,
+ mincol + 1,
+ s.newline_in_sub,
+ (long)-mincol + n_after_newline_in_sub,
+ kExtmarkUndo);
+ }
+ }
+ }
+}
+
/// Perform a substitution from line eap->line1 to line eap->line2 using the
/// command pointed to by eap->arg which should be of the form:
///
@@ -3260,6 +3453,17 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
int save_ma = 0;
int save_b_changed = curbuf->b_changed;
bool preview = (State & CMDPREVIEW);
+ extmark_sub_multi_vec_t extmark_sub_multi = KV_INITIAL_VALUE;
+ extmark_sub_single_vec_t extmark_sub_single = KV_INITIAL_VALUE;
+ linenr_T no_of_lines_changed = 0;
+ linenr_T newline_in_pat = 0;
+ linenr_T newline_in_sub = 0;
+
+ // inccomand tests fail without this check
+ if (!preview) {
+ // Requried for Undo to work for nsmarks,
+ u_save_cursor();
+ }
if (!global_busy) {
sub_nsubs = 0;
@@ -3418,6 +3622,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
// Check for a match on each line.
// If preview: limit to max('cmdwinheight', viewport).
linenr_T line2 = eap->line2;
+
for (linenr_T lnum = eap->line1;
lnum <= line2 && !got_quit && !aborting()
&& (!preview || preview_lines.lines_needed <= (linenr_T)p_cwh
@@ -3876,6 +4081,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
ADJUST_SUB_FIRSTLNUM();
+
// Now the trick is to replace CTRL-M chars with a real line
// break. This would make it impossible to insert a CTRL-M in
// the text. The line break can be avoided by preceding the
@@ -3890,7 +4096,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
*p1 = NUL; // truncate up to the CR
ml_append(lnum - 1, new_start,
(colnr_T)(p1 - new_start + 1), false);
- mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, false);
+ mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, false,
+ kExtmarkNOOP);
+
if (subflags.do_ask) {
appended_lines(lnum - 1, 1L);
} else {
@@ -3917,6 +4125,44 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
current_match.end.lnum = lnum;
}
+ // Adjust extmarks, by delete and then insert
+ if (!preview) {
+ newline_in_pat = (regmatch.endpos[0].lnum
+ - regmatch.startpos[0].lnum);
+ newline_in_sub = current_match.end.lnum - current_match.start.lnum;
+ if (newline_in_pat || newline_in_sub) {
+ ExtmarkSubMulti sub_multi;
+ no_of_lines_changed = newline_in_sub - newline_in_pat;
+
+ sub_multi.newline_in_pat = newline_in_pat;
+ sub_multi.newline_in_sub = newline_in_sub;
+ sub_multi.lnum = lnum;
+ sub_multi.lnum_added = no_of_lines_changed;
+ sub_multi.cm_start = current_match.start;
+ sub_multi.cm_end = current_match.end;
+
+ sub_multi.startpos = regmatch.startpos[0];
+ sub_multi.endpos = regmatch.endpos[0];
+ sub_multi.eol = extmark_eol_col(curbuf, lnum);
+
+ kv_push(extmark_sub_multi, sub_multi);
+ // Collect information required for moving extmarks WITHOUT \n, \r
+ } else {
+ no_of_lines_changed = 0;
+
+ if (regmatch.startpos[0].col != -1) {
+ ExtmarkSubSingle sub_single;
+ sub_single.sublen = sublen;
+ sub_single.lnum = lnum;
+ sub_single.startpos = regmatch.startpos[0];
+ sub_single.endpos = regmatch.endpos[0];
+
+ kv_push(extmark_sub_single, sub_single);
+ }
+ }
+ }
+
+
// 4. If subflags.do_all is set, find next match.
// Prevent endless loop with patterns that match empty
// strings, e.g. :s/$/pat/g or :s/[a-z]* /(&)/g.
@@ -3983,7 +4229,7 @@ skip:
ml_delete(lnum, false);
}
mark_adjust(lnum, lnum + nmatch_tl - 1,
- (long)MAXLNUM, -nmatch_tl, false);
+ (long)MAXLNUM, -nmatch_tl, false, kExtmarkNOOP);
if (subflags.do_ask) {
deleted_lines(lnum, nmatch_tl);
}
@@ -4159,6 +4405,35 @@ skip:
}
}
}
+ if (newline_in_pat || newline_in_sub) {
+ long n = (long)kv_size(extmark_sub_multi);
+ ExtmarkSubMulti sub_multi;
+ if (no_of_lines_changed < 0) {
+ for (i = 0; i < n; i++) {
+ sub_multi = kv_A(extmark_sub_multi, i);
+ extmark_move_regmatch_multi(sub_multi, i);
+ }
+ } else {
+ // Move extmarks in reverse order to avoid moving marks we just moved...
+ for (i = 0; i < n; i++) {
+ sub_multi = kv_Z(extmark_sub_multi, i);
+ extmark_move_regmatch_multi(sub_multi, n - i);
+ }
+ }
+ kv_destroy(extmark_sub_multi);
+ } else {
+ long n = (long)kv_size(extmark_sub_single);
+ ExtmarkSubSingle sub_single;
+ for (i = 0; i < n; i++) {
+ sub_single = kv_Z(extmark_sub_single, i);
+ extmark_move_regmatch_single(sub_single.startpos,
+ sub_single.endpos,
+ sub_single.lnum,
+ sub_single.sublen);
+ }
+
+ kv_destroy(extmark_sub_single);
+ }
kv_destroy(preview_lines.subresults);