cursor.c (15295B)
1 #include <assert.h> 2 #include <inttypes.h> 3 #include <stdbool.h> 4 #include <string.h> 5 6 #include "nvim/ascii_defs.h" 7 #include "nvim/assert_defs.h" 8 #include "nvim/buffer_defs.h" 9 #include "nvim/change.h" 10 #include "nvim/cursor.h" 11 #include "nvim/drawscreen.h" 12 #include "nvim/fold.h" 13 #include "nvim/globals.h" 14 #include "nvim/macros_defs.h" 15 #include "nvim/mark.h" 16 #include "nvim/mbyte.h" 17 #include "nvim/mbyte_defs.h" 18 #include "nvim/memline.h" 19 #include "nvim/memory.h" 20 #include "nvim/move.h" 21 #include "nvim/option.h" 22 #include "nvim/option_vars.h" 23 #include "nvim/plines.h" 24 #include "nvim/pos_defs.h" 25 #include "nvim/state.h" 26 #include "nvim/state_defs.h" 27 #include "nvim/types_defs.h" 28 #include "nvim/vim_defs.h" 29 30 #include "cursor.c.generated.h" 31 32 /// @return the screen position of the cursor. 33 int getviscol(void) 34 { 35 colnr_T x; 36 37 getvvcol(curwin, &curwin->w_cursor, &x, NULL, NULL); 38 return (int)x; 39 } 40 41 /// @return the screen position of character col with a coladd in the cursor line. 42 int getviscol2(colnr_T col, colnr_T coladd) 43 { 44 colnr_T x; 45 pos_T pos; 46 47 pos.lnum = curwin->w_cursor.lnum; 48 pos.col = col; 49 pos.coladd = coladd; 50 getvvcol(curwin, &pos, &x, NULL, NULL); 51 return (int)x; 52 } 53 54 /// Go to column "wcol", and add/insert white space as necessary to get the 55 /// cursor in that column. 56 /// The caller must have saved the cursor line for undo! 57 int coladvance_force(colnr_T wcol) 58 { 59 int rc = coladvance2(curwin, &curwin->w_cursor, true, false, wcol); 60 61 if (wcol == MAXCOL) { 62 curwin->w_valid &= ~VALID_VIRTCOL; 63 } else { 64 // Virtcol is valid 65 set_valid_virtcol(curwin, wcol); 66 } 67 return rc; 68 } 69 70 /// Try to advance the Cursor to the specified screen column. 71 /// If virtual editing: fine tune the cursor position. 72 /// Note that all virtual positions off the end of a line should share 73 /// a curwin->w_cursor.col value (n.b. this is equal to strlen(line)), 74 /// beginning at coladd 0. 75 /// 76 /// @return OK if desired column is reached, FAIL if not 77 int coladvance(win_T *wp, colnr_T wcol) 78 { 79 int rc = getvpos(wp, &wp->w_cursor, wcol); 80 81 if (wcol == MAXCOL || rc == FAIL) { 82 wp->w_valid &= ~VALID_VIRTCOL; 83 } else if (*(ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) + wp->w_cursor.col) != TAB) { 84 // Virtcol is valid when not on a TAB 85 set_valid_virtcol(curwin, wcol); 86 } 87 return rc; 88 } 89 90 /// @param addspaces change the text to achieve our goal? only for wp=curwin! 91 /// @param finetune change char offset for the exact column 92 /// @param wcol_arg column to move to (can be negative) 93 static int coladvance2(win_T *wp, pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_arg) 94 { 95 assert(wp == curwin || !addspaces); 96 colnr_T wcol = wcol_arg; 97 int idx; 98 colnr_T col = 0; 99 int head = 0; 100 101 int one_more = (State & MODE_INSERT) 102 || (State & MODE_TERMINAL) 103 || restart_edit != NUL 104 || (VIsual_active && *p_sel != 'o') 105 || ((get_ve_flags(wp) & kOptVeFlagOnemore) && wcol < MAXCOL); 106 107 char *line = ml_get_buf(wp->w_buffer, pos->lnum); 108 int linelen = ml_get_buf_len(wp->w_buffer, pos->lnum); 109 110 if (wcol >= MAXCOL) { 111 idx = linelen - 1 + one_more; 112 col = wcol; 113 114 if ((addspaces || finetune) && !VIsual_active) { 115 wp->w_curswant = linetabsize(wp, pos->lnum) + one_more; 116 if (wp->w_curswant > 0) { 117 wp->w_curswant--; 118 } 119 } 120 } else { 121 int width = wp->w_view_width - win_col_off(wp); 122 int csize = 0; 123 124 if (finetune 125 && wp->w_p_wrap 126 && wp->w_view_width != 0 127 && wcol >= (colnr_T)width 128 && width > 0) { 129 csize = linetabsize_eol(wp, pos->lnum); 130 if (csize > 0) { 131 csize--; 132 } 133 134 if (wcol / width > (colnr_T)csize / width 135 && ((State & MODE_INSERT) == 0 || (int)wcol > csize + 1)) { 136 // In case of line wrapping don't move the cursor beyond the 137 // right screen edge. In Insert mode allow going just beyond 138 // the last character (like what happens when typing and 139 // reaching the right window edge). 140 wcol = (csize / width + 1) * width - 1; 141 } 142 } 143 144 CharsizeArg csarg; 145 CSType cstype = init_charsize_arg(&csarg, wp, pos->lnum, line); 146 StrCharInfo ci = utf_ptr2StrCharInfo(line); 147 col = 0; 148 while (col <= wcol && *ci.ptr != NUL) { 149 CharSize cs = win_charsize(cstype, col, ci.ptr, ci.chr.value, &csarg); 150 csize = cs.width; 151 head = cs.head; 152 col += cs.width; 153 ci = utfc_next(ci); 154 } 155 idx = (int)(ci.ptr - line); 156 157 // Handle all the special cases. The virtual_active() check 158 // is needed to ensure that a virtual position off the end of 159 // a line has the correct indexing. The one_more comparison 160 // replaces an explicit add of one_more later on. 161 if (col > wcol || (!virtual_active(wp) && one_more == 0)) { 162 idx -= 1; 163 // Don't count the chars from 'showbreak'. 164 csize -= head; 165 col -= csize; 166 } 167 168 if (virtual_active(wp) 169 && addspaces 170 && wcol >= 0 171 && ((col != wcol && col != wcol + 1) || csize > 1)) { 172 // 'virtualedit' is set: The difference between wcol and col is filled with spaces. 173 174 if (line[idx] == NUL) { 175 // Append spaces 176 int correct = wcol - col; 177 size_t newline_size; 178 STRICT_ADD(idx, correct, &newline_size, size_t); 179 char *newline = xmallocz(newline_size); 180 memcpy(newline, line, (size_t)idx); 181 memset(newline + idx, ' ', (size_t)correct); 182 183 ml_replace(pos->lnum, newline, false); 184 inserted_bytes(pos->lnum, (colnr_T)idx, 0, correct); 185 idx += correct; 186 col = wcol; 187 } else { 188 // Break a tab 189 int correct = wcol - col - csize + 1; // negative!! 190 char *newline; 191 192 if (-correct > csize) { 193 return FAIL; 194 } 195 196 size_t n; 197 STRICT_ADD(linelen - 1, csize, &n, size_t); 198 newline = xmallocz(n); 199 // Copy first idx chars 200 memcpy(newline, line, (size_t)idx); 201 // Replace idx'th char with csize spaces 202 memset(newline + idx, ' ', (size_t)csize); 203 // Copy the rest of the line 204 STRICT_SUB(linelen, idx, &n, size_t); 205 STRICT_SUB(n, 1, &n, size_t); 206 memcpy(newline + idx + csize, line + idx + 1, n); 207 208 ml_replace(pos->lnum, newline, false); 209 inserted_bytes(pos->lnum, idx, 1, csize); 210 idx += (csize - 1 + correct); 211 col += correct; 212 } 213 } 214 } 215 216 pos->col = MAX(idx, 0); 217 pos->coladd = 0; 218 219 if (finetune) { 220 if (wcol == MAXCOL) { 221 // The width of the last character is used to set coladd. 222 if (!one_more) { 223 colnr_T scol, ecol; 224 225 getvcol(wp, pos, &scol, NULL, &ecol); 226 pos->coladd = ecol - scol; 227 } 228 } else { 229 int b = (int)wcol - (int)col; 230 231 // The difference between wcol and col is used to set coladd. 232 if (b > 0 && b < (MAXCOL - 2 * wp->w_view_width)) { 233 pos->coladd = b; 234 } 235 236 col += b; 237 } 238 } 239 240 // Prevent from moving onto a trail byte. 241 mark_mb_adjustpos(wp->w_buffer, pos); 242 243 if (wcol < 0 || col < wcol) { 244 return FAIL; 245 } 246 return OK; 247 } 248 249 /// Return in "pos" the position of the cursor advanced to screen column "wcol". 250 /// 251 /// @return OK if desired column is reached, FAIL if not 252 int getvpos(win_T *wp, pos_T *pos, colnr_T wcol) 253 { 254 return coladvance2(wp, pos, false, virtual_active(wp), wcol); 255 } 256 257 /// Increment the cursor position. See inc() for return values. 258 int inc_cursor(void) 259 { 260 return inc(&curwin->w_cursor); 261 } 262 263 /// Decrement the line pointer 'p' crossing line boundaries as necessary. 264 /// 265 /// @return 1 when crossing a line, -1 when at start of file, 0 otherwise. 266 int dec_cursor(void) 267 { 268 return dec(&curwin->w_cursor); 269 } 270 271 /// Get the line number relative to the current cursor position, i.e. the 272 /// difference between line number and cursor position. Only look for lines that 273 /// can be visible, folded lines don't count. 274 /// 275 /// @param lnum line number to get the result for 276 linenr_T get_cursor_rel_lnum(win_T *wp, linenr_T lnum) 277 { 278 linenr_T cursor = wp->w_cursor.lnum; 279 if (lnum == cursor || !hasAnyFolding(wp)) { 280 return lnum - cursor; 281 } 282 283 linenr_T from_line = lnum < cursor ? lnum : cursor; 284 linenr_T to_line = lnum > cursor ? lnum : cursor; 285 linenr_T retval = 0; 286 287 // Loop until we reach to_line, skipping folds. 288 for (; from_line < to_line; from_line++, retval++) { 289 // If from_line is in a fold, set it to the last line of that fold. 290 hasFolding(wp, from_line, NULL, &from_line); 291 } 292 293 // If to_line is in a closed fold, the line count is off by +1. Correct it. 294 if (from_line > to_line) { 295 retval--; 296 } 297 298 return (lnum < cursor) ? -retval : retval; 299 } 300 301 /// Make sure "pos.lnum" and "pos.col" are valid in "buf". 302 /// This allows for the col to be on the NUL byte. 303 void check_pos(buf_T *buf, pos_T *pos) 304 { 305 pos->lnum = MIN(pos->lnum, buf->b_ml.ml_line_count); 306 if (pos->col > 0) { 307 pos->col = MIN(pos->col, ml_get_buf_len(buf, pos->lnum)); 308 } 309 } 310 311 /// Make sure win->w_cursor.lnum is valid. 312 void check_cursor_lnum(win_T *win) 313 { 314 buf_T *buf = win->w_buffer; 315 if (win->w_cursor.lnum > buf->b_ml.ml_line_count) { 316 // If there is a closed fold at the end of the file, put the cursor in 317 // its first line. Otherwise in the last line. 318 if (!hasFolding(win, buf->b_ml.ml_line_count, &win->w_cursor.lnum, NULL)) { 319 win->w_cursor.lnum = buf->b_ml.ml_line_count; 320 } 321 } 322 if (win->w_cursor.lnum <= 0) { 323 win->w_cursor.lnum = 1; 324 } 325 } 326 327 /// Make sure win->w_cursor.col is valid. Special handling of insert-mode. 328 /// @see mb_check_adjust_col 329 void check_cursor_col(win_T *win) 330 { 331 colnr_T oldcol = win->w_cursor.col; 332 colnr_T oldcoladd = win->w_cursor.col + win->w_cursor.coladd; 333 unsigned cur_ve_flags = get_ve_flags(win); 334 335 colnr_T len = ml_get_buf_len(win->w_buffer, win->w_cursor.lnum); 336 if (len == 0) { 337 win->w_cursor.col = 0; 338 } else if (win->w_cursor.col >= len) { 339 // Allow cursor past end-of-line when: 340 // - in Insert mode or restarting Insert mode 341 // - in Terminal mode 342 // - in Visual mode and 'selection' isn't "old" 343 // - 'virtualedit' is set 344 if ((State & MODE_INSERT) || restart_edit 345 || (State & MODE_TERMINAL) 346 || (VIsual_active && *p_sel != 'o') 347 || (cur_ve_flags & kOptVeFlagOnemore) 348 || virtual_active(win)) { 349 win->w_cursor.col = len; 350 } else { 351 win->w_cursor.col = len - 1; 352 // Move the cursor to the head byte. 353 mark_mb_adjustpos(win->w_buffer, &win->w_cursor); 354 } 355 } else if (win->w_cursor.col < 0) { 356 win->w_cursor.col = 0; 357 } 358 359 // If virtual editing is on, we can leave the cursor on the old position, 360 // only we must set it to virtual. But don't do it when at the end of the 361 // line. 362 if (oldcol == MAXCOL) { 363 win->w_cursor.coladd = 0; 364 } else if (cur_ve_flags == kOptVeFlagAll) { 365 if (oldcoladd > win->w_cursor.col) { 366 win->w_cursor.coladd = oldcoladd - win->w_cursor.col; 367 368 // Make sure that coladd is not more than the char width. 369 // Not for the last character, coladd is then used when the cursor 370 // is actually after the last character. 371 if (win->w_cursor.col + 1 < len) { 372 assert(win->w_cursor.coladd > 0); 373 int cs, ce; 374 375 getvcol(win, &win->w_cursor, &cs, NULL, &ce); 376 win->w_cursor.coladd = MIN(win->w_cursor.coladd, ce - cs); 377 } 378 } else { 379 // avoid weird number when there is a miscalculation or overflow 380 win->w_cursor.coladd = 0; 381 } 382 } 383 } 384 385 /// Make sure curwin->w_cursor in on a valid character 386 void check_cursor(win_T *wp) 387 { 388 check_cursor_lnum(wp); 389 check_cursor_col(wp); 390 } 391 392 /// Check if VIsual position is valid, correct it if not. 393 /// Can be called when in Visual mode and a change has been made. 394 void check_visual_pos(void) 395 { 396 if (VIsual.lnum > curbuf->b_ml.ml_line_count) { 397 VIsual.lnum = curbuf->b_ml.ml_line_count; 398 VIsual.col = 0; 399 VIsual.coladd = 0; 400 } else { 401 int len = ml_get_len(VIsual.lnum); 402 403 if (VIsual.col > len) { 404 VIsual.col = len; 405 VIsual.coladd = 0; 406 } 407 } 408 } 409 410 /// Make sure curwin->w_cursor is not on the NUL at the end of the line. 411 /// Allow it when in Visual mode and 'selection' is not "old". 412 void adjust_cursor_col(void) 413 { 414 if (curwin->w_cursor.col > 0 415 && (!VIsual_active || *p_sel == 'o') 416 && gchar_cursor() == NUL) { 417 curwin->w_cursor.col--; 418 } 419 } 420 421 /// Set "curwin->w_leftcol" to "leftcol". 422 /// Adjust the cursor position if needed. 423 /// 424 /// @return true if the cursor was moved. 425 bool set_leftcol(colnr_T leftcol) 426 { 427 // Return quickly when there is no change. 428 if (curwin->w_leftcol == leftcol) { 429 return false; 430 } 431 curwin->w_leftcol = leftcol; 432 433 changed_cline_bef_curs(curwin); 434 // TODO(hinidu): I think it should be colnr_T or int, but p_siso is long. 435 // Perhaps we can change p_siso to int. 436 int64_t lastcol = curwin->w_leftcol + curwin->w_view_width - win_col_off(curwin) - 1; 437 validate_virtcol(curwin); 438 439 bool retval = false; 440 // If the cursor is right or left of the screen, move it to last or first 441 // visible character. 442 int siso = get_sidescrolloff_value(curwin); 443 if (curwin->w_virtcol > (colnr_T)(lastcol - siso)) { 444 retval = true; 445 coladvance(curwin, (colnr_T)(lastcol - siso)); 446 } else if (curwin->w_virtcol < curwin->w_leftcol + siso) { 447 retval = true; 448 coladvance(curwin, (colnr_T)(curwin->w_leftcol + siso)); 449 } 450 451 // If the start of the character under the cursor is not on the screen, 452 // advance the cursor one more char. If this fails (last char of the 453 // line) adjust the scrolling. 454 colnr_T s, e; 455 getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e); 456 if (e > (colnr_T)lastcol) { 457 retval = true; 458 coladvance(curwin, s - 1); 459 } else if (s < curwin->w_leftcol) { 460 retval = true; 461 if (coladvance(curwin, e + 1) == FAIL) { // there isn't another character 462 curwin->w_leftcol = s; // adjust w_leftcol instead 463 changed_cline_bef_curs(curwin); 464 } 465 } 466 467 if (retval) { 468 curwin->w_set_curswant = true; 469 } 470 redraw_later(curwin, UPD_NOT_VALID); 471 return retval; 472 } 473 474 int gchar_cursor(void) 475 { 476 return utf_ptr2char(get_cursor_pos_ptr()); 477 } 478 479 /// Return the character immediately before the cursor. 480 int char_before_cursor(void) 481 { 482 if (curwin->w_cursor.col == 0) { 483 return -1; 484 } 485 486 char *line = get_cursor_line_ptr(); 487 char *p = line + curwin->w_cursor.col; 488 int prev_len = utf_head_off(line, p - 1) + 1; 489 return utf_ptr2char(p - prev_len); 490 } 491 492 /// Write a character at the current cursor position. 493 /// It is directly written into the block. 494 void pchar_cursor(char c) 495 { 496 *(ml_get_buf_mut(curbuf, curwin->w_cursor.lnum) + curwin->w_cursor.col) = c; 497 } 498 499 /// @return pointer to cursor line. 500 char *get_cursor_line_ptr(void) 501 { 502 return ml_get_buf(curbuf, curwin->w_cursor.lnum); 503 } 504 505 /// @return pointer to cursor position. 506 char *get_cursor_pos_ptr(void) 507 { 508 return ml_get_buf(curbuf, curwin->w_cursor.lnum) + curwin->w_cursor.col; 509 } 510 511 /// @return length (excluding the NUL) of the cursor line. 512 colnr_T get_cursor_line_len(void) 513 { 514 return ml_get_buf_len(curbuf, curwin->w_cursor.lnum); 515 } 516 517 /// @return length (excluding the NUL) of the cursor position. 518 colnr_T get_cursor_pos_len(void) 519 { 520 return ml_get_buf_len(curbuf, curwin->w_cursor.lnum) - curwin->w_cursor.col; 521 }