To: vim_dev@googlegroups.com Subject: Patch 9.0.0048 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0048 Problem: Cursor in wrong column with mouse click after concealed text. Solution: Store the text column when drawing text. Files: src/drawline.c, src/globals.h, src/mouse.c, src/screen.c, src/testdir/test_conceal.vim *** ../vim-9.0.0047/src/drawline.c 2022-07-04 17:34:06.386292140 +0100 --- src/drawline.c 2022-07-08 19:15:41.511576553 +0100 *************** *** 779,785 **** trailcol = (colnr_T)STRLEN(ptr); while (trailcol > (colnr_T)0 && VIM_ISWHITE(ptr[trailcol - 1])) --trailcol; ! trailcol += (colnr_T) (ptr - line); } // find end of leading whitespace if (wp->w_lcs_chars.lead || wp->w_lcs_chars.leadmultispace != NULL) --- 779,785 ---- trailcol = (colnr_T)STRLEN(ptr); while (trailcol > (colnr_T)0 && VIM_ISWHITE(ptr[trailcol - 1])) --trailcol; ! trailcol += (colnr_T)(ptr - line); } // find end of leading whitespace if (wp->w_lcs_chars.lead || wp->w_lcs_chars.leadmultispace != NULL) *************** *** 792,798 **** leadcol = (colnr_T)0; else // keep track of the first column not filled with spaces ! leadcol += (colnr_T) (ptr - line) + 1; } } --- 792,798 ---- leadcol = (colnr_T)0; else // keep track of the first column not filled with spaces ! leadcol += (colnr_T)(ptr - line) + 1; } } *************** *** 1027,1038 **** // Repeat for the whole displayed line. for (;;) { #if defined(FEAT_CONCEAL) || defined(FEAT_SEARCH_EXTRA) ! int has_match_conc = 0; // match wants to conceal #endif #ifdef FEAT_CONCEAL ! int did_decrement_ptr = FALSE; #endif // Skip this quickly when working on the text. if (draw_state != WL_LINE) { --- 1027,1040 ---- // Repeat for the whole displayed line. for (;;) { + char_u *prev_ptr = ptr; #if defined(FEAT_CONCEAL) || defined(FEAT_SEARCH_EXTRA) ! int has_match_conc = 0; // match wants to conceal #endif #ifdef FEAT_CONCEAL ! int did_decrement_ptr = FALSE; #endif + // Skip this quickly when working on the text. if (draw_state != WL_LINE) { *************** *** 1392,1397 **** --- 1394,1400 ---- &match_conc, did_line_attr, lcs_eol_one, &on_last_col); ptr = line + v; // "line" may have been changed + prev_ptr = ptr; // Do not allow a conceal over EOL otherwise EOL will be missed // and bad things happen. *************** *** 1553,1558 **** --- 1556,1562 ---- // have made it invalid. line = ml_get_buf(wp->w_buffer, lnum, FALSE); ptr = line + v; + prev_ptr = ptr; # ifdef FEAT_CONCEAL // no concealing past the end of the line, it interferes // with line highlighting *************** *** 1733,1741 **** else { #ifdef FEAT_LINEBREAK ! int c0; #endif VIM_CLEAR(p_extra_free); // Get a character from the line itself. c = *ptr; --- 1737,1746 ---- else { #ifdef FEAT_LINEBREAK ! int c0; #endif VIM_CLEAR(p_extra_free); + prev_ptr = ptr; // Get a character from the line itself. c = *ptr; *************** *** 1942,1958 **** # endif can_spell)) { ! char_u *prev_ptr, *p; int len; hlf_T spell_hlf = HLF_COUNT; if (has_mbyte) - { - prev_ptr = ptr - mb_l; v -= mb_l - 1; - } - else - prev_ptr = ptr - 1; // Use nextline[] if possible, it has the start of the // next line concatenated. --- 1947,1958 ---- # endif can_spell)) { ! char_u *p; int len; hlf_T spell_hlf = HLF_COUNT; if (has_mbyte) v -= mb_l - 1; // Use nextline[] if possible, it has the start of the // next line concatenated. *************** *** 2771,2776 **** --- 2771,2777 ---- } #endif ScreenAttrs[off] = char_attr; + ScreenCols[off] = MAXCOL; #ifdef FEAT_RIGHTLEFT if (wp->w_p_rl) { *************** *** 2839,2844 **** --- 2840,2846 ---- ScreenLines[off] = ' '; if (enc_utf8) ScreenLinesUC[off] = 0; + ScreenCols[off] = MAXCOL; ++col; if (draw_color_col) draw_color_col = advance_color_col(VCOL_HLC, *************** *** 2992,2997 **** --- 2994,3001 ---- else ScreenAttrs[off] = char_attr; + ScreenCols[off] = (colnr_T)(prev_ptr - line); + if (has_mbyte && (*mb_char2cells)(mb_c) > 1) { // Need to fill two screen columns. *************** *** 3013,3018 **** --- 3017,3025 ---- // the character, otherwise highlighting won't stop. if (tocol == vcol) ++tocol; + + ScreenCols[off] = (colnr_T)(prev_ptr - line); + #ifdef FEAT_RIGHTLEFT if (wp->w_p_rl) { *** ../vim-9.0.0047/src/globals.h 2022-07-04 17:34:06.382292138 +0100 --- src/globals.h 2022-07-08 19:15:41.511576553 +0100 *************** *** 32,46 **** * The characters that are currently on the screen are kept in ScreenLines[]. * It is a single block of characters, the size of the screen plus one line. * The attributes for those characters are kept in ScreenAttrs[]. * * "LineOffset[n]" is the offset from ScreenLines[] for the start of line 'n'. ! * The same value is used for ScreenLinesUC[] and ScreenAttrs[]. * * Note: before the screen is initialized and when out of memory these can be * NULL. */ EXTERN schar_T *ScreenLines INIT(= NULL); EXTERN sattr_T *ScreenAttrs INIT(= NULL); EXTERN unsigned *LineOffset INIT(= NULL); EXTERN char_u *LineWraps INIT(= NULL); // line wraps to next line --- 32,48 ---- * The characters that are currently on the screen are kept in ScreenLines[]. * It is a single block of characters, the size of the screen plus one line. * The attributes for those characters are kept in ScreenAttrs[]. + * The byte offset in the line is kept in ScreenCols[]. * * "LineOffset[n]" is the offset from ScreenLines[] for the start of line 'n'. ! * The same value is used for ScreenLinesUC[], ScreenAttrs[] and ScreenCols[]. * * Note: before the screen is initialized and when out of memory these can be * NULL. */ EXTERN schar_T *ScreenLines INIT(= NULL); EXTERN sattr_T *ScreenAttrs INIT(= NULL); + EXTERN colnr_T *ScreenCols INIT(= NULL); EXTERN unsigned *LineOffset INIT(= NULL); EXTERN char_u *LineWraps INIT(= NULL); // line wraps to next line *************** *** 62,68 **** EXTERN schar_T *ScreenLines2 INIT(= NULL); /* ! * Buffer for one screen line (characters and attributes). */ EXTERN schar_T *current_ScreenLine INIT(= NULL); --- 64,70 ---- EXTERN schar_T *ScreenLines2 INIT(= NULL); /* ! * One screen line to be displayed. Points into ScreenLines. */ EXTERN schar_T *current_ScreenLine INIT(= NULL); *** ../vim-9.0.0047/src/mouse.c 2022-07-04 17:34:06.386292140 +0100 --- src/mouse.c 2022-07-08 20:26:19.661411049 +0100 *************** *** 1543,1550 **** int first; int row = mouse_row; int col = mouse_col; #ifdef FEAT_FOLDING ! int mouse_char; #endif mouse_past_bottom = FALSE; --- 1543,1551 ---- int first; int row = mouse_row; int col = mouse_col; + colnr_T col_from_screen = -1; #ifdef FEAT_FOLDING ! int mouse_char = ' '; #endif mouse_past_bottom = FALSE; *************** *** 1626,1641 **** if (flags & MOUSE_SETPOS) goto retnomove; // ugly goto... - #ifdef FEAT_FOLDING - // Remember the character under the mouse, it might be a '-' or '+' in the - // fold column. - if (row >= 0 && row < Rows && col >= 0 && col <= Columns - && ScreenLines != NULL) - mouse_char = ScreenLines[LineOffset[row] + col]; - else - mouse_char = ' '; - #endif - old_curwin = curwin; old_cursor = curwin->w_cursor; --- 1627,1632 ---- *************** *** 1969,1974 **** --- 1960,1981 ---- } } + if (prev_row >= 0 && prev_row < Rows && prev_col >= 0 && prev_col <= Columns + && ScreenLines != NULL) + { + int off = LineOffset[prev_row] + prev_col; + + // Only use ScreenCols[] after the window was redrawn. Mainly matters + // for tests, a user would not click before redrawing. + if (curwin->w_redr_type <= VALID_NO_UPDATE) + col_from_screen = ScreenCols[off]; + #ifdef FEAT_FOLDING + // Remember the character under the mouse, it might be a '-' or '+' in + // the fold column. + mouse_char = ScreenLines[off]; + #endif + } + #ifdef FEAT_FOLDING // Check for position outside of the fold column. if ( *************** *** 2001,2016 **** redraw_cmdline = TRUE; // show visual mode later } ! curwin->w_curswant = col; ! curwin->w_set_curswant = FALSE; // May still have been TRUE ! if (coladvance(col) == FAIL) // Mouse click beyond end of line ! { ! if (inclusive != NULL) ! *inclusive = TRUE; ! mouse_past_eol = TRUE; } - else if (inclusive != NULL) - *inclusive = FALSE; count = IN_BUFFER; if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum --- 2008,2047 ---- redraw_cmdline = TRUE; // show visual mode later } ! if (col_from_screen >= 0) ! { ! // Use the column from ScreenCols[], it is accurate also after ! // concealed characters. ! curwin->w_cursor.col = col_from_screen; ! if (col_from_screen == MAXCOL) ! { ! curwin->w_curswant = col_from_screen; ! curwin->w_set_curswant = FALSE; // May still have been TRUE ! mouse_past_eol = TRUE; ! if (inclusive != NULL) ! *inclusive = TRUE; ! } ! else ! { ! curwin->w_set_curswant = TRUE; ! if (inclusive != NULL) ! *inclusive = FALSE; ! } ! check_cursor_col(); ! } ! else ! { ! curwin->w_curswant = col; ! curwin->w_set_curswant = FALSE; // May still have been TRUE ! if (coladvance(col) == FAIL) // Mouse click beyond end of line ! { ! if (inclusive != NULL) ! *inclusive = TRUE; ! mouse_past_eol = TRUE; ! } ! else if (inclusive != NULL) ! *inclusive = FALSE; } count = IN_BUFFER; if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum *** ../vim-9.0.0047/src/screen.c 2022-07-04 21:03:33.103829294 +0100 --- src/screen.c 2022-07-08 19:47:43.138565783 +0100 *************** *** 17,24 **** * ScreenLines[off] Contains a copy of the whole screen, as it is currently * displayed (excluding text written by external commands). * ScreenAttrs[off] Contains the associated attributes. ! * LineOffset[row] Contains the offset into ScreenLines*[] and ScreenAttrs[] ! * for each line. * LineWraps[row] Flag for each line whether it wraps to the next line. * * For double-byte characters, two consecutive bytes in ScreenLines[] can form --- 17,28 ---- * ScreenLines[off] Contains a copy of the whole screen, as it is currently * displayed (excluding text written by external commands). * ScreenAttrs[off] Contains the associated attributes. ! * ScreenCols[off] Contains the byte offset in the line. -1 means not ! * available (below last line), MAXCOL means after the end ! * of the line. ! * ! * LineOffset[row] Contains the offset into ScreenLines*[], ScreenAttrs[] ! * and ScreenCols[] for each line. * LineWraps[row] Flag for each line whether it wraps to the next line. * * For double-byte characters, two consecutive bytes in ScreenLines[] can form *************** *** 453,459 **** int clear_next = FALSE; int char_cells; // 1: normal char // 2: occupies two display cells - # define CHAR_CELLS char_cells // Check for illegal row and col, just in case. if (row >= Rows) --- 457,462 ---- *************** *** 519,526 **** char_cells = 1; redraw_this = redraw_next; ! redraw_next = force || char_needs_redraw(off_from + CHAR_CELLS, ! off_to + CHAR_CELLS, endcol - col - CHAR_CELLS); #ifdef FEAT_GUI // If the next character was bold, then redraw the current character to --- 522,529 ---- char_cells = 1; redraw_this = redraw_next; ! redraw_next = force || char_needs_redraw(off_from + char_cells, ! off_to + char_cells, endcol - col - char_cells); #ifdef FEAT_GUI // If the next character was bold, then redraw the current character to *************** *** 528,534 **** // happens in the GUI. if (redraw_next && gui.in_use) { ! hl = ScreenAttrs[off_to + CHAR_CELLS]; if (hl > HL_ALL) hl = syn_attr2attr(hl); if (hl & HL_BOLD) --- 531,537 ---- // happens in the GUI. if (redraw_next && gui.in_use) { ! hl = ScreenAttrs[off_to + char_cells]; if (hl > HL_ALL) hl = syn_attr2attr(hl); if (hl & HL_BOLD) *************** *** 666,676 **** --- 669,683 ---- } #endif ScreenAttrs[off_to] = ScreenAttrs[off_from]; + ScreenCols[off_to] = ScreenCols[off_from]; // For simplicity set the attributes of second half of a // double-wide character equal to the first half. if (char_cells == 2) + { ScreenAttrs[off_to + 1] = ScreenAttrs[off_from]; + ScreenCols[off_to + 1] = ScreenCols[off_from + 1]; + } if (enc_dbcs != 0 && char_cells == 2) screen_char_2(off_to, row, col + coloff); *************** *** 695,703 **** screen_stop_highlight(); } ! off_to += CHAR_CELLS; ! off_from += CHAR_CELLS; ! col += CHAR_CELLS; } if (clear_next) --- 702,714 ---- screen_stop_highlight(); } ! ScreenCols[off_to] = ScreenCols[off_from]; ! if (char_cells == 2) ! ScreenCols[off_to + 1] = ScreenCols[off_from]; ! ! off_to += char_cells; ! off_from += char_cells; ! col += char_cells; } if (clear_next) *************** *** 725,730 **** --- 736,742 ---- && ScreenAttrs[off_to] == 0 && (!enc_utf8 || ScreenLinesUC[off_to] == 0)) { + ScreenCols[off_to] = MAXCOL; ++off_to; ++col; } *************** *** 774,781 **** #endif screen_fill(row, row + 1, col + coloff, clear_width + coloff, ' ', ' ', 0); ! off_to += clear_width - col; ! col = clear_width; } } --- 786,796 ---- #endif screen_fill(row, row + 1, col + coloff, clear_width + coloff, ' ', ' ', 0); ! while (col < clear_width) ! { ! ScreenCols[off_to++] = MAXCOL; ! ++col; ! } } } *************** *** 1679,1684 **** --- 1694,1700 ---- ScreenLines[off + mbyte_blen] = 0; ScreenLines[off] = c; ScreenAttrs[off] = attr; + ScreenCols[off] = -1; if (enc_utf8) { if (c < 0x80 && u8cc[0] == 0) *************** *** 1699,1704 **** --- 1715,1721 ---- { ScreenLines[off + 1] = 0; ScreenAttrs[off + 1] = attr; + ScreenCols[off + 1] = -1; } screen_char(off, row, col); } *************** *** 1706,1711 **** --- 1723,1729 ---- { ScreenLines[off + 1] = ptr[1]; ScreenAttrs[off + 1] = attr; + ScreenCols[off + 1] = -1; screen_char_2(off, row, col); } else if (enc_dbcs == DBCS_JPNU && c == 0x8e) *************** *** 2174,2179 **** --- 2192,2198 ---- ) { ScreenAttrs[off] = (sattr_T)-1; + ScreenCols[off] = -1; return; } *************** *** 2250,2255 **** --- 2269,2275 ---- if (row == screen_Rows - 1 && col >= screen_Columns - 2) { ScreenAttrs[off] = (sattr_T)-1; + ScreenCols[off] = -1; return; } *************** *** 2335,2340 **** --- 2355,2361 ---- { ScreenLines[off] = ' '; ScreenAttrs[off] = attr; + ScreenCols[off] = -1; if (enc_utf8) ScreenLinesUC[off] = 0; } *************** *** 2505,2510 **** --- 2526,2532 ---- if (!did_delete || c != ' ') screen_char(off, row, col); } + ScreenCols[off] = -1; ++off; if (col == start_col) { *************** *** 2598,2603 **** --- 2620,2626 ---- schar_T *new_ScreenLines2 = NULL; int i; sattr_T *new_ScreenAttrs; + colnr_T *new_ScreenCols; unsigned *new_LineOffset; char_u *new_LineWraps; short *new_TabPageIdxs; *************** *** 2688,2693 **** --- 2711,2717 ---- if (enc_dbcs == DBCS_JPNU) new_ScreenLines2 = LALLOC_MULT(schar_T, (Rows + 1) * Columns); new_ScreenAttrs = LALLOC_MULT(sattr_T, (Rows + 1) * Columns); + new_ScreenCols = LALLOC_MULT(colnr_T, (Rows + 1) * Columns); new_LineOffset = LALLOC_MULT(unsigned, Rows); new_LineWraps = LALLOC_MULT(char_u, Rows); new_TabPageIdxs = LALLOC_MULT(short, Columns); *************** *** 2735,2740 **** --- 2759,2765 ---- || (enc_utf8 && (new_ScreenLinesUC == NULL || i != p_mco)) || (enc_dbcs == DBCS_JPNU && new_ScreenLines2 == NULL) || new_ScreenAttrs == NULL + || new_ScreenCols == NULL || new_LineOffset == NULL || new_LineWraps == NULL || new_TabPageIdxs == NULL *************** *** 2760,2765 **** --- 2785,2791 ---- VIM_CLEAR(new_ScreenLinesC[i]); VIM_CLEAR(new_ScreenLines2); VIM_CLEAR(new_ScreenAttrs); + VIM_CLEAR(new_ScreenCols); VIM_CLEAR(new_LineOffset); VIM_CLEAR(new_LineWraps); VIM_CLEAR(new_TabPageIdxs); *************** *** 2802,2807 **** --- 2828,2835 ---- 0, (size_t)Columns * sizeof(schar_T)); (void)vim_memset(new_ScreenAttrs + new_row * Columns, 0, (size_t)Columns * sizeof(sattr_T)); + (void)vim_memset(new_ScreenCols + new_row * Columns, + 0, (size_t)Columns * sizeof(colnr_T)); old_row = new_row + (screen_Rows - Rows); if (old_row >= 0 && ScreenLines != NULL) { *************** *** 2835,2840 **** --- 2863,2871 ---- mch_memmove(new_ScreenAttrs + new_LineOffset[new_row], ScreenAttrs + LineOffset[old_row], (size_t)len * sizeof(sattr_T)); + mch_memmove(new_ScreenCols + new_LineOffset[new_row], + ScreenAttrs + LineOffset[old_row], + (size_t)len * sizeof(colnr_T)); } } } *************** *** 2857,2862 **** --- 2888,2894 ---- Screen_mco = p_mco; ScreenLines2 = new_ScreenLines2; ScreenAttrs = new_ScreenAttrs; + ScreenCols = new_ScreenCols; LineOffset = new_LineOffset; LineWraps = new_LineWraps; TabPageIdxs = new_TabPageIdxs; *************** *** 2929,2934 **** --- 2961,2967 ---- VIM_CLEAR(ScreenLines2); VIM_CLEAR(ScreenLines); VIM_CLEAR(ScreenAttrs); + VIM_CLEAR(ScreenCols); VIM_CLEAR(LineOffset); VIM_CLEAR(LineWraps); VIM_CLEAR(TabPageIdxs); *************** *** 3018,3023 **** --- 3051,3057 ---- (void)vim_memset(ScreenLinesUC + off, 0, (size_t)width * sizeof(u8char_T)); (void)vim_memset(ScreenAttrs + off, attr, (size_t)width * sizeof(sattr_T)); + (void)vim_memset(ScreenCols + off, -1, (size_t)width * sizeof(colnr_T)); } /* *************** *** 3028,3033 **** --- 3062,3068 ---- lineinvalid(unsigned off, int width) { (void)vim_memset(ScreenAttrs + off, -1, (size_t)width * sizeof(sattr_T)); + (void)vim_memset(ScreenCols + off, -1, (size_t)width * sizeof(colnr_T)); } /* *************** *** 3066,3071 **** --- 3101,3108 ---- wp->w_width * sizeof(schar_T)); mch_memmove(ScreenAttrs + off_to, ScreenAttrs + off_from, wp->w_width * sizeof(sattr_T)); + mch_memmove(ScreenCols + off_to, ScreenCols + off_from, + wp->w_width * sizeof(colnr_T)); } /* *** ../vim-9.0.0047/src/testdir/test_conceal.vim 2022-02-10 19:08:54.000000000 +0000 --- src/testdir/test_conceal.vim 2022-07-08 20:14:56.576506880 +0100 *************** *** 4,9 **** --- 4,10 ---- CheckFeature conceal source screendump.vim + source view_util.vim func Test_conceal_two_windows() CheckScreendump *************** *** 282,285 **** --- 283,305 ---- set nolist endfunc + func Test_conceal_mouse_click() + enew! + set mouse=a + setlocal conceallevel=2 concealcursor=nc + syn match Concealed "this" conceal + hi link Concealed Search + call setline(1, 'conceal this click here') + redraw + call assert_equal(['conceal click here '], ScreenLines(1, 20)) + + " click on 'h' of "here" puts cursor there + call test_setmouse(1, 16) + call feedkeys("\", "tx") + call assert_equal([0, 1, 20, 0, 20], getcurpos()) + + bwipe! + set mouse& + endfunc + " vim: shiftwidth=2 sts=2 expandtab *** ../vim-9.0.0047/src/version.c 2022-07-07 22:20:28.445941352 +0100 --- src/version.c 2022-07-08 19:17:41.747142877 +0100 *************** *** 737,738 **** --- 737,740 ---- { /* Add new patch number below this line */ + /**/ + 48, /**/ -- hundred-and-one symptoms of being an internet addict: 89. In addition to your e-mail address being on your business cards you even have your own domain. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///