ops.c (128392B)
1 // ops.c: implementation of various operators: op_shift, op_delete, op_tilde, 2 // op_change, op_yank, do_join 3 4 #include <assert.h> 5 #include <ctype.h> 6 #include <inttypes.h> 7 #include <limits.h> 8 #include <stdbool.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <uv.h> 13 14 #include "nvim/api/private/defs.h" 15 #include "nvim/api/private/helpers.h" 16 #include "nvim/ascii_defs.h" 17 #include "nvim/assert_defs.h" 18 #include "nvim/autocmd.h" 19 #include "nvim/autocmd_defs.h" 20 #include "nvim/buffer.h" 21 #include "nvim/buffer_defs.h" 22 #include "nvim/buffer_updates.h" 23 #include "nvim/change.h" 24 #include "nvim/charset.h" 25 #include "nvim/clipboard.h" 26 #include "nvim/cursor.h" 27 #include "nvim/drawscreen.h" 28 #include "nvim/edit.h" 29 #include "nvim/errors.h" 30 #include "nvim/eval.h" 31 #include "nvim/eval/typval.h" 32 #include "nvim/eval/typval_defs.h" 33 #include "nvim/ex_cmds2.h" 34 #include "nvim/ex_cmds_defs.h" 35 #include "nvim/ex_getln.h" 36 #include "nvim/extmark.h" 37 #include "nvim/file_search.h" 38 #include "nvim/fold.h" 39 #include "nvim/garray.h" 40 #include "nvim/garray_defs.h" 41 #include "nvim/getchar.h" 42 #include "nvim/getchar_defs.h" 43 #include "nvim/gettext_defs.h" 44 #include "nvim/globals.h" 45 #include "nvim/highlight_defs.h" 46 #include "nvim/indent.h" 47 #include "nvim/indent_c.h" 48 #include "nvim/keycodes.h" 49 #include "nvim/macros_defs.h" 50 #include "nvim/mark.h" 51 #include "nvim/mark_defs.h" 52 #include "nvim/math.h" 53 #include "nvim/mbyte.h" 54 #include "nvim/mbyte_defs.h" 55 #include "nvim/memline.h" 56 #include "nvim/memline_defs.h" 57 #include "nvim/memory.h" 58 #include "nvim/message.h" 59 #include "nvim/mouse.h" 60 #include "nvim/move.h" 61 #include "nvim/normal.h" 62 #include "nvim/ops.h" 63 #include "nvim/option.h" 64 #include "nvim/option_defs.h" 65 #include "nvim/option_vars.h" 66 #include "nvim/os/input.h" 67 #include "nvim/os/time.h" 68 #include "nvim/plines.h" 69 #include "nvim/register.h" 70 #include "nvim/search.h" 71 #include "nvim/state.h" 72 #include "nvim/state_defs.h" 73 #include "nvim/strings.h" 74 #include "nvim/terminal.h" 75 #include "nvim/textformat.h" 76 #include "nvim/types_defs.h" 77 #include "nvim/ui.h" 78 #include "nvim/ui_defs.h" 79 #include "nvim/undo.h" 80 #include "nvim/vim_defs.h" 81 82 #include "ops.c.generated.h" 83 84 // Flags for third item in "opchars". 85 #define OPF_LINES 1 // operator always works on lines 86 #define OPF_CHANGE 2 // operator changes text 87 88 /// The names of operators. 89 /// IMPORTANT: Index must correspond with defines in ops.h!!! 90 /// The third field indicates whether the operator always works on lines. 91 static const char opchars[][3] = { 92 { NUL, NUL, 0 }, // OP_NOP 93 { 'd', NUL, OPF_CHANGE }, // OP_DELETE 94 { 'y', NUL, 0 }, // OP_YANK 95 { 'c', NUL, OPF_CHANGE }, // OP_CHANGE 96 { '<', NUL, OPF_LINES | OPF_CHANGE }, // OP_LSHIFT 97 { '>', NUL, OPF_LINES | OPF_CHANGE }, // OP_RSHIFT 98 { '!', NUL, OPF_LINES | OPF_CHANGE }, // OP_FILTER 99 { 'g', '~', OPF_CHANGE }, // OP_TILDE 100 { '=', NUL, OPF_LINES | OPF_CHANGE }, // OP_INDENT 101 { 'g', 'q', OPF_LINES | OPF_CHANGE }, // OP_FORMAT 102 { ':', NUL, OPF_LINES }, // OP_COLON 103 { 'g', 'U', OPF_CHANGE }, // OP_UPPER 104 { 'g', 'u', OPF_CHANGE }, // OP_LOWER 105 { 'J', NUL, OPF_LINES | OPF_CHANGE }, // DO_JOIN 106 { 'g', 'J', OPF_LINES | OPF_CHANGE }, // DO_JOIN_NS 107 { 'g', '?', OPF_CHANGE }, // OP_ROT13 108 { 'r', NUL, OPF_CHANGE }, // OP_REPLACE 109 { 'I', NUL, OPF_CHANGE }, // OP_INSERT 110 { 'A', NUL, OPF_CHANGE }, // OP_APPEND 111 { 'z', 'f', 0 }, // OP_FOLD 112 { 'z', 'o', OPF_LINES }, // OP_FOLDOPEN 113 { 'z', 'O', OPF_LINES }, // OP_FOLDOPENREC 114 { 'z', 'c', OPF_LINES }, // OP_FOLDCLOSE 115 { 'z', 'C', OPF_LINES }, // OP_FOLDCLOSEREC 116 { 'z', 'd', OPF_LINES }, // OP_FOLDDEL 117 { 'z', 'D', OPF_LINES }, // OP_FOLDDELREC 118 { 'g', 'w', OPF_LINES | OPF_CHANGE }, // OP_FORMAT2 119 { 'g', '@', OPF_CHANGE }, // OP_FUNCTION 120 { Ctrl_A, NUL, OPF_CHANGE }, // OP_NR_ADD 121 { Ctrl_X, NUL, OPF_CHANGE }, // OP_NR_SUB 122 }; 123 124 /// Translate a command name into an operator type. 125 /// Must only be called with a valid operator name! 126 int get_op_type(int char1, int char2) 127 { 128 int i; 129 130 if (char1 == 'r') { 131 // ignore second character 132 return OP_REPLACE; 133 } 134 if (char1 == '~') { 135 // when tilde is an operator 136 return OP_TILDE; 137 } 138 if (char1 == 'g' && char2 == Ctrl_A) { 139 // add 140 return OP_NR_ADD; 141 } 142 if (char1 == 'g' && char2 == Ctrl_X) { 143 // subtract 144 return OP_NR_SUB; 145 } 146 if (char1 == 'z' && char2 == 'y') { // OP_YANK 147 return OP_YANK; 148 } 149 for (i = 0;; i++) { 150 if (opchars[i][0] == char1 && opchars[i][1] == char2) { 151 break; 152 } 153 if (i == (int)(ARRAY_SIZE(opchars) - 1)) { 154 internal_error("get_op_type()"); 155 break; 156 } 157 } 158 return i; 159 } 160 161 /// @return true if operator "op" always works on whole lines. 162 int op_on_lines(int op) 163 { 164 return opchars[op][2] & OPF_LINES; 165 } 166 167 /// @return true if operator "op" changes text. 168 int op_is_change(int op) 169 { 170 return opchars[op][2] & OPF_CHANGE; 171 } 172 173 /// Get first operator command character. 174 /// 175 /// @return 'g' or 'z' if there is another command character. 176 int get_op_char(int optype) 177 { 178 return opchars[optype][0]; 179 } 180 181 /// Get second operator command character. 182 int get_extra_op_char(int optype) 183 { 184 return opchars[optype][1]; 185 } 186 187 /// handle a shift operation 188 void op_shift(oparg_T *oap, bool curs_top, int amount) 189 { 190 int block_col = 0; 191 192 if (u_save((linenr_T)(oap->start.lnum - 1), 193 (linenr_T)(oap->end.lnum + 1)) == FAIL) { 194 return; 195 } 196 197 if (oap->motion_type == kMTBlockWise) { 198 block_col = curwin->w_cursor.col; 199 } 200 201 for (int i = oap->line_count - 1; i >= 0; i--) { 202 int first_char = (uint8_t)(*get_cursor_line_ptr()); 203 if (first_char == NUL) { // empty line 204 curwin->w_cursor.col = 0; 205 } else if (oap->motion_type == kMTBlockWise) { 206 shift_block(oap, amount); 207 } else if (first_char != '#' || !preprocs_left()) { 208 // Move the line right if it doesn't start with '#', 'smartindent' 209 // isn't set or 'cindent' isn't set or '#' isn't in 'cino'. 210 shift_line(oap->op_type == OP_LSHIFT, p_sr, amount, false); 211 } 212 curwin->w_cursor.lnum++; 213 } 214 215 if (oap->motion_type == kMTBlockWise) { 216 curwin->w_cursor.lnum = oap->start.lnum; 217 curwin->w_cursor.col = block_col; 218 } else if (curs_top) { // put cursor on first line, for ">>" 219 curwin->w_cursor.lnum = oap->start.lnum; 220 beginline(BL_SOL | BL_FIX); // shift_line() may have set cursor.col 221 } else { 222 curwin->w_cursor.lnum--; // put cursor on last line, for ":>" 223 } 224 // The cursor line is not in a closed fold 225 foldOpenCursor(); 226 227 if (oap->line_count > p_report) { 228 char *op = oap->op_type == OP_RSHIFT ? ">" : "<"; 229 230 char *msg_line_single = NGETTEXT("%" PRId64 " line %sed %d time", 231 "%" PRId64 " line %sed %d times", amount); 232 char *msg_line_plural = NGETTEXT("%" PRId64 " lines %sed %d time", 233 "%" PRId64 " lines %sed %d times", amount); 234 vim_snprintf(IObuff, IOSIZE, 235 NGETTEXT(msg_line_single, msg_line_plural, oap->line_count), 236 (int64_t)oap->line_count, op, amount); 237 msg_keep(IObuff, 0, true, false); 238 } 239 240 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { 241 // Set "'[" and "']" marks. 242 curbuf->b_op_start = oap->start; 243 curbuf->b_op_end.lnum = oap->end.lnum; 244 curbuf->b_op_end.col = ml_get_len(oap->end.lnum); 245 if (curbuf->b_op_end.col > 0) { 246 curbuf->b_op_end.col--; 247 } 248 } 249 250 changed_lines(curbuf, oap->start.lnum, 0, oap->end.lnum + 1, 0, true); 251 } 252 253 /// Return the tabstop width at the index of the variable tabstop array. If an 254 /// index greater than the length of the array is given, the last tabstop width 255 /// in the array is returned. 256 static int get_vts(const int *vts_array, int index) 257 { 258 int ts; 259 260 if (index < 1) { 261 ts = 0; 262 } else if (index <= vts_array[0]) { 263 ts = vts_array[index]; 264 } else { 265 ts = vts_array[vts_array[0]]; 266 } 267 268 return ts; 269 } 270 271 /// Return the sum of all the tabstops through the index-th. 272 static int get_vts_sum(const int *vts_array, int index) 273 { 274 int sum = 0; 275 int i; 276 277 // Perform the summation for indices within the actual array. 278 for (i = 1; i <= index && i <= vts_array[0]; i++) { 279 sum += vts_array[i]; 280 } 281 282 // Add tabstops whose indices exceed the actual array. 283 if (i <= index) { 284 sum += vts_array[vts_array[0]] * (index - vts_array[0]); 285 } 286 287 return sum; 288 } 289 290 /// @param left true if shift is to the left 291 /// @param count true if new indent is to be to a tabstop 292 /// @param amount number of shifts 293 static int64_t get_new_sw_indent(bool left, bool round, int64_t amount, int64_t sw_val) 294 { 295 int64_t count = get_indent(); // get current indent 296 297 if (round) { // round off indent 298 int64_t i = trim_to_int(count / sw_val); // number of 'shiftwidth' rounded down 299 int64_t j = trim_to_int(count % sw_val); // extra spaces 300 if (j && left) { // first remove extra spaces 301 amount--; 302 } 303 if (left) { 304 i = MAX(i - amount, 0); 305 } else { 306 i += amount; 307 } 308 count = i * sw_val; 309 } else { // original vi indent 310 if (left) { 311 count = MAX(count - sw_val * amount, 0); 312 } else { 313 count += sw_val * amount; 314 } 315 } 316 317 return count; 318 } 319 320 /// @param left true if shift is to the left 321 /// @param count true if new indent is to be to a tabstop 322 /// @param amount number of shifts 323 static int64_t get_new_vts_indent(bool left, bool round, int amount, int *vts_array) 324 { 325 int64_t indent = get_indent(); 326 int vtsi = 0; 327 int vts_indent = 0; 328 int ts = 0; // Silence uninitialized variable warning. 329 330 // Find the tabstop at or to the left of the current indent. 331 while (vts_indent <= indent) { 332 vtsi++; 333 ts = get_vts(vts_array, vtsi); 334 vts_indent += ts; 335 } 336 vts_indent -= ts; 337 vtsi--; 338 339 // Extra indent spaces to the right of the tabstop 340 int64_t offset = indent - vts_indent; 341 342 if (round) { 343 if (left) { 344 if (offset == 0) { 345 indent = get_vts_sum(vts_array, vtsi - amount); 346 } else { 347 indent = get_vts_sum(vts_array, vtsi - (amount - 1)); 348 } 349 } else { 350 indent = get_vts_sum(vts_array, vtsi + amount); 351 } 352 } else { 353 if (left) { 354 if (amount > vtsi) { 355 indent = 0; 356 } else { 357 indent = get_vts_sum(vts_array, vtsi - amount) + offset; 358 } 359 } else { 360 indent = get_vts_sum(vts_array, vtsi + amount) + offset; 361 } 362 } 363 364 return indent; 365 } 366 367 /// Shift the current line 'amount' shiftwidth(s) left (if 'left' is true) or 368 /// right. 369 /// 370 /// The rules for choosing a shiftwidth are: If 'shiftwidth' is non-zero, use 371 /// 'shiftwidth'; else if 'vartabstop' is not empty, use 'vartabstop'; else use 372 /// 'tabstop'. The Vim documentation says nothing about 'softtabstop' or 373 /// 'varsofttabstop' affecting the shiftwidth, and neither affects the 374 /// shiftwidth in current versions of Vim, so they are not considered here. 375 /// 376 /// @param left true if shift is to the left 377 /// @param count true if new indent is to be to a tabstop 378 /// @param amount number of shifts 379 /// @param call_changed_bytes call changed_bytes() 380 void shift_line(bool left, bool round, int amount, int call_changed_bytes) 381 { 382 int64_t count; 383 int64_t sw_val = curbuf->b_p_sw; 384 int64_t ts_val = curbuf->b_p_ts; 385 int *vts_array = curbuf->b_p_vts_array; 386 387 if (sw_val != 0) { 388 // 'shiftwidth' is not zero; use it as the shift size. 389 count = get_new_sw_indent(left, round, amount, sw_val); 390 } else if ((vts_array == NULL) || (vts_array[0] == 0)) { 391 // 'shiftwidth' is zero and 'vartabstop' is empty; use 'tabstop' as the 392 // shift size. 393 count = get_new_sw_indent(left, round, amount, ts_val); 394 } else { 395 // 'shiftwidth' is zero and 'vartabstop' is defined; use 'vartabstop' 396 // to determine the new indent. 397 count = get_new_vts_indent(left, round, amount, vts_array); 398 } 399 400 // Set new indent 401 if (State & VREPLACE_FLAG) { 402 change_indent(INDENT_SET, trim_to_int(count), false, call_changed_bytes); 403 } else { 404 set_indent(trim_to_int(count), call_changed_bytes ? SIN_CHANGED : 0); 405 } 406 } 407 408 /// Shift one line of the current block one shiftwidth right or left. 409 /// Leaves cursor on first character in block. 410 static void shift_block(oparg_T *oap, int amount) 411 { 412 const bool left = (oap->op_type == OP_LSHIFT); 413 const int oldstate = State; 414 char *newp; 415 const int oldcol = curwin->w_cursor.col; 416 const int sw_val = get_sw_value_indent(curbuf, left); 417 const int ts_val = (int)curbuf->b_p_ts; 418 struct block_def bd; 419 int incr; 420 const int old_p_ri = p_ri; 421 422 p_ri = 0; // don't want revins in indent 423 424 State = MODE_INSERT; // don't want MODE_REPLACE for State 425 block_prep(oap, &bd, curwin->w_cursor.lnum, true); 426 if (bd.is_short) { 427 return; 428 } 429 430 // total is number of screen columns to be inserted/removed 431 int total = (int)((unsigned)amount * (unsigned)sw_val); 432 if ((total / sw_val) != amount) { 433 return; // multiplication overflow 434 } 435 436 char *const oldp = get_cursor_line_ptr(); 437 const int old_line_len = get_cursor_line_len(); 438 439 int startcol, oldlen, newlen; 440 441 if (!left) { 442 // 1. Get start vcol 443 // 2. Total ws vcols 444 // 3. Divvy into TABs & spp 445 // 4. Construct new string 446 total += bd.pre_whitesp; // all virtual WS up to & incl a split TAB 447 colnr_T ws_vcol = bd.start_vcol - bd.pre_whitesp; 448 char *old_textstart = bd.textstart; 449 if (bd.startspaces) { 450 if (utfc_ptr2len(bd.textstart) == 1) { 451 bd.textstart++; 452 } else { 453 ws_vcol = 0; 454 bd.startspaces = 0; 455 } 456 } 457 458 // TODO(vim): is passing bd.textstart for start of the line OK? 459 CharsizeArg csarg; 460 CSType cstype = init_charsize_arg(&csarg, curwin, curwin->w_cursor.lnum, bd.textstart); 461 StrCharInfo ci = utf_ptr2StrCharInfo(bd.textstart); 462 int vcol = bd.start_vcol; 463 while (ascii_iswhite(ci.chr.value)) { 464 incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; 465 ci = utfc_next(ci); 466 total += incr; 467 vcol += incr; 468 } 469 bd.textstart = ci.ptr; 470 bd.start_vcol = vcol; 471 472 int tabs = 0; 473 int spaces = 0; 474 // OK, now total=all the VWS reqd, and textstart points at the 1st 475 // non-ws char in the block. 476 if (!curbuf->b_p_et) { 477 tabstop_fromto(ws_vcol, ws_vcol + total, 478 ts_val, curbuf->b_p_vts_array, &tabs, &spaces); 479 } else { 480 spaces = total; 481 } 482 483 // if we're splitting a TAB, allow for it 484 const int col_pre = bd.pre_whitesp_c - (bd.startspaces != 0); 485 bd.textcol -= col_pre; 486 487 const int new_line_len // the length of the line after the block shift 488 = bd.textcol + tabs + spaces + (old_line_len - (int)(bd.textstart - oldp)); 489 newp = xmalloc((size_t)new_line_len + 1); 490 memmove(newp, oldp, (size_t)bd.textcol); 491 startcol = bd.textcol; 492 oldlen = (int)(bd.textstart - old_textstart) + col_pre; 493 newlen = tabs + spaces; 494 memset(newp + bd.textcol, TAB, (size_t)tabs); 495 memset(newp + bd.textcol + tabs, ' ', (size_t)spaces); 496 STRCPY(newp + bd.textcol + tabs + spaces, bd.textstart); 497 assert(newlen - oldlen == new_line_len - old_line_len); 498 } else { // left 499 char *verbatim_copy_end; // end of the part of the line which is 500 // copied verbatim 501 colnr_T verbatim_copy_width; // the (displayed) width of this part 502 // of line 503 char *non_white = bd.textstart; 504 505 // Firstly, let's find the first non-whitespace character that is 506 // displayed after the block's start column and the character's column 507 // number. Also, let's calculate the width of all the whitespace 508 // characters that are displayed in the block and precede the searched 509 // non-whitespace character. 510 511 // If "bd.startspaces" is set, "bd.textstart" points to the character, 512 // the part of which is displayed at the block's beginning. Let's start 513 // searching from the next character. 514 if (bd.startspaces) { 515 MB_PTR_ADV(non_white); 516 } 517 518 // The character's column is in "bd.start_vcol". 519 colnr_T non_white_col = bd.start_vcol; 520 521 CharsizeArg csarg; 522 CSType cstype = init_charsize_arg(&csarg, curwin, curwin->w_cursor.lnum, bd.textstart); 523 while (ascii_iswhite(*non_white)) { 524 incr = win_charsize(cstype, non_white_col, non_white, (uint8_t)(*non_white), &csarg).width; 525 non_white_col += incr; 526 non_white++; 527 } 528 529 const colnr_T block_space_width = non_white_col - oap->start_vcol; 530 // We will shift by "total" or "block_space_width", whichever is less. 531 const colnr_T shift_amount = MIN(block_space_width, total); 532 // The column to which we will shift the text. 533 const colnr_T destination_col = non_white_col - shift_amount; 534 535 // Now let's find out how much of the beginning of the line we can 536 // reuse without modification. 537 verbatim_copy_end = bd.textstart; 538 verbatim_copy_width = bd.start_vcol; 539 540 // If "bd.startspaces" is set, "bd.textstart" points to the character 541 // preceding the block. We have to subtract its width to obtain its 542 // column number. 543 if (bd.startspaces) { 544 verbatim_copy_width -= bd.start_char_vcols; 545 } 546 cstype = init_charsize_arg(&csarg, curwin, 0, bd.textstart); 547 StrCharInfo ci = utf_ptr2StrCharInfo(verbatim_copy_end); 548 while (verbatim_copy_width < destination_col) { 549 incr = win_charsize(cstype, verbatim_copy_width, ci.ptr, ci.chr.value, &csarg).width; 550 if (verbatim_copy_width + incr > destination_col) { 551 break; 552 } 553 verbatim_copy_width += incr; 554 ci = utfc_next(ci); 555 } 556 verbatim_copy_end = ci.ptr; 557 558 // If "destination_col" is different from the width of the initial 559 // part of the line that will be copied, it means we encountered a tab 560 // character, which we will have to partly replace with spaces. 561 assert(destination_col - verbatim_copy_width >= 0); 562 const int fill // nr of spaces that replace a TAB 563 = destination_col - verbatim_copy_width; 564 565 assert(verbatim_copy_end - oldp >= 0); 566 // length of string left of the shift position (ie the string not being shifted) 567 const int fixedlen = (int)(verbatim_copy_end - oldp); 568 // The replacement line will consist of: 569 // - the beginning of the original line up to "verbatim_copy_end", 570 // - "fill" number of spaces, 571 // - the rest of the line, pointed to by non_white. 572 const int new_line_len // the length of the line after the block shift 573 = fixedlen + fill + (old_line_len - (int)(non_white - oldp)); 574 575 newp = xmalloc((size_t)new_line_len + 1); 576 startcol = fixedlen; 577 oldlen = bd.textcol + (int)(non_white - bd.textstart) - fixedlen; 578 newlen = fill; 579 memmove(newp, oldp, (size_t)fixedlen); 580 memset(newp + fixedlen, ' ', (size_t)fill); 581 STRCPY(newp + fixedlen + fill, non_white); 582 assert(newlen - oldlen == new_line_len - old_line_len); 583 } 584 // replace the line 585 ml_replace(curwin->w_cursor.lnum, newp, false); 586 changed_bytes(curwin->w_cursor.lnum, bd.textcol); 587 extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, startcol, 588 oldlen, newlen, 589 kExtmarkUndo); 590 State = oldstate; 591 curwin->w_cursor.col = oldcol; 592 p_ri = old_p_ri; 593 } 594 595 /// Insert string "s" (b_insert ? before : after) block :AKelly 596 /// Caller must prepare for undo. 597 static void block_insert(oparg_T *oap, const char *s, size_t slen, bool b_insert, 598 struct block_def *bdp) 599 { 600 int ts_val; 601 int count = 0; // extra spaces to replace a cut TAB 602 int spaces = 0; // non-zero if cutting a TAB 603 colnr_T offset; // pointer along new line 604 char *newp, *oldp; // new, old lines 605 int oldstate = State; 606 State = MODE_INSERT; // don't want MODE_REPLACE for State 607 608 for (linenr_T lnum = oap->start.lnum + 1; lnum <= oap->end.lnum; lnum++) { 609 block_prep(oap, bdp, lnum, true); 610 if (bdp->is_short && b_insert) { 611 continue; // OP_INSERT, line ends before block start 612 } 613 614 oldp = ml_get(lnum); 615 616 if (b_insert) { 617 ts_val = bdp->start_char_vcols; 618 spaces = bdp->startspaces; 619 if (spaces != 0) { 620 count = ts_val - 1; // we're cutting a TAB 621 } 622 offset = bdp->textcol; 623 } else { // append 624 ts_val = bdp->end_char_vcols; 625 if (!bdp->is_short) { // spaces = padding after block 626 spaces = (bdp->endspaces ? ts_val - bdp->endspaces : 0); 627 if (spaces != 0) { 628 count = ts_val - 1; // we're cutting a TAB 629 } 630 offset = bdp->textcol + bdp->textlen - (spaces != 0); 631 } else { // spaces = padding to block edge 632 // if $ used, just append to EOL (ie spaces==0) 633 if (!bdp->is_MAX) { 634 spaces = (oap->end_vcol - bdp->end_vcol) + 1; 635 } 636 count = spaces; 637 offset = bdp->textcol + bdp->textlen; 638 } 639 } 640 641 if (spaces > 0) { 642 // avoid copying part of a multi-byte character 643 offset -= utf_head_off(oldp, oldp + offset); 644 } 645 spaces = MAX(spaces, 0); // can happen when the cursor was moved 646 647 assert(count >= 0); 648 // Make sure the allocated size matches what is actually copied below. 649 newp = xmalloc((size_t)ml_get_len(lnum) + (size_t)spaces + slen 650 + (spaces > 0 && !bdp->is_short ? (size_t)(ts_val - spaces) : 0) 651 + (size_t)count + 1); 652 653 // copy up to shifted part 654 memmove(newp, oldp, (size_t)offset); 655 oldp += offset; 656 int startcol = offset; 657 658 // insert pre-padding 659 memset(newp + offset, ' ', (size_t)spaces); 660 661 // copy the new text 662 memmove(newp + offset + spaces, s, slen); 663 offset += (int)slen; 664 665 int skipped = 0; 666 if (spaces > 0 && !bdp->is_short) { 667 if (*oldp == TAB) { 668 // insert post-padding 669 memset(newp + offset + spaces, ' ', (size_t)(ts_val - spaces)); 670 // We're splitting a TAB, don't copy it. 671 oldp++; 672 // We allowed for that TAB, remember this now 673 count++; 674 skipped = 1; 675 } else { 676 // Not a TAB, no extra spaces 677 count = spaces; 678 } 679 } 680 681 if (spaces > 0) { 682 offset += count; 683 } 684 STRCPY(newp + offset, oldp); 685 686 ml_replace(lnum, newp, false); 687 extmark_splice_cols(curbuf, (int)lnum - 1, startcol, 688 skipped, offset - startcol, kExtmarkUndo); 689 690 if (lnum == oap->end.lnum) { 691 // Set "']" mark to the end of the block instead of the end of 692 // the insert in the first line. 693 curbuf->b_op_end.lnum = oap->end.lnum; 694 curbuf->b_op_end.col = offset; 695 if (curbuf->b_visual.vi_end.coladd) { 696 curbuf->b_visual.vi_end.col += curbuf->b_visual.vi_end.coladd; 697 curbuf->b_visual.vi_end.coladd = 0; 698 } 699 } 700 } // for all lnum 701 702 State = oldstate; 703 704 // Only call changed_lines if we actually modified additional lines beyond the first 705 // This matches the condition for the for loop above: start + 1 <= end 706 if (oap->start.lnum < oap->end.lnum) { 707 changed_lines(curbuf, oap->start.lnum + 1, 0, oap->end.lnum + 1, 0, true); 708 } 709 } 710 711 /// Handle a delete operation. 712 /// 713 /// @return FAIL if undo failed, OK otherwise. 714 int op_delete(oparg_T *oap) 715 { 716 linenr_T lnum; 717 struct block_def bd = { 0 }; 718 linenr_T old_lcount = curbuf->b_ml.ml_line_count; 719 720 if (curbuf->b_ml.ml_flags & ML_EMPTY) { // nothing to do 721 return OK; 722 } 723 724 // Nothing to delete, return here. Do prepare undo, for op_change(). 725 if (oap->empty) { 726 return u_save_cursor(); 727 } 728 729 if (!MODIFIABLE(curbuf)) { 730 emsg(_(e_modifiable)); 731 return FAIL; 732 } 733 734 if (VIsual_select && oap->is_VIsual) { 735 // Use the register given with CTRL_R, defaults to zero 736 oap->regname = VIsual_select_reg; 737 } 738 739 mb_adjust_opend(oap); 740 741 // Imitate the strange Vi behaviour: If the delete spans more than one 742 // line and motion_type == kMTCharWise and the result is a blank line, make the 743 // delete linewise. Don't do this for the change command or Visual mode. 744 if (oap->motion_type == kMTCharWise 745 && !oap->is_VIsual 746 && oap->line_count > 1 747 && oap->motion_force == NUL 748 && oap->op_type == OP_DELETE) { 749 char *ptr = ml_get(oap->end.lnum) + oap->end.col; 750 if (*ptr != NUL) { 751 ptr += oap->inclusive; 752 } 753 ptr = skipwhite(ptr); 754 if (*ptr == NUL && inindent(0)) { 755 oap->motion_type = kMTLineWise; 756 } 757 } 758 759 // Check for trying to delete (e.g. "D") in an empty line. 760 // Note: For the change operator it is ok. 761 if (oap->motion_type != kMTLineWise 762 && oap->line_count == 1 763 && oap->op_type == OP_DELETE 764 && *ml_get(oap->start.lnum) == NUL) { 765 // It's an error to operate on an empty region, when 'E' included in 766 // 'cpoptions' (Vi compatible). 767 if (virtual_op) { 768 // Virtual editing: Nothing gets deleted, but we set the '[ and '] 769 // marks as if it happened. 770 goto setmarks; 771 } 772 if (vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL) { 773 beep_flush(); 774 } 775 return OK; 776 } 777 778 // Do a yank of whatever we're about to delete. 779 // If a yank register was specified, put the deleted text into that 780 // register. For the black hole register '_' don't yank anything. 781 if (oap->regname != '_') { 782 yankreg_T *reg = NULL; 783 bool did_yank = false; 784 if (oap->regname != 0) { 785 // check for read-only register 786 if (!valid_yank_reg(oap->regname, true)) { 787 beep_flush(); 788 return OK; 789 } 790 reg = get_yank_register(oap->regname, YREG_YANK); // yank into specif'd reg 791 op_yank_reg(oap, false, reg, is_append_register(oap->regname)); // yank without message 792 did_yank = true; 793 } 794 795 // Put deleted text into register 1 and shift number registers if the 796 // delete contains a line break, or when using a specific operator (Vi 797 // compatible) 798 799 if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) { 800 shift_delete_registers(is_append_register(oap->regname)); 801 reg = get_y_register(1); 802 op_yank_reg(oap, false, reg, false); 803 did_yank = true; 804 } 805 806 // Yank into small delete register when no named register specified 807 // and the delete is within one line. 808 if (oap->regname == 0 && oap->motion_type != kMTLineWise 809 && oap->line_count == 1) { 810 reg = get_yank_register('-', YREG_YANK); 811 op_yank_reg(oap, false, reg, false); 812 did_yank = true; 813 } 814 815 if (did_yank || oap->regname == 0) { 816 if (reg == NULL) { 817 abort(); 818 } 819 set_clipboard(oap->regname, reg); 820 do_autocmd_textyankpost(oap, reg); 821 } 822 } 823 824 // block mode delete 825 if (oap->motion_type == kMTBlockWise) { 826 if (u_save((linenr_T)(oap->start.lnum - 1), 827 (linenr_T)(oap->end.lnum + 1)) == FAIL) { 828 return FAIL; 829 } 830 831 for (lnum = curwin->w_cursor.lnum; lnum <= oap->end.lnum; lnum++) { 832 block_prep(oap, &bd, lnum, true); 833 if (bd.textlen == 0) { // nothing to delete 834 continue; 835 } 836 837 // Adjust cursor position for tab replaced by spaces and 'lbr'. 838 if (lnum == curwin->w_cursor.lnum) { 839 curwin->w_cursor.col = bd.textcol + bd.startspaces; 840 curwin->w_cursor.coladd = 0; 841 } 842 843 // "n" == number of chars deleted 844 // If we delete a TAB, it may be replaced by several characters. 845 // Thus the number of characters may increase! 846 int n = bd.textlen - bd.startspaces - bd.endspaces; 847 char *oldp = ml_get(lnum); 848 char *newp = xmalloc((size_t)ml_get_len(lnum) - (size_t)n + 1); 849 // copy up to deleted part 850 memmove(newp, oldp, (size_t)bd.textcol); 851 // insert spaces 852 memset(newp + bd.textcol, ' ', (size_t)bd.startspaces + 853 (size_t)bd.endspaces); 854 // copy the part after the deleted part 855 STRCPY(newp + bd.textcol + bd.startspaces + bd.endspaces, 856 oldp + bd.textcol + bd.textlen); 857 // replace the line 858 ml_replace(lnum, newp, false); 859 860 extmark_splice_cols(curbuf, (int)lnum - 1, bd.textcol, 861 bd.textlen, bd.startspaces + bd.endspaces, 862 kExtmarkUndo); 863 } 864 865 check_cursor_col(curwin); 866 changed_lines(curbuf, curwin->w_cursor.lnum, curwin->w_cursor.col, 867 oap->end.lnum + 1, 0, true); 868 oap->line_count = 0; // no lines deleted 869 } else if (oap->motion_type == kMTLineWise) { 870 if (oap->op_type == OP_CHANGE) { 871 // Delete the lines except the first one. Temporarily move the 872 // cursor to the next line. Save the current line number, if the 873 // last line is deleted it may be changed. 874 875 if (oap->line_count > 1) { 876 lnum = curwin->w_cursor.lnum; 877 curwin->w_cursor.lnum++; 878 del_lines(oap->line_count - 1, true); 879 curwin->w_cursor.lnum = lnum; 880 } 881 if (u_save_cursor() == FAIL) { 882 return FAIL; 883 } 884 if (curbuf->b_p_ai) { // don't delete indent 885 beginline(BL_WHITE); // cursor on first non-white 886 did_ai = true; // delete the indent when ESC hit 887 ai_col = curwin->w_cursor.col; 888 } else { 889 beginline(0); // cursor in column 0 890 } 891 truncate_line(false); // delete the rest of the line, 892 // leaving cursor past last char in line 893 if (oap->line_count > 1) { 894 u_clearline(curbuf); // "U" command not possible after "2cc" 895 } 896 } else { 897 del_lines(oap->line_count, true); 898 beginline(BL_WHITE | BL_FIX); 899 u_clearline(curbuf); // "U" command not possible after "dd" 900 } 901 } else { 902 if (virtual_op) { 903 // For virtualedit: break the tabs that are partly included. 904 if (gchar_pos(&oap->start) == '\t') { 905 int endcol = 0; 906 if (u_save_cursor() == FAIL) { // save first line for undo 907 return FAIL; 908 } 909 if (oap->line_count == 1) { 910 endcol = getviscol2(oap->end.col, oap->end.coladd); 911 } 912 coladvance_force(getviscol2(oap->start.col, oap->start.coladd)); 913 oap->start = curwin->w_cursor; 914 if (oap->line_count == 1) { 915 coladvance(curwin, endcol); 916 oap->end.col = curwin->w_cursor.col; 917 oap->end.coladd = curwin->w_cursor.coladd; 918 curwin->w_cursor = oap->start; 919 } 920 } 921 922 // Break a tab only when it's included in the area. 923 if (gchar_pos(&oap->end) == '\t' 924 && oap->end.coladd == 0 925 && oap->inclusive) { 926 // save last line for undo 927 if (u_save((linenr_T)(oap->end.lnum - 1), 928 (linenr_T)(oap->end.lnum + 1)) == FAIL) { 929 return FAIL; 930 } 931 curwin->w_cursor = oap->end; 932 coladvance_force(getviscol2(oap->end.col, oap->end.coladd)); 933 oap->end = curwin->w_cursor; 934 curwin->w_cursor = oap->start; 935 } 936 mb_adjust_opend(oap); 937 } 938 939 if (oap->line_count == 1) { // delete characters within one line 940 if (u_save_cursor() == FAIL) { // save line for undo 941 return FAIL; 942 } 943 944 // if 'cpoptions' contains '$', display '$' at end of change 945 if (vim_strchr(p_cpo, CPO_DOLLAR) != NULL 946 && oap->op_type == OP_CHANGE 947 && oap->end.lnum == curwin->w_cursor.lnum 948 && !oap->is_VIsual) { 949 display_dollar(oap->end.col - !oap->inclusive); 950 } 951 952 int n = oap->end.col - oap->start.col + 1 - !oap->inclusive; 953 954 if (virtual_op) { 955 // fix up things for virtualedit-delete: 956 // break the tabs which are going to get in our way 957 int len = get_cursor_line_len(); 958 959 if (oap->end.coladd != 0 960 && (int)oap->end.col >= len - 1 961 && !(oap->start.coladd && (int)oap->end.col >= len - 1)) { 962 n++; 963 } 964 // Delete at least one char (e.g, when on a control char). 965 if (n == 0 && oap->start.coladd != oap->end.coladd) { 966 n = 1; 967 } 968 969 // When deleted a char in the line, reset coladd. 970 if (gchar_cursor() != NUL) { 971 curwin->w_cursor.coladd = 0; 972 } 973 } 974 975 del_bytes((colnr_T)n, !virtual_op, 976 oap->op_type == OP_DELETE && !oap->is_VIsual); 977 } else { 978 // delete characters between lines 979 pos_T curpos; 980 981 // save deleted and changed lines for undo 982 if (u_save(curwin->w_cursor.lnum - 1, 983 curwin->w_cursor.lnum + oap->line_count) == FAIL) { 984 return FAIL; 985 } 986 987 curbuf_splice_pending++; 988 pos_T startpos = curwin->w_cursor; // start position for delete 989 bcount_t deleted_bytes = get_region_bytecount(curbuf, startpos.lnum, oap->end.lnum, 990 startpos.col, 991 oap->end.col) + oap->inclusive; 992 truncate_line(true); // delete from cursor to end of line 993 994 curpos = curwin->w_cursor; // remember curwin->w_cursor 995 curwin->w_cursor.lnum++; 996 997 del_lines(oap->line_count - 2, false); 998 999 // delete from start of line until op_end 1000 int n = (oap->end.col + 1 - !oap->inclusive); 1001 curwin->w_cursor.col = 0; 1002 del_bytes((colnr_T)n, !virtual_op, 1003 oap->op_type == OP_DELETE && !oap->is_VIsual); 1004 curwin->w_cursor = curpos; // restore curwin->w_cursor 1005 do_join(2, false, false, false, false); 1006 curbuf_splice_pending--; 1007 extmark_splice(curbuf, (int)startpos.lnum - 1, startpos.col, 1008 (int)oap->line_count - 1, n, deleted_bytes, 1009 0, 0, 0, kExtmarkUndo); 1010 } 1011 if (oap->op_type == OP_DELETE) { 1012 auto_format(false, true); 1013 } 1014 } 1015 1016 msgmore(curbuf->b_ml.ml_line_count - old_lcount); 1017 1018 setmarks: 1019 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { 1020 if (oap->motion_type == kMTBlockWise) { 1021 curbuf->b_op_end.lnum = oap->end.lnum; 1022 curbuf->b_op_end.col = oap->start.col; 1023 } else { 1024 curbuf->b_op_end = oap->start; 1025 } 1026 curbuf->b_op_start = oap->start; 1027 } 1028 1029 return OK; 1030 } 1031 1032 /// Adjust end of operating area for ending on a multi-byte character. 1033 /// Used for deletion. 1034 static void mb_adjust_opend(oparg_T *oap) 1035 { 1036 if (!oap->inclusive) { 1037 return; 1038 } 1039 1040 const char *line = ml_get(oap->end.lnum); 1041 const char *ptr = line + oap->end.col; 1042 if (*ptr != NUL) { 1043 ptr -= utf_head_off(line, ptr); 1044 ptr += utfc_ptr2len(ptr) - 1; 1045 oap->end.col = (colnr_T)(ptr - line); 1046 } 1047 } 1048 1049 /// put byte 'c' at position 'lp', but 1050 /// verify, that the position to place 1051 /// is actually safe 1052 static void pbyte(pos_T lp, int c) 1053 { 1054 assert(c <= UCHAR_MAX); 1055 char *p = ml_get_buf_mut(curbuf, lp.lnum); 1056 colnr_T len = curbuf->b_ml.ml_line_textlen; 1057 1058 // safety check 1059 if (lp.col >= len) { 1060 lp.col = (len > 1 ? len - 2 : 0); 1061 } 1062 *(p + lp.col) = (char)c; 1063 if (!curbuf_splice_pending) { 1064 extmark_splice_cols(curbuf, (int)lp.lnum - 1, lp.col, 1, 1, kExtmarkUndo); 1065 } 1066 } 1067 1068 /// Replace the character under the cursor with "c". 1069 /// This takes care of multi-byte characters. 1070 static void replace_character(int c) 1071 { 1072 const int n = State; 1073 1074 State = MODE_REPLACE; 1075 ins_char(c); 1076 State = n; 1077 // Backup to the replaced character. 1078 dec_cursor(); 1079 } 1080 1081 /// Replace a whole area with one character. 1082 static int op_replace(oparg_T *oap, int c) 1083 { 1084 int n; 1085 struct block_def bd; 1086 char *after_p = NULL; 1087 bool had_ctrl_v_cr = false; 1088 1089 if ((curbuf->b_ml.ml_flags & ML_EMPTY) || oap->empty) { 1090 return OK; // nothing to do 1091 } 1092 if (c == REPLACE_CR_NCHAR) { 1093 had_ctrl_v_cr = true; 1094 c = CAR; 1095 } else if (c == REPLACE_NL_NCHAR) { 1096 had_ctrl_v_cr = true; 1097 c = NL; 1098 } 1099 1100 mb_adjust_opend(oap); 1101 1102 if (u_save((linenr_T)(oap->start.lnum - 1), 1103 (linenr_T)(oap->end.lnum + 1)) == FAIL) { 1104 return FAIL; 1105 } 1106 1107 // block mode replace 1108 if (oap->motion_type == kMTBlockWise) { 1109 bd.is_MAX = (curwin->w_curswant == MAXCOL); 1110 for (; curwin->w_cursor.lnum <= oap->end.lnum; curwin->w_cursor.lnum++) { 1111 curwin->w_cursor.col = 0; // make sure cursor position is valid 1112 block_prep(oap, &bd, curwin->w_cursor.lnum, true); 1113 if (bd.textlen == 0 && (!virtual_op || bd.is_MAX)) { 1114 continue; // nothing to replace 1115 } 1116 1117 // n == number of extra chars required 1118 // If we split a TAB, it may be replaced by several characters. 1119 // Thus the number of characters may increase! 1120 // If the range starts in virtual space, count the initial 1121 // coladd offset as part of "startspaces" 1122 if (virtual_op && bd.is_short && *bd.textstart == NUL) { 1123 pos_T vpos; 1124 1125 vpos.lnum = curwin->w_cursor.lnum; 1126 getvpos(curwin, &vpos, oap->start_vcol); 1127 bd.startspaces += vpos.coladd; 1128 n = bd.startspaces; 1129 } else { 1130 // allow for pre spaces 1131 n = (bd.startspaces ? bd.start_char_vcols - 1 : 0); 1132 } 1133 1134 // allow for post spp 1135 n += (bd.endspaces 1136 && !bd.is_oneChar 1137 && bd.end_char_vcols > 0) ? bd.end_char_vcols - 1 : 0; 1138 // Figure out how many characters to replace. 1139 int numc = oap->end_vcol - oap->start_vcol + 1; 1140 if (bd.is_short && (!virtual_op || bd.is_MAX)) { 1141 numc -= (oap->end_vcol - bd.end_vcol) + 1; 1142 } 1143 1144 // A double-wide character can be replaced only up to half the 1145 // times. 1146 if (utf_char2cells(c) > 1) { 1147 if ((numc & 1) && !bd.is_short) { 1148 bd.endspaces++; 1149 n++; 1150 } 1151 numc = numc / 2; 1152 } 1153 1154 // Compute bytes needed, move character count to num_chars. 1155 int num_chars = numc; 1156 numc *= utf_char2len(c); 1157 1158 char *oldp = get_cursor_line_ptr(); 1159 colnr_T oldlen = get_cursor_line_len(); 1160 1161 size_t newp_size = (size_t)bd.textcol + (size_t)bd.startspaces; 1162 if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { 1163 newp_size += (size_t)numc; 1164 if (!bd.is_short) { 1165 newp_size += (size_t)(bd.endspaces + oldlen 1166 - bd.textcol - bd.textlen); 1167 } 1168 } 1169 char *newp = xmallocz(newp_size); 1170 // copy up to deleted part 1171 memmove(newp, oldp, (size_t)bd.textcol); 1172 oldp += bd.textcol + bd.textlen; 1173 // insert pre-spaces 1174 memset(newp + bd.textcol, ' ', (size_t)bd.startspaces); 1175 // insert replacement chars CHECK FOR ALLOCATED SPACE 1176 // REPLACE_CR_NCHAR/REPLACE_NL_NCHAR is used for entering CR literally. 1177 size_t after_p_len = 0; 1178 int col = oldlen - bd.textcol - bd.textlen + 1; 1179 assert(col >= 0); 1180 int newrows = 0; 1181 int newcols = 0; 1182 if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { 1183 // strlen(newp) at this point 1184 int newp_len = bd.textcol + bd.startspaces; 1185 while (--num_chars >= 0) { 1186 newp_len += utf_char2bytes(c, newp + newp_len); 1187 } 1188 if (!bd.is_short) { 1189 // insert post-spaces 1190 memset(newp + newp_len, ' ', (size_t)bd.endspaces); 1191 newp_len += bd.endspaces; 1192 // copy the part after the changed part 1193 memmove(newp + newp_len, oldp, (size_t)col); 1194 } 1195 newcols = newp_len - bd.textcol; 1196 } else { 1197 // Replacing with \r or \n means splitting the line. 1198 after_p_len = (size_t)col; 1199 after_p = xmalloc(after_p_len); 1200 memmove(after_p, oldp, after_p_len); 1201 newrows = 1; 1202 } 1203 // replace the line 1204 ml_replace(curwin->w_cursor.lnum, newp, false); 1205 curbuf_splice_pending++; 1206 linenr_T baselnum = curwin->w_cursor.lnum; 1207 if (after_p != NULL) { 1208 ml_append(curwin->w_cursor.lnum++, after_p, (int)after_p_len, false); 1209 appended_lines_mark(curwin->w_cursor.lnum, 1); 1210 oap->end.lnum++; 1211 xfree(after_p); 1212 } 1213 curbuf_splice_pending--; 1214 extmark_splice(curbuf, (int)baselnum - 1, bd.textcol, 1215 0, bd.textlen, bd.textlen, 1216 newrows, newcols, newrows + newcols, kExtmarkUndo); 1217 } 1218 } else { 1219 // Characterwise or linewise motion replace. 1220 if (oap->motion_type == kMTLineWise) { 1221 oap->start.col = 0; 1222 curwin->w_cursor.col = 0; 1223 oap->end.col = ml_get_len(oap->end.lnum); 1224 if (oap->end.col) { 1225 oap->end.col--; 1226 } 1227 } else if (!oap->inclusive) { 1228 dec(&(oap->end)); 1229 } 1230 1231 // TODO(bfredl): we could batch all the splicing 1232 // done on the same line, at least 1233 while (ltoreq(curwin->w_cursor, oap->end)) { 1234 bool done = false; 1235 1236 n = gchar_cursor(); 1237 if (n != NUL) { 1238 int new_byte_len = utf_char2len(c); 1239 int old_byte_len = utfc_ptr2len(get_cursor_pos_ptr()); 1240 1241 if (new_byte_len > 1 || old_byte_len > 1) { 1242 // This is slow, but it handles replacing a single-byte 1243 // with a multi-byte and the other way around. 1244 if (curwin->w_cursor.lnum == oap->end.lnum) { 1245 oap->end.col += new_byte_len - old_byte_len; 1246 } 1247 replace_character(c); 1248 done = true; 1249 } else { 1250 if (n == TAB) { 1251 int end_vcol = 0; 1252 1253 if (curwin->w_cursor.lnum == oap->end.lnum) { 1254 // oap->end has to be recalculated when 1255 // the tab breaks 1256 end_vcol = getviscol2(oap->end.col, 1257 oap->end.coladd); 1258 } 1259 coladvance_force(getviscol()); 1260 if (curwin->w_cursor.lnum == oap->end.lnum) { 1261 getvpos(curwin, &oap->end, end_vcol); 1262 } 1263 } 1264 // with "coladd" set may move to just after a TAB 1265 if (gchar_cursor() != NUL) { 1266 pbyte(curwin->w_cursor, c); 1267 done = true; 1268 } 1269 } 1270 } 1271 if (!done && virtual_op && curwin->w_cursor.lnum == oap->end.lnum) { 1272 int virtcols = oap->end.coladd; 1273 1274 if (curwin->w_cursor.lnum == oap->start.lnum 1275 && oap->start.col == oap->end.col && oap->start.coladd) { 1276 virtcols -= oap->start.coladd; 1277 } 1278 1279 // oap->end has been trimmed so it's effectively inclusive; 1280 // as a result an extra +1 must be counted so we don't 1281 // trample the NUL byte. 1282 coladvance_force(getviscol2(oap->end.col, oap->end.coladd) + 1); 1283 curwin->w_cursor.col -= (virtcols + 1); 1284 for (; virtcols >= 0; virtcols--) { 1285 if (utf_char2len(c) > 1) { 1286 replace_character(c); 1287 } else { 1288 pbyte(curwin->w_cursor, c); 1289 } 1290 if (inc(&curwin->w_cursor) == -1) { 1291 break; 1292 } 1293 } 1294 } 1295 1296 // Advance to next character, stop at the end of the file. 1297 if (inc_cursor() == -1) { 1298 break; 1299 } 1300 } 1301 } 1302 1303 curwin->w_cursor = oap->start; 1304 check_cursor(curwin); 1305 changed_lines(curbuf, oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0, true); 1306 1307 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { 1308 // Set "'[" and "']" marks. 1309 curbuf->b_op_start = oap->start; 1310 curbuf->b_op_end = oap->end; 1311 } 1312 1313 return OK; 1314 } 1315 1316 /// Handle the (non-standard vi) tilde operator. Also for "gu", "gU" and "g?". 1317 void op_tilde(oparg_T *oap) 1318 { 1319 struct block_def bd; 1320 bool did_change = false; 1321 1322 if (u_save((linenr_T)(oap->start.lnum - 1), 1323 (linenr_T)(oap->end.lnum + 1)) == FAIL) { 1324 return; 1325 } 1326 1327 pos_T pos = oap->start; 1328 if (oap->motion_type == kMTBlockWise) { // Visual block mode 1329 for (; pos.lnum <= oap->end.lnum; pos.lnum++) { 1330 block_prep(oap, &bd, pos.lnum, false); 1331 pos.col = bd.textcol; 1332 bool one_change = swapchars(oap->op_type, &pos, bd.textlen); 1333 did_change |= one_change; 1334 } 1335 if (did_change) { 1336 changed_lines(curbuf, oap->start.lnum, 0, oap->end.lnum + 1, 0, true); 1337 } 1338 } else { // not block mode 1339 if (oap->motion_type == kMTLineWise) { 1340 oap->start.col = 0; 1341 pos.col = 0; 1342 oap->end.col = ml_get_len(oap->end.lnum); 1343 if (oap->end.col) { 1344 oap->end.col--; 1345 } 1346 } else if (!oap->inclusive) { 1347 dec(&(oap->end)); 1348 } 1349 1350 if (pos.lnum == oap->end.lnum) { 1351 did_change = swapchars(oap->op_type, &pos, 1352 oap->end.col - pos.col + 1); 1353 } else { 1354 while (true) { 1355 did_change |= swapchars(oap->op_type, &pos, 1356 pos.lnum == oap->end.lnum ? oap->end.col + 1 1357 : ml_get_pos_len(&pos)); 1358 if (ltoreq(oap->end, pos) || inc(&pos) == -1) { 1359 break; 1360 } 1361 } 1362 } 1363 if (did_change) { 1364 changed_lines(curbuf, oap->start.lnum, oap->start.col, oap->end.lnum + 1, 1365 0, true); 1366 } 1367 } 1368 1369 if (!did_change && oap->is_VIsual) { 1370 // No change: need to remove the Visual selection 1371 redraw_curbuf_later(UPD_INVERTED); 1372 } 1373 1374 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { 1375 // Set '[ and '] marks. 1376 curbuf->b_op_start = oap->start; 1377 curbuf->b_op_end = oap->end; 1378 } 1379 1380 if (oap->line_count > p_report) { 1381 smsg(0, NGETTEXT("%" PRId64 " line changed", "%" PRId64 " lines changed", oap->line_count), 1382 (int64_t)oap->line_count); 1383 } 1384 } 1385 1386 /// Invoke swapchar() on "length" bytes at position "pos". 1387 /// 1388 /// @param pos is advanced to just after the changed characters. 1389 /// @param length is rounded up to include the whole last multi-byte character. 1390 /// Also works correctly when the number of bytes changes. 1391 /// 1392 /// @return true if some character was changed. 1393 static int swapchars(int op_type, pos_T *pos, int length) 1394 FUNC_ATTR_NONNULL_ALL 1395 { 1396 int did_change = 0; 1397 1398 for (int todo = length; todo > 0; todo--) { 1399 const int len = utfc_ptr2len(ml_get_pos(pos)); 1400 1401 // we're counting bytes, not characters 1402 if (len > 0) { 1403 todo -= len - 1; 1404 } 1405 did_change |= swapchar(op_type, pos); 1406 if (inc(pos) == -1) { // at end of file 1407 break; 1408 } 1409 } 1410 return did_change; 1411 } 1412 1413 /// @param op_type 1414 /// == OP_UPPER: make uppercase, 1415 /// == OP_LOWER: make lowercase, 1416 /// == OP_ROT13: do rot13 encoding, 1417 /// else swap case of character at 'pos' 1418 /// 1419 /// @return true when something actually changed. 1420 bool swapchar(int op_type, pos_T *pos) 1421 FUNC_ATTR_NONNULL_ARG(2) 1422 { 1423 const int c = gchar_pos(pos); 1424 1425 // Only do rot13 encoding for ASCII characters. 1426 if (c >= 0x80 && op_type == OP_ROT13) { 1427 return false; 1428 } 1429 1430 int nc = c; 1431 if (mb_islower(c)) { 1432 if (op_type == OP_ROT13) { 1433 nc = ROT13(c, 'a'); 1434 } else if (op_type != OP_LOWER) { 1435 nc = mb_toupper(c); 1436 } 1437 } else if (mb_isupper(c)) { 1438 if (op_type == OP_ROT13) { 1439 nc = ROT13(c, 'A'); 1440 } else if (op_type != OP_UPPER) { 1441 nc = mb_tolower(c); 1442 } 1443 } 1444 if (nc != c) { 1445 if (c >= 0x80 || nc >= 0x80) { 1446 pos_T sp = curwin->w_cursor; 1447 1448 curwin->w_cursor = *pos; 1449 // don't use del_char(), it also removes composing chars 1450 del_bytes(utf_ptr2len(get_cursor_pos_ptr()), false, false); 1451 ins_char(nc); 1452 curwin->w_cursor = sp; 1453 } else { 1454 pbyte(*pos, nc); 1455 } 1456 return true; 1457 } 1458 return false; 1459 } 1460 1461 /// Insert and append operators for Visual mode. 1462 void op_insert(oparg_T *oap, int count1) 1463 { 1464 int pre_textlen = 0; 1465 colnr_T ind_pre_col = 0; 1466 int ind_pre_vcol = 0; 1467 struct block_def bd; 1468 1469 // edit() changes this - record it for OP_APPEND 1470 bd.is_MAX = (curwin->w_curswant == MAXCOL); 1471 1472 // vis block is still marked. Get rid of it now. 1473 curwin->w_cursor.lnum = oap->start.lnum; 1474 redraw_curbuf_later(UPD_INVERTED); 1475 update_screen(); 1476 1477 if (oap->motion_type == kMTBlockWise) { 1478 // When 'virtualedit' is used, need to insert the extra spaces before 1479 // doing block_prep(). When only "block" is used, virtual edit is 1480 // already disabled, but still need it when calling 1481 // coladvance_force(). 1482 // coladvance_force() uses get_ve_flags() to get the 'virtualedit' 1483 // state for the current window. To override that state, we need to 1484 // set the window-local value of ve_flags rather than the global value. 1485 if (curwin->w_cursor.coladd > 0) { 1486 unsigned old_ve_flags = curwin->w_ve_flags; 1487 1488 if (u_save_cursor() == FAIL) { 1489 return; 1490 } 1491 curwin->w_ve_flags = kOptVeFlagAll; 1492 coladvance_force(oap->op_type == OP_APPEND 1493 ? oap->end_vcol + 1 : getviscol()); 1494 if (oap->op_type == OP_APPEND) { 1495 curwin->w_cursor.col--; 1496 } 1497 curwin->w_ve_flags = old_ve_flags; 1498 } 1499 // Get the info about the block before entering the text 1500 block_prep(oap, &bd, oap->start.lnum, true); 1501 // Get indent information 1502 ind_pre_col = (colnr_T)getwhitecols_curline(); 1503 ind_pre_vcol = get_indent(); 1504 pre_textlen = ml_get_len(oap->start.lnum) - bd.textcol; 1505 if (oap->op_type == OP_APPEND) { 1506 pre_textlen -= bd.textlen; 1507 } 1508 } 1509 1510 if (oap->op_type == OP_APPEND) { 1511 if (oap->motion_type == kMTBlockWise 1512 && curwin->w_cursor.coladd == 0) { 1513 // Move the cursor to the character right of the block. 1514 curwin->w_set_curswant = true; 1515 while (*get_cursor_pos_ptr() != NUL 1516 && (curwin->w_cursor.col < bd.textcol + bd.textlen)) { 1517 curwin->w_cursor.col++; 1518 } 1519 if (bd.is_short && !bd.is_MAX) { 1520 // First line was too short, make it longer and adjust the 1521 // values in "bd". 1522 if (u_save_cursor() == FAIL) { 1523 return; 1524 } 1525 for (int i = 0; i < bd.endspaces; i++) { 1526 ins_char(' '); 1527 } 1528 bd.textlen += bd.endspaces; 1529 } 1530 } else { 1531 curwin->w_cursor = oap->end; 1532 check_cursor_col(curwin); 1533 1534 // Works just like an 'i'nsert on the next character. 1535 if (!LINEEMPTY(curwin->w_cursor.lnum) 1536 && oap->start_vcol != oap->end_vcol) { 1537 inc_cursor(); 1538 } 1539 } 1540 } 1541 1542 pos_T t1 = oap->start; 1543 const pos_T start_insert = curwin->w_cursor; 1544 edit(NUL, false, (linenr_T)count1); 1545 1546 // When a tab was inserted, and the characters in front of the tab 1547 // have been converted to a tab as well, the column of the cursor 1548 // might have actually been reduced, so need to adjust here. 1549 if (t1.lnum == curbuf->b_op_start_orig.lnum 1550 && lt(curbuf->b_op_start_orig, t1)) { 1551 oap->start = curbuf->b_op_start_orig; 1552 } 1553 1554 // If user has moved off this line, we don't know what to do, so do 1555 // nothing. 1556 // Also don't repeat the insert when Insert mode ended with CTRL-C. 1557 if (curwin->w_cursor.lnum != oap->start.lnum || got_int) { 1558 return; 1559 } 1560 1561 if (oap->motion_type == kMTBlockWise) { 1562 int ind_post_vcol = 0; 1563 struct block_def bd2; 1564 bool did_indent = false; 1565 1566 // if indent kicked in, the firstline might have changed 1567 // but only do that, if the indent actually increased 1568 colnr_T ind_post_col = (colnr_T)getwhitecols_curline(); 1569 if (curbuf->b_op_start.col > ind_pre_col && ind_post_col > ind_pre_col) { 1570 bd.textcol += ind_post_col - ind_pre_col; 1571 ind_post_vcol = get_indent(); 1572 bd.start_vcol += ind_post_vcol - ind_pre_vcol; 1573 did_indent = true; 1574 } 1575 1576 // The user may have moved the cursor before inserting something, try 1577 // to adjust the block for that. But only do it, if the difference 1578 // does not come from indent kicking in. 1579 if (oap->start.lnum == curbuf->b_op_start_orig.lnum && !bd.is_MAX && !did_indent) { 1580 const int t = getviscol2(curbuf->b_op_start_orig.col, curbuf->b_op_start_orig.coladd); 1581 1582 if (oap->op_type == OP_INSERT 1583 && oap->start.col + oap->start.coladd 1584 != curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { 1585 oap->start.col = curbuf->b_op_start_orig.col; 1586 pre_textlen -= t - oap->start_vcol; 1587 oap->start_vcol = t; 1588 } else if (oap->op_type == OP_APPEND 1589 && oap->start.col + oap->start.coladd 1590 >= curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { 1591 oap->start.col = curbuf->b_op_start_orig.col; 1592 // reset pre_textlen to the value of OP_INSERT 1593 pre_textlen += bd.textlen; 1594 pre_textlen -= t - oap->start_vcol; 1595 oap->start_vcol = t; 1596 oap->op_type = OP_INSERT; 1597 } 1598 } 1599 1600 // Spaces and tabs in the indent may have changed to other spaces and 1601 // tabs. Get the starting column again and correct the length. 1602 // Don't do this when "$" used, end-of-line will have changed. 1603 // 1604 // if indent was added and the inserted text was after the indent, 1605 // correct the selection for the new indent. 1606 if (did_indent && bd.textcol - ind_post_col > 0) { 1607 oap->start.col += ind_post_col - ind_pre_col; 1608 oap->start_vcol += ind_post_vcol - ind_pre_vcol; 1609 oap->end.col += ind_post_col - ind_pre_col; 1610 oap->end_vcol += ind_post_vcol - ind_pre_vcol; 1611 } 1612 block_prep(oap, &bd2, oap->start.lnum, true); 1613 if (did_indent && bd.textcol - ind_post_col > 0) { 1614 // undo for where "oap" is used below 1615 oap->start.col -= ind_post_col - ind_pre_col; 1616 oap->start_vcol -= ind_post_vcol - ind_pre_vcol; 1617 oap->end.col -= ind_post_col - ind_pre_col; 1618 oap->end_vcol -= ind_post_vcol - ind_pre_vcol; 1619 } 1620 if (!bd.is_MAX || bd2.textlen < bd.textlen) { 1621 if (oap->op_type == OP_APPEND) { 1622 pre_textlen += bd2.textlen - bd.textlen; 1623 if (bd2.endspaces) { 1624 bd2.textlen--; 1625 } 1626 } 1627 bd.textcol = bd2.textcol; 1628 bd.textlen = bd2.textlen; 1629 } 1630 1631 // Subsequent calls to ml_get() flush the firstline data - take a 1632 // copy of the required string. 1633 char *firstline = ml_get(oap->start.lnum); 1634 colnr_T len = ml_get_len(oap->start.lnum); 1635 colnr_T add = bd.textcol; 1636 colnr_T offset = 0; // offset when cursor was moved in insert mode 1637 if (oap->op_type == OP_APPEND) { 1638 add += bd.textlen; 1639 // account for pressing cursor in insert mode when '$' was used 1640 if (bd.is_MAX && start_insert.lnum == Insstart.lnum && start_insert.col > Insstart.col) { 1641 offset = start_insert.col - Insstart.col; 1642 add -= offset; 1643 if (oap->end_vcol > offset) { 1644 oap->end_vcol -= offset + 1; 1645 } else { 1646 // moved outside of the visual block, what to do? 1647 return; 1648 } 1649 } 1650 } 1651 add = MIN(add, len); // short line, point to the NUL 1652 firstline += add; 1653 len -= add; 1654 int ins_len = len - pre_textlen - offset; 1655 if (pre_textlen >= 0 && ins_len > 0) { 1656 char *ins_text = xmemdupz(firstline, (size_t)ins_len); 1657 // block handled here 1658 if (u_save(oap->start.lnum, (linenr_T)(oap->end.lnum + 1)) == OK) { 1659 block_insert(oap, ins_text, (size_t)ins_len, (oap->op_type == OP_INSERT), &bd); 1660 } 1661 1662 curwin->w_cursor.col = oap->start.col; 1663 check_cursor(curwin); 1664 xfree(ins_text); 1665 } 1666 } 1667 } 1668 1669 /// handle a change operation 1670 /// 1671 /// @return true if edit() returns because of a CTRL-O command 1672 int op_change(oparg_T *oap) 1673 { 1674 int pre_textlen = 0; 1675 int pre_indent = 0; 1676 char *firstline; 1677 struct block_def bd; 1678 1679 colnr_T l = oap->start.col; 1680 if (oap->motion_type == kMTLineWise) { 1681 l = 0; 1682 can_si = may_do_si(); // Like opening a new line, do smart indent 1683 } 1684 1685 // First delete the text in the region. In an empty buffer only need to 1686 // save for undo 1687 if (curbuf->b_ml.ml_flags & ML_EMPTY) { 1688 if (u_save_cursor() == FAIL) { 1689 return false; 1690 } 1691 } else if (op_delete(oap) == FAIL) { 1692 return false; 1693 } 1694 1695 if ((l > curwin->w_cursor.col) && !LINEEMPTY(curwin->w_cursor.lnum) 1696 && !virtual_op) { 1697 inc_cursor(); 1698 } 1699 1700 // check for still on same line (<CR> in inserted text meaningless) 1701 // skip blank lines too 1702 if (oap->motion_type == kMTBlockWise) { 1703 // Add spaces before getting the current line length. 1704 if (virtual_op && (curwin->w_cursor.coladd > 0 1705 || gchar_cursor() == NUL)) { 1706 coladvance_force(getviscol()); 1707 } 1708 firstline = ml_get(oap->start.lnum); 1709 pre_textlen = ml_get_len(oap->start.lnum); 1710 pre_indent = (int)getwhitecols(firstline); 1711 bd.textcol = curwin->w_cursor.col; 1712 } 1713 1714 if (oap->motion_type == kMTLineWise) { 1715 fix_indent(); 1716 } 1717 1718 // Reset finish_op now, don't want it set inside edit(). 1719 const bool save_finish_op = finish_op; 1720 finish_op = false; 1721 1722 int retval = edit(NUL, false, 1); 1723 1724 finish_op = save_finish_op; 1725 1726 // In Visual block mode, handle copying the new text to all lines of the 1727 // block. 1728 // Don't repeat the insert when Insert mode ended with CTRL-C. 1729 if (oap->motion_type == kMTBlockWise 1730 && oap->start.lnum != oap->end.lnum && !got_int) { 1731 // Auto-indenting may have changed the indent. If the cursor was past 1732 // the indent, exclude that indent change from the inserted text. 1733 firstline = ml_get(oap->start.lnum); 1734 if (bd.textcol > (colnr_T)pre_indent) { 1735 int new_indent = (int)getwhitecols(firstline); 1736 1737 pre_textlen += new_indent - pre_indent; 1738 bd.textcol += (colnr_T)(new_indent - pre_indent); 1739 } 1740 1741 int ins_len = ml_get_len(oap->start.lnum) - pre_textlen; 1742 if (ins_len > 0) { 1743 // Subsequent calls to ml_get() flush the firstline data - take a 1744 // copy of the inserted text. 1745 char *ins_text = xmalloc((size_t)ins_len + 1); 1746 xmemcpyz(ins_text, firstline + bd.textcol, (size_t)ins_len); 1747 for (linenr_T linenr = oap->start.lnum + 1; linenr <= oap->end.lnum; 1748 linenr++) { 1749 block_prep(oap, &bd, linenr, true); 1750 if (!bd.is_short || virtual_op) { 1751 pos_T vpos; 1752 1753 // If the block starts in virtual space, count the 1754 // initial coladd offset as part of "startspaces" 1755 if (bd.is_short) { 1756 vpos.lnum = linenr; 1757 getvpos(curwin, &vpos, oap->start_vcol); 1758 } else { 1759 vpos.coladd = 0; 1760 } 1761 char *oldp = ml_get(linenr); 1762 char *newp = xmalloc((size_t)ml_get_len(linenr) 1763 + (size_t)vpos.coladd + (size_t)ins_len + 1); 1764 // copy up to block start 1765 memmove(newp, oldp, (size_t)bd.textcol); 1766 int newlen = bd.textcol; 1767 memset(newp + newlen, ' ', (size_t)vpos.coladd); 1768 newlen += vpos.coladd; 1769 memmove(newp + newlen, ins_text, (size_t)ins_len); 1770 newlen += ins_len; 1771 STRCPY(newp + newlen, oldp + bd.textcol); 1772 ml_replace(linenr, newp, false); 1773 extmark_splice_cols(curbuf, (int)linenr - 1, bd.textcol, 1774 0, vpos.coladd + ins_len, kExtmarkUndo); 1775 } 1776 } 1777 check_cursor(curwin); 1778 changed_lines(curbuf, oap->start.lnum + 1, 0, oap->end.lnum + 1, 0, true); 1779 xfree(ins_text); 1780 } 1781 } 1782 auto_format(false, true); 1783 1784 return retval; 1785 } 1786 1787 /// When the cursor is on the NUL past the end of the line and it should not be 1788 /// there move it left. 1789 void adjust_cursor_eol(void) 1790 { 1791 unsigned cur_ve_flags = get_ve_flags(curwin); 1792 1793 const bool adj_cursor = (curwin->w_cursor.col > 0 1794 && gchar_cursor() == NUL 1795 && (cur_ve_flags & kOptVeFlagOnemore) == 0 1796 && (cur_ve_flags & kOptVeFlagAll) == 0 1797 && !(restart_edit || (State & MODE_INSERT))); 1798 if (!adj_cursor) { 1799 return; 1800 } 1801 1802 // Put the cursor on the last character in the line. 1803 dec_cursor(); 1804 1805 if (cur_ve_flags == kOptVeFlagAll) { 1806 colnr_T scol, ecol; 1807 1808 // Coladd is set to the width of the last character. 1809 getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol); 1810 curwin->w_cursor.coladd = ecol - scol + 1; 1811 } 1812 } 1813 1814 /// If \p "process" is true and the line begins with a comment leader (possibly 1815 /// after some white space), return a pointer to the text after it. 1816 /// Put a boolean value indicating whether the line ends with an unclosed 1817 /// comment in "is_comment". 1818 /// 1819 /// @param line - line to be processed 1820 /// @param process - if false, will only check whether the line ends 1821 /// with an unclosed comment, 1822 /// @param include_space - whether to skip space following the comment leader 1823 /// @param[out] is_comment - whether the current line ends with an unclosed 1824 /// comment. 1825 char *skip_comment(char *line, bool process, bool include_space, bool *is_comment) 1826 { 1827 char *comment_flags = NULL; 1828 int leader_offset = get_last_leader_offset(line, &comment_flags); 1829 1830 *is_comment = false; 1831 if (leader_offset != -1) { 1832 // Let's check whether the line ends with an unclosed comment. 1833 // If the last comment leader has COM_END in flags, there's no comment. 1834 while (*comment_flags) { 1835 if (*comment_flags == COM_END 1836 || *comment_flags == ':') { 1837 break; 1838 } 1839 comment_flags++; 1840 } 1841 if (*comment_flags != COM_END) { 1842 *is_comment = true; 1843 } 1844 } 1845 1846 if (process == false) { 1847 return line; 1848 } 1849 1850 int lead_len = get_leader_len(line, &comment_flags, false, include_space); 1851 1852 if (lead_len == 0) { 1853 return line; 1854 } 1855 1856 // Find: 1857 // - COM_END, 1858 // - colon, 1859 // whichever comes first. 1860 while (*comment_flags) { 1861 if (*comment_flags == COM_END 1862 || *comment_flags == ':') { 1863 break; 1864 } 1865 comment_flags++; 1866 } 1867 1868 // If we found a colon, it means that we are not processing a line 1869 // starting with a closing part of a three-part comment. That's good, 1870 // because we don't want to remove those as this would be annoying. 1871 if (*comment_flags == ':' || *comment_flags == NUL) { 1872 line += lead_len; 1873 } 1874 1875 return line; 1876 } 1877 1878 /// @param count number of lines (minimal 2) to join at the cursor position. 1879 /// @param save_undo when true, save lines for undo first. 1880 /// @param use_formatoptions set to false when e.g. processing backspace and comment 1881 /// leaders should not be removed. 1882 /// @param setmark when true, sets the '[ and '] mark, else, the caller is expected 1883 /// to set those marks. 1884 /// 1885 /// @return FAIL for failure, OK otherwise 1886 int do_join(size_t count, bool insert_space, bool save_undo, bool use_formatoptions, bool setmark) 1887 { 1888 char *curr = NULL; 1889 char *curr_start = NULL; 1890 char *cend; 1891 int endcurr1 = NUL; 1892 int endcurr2 = NUL; 1893 int currsize = 0; // size of the current line 1894 int sumsize = 0; // size of the long new line 1895 int ret = OK; 1896 int *comments = NULL; 1897 bool remove_comments = use_formatoptions && has_format_option(FO_REMOVE_COMS); 1898 bool prev_was_comment = false; 1899 assert(count >= 1); 1900 1901 if (save_undo && u_save(curwin->w_cursor.lnum - 1, 1902 curwin->w_cursor.lnum + (linenr_T)count) == FAIL) { 1903 return FAIL; 1904 } 1905 // Allocate an array to store the number of spaces inserted before each 1906 // line. We will use it to pre-compute the length of the new line and the 1907 // proper placement of each original line in the new one. 1908 char *spaces = xcalloc(count, 1); // number of spaces inserted before a line 1909 if (remove_comments) { 1910 comments = xcalloc(count, sizeof(*comments)); 1911 } 1912 1913 // Don't move anything yet, just compute the final line length 1914 // and setup the array of space strings lengths 1915 // This loops forward over joined lines. 1916 for (linenr_T t = 0; t < (linenr_T)count; t++) { 1917 curr_start = ml_get(curwin->w_cursor.lnum + t); 1918 curr = curr_start; 1919 if (t == 0 && setmark && (cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { 1920 // Set the '[ mark. 1921 curwin->w_buffer->b_op_start.lnum = curwin->w_cursor.lnum; 1922 curwin->w_buffer->b_op_start.col = (colnr_T)strlen(curr); 1923 } 1924 if (remove_comments) { 1925 // We don't want to remove the comment leader if the 1926 // previous line is not a comment. 1927 if (t > 0 && prev_was_comment) { 1928 char *new_curr = skip_comment(curr, true, insert_space, &prev_was_comment); 1929 comments[t] = (int)(new_curr - curr); 1930 curr = new_curr; 1931 } else { 1932 curr = skip_comment(curr, false, insert_space, &prev_was_comment); 1933 } 1934 } 1935 1936 if (insert_space && t > 0) { 1937 curr = skipwhite(curr); 1938 if (*curr != NUL 1939 && *curr != ')' 1940 && sumsize != 0 1941 && endcurr1 != TAB 1942 && (!has_format_option(FO_MBYTE_JOIN) 1943 || (utf_ptr2char(curr) < 0x100 && endcurr1 < 0x100)) 1944 && (!has_format_option(FO_MBYTE_JOIN2) 1945 || (utf_ptr2char(curr) < 0x100 && !utf_eat_space(endcurr1)) 1946 || (endcurr1 < 0x100 1947 && !utf_eat_space(utf_ptr2char(curr))))) { 1948 // don't add a space if the line is ending in a space 1949 if (endcurr1 == ' ') { 1950 endcurr1 = endcurr2; 1951 } else { 1952 spaces[t]++; 1953 } 1954 // Extra space when 'joinspaces' set and line ends in '.', '?', or '!'. 1955 if (p_js && (endcurr1 == '.' || endcurr1 == '?' || endcurr1 == '!')) { 1956 spaces[t]++; 1957 } 1958 } 1959 } 1960 1961 if (t > 0 && curbuf_splice_pending == 0) { 1962 colnr_T removed = (int)(curr - curr_start); 1963 extmark_splice(curbuf, (int)curwin->w_cursor.lnum - 1, sumsize, 1964 1, removed, removed + 1, 1965 0, spaces[t], spaces[t], 1966 kExtmarkUndo); 1967 } 1968 currsize = (int)strlen(curr); 1969 sumsize += currsize + spaces[t]; 1970 endcurr1 = endcurr2 = NUL; 1971 if (insert_space && currsize > 0) { 1972 cend = curr + currsize; 1973 MB_PTR_BACK(curr, cend); 1974 endcurr1 = utf_ptr2char(cend); 1975 if (cend > curr) { 1976 MB_PTR_BACK(curr, cend); 1977 endcurr2 = utf_ptr2char(cend); 1978 } 1979 } 1980 line_breakcheck(); 1981 if (got_int) { 1982 ret = FAIL; 1983 goto theend; 1984 } 1985 } 1986 1987 // store the column position before last line 1988 colnr_T col = sumsize - currsize - spaces[count - 1]; 1989 1990 // allocate the space for the new line 1991 size_t newp_len = (size_t)sumsize; 1992 char *newp = xmallocz(newp_len); 1993 cend = newp + sumsize; 1994 1995 // Move affected lines to the new long one. 1996 // This loops backwards over the joined lines, including the original line. 1997 // 1998 // Move marks from each deleted line to the joined line, adjusting the 1999 // column. This is not Vi compatible, but Vi deletes the marks, thus that 2000 // should not really be a problem. 2001 2002 curbuf_splice_pending++; 2003 2004 for (linenr_T t = (linenr_T)count - 1;; t--) { 2005 cend -= currsize; 2006 memmove(cend, curr, (size_t)currsize); 2007 2008 if (spaces[t] > 0) { 2009 cend -= spaces[t]; 2010 memset(cend, ' ', (size_t)(spaces[t])); 2011 } 2012 2013 // If deleting more spaces than adding, the cursor moves no more than 2014 // what is added if it is inside these spaces. 2015 const int spaces_removed = (int)((curr - curr_start) - spaces[t]); 2016 linenr_T lnum = curwin->w_cursor.lnum + t; 2017 colnr_T mincol = 0; 2018 linenr_T lnum_amount = -t; 2019 colnr_T col_amount = (colnr_T)(cend - newp - spaces_removed); 2020 2021 mark_col_adjust(lnum, mincol, lnum_amount, col_amount, spaces_removed); 2022 2023 if (t == 0) { 2024 break; 2025 } 2026 2027 curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1)); 2028 curr = curr_start; 2029 if (remove_comments) { 2030 curr += comments[t - 1]; 2031 } 2032 if (insert_space && t > 1) { 2033 curr = skipwhite(curr); 2034 } 2035 currsize = (int)strlen(curr); 2036 } 2037 2038 ml_replace_len(curwin->w_cursor.lnum, newp, newp_len, false); 2039 2040 if (setmark && (cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { 2041 // Set the '] mark. 2042 curwin->w_buffer->b_op_end.lnum = curwin->w_cursor.lnum; 2043 curwin->w_buffer->b_op_end.col = sumsize; 2044 } 2045 2046 // Only report the change in the first line here, del_lines() will report 2047 // the deleted line. 2048 changed_lines(curbuf, curwin->w_cursor.lnum, currsize, 2049 curwin->w_cursor.lnum + 1, 0, true); 2050 2051 // Delete following lines. To do this we move the cursor there 2052 // briefly, and then move it back. After del_lines() the cursor may 2053 // have moved up (last line deleted), so the current lnum is kept in t. 2054 linenr_T t = curwin->w_cursor.lnum; 2055 curwin->w_cursor.lnum++; 2056 del_lines((int)count - 1, false); 2057 curwin->w_cursor.lnum = t; 2058 curbuf_splice_pending--; 2059 curbuf->deleted_bytes2 = 0; 2060 2061 // Set the cursor column: 2062 // Vi compatible: use the column of the first join 2063 // vim: use the column of the last join 2064 curwin->w_cursor.col = 2065 (vim_strchr(p_cpo, CPO_JOINCOL) != NULL ? currsize : col); 2066 check_cursor_col(curwin); 2067 2068 curwin->w_cursor.coladd = 0; 2069 curwin->w_set_curswant = true; 2070 2071 theend: 2072 xfree(spaces); 2073 if (remove_comments) { 2074 xfree(comments); 2075 } 2076 return ret; 2077 } 2078 2079 /// Reset 'linebreak' and take care of side effects. 2080 /// @return the previous value, to be passed to restore_lbr(). 2081 bool reset_lbr(void) 2082 { 2083 if (!curwin->w_p_lbr) { 2084 return false; 2085 } 2086 // changing 'linebreak' may require w_virtcol to be updated 2087 curwin->w_p_lbr = false; 2088 curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); 2089 return true; 2090 } 2091 2092 /// Restore 'linebreak' and take care of side effects. 2093 void restore_lbr(bool lbr_saved) 2094 { 2095 if (curwin->w_p_lbr || !lbr_saved) { 2096 return; 2097 } 2098 2099 // changing 'linebreak' may require w_virtcol to be updated 2100 curwin->w_p_lbr = true; 2101 curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); 2102 } 2103 2104 /// prepare a few things for block mode yank/delete/tilde 2105 /// 2106 /// for delete: 2107 /// - textlen includes the first/last char to be (partly) deleted 2108 /// - start/endspaces is the number of columns that are taken by the 2109 /// first/last deleted char minus the number of columns that have to be 2110 /// deleted. 2111 /// for yank and tilde: 2112 /// - textlen includes the first/last char to be wholly yanked 2113 /// - start/endspaces is the number of columns of the first/last yanked char 2114 /// that are to be yanked. 2115 void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool is_del) 2116 { 2117 int incr = 0; 2118 // Avoid a problem with unwanted linebreaks in block mode. 2119 const bool lbr_saved = reset_lbr(); 2120 2121 bdp->startspaces = 0; 2122 bdp->endspaces = 0; 2123 bdp->textlen = 0; 2124 bdp->start_vcol = 0; 2125 bdp->end_vcol = 0; 2126 bdp->is_short = false; 2127 bdp->is_oneChar = false; 2128 bdp->pre_whitesp = 0; 2129 bdp->pre_whitesp_c = 0; 2130 bdp->end_char_vcols = 0; 2131 bdp->start_char_vcols = 0; 2132 2133 char *line = ml_get(lnum); 2134 char *prev_pstart = line; 2135 2136 CharsizeArg csarg; 2137 CSType cstype = init_charsize_arg(&csarg, curwin, lnum, line); 2138 StrCharInfo ci = utf_ptr2StrCharInfo(line); 2139 int vcol = bdp->start_vcol; 2140 while (vcol < oap->start_vcol && *ci.ptr != NUL) { 2141 incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; 2142 vcol += incr; 2143 if (ascii_iswhite(ci.chr.value)) { 2144 bdp->pre_whitesp += incr; 2145 bdp->pre_whitesp_c++; 2146 } else { 2147 bdp->pre_whitesp = 0; 2148 bdp->pre_whitesp_c = 0; 2149 } 2150 prev_pstart = ci.ptr; 2151 ci = utfc_next(ci); 2152 } 2153 bdp->start_vcol = vcol; 2154 char *pstart = ci.ptr; 2155 2156 bdp->start_char_vcols = incr; 2157 if (bdp->start_vcol < oap->start_vcol) { // line too short 2158 bdp->end_vcol = bdp->start_vcol; 2159 bdp->is_short = true; 2160 if (!is_del || oap->op_type == OP_APPEND) { 2161 bdp->endspaces = oap->end_vcol - oap->start_vcol + 1; 2162 } 2163 } else { 2164 // notice: this converts partly selected Multibyte characters to 2165 // spaces, too. 2166 bdp->startspaces = bdp->start_vcol - oap->start_vcol; 2167 if (is_del && bdp->startspaces) { 2168 bdp->startspaces = bdp->start_char_vcols - bdp->startspaces; 2169 } 2170 char *pend = pstart; 2171 bdp->end_vcol = bdp->start_vcol; 2172 if (bdp->end_vcol > oap->end_vcol) { // it's all in one character 2173 bdp->is_oneChar = true; 2174 if (oap->op_type == OP_INSERT) { 2175 bdp->endspaces = bdp->start_char_vcols - bdp->startspaces; 2176 } else if (oap->op_type == OP_APPEND) { 2177 bdp->startspaces += oap->end_vcol - oap->start_vcol + 1; 2178 bdp->endspaces = bdp->start_char_vcols - bdp->startspaces; 2179 } else { 2180 bdp->startspaces = oap->end_vcol - oap->start_vcol + 1; 2181 if (is_del && oap->op_type != OP_LSHIFT) { 2182 // just putting the sum of those two into 2183 // bdp->startspaces doesn't work for Visual replace, 2184 // so we have to split the tab in two 2185 bdp->startspaces = bdp->start_char_vcols 2186 - (bdp->start_vcol - oap->start_vcol); 2187 bdp->endspaces = bdp->end_vcol - oap->end_vcol - 1; 2188 } 2189 } 2190 } else { 2191 cstype = init_charsize_arg(&csarg, curwin, lnum, line); 2192 ci = utf_ptr2StrCharInfo(pend); 2193 vcol = bdp->end_vcol; 2194 char *prev_pend = pend; 2195 while (vcol <= oap->end_vcol && *ci.ptr != NUL) { 2196 prev_pend = ci.ptr; 2197 incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; 2198 vcol += incr; 2199 ci = utfc_next(ci); 2200 } 2201 bdp->end_vcol = vcol; 2202 pend = ci.ptr; 2203 2204 if (bdp->end_vcol <= oap->end_vcol 2205 && (!is_del 2206 || oap->op_type == OP_APPEND 2207 || oap->op_type == OP_REPLACE)) { // line too short 2208 bdp->is_short = true; 2209 // Alternative: include spaces to fill up the block. 2210 // Disadvantage: can lead to trailing spaces when the line is 2211 // short where the text is put 2212 // if (!is_del || oap->op_type == OP_APPEND) 2213 if (oap->op_type == OP_APPEND || virtual_op) { 2214 bdp->endspaces = oap->end_vcol - bdp->end_vcol 2215 + oap->inclusive; 2216 } 2217 } else if (bdp->end_vcol > oap->end_vcol) { 2218 bdp->endspaces = bdp->end_vcol - oap->end_vcol - 1; 2219 if (!is_del && bdp->endspaces) { 2220 bdp->endspaces = incr - bdp->endspaces; 2221 if (pend != pstart) { 2222 pend = prev_pend; 2223 } 2224 } 2225 } 2226 } 2227 bdp->end_char_vcols = incr; 2228 if (is_del && bdp->startspaces) { 2229 pstart = prev_pstart; 2230 } 2231 bdp->textlen = (int)(pend - pstart); 2232 } 2233 bdp->textcol = (colnr_T)(pstart - line); 2234 bdp->textstart = pstart; 2235 restore_lbr(lbr_saved); 2236 } 2237 2238 /// Get block text from "start" to "end" 2239 void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T lnum, 2240 bool inclusive) 2241 { 2242 colnr_T startcol = 0; 2243 colnr_T endcol = MAXCOL; 2244 colnr_T cs, ce; 2245 char *p = ml_get(lnum); 2246 int plen = ml_get_len(lnum); 2247 2248 bdp->startspaces = 0; 2249 bdp->endspaces = 0; 2250 bdp->is_oneChar = false; 2251 bdp->start_char_vcols = 0; 2252 2253 if (lnum == start.lnum) { 2254 startcol = start.col; 2255 if (virtual_op) { 2256 getvcol(curwin, &start, &cs, NULL, &ce); 2257 if (ce != cs && start.coladd > 0) { 2258 // Part of a tab selected -- but don't double-count it. 2259 bdp->start_char_vcols = ce - cs + 1; 2260 bdp->startspaces = MAX(bdp->start_char_vcols - start.coladd, 0); 2261 startcol++; 2262 } 2263 } 2264 } 2265 2266 if (lnum == end.lnum) { 2267 endcol = end.col; 2268 if (virtual_op) { 2269 getvcol(curwin, &end, &cs, NULL, &ce); 2270 if (p[endcol] == NUL || (cs + end.coladd < ce 2271 // Don't add space for double-wide 2272 // char; endcol will be on last byte 2273 // of multi-byte char. 2274 && utf_head_off(p, p + endcol) == 0)) { 2275 if (start.lnum == end.lnum && start.col == end.col) { 2276 // Special case: inside a single char 2277 bdp->is_oneChar = true; 2278 bdp->startspaces = end.coladd - start.coladd + inclusive; 2279 endcol = startcol; 2280 } else { 2281 bdp->endspaces = end.coladd + inclusive; 2282 endcol -= inclusive; 2283 } 2284 } 2285 } 2286 } 2287 if (endcol == MAXCOL) { 2288 endcol = ml_get_len(lnum); 2289 } 2290 if (startcol > endcol || bdp->is_oneChar) { 2291 bdp->textlen = 0; 2292 } else { 2293 bdp->textlen = endcol - startcol + inclusive; 2294 } 2295 bdp->textcol = startcol; 2296 bdp->textstart = startcol <= plen ? p + startcol : p; 2297 } 2298 2299 /// Handle the add/subtract operator. 2300 /// 2301 /// @param[in] oap Arguments of operator. 2302 /// @param[in] Prenum1 Amount of addition or subtraction. 2303 /// @param[in] g_cmd Prefixed with `g`. 2304 void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) 2305 { 2306 struct block_def bd; 2307 ssize_t change_cnt = 0; 2308 linenr_T amount = Prenum1; 2309 2310 // do_addsub() might trigger re-evaluation of 'foldexpr' halfway, when the 2311 // buffer is not completely updated yet. Postpone updating folds until before 2312 // the call to changed_lines(). 2313 disable_fold_update++; 2314 2315 if (!VIsual_active) { 2316 pos_T pos = curwin->w_cursor; 2317 if (u_save_cursor() == FAIL) { 2318 disable_fold_update--; 2319 return; 2320 } 2321 change_cnt = do_addsub(oap->op_type, &pos, 0, amount); 2322 disable_fold_update--; 2323 if (change_cnt) { 2324 changed_lines(curbuf, pos.lnum, 0, pos.lnum + 1, 0, true); 2325 } 2326 } else { 2327 int length; 2328 pos_T startpos; 2329 2330 if (u_save((linenr_T)(oap->start.lnum - 1), 2331 (linenr_T)(oap->end.lnum + 1)) == FAIL) { 2332 disable_fold_update--; 2333 return; 2334 } 2335 2336 pos_T pos = oap->start; 2337 for (; pos.lnum <= oap->end.lnum; pos.lnum++) { 2338 if (oap->motion_type == kMTBlockWise) { 2339 // Visual block mode 2340 block_prep(oap, &bd, pos.lnum, false); 2341 pos.col = bd.textcol; 2342 length = bd.textlen; 2343 } else if (oap->motion_type == kMTLineWise) { 2344 curwin->w_cursor.col = 0; 2345 pos.col = 0; 2346 length = ml_get_len(pos.lnum); 2347 } else { 2348 // oap->motion_type == kMTCharWise 2349 if (pos.lnum == oap->start.lnum && !oap->inclusive) { 2350 dec(&(oap->end)); 2351 } 2352 length = ml_get_len(pos.lnum); 2353 pos.col = 0; 2354 if (pos.lnum == oap->start.lnum) { 2355 pos.col += oap->start.col; 2356 length -= oap->start.col; 2357 } 2358 if (pos.lnum == oap->end.lnum) { 2359 length = ml_get_len(oap->end.lnum); 2360 oap->end.col = MIN(oap->end.col, length - 1); 2361 length = oap->end.col - pos.col + 1; 2362 } 2363 } 2364 bool one_change = do_addsub(oap->op_type, &pos, length, amount); 2365 if (one_change) { 2366 // Remember the start position of the first change. 2367 if (change_cnt == 0) { 2368 startpos = curbuf->b_op_start; 2369 } 2370 change_cnt++; 2371 } 2372 2373 if (g_cmd && one_change) { 2374 amount += Prenum1; 2375 } 2376 } 2377 2378 disable_fold_update--; 2379 if (change_cnt) { 2380 changed_lines(curbuf, oap->start.lnum, 0, oap->end.lnum + 1, 0, true); 2381 } 2382 2383 if (!change_cnt && oap->is_VIsual) { 2384 // No change: need to remove the Visual selection 2385 redraw_curbuf_later(UPD_INVERTED); 2386 } 2387 2388 // Set '[ mark if something changed. Keep the last end 2389 // position from do_addsub(). 2390 if (change_cnt > 0 && (cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { 2391 curbuf->b_op_start = startpos; 2392 } 2393 2394 if (change_cnt > p_report) { 2395 smsg(0, NGETTEXT("%" PRId64 " lines changed", "%" PRId64 " lines changed", change_cnt), 2396 (int64_t)change_cnt); 2397 } 2398 } 2399 } 2400 2401 /// Add or subtract from a number in a line. 2402 /// 2403 /// @param op_type OP_NR_ADD or OP_NR_SUB. 2404 /// @param pos Cursor position. 2405 /// @param length Target number length. 2406 /// @param Prenum1 Amount of addition or subtraction. 2407 /// 2408 /// @return true if some character was changed. 2409 bool do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) 2410 { 2411 int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin 2412 static bool hexupper = false; // 0xABC 2413 uvarnumber_T n; 2414 bool blank_unsigned = false; // blank: treat as unsigned? 2415 bool negative = false; 2416 bool was_positive = true; 2417 bool visual = VIsual_active; 2418 bool did_change = false; 2419 pos_T save_cursor = curwin->w_cursor; 2420 int maxlen = 0; 2421 pos_T startpos; 2422 pos_T endpos; 2423 colnr_T save_coladd = 0; 2424 2425 const bool do_hex = vim_strchr(curbuf->b_p_nf, 'x') != NULL; // "heX" 2426 const bool do_oct = vim_strchr(curbuf->b_p_nf, 'o') != NULL; // "Octal" 2427 const bool do_bin = vim_strchr(curbuf->b_p_nf, 'b') != NULL; // "Bin" 2428 const bool do_alpha = vim_strchr(curbuf->b_p_nf, 'p') != NULL; // "alPha" 2429 const bool do_unsigned = vim_strchr(curbuf->b_p_nf, 'u') != NULL; // "Unsigned" 2430 const bool do_blank = vim_strchr(curbuf->b_p_nf, 'k') != NULL; // "blanK" 2431 2432 if (virtual_active(curwin)) { 2433 save_coladd = pos->coladd; 2434 pos->coladd = 0; 2435 } 2436 2437 curwin->w_cursor = *pos; 2438 char *ptr = ml_get(pos->lnum); 2439 int linelen = ml_get_len(pos->lnum); 2440 int col = pos->col; 2441 2442 if (col + !!save_coladd >= linelen) { 2443 goto theend; 2444 } 2445 2446 // First check if we are on a hexadecimal number, after the "0x". 2447 if (!VIsual_active) { 2448 if (do_bin) { 2449 while (col > 0 && ascii_isbdigit(ptr[col])) { 2450 col--; 2451 col -= utf_head_off(ptr, ptr + col); 2452 } 2453 } 2454 2455 if (do_hex) { 2456 while (col > 0 && ascii_isxdigit(ptr[col])) { 2457 col--; 2458 col -= utf_head_off(ptr, ptr + col); 2459 } 2460 } 2461 if (do_bin 2462 && do_hex 2463 && !((col > 0 2464 && (ptr[col] == 'X' || ptr[col] == 'x') 2465 && ptr[col - 1] == '0' 2466 && !utf_head_off(ptr, ptr + col - 1) 2467 && ascii_isxdigit(ptr[col + 1])))) { 2468 // In case of binary/hexadecimal pattern overlap match, rescan 2469 2470 col = curwin->w_cursor.col; 2471 2472 while (col > 0 && ascii_isdigit(ptr[col])) { 2473 col--; 2474 col -= utf_head_off(ptr, ptr + col); 2475 } 2476 } 2477 2478 if ((do_hex 2479 && col > 0 2480 && (ptr[col] == 'X' || ptr[col] == 'x') 2481 && ptr[col - 1] == '0' 2482 && !utf_head_off(ptr, ptr + col - 1) 2483 && ascii_isxdigit(ptr[col + 1])) 2484 || (do_bin 2485 && col > 0 2486 && (ptr[col] == 'B' || ptr[col] == 'b') 2487 && ptr[col - 1] == '0' 2488 && !utf_head_off(ptr, ptr + col - 1) 2489 && ascii_isbdigit(ptr[col + 1]))) { 2490 // Found hexadecimal or binary number, move to its start. 2491 col--; 2492 col -= utf_head_off(ptr, ptr + col); 2493 } else { 2494 // Search forward and then backward to find the start of number. 2495 col = pos->col; 2496 2497 while (ptr[col] != NUL 2498 && !ascii_isdigit(ptr[col]) 2499 && !(do_alpha && ASCII_ISALPHA(ptr[col]))) { 2500 col++; 2501 } 2502 2503 while (col > 0 2504 && ascii_isdigit(ptr[col - 1]) 2505 && !(do_alpha && ASCII_ISALPHA(ptr[col]))) { 2506 col--; 2507 } 2508 } 2509 } 2510 2511 if (visual) { 2512 while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col]) 2513 && !(do_alpha && ASCII_ISALPHA(ptr[col]))) { 2514 int mb_len = utfc_ptr2len(ptr + col); 2515 2516 col += mb_len; 2517 length -= mb_len; 2518 } 2519 2520 if (length == 0) { 2521 goto theend; 2522 } 2523 2524 if (col > pos->col && ptr[col - 1] == '-' 2525 && !utf_head_off(ptr, ptr + col - 1) 2526 && !do_unsigned) { 2527 if (do_blank && col >= 2 && !ascii_iswhite(ptr[col - 2])) { 2528 blank_unsigned = true; 2529 } else { 2530 negative = true; 2531 was_positive = false; 2532 } 2533 } 2534 } 2535 2536 // If a number was found, and saving for undo works, replace the number. 2537 int firstdigit = (uint8_t)ptr[col]; 2538 if (!ascii_isdigit(firstdigit) && !(do_alpha && ASCII_ISALPHA(firstdigit))) { 2539 beep_flush(); 2540 goto theend; 2541 } 2542 2543 if (do_alpha && ASCII_ISALPHA(firstdigit)) { 2544 // decrement or increment alphabetic character 2545 if (op_type == OP_NR_SUB) { 2546 if (CHAR_ORD(firstdigit) < Prenum1) { 2547 firstdigit = isupper(firstdigit) ? 'A' : 'a'; 2548 } else { 2549 firstdigit -= (int)Prenum1; 2550 } 2551 } else { 2552 if (26 - CHAR_ORD(firstdigit) - 1 < Prenum1) { 2553 firstdigit = isupper(firstdigit) ? 'Z' : 'z'; 2554 } else { 2555 firstdigit += (int)Prenum1; 2556 } 2557 } 2558 curwin->w_cursor.col = col; 2559 startpos = curwin->w_cursor; 2560 did_change = true; 2561 del_char(false); 2562 ins_char(firstdigit); 2563 endpos = curwin->w_cursor; 2564 curwin->w_cursor.col = col; 2565 } else { 2566 if (col > 0 && ptr[col - 1] == '-' 2567 && !utf_head_off(ptr, ptr + col - 1) 2568 && !visual 2569 && !do_unsigned) { 2570 if (do_blank && col >= 2 && !ascii_iswhite(ptr[col - 2])) { 2571 blank_unsigned = true; 2572 } else { 2573 // negative number 2574 col--; 2575 negative = true; 2576 } 2577 } 2578 2579 // get the number value (unsigned) 2580 if (visual && VIsual_mode != 'V') { 2581 maxlen = curbuf->b_visual.vi_curswant == MAXCOL ? linelen - col : length; 2582 } 2583 2584 bool overflow = false; 2585 vim_str2nr(ptr + col, &pre, &length, 2586 0 + (do_bin ? STR2NR_BIN : 0) 2587 + (do_oct ? STR2NR_OCT : 0) 2588 + (do_hex ? STR2NR_HEX : 0), 2589 NULL, &n, maxlen, false, &overflow); 2590 2591 // ignore leading '-' for hex, octal and bin numbers 2592 if (pre && negative) { 2593 col++; 2594 length--; 2595 negative = false; 2596 } 2597 2598 // add or subtract 2599 bool subtract = false; 2600 if (op_type == OP_NR_SUB) { 2601 subtract ^= true; 2602 } 2603 if (negative) { 2604 subtract ^= true; 2605 } 2606 2607 uvarnumber_T oldn = n; 2608 2609 if (!overflow) { // if number is too big don't add/subtract 2610 n = subtract ? n - (uvarnumber_T)Prenum1 2611 : n + (uvarnumber_T)Prenum1; 2612 } 2613 2614 // handle wraparound for decimal numbers 2615 if (!pre) { 2616 if (subtract) { 2617 if (n > oldn) { 2618 n = 1 + (n ^ (uvarnumber_T)(-1)); 2619 negative ^= true; 2620 } 2621 } else { 2622 // add 2623 if (n < oldn) { 2624 n = (n ^ (uvarnumber_T)(-1)); 2625 negative ^= true; 2626 } 2627 } 2628 if (n == 0) { 2629 negative = false; 2630 } 2631 } 2632 2633 if ((do_unsigned || blank_unsigned) && negative) { 2634 if (subtract) { 2635 // sticking at zero. 2636 n = 0; 2637 } else { 2638 // sticking at 2^64 - 1. 2639 n = (uvarnumber_T)(-1); 2640 } 2641 negative = false; 2642 } 2643 2644 if (visual && !was_positive && !negative && col > 0) { 2645 // need to remove the '-' 2646 col--; 2647 length++; 2648 } 2649 2650 // Delete the old number. 2651 curwin->w_cursor.col = col; 2652 startpos = curwin->w_cursor; 2653 did_change = true; 2654 int todel = length; 2655 int c = gchar_cursor(); 2656 2657 // Don't include the '-' in the length, only the length of the part 2658 // after it is kept the same. 2659 if (c == '-') { 2660 length--; 2661 } 2662 while (todel-- > 0) { 2663 if (c < 0x100 && isalpha(c)) { 2664 hexupper = isupper(c); 2665 } 2666 // del_char() will mark line needing displaying 2667 del_char(false); 2668 c = gchar_cursor(); 2669 } 2670 2671 // Prepare the leading characters in buf1[]. 2672 // When there are many leading zeros it could be very long. 2673 // Allocate a bit too much. 2674 char *buf1 = xmalloc((size_t)length + NUMBUFLEN); 2675 ptr = buf1; 2676 if (negative && (!visual || was_positive)) { 2677 *ptr++ = '-'; 2678 } 2679 if (pre) { 2680 *ptr++ = '0'; 2681 length--; 2682 } 2683 if (pre == 'b' || pre == 'B' || pre == 'x' || pre == 'X') { 2684 *ptr++ = (char)pre; 2685 length--; 2686 } 2687 2688 // Put the number characters in buf2[]. 2689 char buf2[NUMBUFLEN]; 2690 int buf2len = 0; 2691 if (pre == 'b' || pre == 'B') { 2692 size_t bits = 0; 2693 2694 // leading zeros 2695 for (bits = 8 * sizeof(n); bits > 0; bits--) { 2696 if ((n >> (bits - 1)) & 0x1) { 2697 break; 2698 } 2699 } 2700 2701 while (bits > 0 && buf2len < NUMBUFLEN - 1) { 2702 buf2[buf2len++] = ((n >> --bits) & 0x1) ? '1' : '0'; 2703 } 2704 2705 buf2[buf2len] = NUL; 2706 } else if (pre == 0) { 2707 buf2len = vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n); 2708 } else if (pre == '0') { 2709 buf2len = vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIo64, (uint64_t)n); 2710 } else if (hexupper) { 2711 buf2len = vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIX64, (uint64_t)n); 2712 } else { 2713 buf2len = vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIx64, (uint64_t)n); 2714 } 2715 length -= buf2len; 2716 2717 // Adjust number of zeros to the new number of digits, so the 2718 // total length of the number remains the same. 2719 // Don't do this when 2720 // the result may look like an octal number. 2721 if (firstdigit == '0' && !(do_oct && pre == 0)) { 2722 while (length-- > 0) { 2723 *ptr++ = '0'; 2724 } 2725 } 2726 *ptr = NUL; 2727 int buf1len = (int)(ptr - buf1); 2728 2729 STRCPY(buf1 + buf1len, buf2); 2730 buf1len += buf2len; 2731 2732 ins_str(buf1, (size_t)buf1len); // insert the new number 2733 xfree(buf1); 2734 2735 endpos = curwin->w_cursor; 2736 if (curwin->w_cursor.col) { 2737 curwin->w_cursor.col--; 2738 } 2739 } 2740 2741 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { 2742 // set the '[ and '] marks 2743 curbuf->b_op_start = startpos; 2744 curbuf->b_op_end = endpos; 2745 if (curbuf->b_op_end.col > 0) { 2746 curbuf->b_op_end.col--; 2747 } 2748 } 2749 2750 theend: 2751 if (visual) { 2752 curwin->w_cursor = save_cursor; 2753 } else if (did_change) { 2754 curwin->w_set_curswant = true; 2755 } else if (virtual_active(curwin)) { 2756 curwin->w_cursor.coladd = save_coladd; 2757 } 2758 2759 return did_change; 2760 } 2761 2762 void clear_oparg(oparg_T *oap) 2763 { 2764 CLEAR_POINTER(oap); 2765 } 2766 2767 /// Count the number of bytes, characters and "words" in a line. 2768 /// 2769 /// "Words" are counted by looking for boundaries between non-space and 2770 /// space characters. (it seems to produce results that match 'wc'.) 2771 /// 2772 /// Return value is byte count; word count for the line is added to "*wc". 2773 /// Char count is added to "*cc". 2774 /// 2775 /// The function will only examine the first "limit" characters in the 2776 /// line, stopping if it encounters an end-of-line (NUL byte). In that 2777 /// case, eol_size will be added to the character count to account for 2778 /// the size of the EOL character. 2779 static varnumber_T line_count_info(char *line, varnumber_T *wc, varnumber_T *cc, varnumber_T limit, 2780 int eol_size) 2781 { 2782 varnumber_T i; 2783 varnumber_T words = 0; 2784 varnumber_T chars = 0; 2785 bool is_word = false; 2786 2787 for (i = 0; i < limit && line[i] != NUL;) { 2788 if (is_word) { 2789 if (ascii_isspace(line[i])) { 2790 words++; 2791 is_word = false; 2792 } 2793 } else if (!ascii_isspace(line[i])) { 2794 is_word = true; 2795 } 2796 chars++; 2797 i += utfc_ptr2len(line + i); 2798 } 2799 2800 if (is_word) { 2801 words++; 2802 } 2803 *wc += words; 2804 2805 // Add eol_size if the end of line was reached before hitting limit. 2806 if (i < limit && line[i] == NUL) { 2807 i += eol_size; 2808 chars += eol_size; 2809 } 2810 *cc += chars; 2811 return i; 2812 } 2813 2814 /// Give some info about the position of the cursor (for "g CTRL-G"). 2815 /// In Visual mode, give some info about the selected region. (In this case, 2816 /// the *_count_cursor variables store running totals for the selection.) 2817 /// 2818 /// @param dict when not NULL, store the info there instead of showing it. 2819 void cursor_pos_info(dict_T *dict) 2820 { 2821 char buf1[50]; 2822 char buf2[40]; 2823 varnumber_T byte_count = 0; 2824 varnumber_T bom_count = 0; 2825 varnumber_T byte_count_cursor = 0; 2826 varnumber_T char_count = 0; 2827 varnumber_T char_count_cursor = 0; 2828 varnumber_T word_count = 0; 2829 varnumber_T word_count_cursor = 0; 2830 pos_T min_pos, max_pos; 2831 oparg_T oparg; 2832 struct block_def bd; 2833 const int l_VIsual_active = VIsual_active; 2834 const int l_VIsual_mode = VIsual_mode; 2835 2836 // Compute the length of the file in characters. 2837 if (curbuf->b_ml.ml_flags & ML_EMPTY) { 2838 if (dict == NULL) { 2839 msg(_(no_lines_msg), 0); 2840 return; 2841 } 2842 } else { 2843 int eol_size; 2844 varnumber_T last_check = 100000; 2845 int line_count_selected = 0; 2846 if (get_fileformat(curbuf) == EOL_DOS) { 2847 eol_size = 2; 2848 } else { 2849 eol_size = 1; 2850 } 2851 2852 if (l_VIsual_active) { 2853 if (lt(VIsual, curwin->w_cursor)) { 2854 min_pos = VIsual; 2855 max_pos = curwin->w_cursor; 2856 } else { 2857 min_pos = curwin->w_cursor; 2858 max_pos = VIsual; 2859 } 2860 if (*p_sel == 'e' && max_pos.col > 0) { 2861 max_pos.col--; 2862 } 2863 2864 if (l_VIsual_mode == Ctrl_V) { 2865 char *const saved_sbr = p_sbr; 2866 char *const saved_w_sbr = curwin->w_p_sbr; 2867 2868 // Make 'sbr' empty for a moment to get the correct size. 2869 p_sbr = empty_string_option; 2870 curwin->w_p_sbr = empty_string_option; 2871 oparg.is_VIsual = true; 2872 oparg.motion_type = kMTBlockWise; 2873 oparg.op_type = OP_NOP; 2874 getvcols(curwin, &min_pos, &max_pos, &oparg.start_vcol, &oparg.end_vcol); 2875 p_sbr = saved_sbr; 2876 curwin->w_p_sbr = saved_w_sbr; 2877 if (curwin->w_curswant == MAXCOL) { 2878 oparg.end_vcol = MAXCOL; 2879 } 2880 // Swap the start, end vcol if needed 2881 if (oparg.end_vcol < oparg.start_vcol) { 2882 oparg.end_vcol += oparg.start_vcol; 2883 oparg.start_vcol = oparg.end_vcol - oparg.start_vcol; 2884 oparg.end_vcol -= oparg.start_vcol; 2885 } 2886 } 2887 line_count_selected = max_pos.lnum - min_pos.lnum + 1; 2888 } 2889 2890 for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) { 2891 // Check for a CTRL-C every 100000 characters. 2892 if (byte_count > last_check) { 2893 os_breakcheck(); 2894 if (got_int) { 2895 return; 2896 } 2897 last_check = byte_count + 100000; 2898 } 2899 2900 // Do extra processing for VIsual mode. 2901 if (l_VIsual_active 2902 && lnum >= min_pos.lnum && lnum <= max_pos.lnum) { 2903 char *s = NULL; 2904 int len = 0; 2905 2906 switch (l_VIsual_mode) { 2907 case Ctrl_V: 2908 virtual_op = virtual_active(curwin); 2909 block_prep(&oparg, &bd, lnum, false); 2910 virtual_op = kNone; 2911 s = bd.textstart; 2912 len = bd.textlen; 2913 break; 2914 case 'V': 2915 s = ml_get(lnum); 2916 len = MAXCOL; 2917 break; 2918 case 'v': { 2919 colnr_T start_col = (lnum == min_pos.lnum) 2920 ? min_pos.col : 0; 2921 colnr_T end_col = (lnum == max_pos.lnum) 2922 ? max_pos.col - start_col + 1 : MAXCOL; 2923 2924 s = ml_get(lnum) + start_col; 2925 len = end_col; 2926 } 2927 break; 2928 } 2929 if (s != NULL) { 2930 byte_count_cursor += line_count_info(s, &word_count_cursor, 2931 &char_count_cursor, len, eol_size); 2932 if (lnum == curbuf->b_ml.ml_line_count 2933 && !curbuf->b_p_eol 2934 && (curbuf->b_p_bin || !curbuf->b_p_fixeol) 2935 && (int)strlen(s) < len) { 2936 byte_count_cursor -= eol_size; 2937 } 2938 } 2939 } else { 2940 // In non-visual mode, check for the line the cursor is on 2941 if (lnum == curwin->w_cursor.lnum) { 2942 word_count_cursor += word_count; 2943 char_count_cursor += char_count; 2944 byte_count_cursor = byte_count 2945 + line_count_info(ml_get(lnum), &word_count_cursor, 2946 &char_count_cursor, 2947 (varnumber_T)curwin->w_cursor.col + 1, 2948 eol_size); 2949 } 2950 } 2951 // Add to the running totals 2952 byte_count += line_count_info(ml_get(lnum), &word_count, &char_count, 2953 (varnumber_T)MAXCOL, eol_size); 2954 } 2955 2956 // Correction for when last line doesn't have an EOL. 2957 if (!curbuf->b_p_eol && (curbuf->b_p_bin || !curbuf->b_p_fixeol)) { 2958 byte_count -= eol_size; 2959 } 2960 2961 if (dict == NULL) { 2962 if (l_VIsual_active) { 2963 if (l_VIsual_mode == Ctrl_V && curwin->w_curswant < MAXCOL) { 2964 getvcols(curwin, &min_pos, &max_pos, &min_pos.col, &max_pos.col); 2965 int64_t cols; 2966 STRICT_SUB(oparg.end_vcol + 1, oparg.start_vcol, &cols, int64_t); 2967 vim_snprintf(buf1, sizeof(buf1), _("%" PRId64 " Cols; "), 2968 cols); 2969 } else { 2970 buf1[0] = NUL; 2971 } 2972 2973 if (char_count_cursor == byte_count_cursor 2974 && char_count == byte_count) { 2975 vim_snprintf(IObuff, IOSIZE, 2976 _("Selected %s%" PRId64 " of %" PRId64 " Lines;" 2977 " %" PRId64 " of %" PRId64 " Words;" 2978 " %" PRId64 " of %" PRId64 " Bytes"), 2979 buf1, (int64_t)line_count_selected, 2980 (int64_t)curbuf->b_ml.ml_line_count, 2981 (int64_t)word_count_cursor, (int64_t)word_count, 2982 (int64_t)byte_count_cursor, (int64_t)byte_count); 2983 } else { 2984 vim_snprintf(IObuff, IOSIZE, 2985 _("Selected %s%" PRId64 " of %" PRId64 " Lines;" 2986 " %" PRId64 " of %" PRId64 " Words;" 2987 " %" PRId64 " of %" PRId64 " Chars;" 2988 " %" PRId64 " of %" PRId64 " Bytes"), 2989 buf1, (int64_t)line_count_selected, 2990 (int64_t)curbuf->b_ml.ml_line_count, 2991 (int64_t)word_count_cursor, (int64_t)word_count, 2992 (int64_t)char_count_cursor, (int64_t)char_count, 2993 (int64_t)byte_count_cursor, (int64_t)byte_count); 2994 } 2995 } else { 2996 char *p = get_cursor_line_ptr(); 2997 validate_virtcol(curwin); 2998 col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, 2999 (int)curwin->w_virtcol + 1); 3000 col_print(buf2, sizeof(buf2), get_cursor_line_len(), linetabsize_str(p)); 3001 3002 if (char_count_cursor == byte_count_cursor 3003 && char_count == byte_count) { 3004 vim_snprintf(IObuff, IOSIZE, 3005 _("Col %s of %s; Line %" PRId64 " of %" PRId64 ";" 3006 " Word %" PRId64 " of %" PRId64 ";" 3007 " Byte %" PRId64 " of %" PRId64 ""), 3008 buf1, buf2, 3009 (int64_t)curwin->w_cursor.lnum, 3010 (int64_t)curbuf->b_ml.ml_line_count, 3011 (int64_t)word_count_cursor, (int64_t)word_count, 3012 (int64_t)byte_count_cursor, (int64_t)byte_count); 3013 } else { 3014 vim_snprintf(IObuff, IOSIZE, 3015 _("Col %s of %s; Line %" PRId64 " of %" PRId64 ";" 3016 " Word %" PRId64 " of %" PRId64 ";" 3017 " Char %" PRId64 " of %" PRId64 ";" 3018 " Byte %" PRId64 " of %" PRId64 ""), 3019 buf1, buf2, 3020 (int64_t)curwin->w_cursor.lnum, 3021 (int64_t)curbuf->b_ml.ml_line_count, 3022 (int64_t)word_count_cursor, (int64_t)word_count, 3023 (int64_t)char_count_cursor, (int64_t)char_count, 3024 (int64_t)byte_count_cursor, (int64_t)byte_count); 3025 } 3026 } 3027 } 3028 3029 bom_count = bomb_size(); 3030 if (dict == NULL && bom_count > 0) { 3031 const size_t len = strlen(IObuff); 3032 vim_snprintf(IObuff + len, IOSIZE - len, 3033 _("(+%" PRId64 " for BOM)"), (int64_t)bom_count); 3034 } 3035 if (dict == NULL) { 3036 // Don't shorten this message, the user asked for it. 3037 char *p = p_shm; 3038 p_shm = ""; 3039 if (p_ch < 1) { 3040 msg_start(); 3041 msg_scroll = true; 3042 } 3043 msg(IObuff, 0); 3044 p_shm = p; 3045 } 3046 } 3047 3048 if (dict != NULL) { 3049 // Don't shorten this message, the user asked for it. 3050 tv_dict_add_nr(dict, S_LEN("words"), word_count); 3051 tv_dict_add_nr(dict, S_LEN("chars"), char_count); 3052 tv_dict_add_nr(dict, S_LEN("bytes"), byte_count + bom_count); 3053 3054 STATIC_ASSERT(sizeof("visual") == sizeof("cursor"), 3055 "key_len argument in tv_dict_add_nr is wrong"); 3056 tv_dict_add_nr(dict, l_VIsual_active ? "visual_bytes" : "cursor_bytes", 3057 sizeof("visual_bytes") - 1, byte_count_cursor); 3058 tv_dict_add_nr(dict, l_VIsual_active ? "visual_chars" : "cursor_chars", 3059 sizeof("visual_chars") - 1, char_count_cursor); 3060 tv_dict_add_nr(dict, l_VIsual_active ? "visual_words" : "cursor_words", 3061 sizeof("visual_words") - 1, word_count_cursor); 3062 } 3063 } 3064 3065 /// Handle indent and format operators and visual mode ":". 3066 static void op_colon(oparg_T *oap) 3067 { 3068 stuffcharReadbuff(':'); 3069 if (oap->is_VIsual) { 3070 stuffReadbuff("'<,'>"); 3071 } else { 3072 // Make the range look nice, so it can be repeated. 3073 if (oap->start.lnum == curwin->w_cursor.lnum) { 3074 stuffcharReadbuff('.'); 3075 } else { 3076 stuffnumReadbuff(oap->start.lnum); 3077 } 3078 3079 // When using !! on a closed fold the range ".!" works best to operate 3080 // on, it will be made the whole closed fold later. 3081 linenr_T endOfStartFold = oap->start.lnum; 3082 hasFolding(curwin, oap->start.lnum, NULL, &endOfStartFold); 3083 if (oap->end.lnum != oap->start.lnum && oap->end.lnum != endOfStartFold) { 3084 // Make it a range with the end line. 3085 stuffcharReadbuff(','); 3086 if (oap->end.lnum == curwin->w_cursor.lnum) { 3087 stuffcharReadbuff('.'); 3088 } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { 3089 stuffcharReadbuff('$'); 3090 } else if (oap->start.lnum == curwin->w_cursor.lnum 3091 // do not use ".+number" for a closed fold, it would count 3092 // folded lines twice 3093 && !hasFolding(curwin, oap->end.lnum, NULL, NULL)) { 3094 stuffReadbuff(".+"); 3095 stuffnumReadbuff(oap->line_count - 1); 3096 } else { 3097 stuffnumReadbuff(oap->end.lnum); 3098 } 3099 } 3100 } 3101 if (oap->op_type != OP_COLON) { 3102 stuffReadbuff("!"); 3103 } 3104 if (oap->op_type == OP_INDENT) { 3105 stuffReadbuff(get_equalprg()); 3106 stuffReadbuff("\n"); 3107 } else if (oap->op_type == OP_FORMAT) { 3108 if (*curbuf->b_p_fp != NUL) { 3109 stuffReadbuff(curbuf->b_p_fp); 3110 } else if (*p_fp != NUL) { 3111 stuffReadbuff(p_fp); 3112 } else { 3113 stuffReadbuff("fmt"); 3114 } 3115 stuffReadbuff("\n']"); 3116 } 3117 3118 // do_cmdline() does the rest 3119 } 3120 3121 /// callback function for 'operatorfunc' 3122 static Callback opfunc_cb; 3123 3124 /// Process the 'operatorfunc' option value. 3125 const char *did_set_operatorfunc(optset_T *args FUNC_ATTR_UNUSED) 3126 { 3127 if (option_set_callback_func(p_opfunc, &opfunc_cb) == FAIL) { 3128 return e_invarg; 3129 } 3130 return NULL; 3131 } 3132 3133 #if defined(EXITFREE) 3134 void free_operatorfunc_option(void) 3135 { 3136 callback_free(&opfunc_cb); 3137 } 3138 #endif 3139 3140 /// Mark the global 'operatorfunc' callback with "copyID" so that it is not 3141 /// garbage collected. 3142 bool set_ref_in_opfunc(int copyID) 3143 { 3144 return set_ref_in_callback(&opfunc_cb, copyID, NULL, NULL); 3145 } 3146 3147 /// Handle the "g@" operator: call 'operatorfunc'. 3148 static void op_function(const oparg_T *oap) 3149 FUNC_ATTR_NONNULL_ALL 3150 { 3151 const pos_T orig_start = curbuf->b_op_start; 3152 const pos_T orig_end = curbuf->b_op_end; 3153 3154 if (*p_opfunc == NUL) { 3155 emsg(_("E774: 'operatorfunc' is empty")); 3156 } else { 3157 // Set '[ and '] marks to text to be operated on. 3158 curbuf->b_op_start = oap->start; 3159 curbuf->b_op_end = oap->end; 3160 if (oap->motion_type != kMTLineWise && !oap->inclusive) { 3161 // Exclude the end position. 3162 decl(&curbuf->b_op_end); 3163 } 3164 3165 typval_T argv[2]; 3166 argv[0].v_type = VAR_STRING; 3167 argv[1].v_type = VAR_UNKNOWN; 3168 argv[0].vval.v_string = 3169 (char *)(((const char *const[]) { 3170 [kMTBlockWise] = "block", 3171 [kMTLineWise] = "line", 3172 [kMTCharWise] = "char", 3173 })[oap->motion_type]); 3174 3175 // Reset virtual_op so that 'virtualedit' can be changed in the 3176 // function. 3177 const TriState save_virtual_op = virtual_op; 3178 virtual_op = kNone; 3179 3180 // Reset finish_op so that mode() returns the right value. 3181 const bool save_finish_op = finish_op; 3182 finish_op = false; 3183 3184 typval_T rettv; 3185 if (callback_call(&opfunc_cb, 1, argv, &rettv)) { 3186 tv_clear(&rettv); 3187 } 3188 3189 virtual_op = save_virtual_op; 3190 finish_op = save_finish_op; 3191 if (cmdmod.cmod_flags & CMOD_LOCKMARKS) { 3192 curbuf->b_op_start = orig_start; 3193 curbuf->b_op_end = orig_end; 3194 } 3195 } 3196 } 3197 3198 /// Calculate start/end virtual columns for operating in block mode. 3199 /// 3200 /// @param initial when true: adjust position for 'selectmode' 3201 static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial) 3202 { 3203 colnr_T start; 3204 colnr_T end; 3205 3206 if (VIsual_mode != Ctrl_V 3207 || (!initial && oap->end.col < curwin->w_view_width)) { 3208 return; 3209 } 3210 3211 oap->motion_type = kMTBlockWise; 3212 3213 // prevent from moving onto a trail byte 3214 mark_mb_adjustpos(curwin->w_buffer, &oap->end); 3215 3216 getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); 3217 if (!redo_VIsual_busy) { 3218 getvvcol(curwin, &(oap->end), &start, NULL, &end); 3219 3220 oap->start_vcol = MIN(oap->start_vcol, start); 3221 if (end > oap->end_vcol) { 3222 if (initial && *p_sel == 'e' 3223 && start >= 1 3224 && start - 1 >= oap->end_vcol) { 3225 oap->end_vcol = start - 1; 3226 } else { 3227 oap->end_vcol = end; 3228 } 3229 } 3230 } 3231 3232 // if '$' was used, get oap->end_vcol from longest line 3233 if (curwin->w_curswant == MAXCOL) { 3234 curwin->w_cursor.col = MAXCOL; 3235 oap->end_vcol = 0; 3236 for (curwin->w_cursor.lnum = oap->start.lnum; 3237 curwin->w_cursor.lnum <= oap->end.lnum; curwin->w_cursor.lnum++) { 3238 getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); 3239 oap->end_vcol = MAX(oap->end_vcol, end); 3240 } 3241 } else if (redo_VIsual_busy) { 3242 oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; 3243 } 3244 3245 // Correct oap->end.col and oap->start.col to be the 3246 // upper-left and lower-right corner of the block area. 3247 // 3248 // (Actually, this does convert column positions into character 3249 // positions) 3250 curwin->w_cursor.lnum = oap->end.lnum; 3251 coladvance(curwin, oap->end_vcol); 3252 oap->end = curwin->w_cursor; 3253 3254 curwin->w_cursor = oap->start; 3255 coladvance(curwin, oap->start_vcol); 3256 oap->start = curwin->w_cursor; 3257 } 3258 3259 /// Information for redoing the previous Visual selection. 3260 typedef struct { 3261 int rv_mode; ///< 'v', 'V', or Ctrl-V 3262 linenr_T rv_line_count; ///< number of lines 3263 colnr_T rv_vcol; ///< number of cols or end column 3264 int rv_count; ///< count for Visual operator 3265 int rv_arg; ///< extra argument 3266 } redo_VIsual_T; 3267 3268 static bool is_ex_cmdchar(cmdarg_T *cap) 3269 { 3270 return cap->cmdchar == ':' || cap->cmdchar == K_COMMAND; 3271 } 3272 3273 /// Handle an operator after Visual mode or when the movement is finished. 3274 /// "gui_yank" is true when yanking text for the clipboard. 3275 void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) 3276 { 3277 oparg_T *oap = cap->oap; 3278 int lbr_saved = curwin->w_p_lbr; 3279 3280 // The visual area is remembered for redo 3281 static redo_VIsual_T redo_VIsual = { NUL, 0, 0, 0, 0 }; 3282 3283 pos_T old_cursor = curwin->w_cursor; 3284 3285 // If an operation is pending, handle it... 3286 if ((finish_op 3287 || VIsual_active) 3288 && oap->op_type != OP_NOP) { 3289 bool empty_region_error; 3290 int restart_edit_save; 3291 bool include_line_break = false; 3292 // Yank can be redone when 'y' is in 'cpoptions', but not when yanking 3293 // for the clipboard. 3294 const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; 3295 3296 // Avoid a problem with unwanted linebreaks in block mode 3297 reset_lbr(); 3298 oap->is_VIsual = VIsual_active; 3299 if (oap->motion_force == 'V') { 3300 oap->motion_type = kMTLineWise; 3301 } else if (oap->motion_force == 'v') { 3302 // If the motion was linewise, "inclusive" will not have been set. 3303 // Use "exclusive" to be consistent. Makes "dvj" work nice. 3304 if (oap->motion_type == kMTLineWise) { 3305 oap->inclusive = false; 3306 } else if (oap->motion_type == kMTCharWise) { 3307 // If the motion already was charwise, toggle "inclusive" 3308 oap->inclusive = !oap->inclusive; 3309 } 3310 oap->motion_type = kMTCharWise; 3311 } else if (oap->motion_force == Ctrl_V) { 3312 // Change line- or charwise motion into Visual block mode. 3313 if (!VIsual_active) { 3314 VIsual_active = true; 3315 VIsual = oap->start; 3316 } 3317 VIsual_mode = Ctrl_V; 3318 VIsual_select = false; 3319 VIsual_reselect = false; 3320 } 3321 3322 // Only redo yank when 'y' flag is in 'cpoptions'. 3323 // Never redo "zf" (define fold). 3324 if ((redo_yank || oap->op_type != OP_YANK) 3325 && ((!VIsual_active || oap->motion_force) 3326 // Also redo Operator-pending Visual mode mappings. 3327 || ((is_ex_cmdchar(cap) || cap->cmdchar == K_LUA) 3328 && oap->op_type != OP_COLON)) 3329 && cap->cmdchar != 'D' 3330 && oap->op_type != OP_FOLD 3331 && oap->op_type != OP_FOLDOPEN 3332 && oap->op_type != OP_FOLDOPENREC 3333 && oap->op_type != OP_FOLDCLOSE 3334 && oap->op_type != OP_FOLDCLOSEREC 3335 && oap->op_type != OP_FOLDDEL 3336 && oap->op_type != OP_FOLDDELREC) { 3337 prep_redo(oap->regname, cap->count0, 3338 get_op_char(oap->op_type), get_extra_op_char(oap->op_type), 3339 oap->motion_force, cap->cmdchar, cap->nchar); 3340 if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search 3341 // If 'cpoptions' does not contain 'r', insert the search 3342 // pattern to really repeat the same command. 3343 if (vim_strchr(p_cpo, CPO_REDO) == NULL) { 3344 AppendToRedobuffLit(cap->searchbuf, -1); 3345 } 3346 AppendToRedobuff(NL_STR); 3347 } else if (is_ex_cmdchar(cap)) { 3348 // do_cmdline() has stored the first typed line in 3349 // "repeat_cmdline". When several lines are typed repeating 3350 // won't be possible. 3351 if (repeat_cmdline == NULL) { 3352 ResetRedobuff(); 3353 } else { 3354 if (cap->cmdchar == ':') { 3355 AppendToRedobuffLit(repeat_cmdline, -1); 3356 } else { 3357 AppendToRedobuffSpec(repeat_cmdline); 3358 } 3359 AppendToRedobuff(NL_STR); 3360 XFREE_CLEAR(repeat_cmdline); 3361 } 3362 } else if (cap->cmdchar == K_LUA) { 3363 AppendNumberToRedobuff(repeat_luaref); 3364 AppendToRedobuff(NL_STR); 3365 } 3366 } 3367 3368 if (redo_VIsual_busy) { 3369 // Redo of an operation on a Visual area. Use the same size from 3370 // redo_VIsual.rv_line_count and redo_VIsual.rv_vcol. 3371 oap->start = curwin->w_cursor; 3372 curwin->w_cursor.lnum += redo_VIsual.rv_line_count - 1; 3373 curwin->w_cursor.lnum = MIN(curwin->w_cursor.lnum, curbuf->b_ml.ml_line_count); 3374 VIsual_mode = redo_VIsual.rv_mode; 3375 if (redo_VIsual.rv_vcol == MAXCOL || VIsual_mode == 'v') { 3376 if (VIsual_mode == 'v') { 3377 if (redo_VIsual.rv_line_count <= 1) { 3378 validate_virtcol(curwin); 3379 curwin->w_curswant = curwin->w_virtcol + redo_VIsual.rv_vcol - 1; 3380 } else { 3381 curwin->w_curswant = redo_VIsual.rv_vcol; 3382 } 3383 } else { 3384 curwin->w_curswant = MAXCOL; 3385 } 3386 coladvance(curwin, curwin->w_curswant); 3387 } 3388 cap->count0 = redo_VIsual.rv_count; 3389 cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); 3390 } else if (VIsual_active) { 3391 if (!gui_yank) { 3392 // Save the current VIsual area for '< and '> marks, and "gv" 3393 curbuf->b_visual.vi_start = VIsual; 3394 curbuf->b_visual.vi_end = curwin->w_cursor; 3395 curbuf->b_visual.vi_mode = VIsual_mode; 3396 restore_visual_mode(); 3397 curbuf->b_visual.vi_curswant = curwin->w_curswant; 3398 curbuf->b_visual_mode_eval = VIsual_mode; 3399 } 3400 3401 // In Select mode, a linewise selection is operated upon like a 3402 // charwise selection. 3403 // Special case: gH<Del> deletes the last line. 3404 if (VIsual_select && VIsual_mode == 'V' 3405 && cap->oap->op_type != OP_DELETE) { 3406 if (lt(VIsual, curwin->w_cursor)) { 3407 VIsual.col = 0; 3408 curwin->w_cursor.col = ml_get_len(curwin->w_cursor.lnum); 3409 } else { 3410 curwin->w_cursor.col = 0; 3411 VIsual.col = ml_get_len(VIsual.lnum); 3412 } 3413 VIsual_mode = 'v'; 3414 } else if (VIsual_mode == 'v') { 3415 // If 'selection' is "exclusive", backup one character for 3416 // charwise selections. 3417 include_line_break = unadjust_for_sel(); 3418 } 3419 3420 oap->start = VIsual; 3421 if (VIsual_mode == 'V') { 3422 oap->start.col = 0; 3423 oap->start.coladd = 0; 3424 } 3425 } 3426 3427 // Set oap->start to the first position of the operated text, oap->end 3428 // to the end of the operated text. w_cursor is equal to oap->start. 3429 if (lt(oap->start, curwin->w_cursor)) { 3430 // Include folded lines completely. 3431 if (!VIsual_active) { 3432 if (hasFolding(curwin, oap->start.lnum, &oap->start.lnum, NULL)) { 3433 oap->start.col = 0; 3434 } 3435 if ((curwin->w_cursor.col > 0 3436 || oap->inclusive 3437 || oap->motion_type == kMTLineWise) 3438 && hasFolding(curwin, curwin->w_cursor.lnum, NULL, 3439 &curwin->w_cursor.lnum)) { 3440 curwin->w_cursor.col = get_cursor_line_len(); 3441 } 3442 } 3443 oap->end = curwin->w_cursor; 3444 curwin->w_cursor = oap->start; 3445 3446 // w_virtcol may have been updated; if the cursor goes back to its 3447 // previous position w_virtcol becomes invalid and isn't updated 3448 // automatically. 3449 curwin->w_valid &= ~VALID_VIRTCOL; 3450 } else { 3451 // Include folded lines completely. 3452 if (!VIsual_active && oap->motion_type == kMTLineWise) { 3453 if (hasFolding(curwin, curwin->w_cursor.lnum, &curwin->w_cursor.lnum, 3454 NULL)) { 3455 curwin->w_cursor.col = 0; 3456 } 3457 if (hasFolding(curwin, oap->start.lnum, NULL, &oap->start.lnum)) { 3458 oap->start.col = ml_get_len(oap->start.lnum); 3459 } 3460 } 3461 oap->end = oap->start; 3462 oap->start = curwin->w_cursor; 3463 } 3464 3465 // Just in case lines were deleted that make the position invalid. 3466 check_pos(curwin->w_buffer, &oap->end); 3467 oap->line_count = oap->end.lnum - oap->start.lnum + 1; 3468 3469 // Set "virtual_op" before resetting VIsual_active. 3470 virtual_op = virtual_active(curwin); 3471 3472 if (VIsual_active || redo_VIsual_busy) { 3473 get_op_vcol(oap, redo_VIsual.rv_vcol, true); 3474 3475 if (!redo_VIsual_busy && !gui_yank) { 3476 // Prepare to reselect and redo Visual: this is based on the 3477 // size of the Visual text 3478 resel_VIsual_mode = VIsual_mode; 3479 if (curwin->w_curswant == MAXCOL) { 3480 resel_VIsual_vcol = MAXCOL; 3481 } else { 3482 if (VIsual_mode != Ctrl_V) { 3483 getvvcol(curwin, &(oap->end), 3484 NULL, NULL, &oap->end_vcol); 3485 } 3486 if (VIsual_mode == Ctrl_V || oap->line_count <= 1) { 3487 if (VIsual_mode != Ctrl_V) { 3488 getvvcol(curwin, &(oap->start), 3489 &oap->start_vcol, NULL, NULL); 3490 } 3491 resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; 3492 } else { 3493 resel_VIsual_vcol = oap->end_vcol; 3494 } 3495 } 3496 resel_VIsual_line_count = oap->line_count; 3497 } 3498 3499 // can't redo yank (unless 'y' is in 'cpoptions') and ":" 3500 if ((redo_yank || oap->op_type != OP_YANK) 3501 && oap->op_type != OP_COLON 3502 && oap->op_type != OP_FOLD 3503 && oap->op_type != OP_FOLDOPEN 3504 && oap->op_type != OP_FOLDOPENREC 3505 && oap->op_type != OP_FOLDCLOSE 3506 && oap->op_type != OP_FOLDCLOSEREC 3507 && oap->op_type != OP_FOLDDEL 3508 && oap->op_type != OP_FOLDDELREC 3509 && oap->motion_force == NUL) { 3510 // Prepare for redoing. Only use the nchar field for "r", 3511 // otherwise it might be the second char of the operator. 3512 if (cap->cmdchar == 'g' && (cap->nchar == 'n' 3513 || cap->nchar == 'N')) { 3514 prep_redo(oap->regname, cap->count0, 3515 get_op_char(oap->op_type), get_extra_op_char(oap->op_type), 3516 oap->motion_force, cap->cmdchar, cap->nchar); 3517 } else if (!is_ex_cmdchar(cap) && cap->cmdchar != K_LUA) { 3518 int opchar = get_op_char(oap->op_type); 3519 int extra_opchar = get_extra_op_char(oap->op_type); 3520 int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; 3521 3522 // reverse what nv_replace() did 3523 if (nchar == REPLACE_CR_NCHAR) { 3524 nchar = CAR; 3525 } else if (nchar == REPLACE_NL_NCHAR) { 3526 nchar = NL; 3527 } 3528 3529 if (opchar == 'g' && extra_opchar == '@') { 3530 // also repeat the count for 'operatorfunc' 3531 prep_redo_num2(oap->regname, 0, NUL, 'v', cap->count0, opchar, extra_opchar, nchar); 3532 } else { 3533 prep_redo(oap->regname, 0, NUL, 'v', opchar, extra_opchar, nchar); 3534 } 3535 } 3536 if (!redo_VIsual_busy) { 3537 redo_VIsual.rv_mode = resel_VIsual_mode; 3538 redo_VIsual.rv_vcol = resel_VIsual_vcol; 3539 redo_VIsual.rv_line_count = resel_VIsual_line_count; 3540 redo_VIsual.rv_count = cap->count0; 3541 redo_VIsual.rv_arg = cap->arg; 3542 } 3543 } 3544 3545 // oap->inclusive defaults to true. 3546 // If oap->end is on a NUL (empty line) oap->inclusive becomes 3547 // false. This makes "d}P" and "v}dP" work the same. 3548 if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) { 3549 oap->inclusive = true; 3550 } 3551 if (VIsual_mode == 'V') { 3552 oap->motion_type = kMTLineWise; 3553 } else if (VIsual_mode == 'v') { 3554 oap->motion_type = kMTCharWise; 3555 if (*ml_get_pos(&(oap->end)) == NUL 3556 && (include_line_break || !virtual_op)) { 3557 oap->inclusive = false; 3558 // Try to include the newline, unless it's an operator 3559 // that works on lines only. 3560 if (*p_sel != 'o' 3561 && !op_on_lines(oap->op_type) 3562 && oap->end.lnum < curbuf->b_ml.ml_line_count) { 3563 oap->end.lnum++; 3564 oap->end.col = 0; 3565 oap->end.coladd = 0; 3566 oap->line_count++; 3567 } 3568 } 3569 } 3570 3571 redo_VIsual_busy = false; 3572 3573 // Switch Visual off now, so screen updating does 3574 // not show inverted text when the screen is redrawn. 3575 // With OP_YANK and sometimes with OP_COLON and OP_FILTER there is 3576 // no screen redraw, so it is done here to remove the inverted 3577 // part. 3578 if (!gui_yank) { 3579 VIsual_active = false; 3580 setmouse(); 3581 mouse_dragging = 0; 3582 may_clear_cmdline(); 3583 if ((oap->op_type == OP_YANK 3584 || oap->op_type == OP_COLON 3585 || oap->op_type == OP_FUNCTION 3586 || oap->op_type == OP_FILTER) 3587 && oap->motion_force == NUL) { 3588 // Make sure redrawing is correct. 3589 restore_lbr(lbr_saved); 3590 redraw_curbuf_later(UPD_INVERTED); 3591 } 3592 } 3593 } 3594 3595 // Include the trailing byte of a multi-byte char. 3596 if (oap->inclusive) { 3597 const int l = utfc_ptr2len(ml_get_pos(&oap->end)); 3598 if (l > 1) { 3599 oap->end.col += l - 1; 3600 } 3601 } 3602 curwin->w_set_curswant = true; 3603 3604 // oap->empty is set when start and end are the same. The inclusive 3605 // flag affects this too, unless yanking and the end is on a NUL. 3606 oap->empty = (oap->motion_type != kMTLineWise 3607 && (!oap->inclusive 3608 || (oap->op_type == OP_YANK 3609 && gchar_pos(&oap->end) == NUL)) 3610 && equalpos(oap->start, oap->end) 3611 && !(virtual_op && oap->start.coladd != oap->end.coladd)); 3612 // For delete, change and yank, it's an error to operate on an 3613 // empty region, when 'E' included in 'cpoptions' (Vi compatible). 3614 empty_region_error = (oap->empty 3615 && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL); 3616 3617 // Force a redraw when operating on an empty Visual region, when 3618 // 'modifiable is off or creating a fold. 3619 if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf) 3620 || oap->op_type == OP_FOLD)) { 3621 restore_lbr(lbr_saved); 3622 redraw_curbuf_later(UPD_INVERTED); 3623 } 3624 3625 // If the end of an operator is in column one while oap->motion_type 3626 // is kMTCharWise and oap->inclusive is false, we put op_end after the last 3627 // character in the previous line. If op_start is on or before the 3628 // first non-blank in the line, the operator becomes linewise 3629 // (strange, but that's the way vi does it). 3630 if (oap->motion_type == kMTCharWise 3631 && oap->inclusive == false 3632 && !(cap->retval & CA_NO_ADJ_OP_END) 3633 && oap->end.col == 0 3634 && (!oap->is_VIsual || *p_sel == 'o') 3635 && oap->line_count > 1) { 3636 oap->end_adjusted = true; // remember that we did this 3637 oap->line_count--; 3638 oap->end.lnum--; 3639 if (inindent(0)) { 3640 oap->motion_type = kMTLineWise; 3641 } else { 3642 oap->end.col = ml_get_len(oap->end.lnum); 3643 if (oap->end.col) { 3644 oap->end.col--; 3645 oap->inclusive = true; 3646 } 3647 } 3648 } else { 3649 oap->end_adjusted = false; 3650 } 3651 3652 switch (oap->op_type) { 3653 case OP_LSHIFT: 3654 case OP_RSHIFT: 3655 op_shift(oap, true, oap->is_VIsual ? cap->count1 : 1); 3656 auto_format(false, true); 3657 break; 3658 3659 case OP_JOIN_NS: 3660 case OP_JOIN: 3661 oap->line_count = MAX(oap->line_count, 2); 3662 if (curwin->w_cursor.lnum + oap->line_count - 1 > 3663 curbuf->b_ml.ml_line_count) { 3664 beep_flush(); 3665 } else { 3666 do_join((size_t)oap->line_count, oap->op_type == OP_JOIN, 3667 true, true, true); 3668 auto_format(false, true); 3669 } 3670 break; 3671 3672 case OP_DELETE: 3673 VIsual_reselect = false; // don't reselect now 3674 if (empty_region_error) { 3675 vim_beep(kOptBoFlagOperator); 3676 CancelRedo(); 3677 } else { 3678 op_delete(oap); 3679 // save cursor line for undo if it wasn't saved yet 3680 if (oap->motion_type == kMTLineWise 3681 && has_format_option(FO_AUTO) 3682 && u_save_cursor() == OK) { 3683 auto_format(false, true); 3684 } 3685 } 3686 break; 3687 3688 case OP_YANK: 3689 if (empty_region_error) { 3690 if (!gui_yank) { 3691 vim_beep(kOptBoFlagOperator); 3692 CancelRedo(); 3693 } 3694 } else { 3695 restore_lbr(lbr_saved); 3696 oap->excl_tr_ws = cap->cmdchar == 'z'; 3697 op_yank(oap, !gui_yank); 3698 } 3699 check_cursor_col(curwin); 3700 break; 3701 3702 case OP_CHANGE: 3703 VIsual_reselect = false; // don't reselect now 3704 if (empty_region_error) { 3705 vim_beep(kOptBoFlagOperator); 3706 CancelRedo(); 3707 } else { 3708 // This is a new edit command, not a restart. Need to 3709 // remember it to make i_CTRL-O work with mappings for 3710 // Visual mode. But do this only once and not when typed. 3711 if (!KeyTyped) { 3712 restart_edit_save = restart_edit; 3713 } else { 3714 restart_edit_save = 0; 3715 } 3716 restart_edit = 0; 3717 3718 // Restore linebreak, so that when the user edits it looks as before. 3719 restore_lbr(lbr_saved); 3720 3721 // trigger TextChangedI 3722 curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); 3723 3724 if (op_change(oap)) { // will call edit() 3725 cap->retval |= CA_COMMAND_BUSY; 3726 } 3727 if (restart_edit == 0) { 3728 restart_edit = restart_edit_save; 3729 } 3730 } 3731 break; 3732 3733 case OP_FILTER: 3734 if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { 3735 AppendToRedobuff("!\r"); // Use any last used !cmd. 3736 } else { 3737 bangredo = true; // do_bang() will put cmd in redo buffer. 3738 } 3739 FALLTHROUGH; 3740 3741 case OP_INDENT: 3742 case OP_COLON: 3743 3744 // If 'equalprg' is empty, do the indenting internally. 3745 if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) { 3746 if (curbuf->b_p_lisp) { 3747 if (use_indentexpr_for_lisp()) { 3748 op_reindent(oap, get_expr_indent); 3749 } else { 3750 op_reindent(oap, get_lisp_indent); 3751 } 3752 break; 3753 } 3754 op_reindent(oap, 3755 *curbuf->b_p_inde != NUL ? get_expr_indent 3756 : get_c_indent); 3757 break; 3758 } 3759 3760 op_colon(oap); 3761 break; 3762 3763 case OP_TILDE: 3764 case OP_UPPER: 3765 case OP_LOWER: 3766 case OP_ROT13: 3767 if (empty_region_error) { 3768 vim_beep(kOptBoFlagOperator); 3769 CancelRedo(); 3770 } else { 3771 op_tilde(oap); 3772 } 3773 check_cursor_col(curwin); 3774 break; 3775 3776 case OP_FORMAT: 3777 if (*curbuf->b_p_fex != NUL) { 3778 op_formatexpr(oap); // use expression 3779 } else { 3780 if (*p_fp != NUL || *curbuf->b_p_fp != NUL) { 3781 op_colon(oap); // use external command 3782 } else { 3783 op_format(oap, false); // use internal function 3784 } 3785 } 3786 break; 3787 3788 case OP_FORMAT2: 3789 op_format(oap, true); // use internal function 3790 break; 3791 3792 case OP_FUNCTION: { 3793 redo_VIsual_T save_redo_VIsual = redo_VIsual; 3794 3795 // Restore linebreak, so that when the user edits it looks as before. 3796 restore_lbr(lbr_saved); 3797 // call 'operatorfunc' 3798 op_function(oap); 3799 3800 // Restore the info for redoing Visual mode, the function may 3801 // invoke another operator and unintentionally change it. 3802 redo_VIsual = save_redo_VIsual; 3803 break; 3804 } 3805 3806 case OP_INSERT: 3807 case OP_APPEND: 3808 VIsual_reselect = false; // don't reselect now 3809 if (empty_region_error) { 3810 vim_beep(kOptBoFlagOperator); 3811 CancelRedo(); 3812 } else { 3813 // This is a new edit command, not a restart. Need to 3814 // remember it to make i_CTRL-O work with mappings for 3815 // Visual mode. But do this only once. 3816 restart_edit_save = restart_edit; 3817 restart_edit = 0; 3818 3819 // Restore linebreak, so that when the user edits it looks as before. 3820 restore_lbr(lbr_saved); 3821 3822 // trigger TextChangedI 3823 curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); 3824 3825 op_insert(oap, cap->count1); 3826 3827 // Reset linebreak, so that formatting works correctly. 3828 reset_lbr(); 3829 3830 // TODO(brammool): when inserting in several lines, should format all 3831 // the lines. 3832 auto_format(false, true); 3833 3834 if (restart_edit == 0) { 3835 restart_edit = restart_edit_save; 3836 } else { 3837 cap->retval |= CA_COMMAND_BUSY; 3838 } 3839 } 3840 break; 3841 3842 case OP_REPLACE: 3843 VIsual_reselect = false; // don't reselect now 3844 if (empty_region_error) { 3845 vim_beep(kOptBoFlagOperator); 3846 CancelRedo(); 3847 } else { 3848 // Restore linebreak, so that when the user edits it looks as before. 3849 restore_lbr(lbr_saved); 3850 3851 op_replace(oap, cap->nchar); 3852 } 3853 break; 3854 3855 case OP_FOLD: 3856 VIsual_reselect = false; // don't reselect now 3857 foldCreate(curwin, oap->start, oap->end); 3858 break; 3859 3860 case OP_FOLDOPEN: 3861 case OP_FOLDOPENREC: 3862 case OP_FOLDCLOSE: 3863 case OP_FOLDCLOSEREC: 3864 VIsual_reselect = false; // don't reselect now 3865 opFoldRange(oap->start, oap->end, 3866 oap->op_type == OP_FOLDOPEN 3867 || oap->op_type == OP_FOLDOPENREC, 3868 oap->op_type == OP_FOLDOPENREC 3869 || oap->op_type == OP_FOLDCLOSEREC, 3870 oap->is_VIsual); 3871 break; 3872 3873 case OP_FOLDDEL: 3874 case OP_FOLDDELREC: 3875 VIsual_reselect = false; // don't reselect now 3876 deleteFold(curwin, oap->start.lnum, oap->end.lnum, 3877 oap->op_type == OP_FOLDDELREC, oap->is_VIsual); 3878 break; 3879 3880 case OP_NR_ADD: 3881 case OP_NR_SUB: 3882 if (empty_region_error) { 3883 vim_beep(kOptBoFlagOperator); 3884 CancelRedo(); 3885 } else { 3886 VIsual_active = true; 3887 restore_lbr(lbr_saved); 3888 op_addsub(oap, (linenr_T)cap->count1, redo_VIsual.rv_arg); 3889 VIsual_active = false; 3890 } 3891 check_cursor_col(curwin); 3892 break; 3893 default: 3894 clearopbeep(oap); 3895 } 3896 virtual_op = kNone; 3897 if (!gui_yank) { 3898 // if 'sol' not set, go back to old column for some commands 3899 if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted 3900 && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT 3901 || oap->op_type == OP_DELETE)) { 3902 reset_lbr(); 3903 coladvance(curwin, curwin->w_curswant = old_col); 3904 } 3905 } else { 3906 curwin->w_cursor = old_cursor; 3907 } 3908 clearop(oap); 3909 motion_force = NUL; 3910 } 3911 restore_lbr(lbr_saved); 3912 } 3913 3914 /// Get the byte count of buffer region. End-exclusive. 3915 /// 3916 /// @return number of bytes 3917 bcount_t get_region_bytecount(buf_T *buf, linenr_T start_lnum, linenr_T end_lnum, colnr_T start_col, 3918 colnr_T end_col) 3919 { 3920 linenr_T max_lnum = buf->b_ml.ml_line_count; 3921 if (start_lnum > max_lnum) { 3922 return 0; 3923 } 3924 if (start_lnum == end_lnum) { 3925 return end_col - start_col; 3926 } 3927 bcount_t deleted_bytes = ml_get_buf_len(buf, start_lnum) - start_col + 1; 3928 3929 for (linenr_T i = 1; i <= end_lnum - start_lnum - 1; i++) { 3930 if (start_lnum + i > max_lnum) { 3931 return deleted_bytes; 3932 } 3933 deleted_bytes += ml_get_buf_len(buf, start_lnum + i) + 1; 3934 } 3935 if (end_lnum > max_lnum) { 3936 return deleted_bytes; 3937 } 3938 return deleted_bytes + end_col; 3939 }