patch 9.2.0441: statusline: click handler not called on multi-line statusline
Commit: https://github.com/vim/vim/commit/8c7d824b73ff939890f13c3792e45833a651a840 Author: Hirohito Higashi <[email protected]> Date: Mon May 4 20:03:46 2026 +0000 patch 9.2.0441: statusline: click handler not called on multi-line statusline Problem: With a multi-line statusline clicking on a "%[FuncName]...%[]" or "%@FuncName@..." region defined on a row other than the last drawn row does not invoke the handler (Christian Robinson, after v9.2.0338) Solution: In win_redr_custom() the click region table reflects only the last iteration of the per-row draw loop, so click regions are recorded only for the last row. Move the click-region resolution inside the loop and append regions for each row using vim_realloc(). This also fixes a leak of clicktab[].funcname for non-last rows (Hirohito Higashi). fixes: #20116 closes: #20120 Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> Signed-off-by: Hirohito Higashi <[email protected]> Signed-off-by: Christian Brabandt <[email protected]> diff --git a/src/screen.c b/src/screen.c index 5dfaed93c..da4d6e278 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1414,6 +1414,36 @@ win_redr_custom( char_u *stl_tmp = (stl == NULL) ? (char_u *)"" : stl; int col_save = col; + // Determine where click regions for this draw will be stored, and reset + // the destination so each row in the loop below can append. This must + // be done before the loop because for a multi-line statusline each row + // contributes its own click regions. + stl_click_region_T **out_regions = NULL; + int *out_count = NULL; + int region_base_col = 0; + if (!draw_ruler) + { + if (wp != NULL) + { + out_regions = &wp->w_stl_click; + out_count = &wp->w_stl_click_count; + region_base_col = wp->w_wincol; + } + else + { + // 'tabline': store regions in global state since there is no + // associated window. + out_regions = &tabline_stl_click; + out_count = &tabline_stl_click_count; + region_base_col = firstwin->w_wincol; + } + + for (n = 0; n < *out_count; n++) + vim_free((*out_regions)[n].funcname); + VIM_CLEAR(*out_regions); + *out_count = 0; + } + for (int i = 0; i < stlh_cnt; i++) { col = col_save; @@ -1472,6 +1502,76 @@ win_redr_custom( curattr = highlight_user[hltab[n].userhl - 1]; } screen_puts(p, row + i, col, curattr); + + // Append click regions for this row. clicktab reflects the line + // just rendered, so each row of a multi-line statusline contributes + // its own regions. + if (out_regions != NULL) + { + int click_count = 0; + for (n = 0; clicktab[n].start != NULL; n++) + click_count++; + + if (click_count > 0) + { + stl_click_region_T *new_arr = vim_realloc(*out_regions, + sizeof(stl_click_region_T) + * (*out_count + click_count)); + if (new_arr != NULL) + { + char_u *cur_funcname = NULL; + int cur_minwid = 0; + int region_start = region_base_col; + + *out_regions = new_arr; + + len = 0; + p = buf; + for (n = 0; clicktab[n].start != NULL; n++) + { + len += vim_strnsize(p, + (int)(clicktab[n].start - p)); + p = clicktab[n].start; + + if (cur_funcname != NULL) + { + stl_click_region_T *r = + &(*out_regions)[*out_count]; + r->row = row + i; + r->col_start = region_start; + r->col_end = region_base_col + len; + r->funcname = vim_strsave(cur_funcname); + r->minwid = cur_minwid; + r->tabnr = 0; + (*out_count)++; + } + + cur_funcname = clicktab[n].funcname; + cur_minwid = clicktab[n].minwid; + region_start = region_base_col + len; + } + + // Close final region if it extends to the end. + if (cur_funcname != NULL) + { + stl_click_region_T *r = + &(*out_regions)[*out_count]; + r->row = row + i; + r->col_start = region_start; + r->col_end = region_base_col + maxwidth; + r->funcname = vim_strsave(cur_funcname); + r->minwid = cur_minwid; + r->tabnr = 0; + (*out_count)++; + } + } + } + } + + // Free the funcname strings allocated by build_stl_str_hl_local() + // for this line. They have been copied into the region array above. + for (n = 0; clicktab[n].start != NULL; n++) + vim_free(clicktab[n].funcname); } ewp->w_p_crb = p_crb_save; @@ -1501,110 +1601,6 @@ win_redr_custom( TabPageIdxs[col++] = fillchar; } - // Resolve click function regions for statusline or tabline. - if (!draw_ruler) - { - stl_click_region_T **out_regions; - int *out_count; - int base_col; - int base_row; - int click_count = 0; - - // clicktab reflects the last iteration of the draw loop above, so - // the regions belong to the last drawn row. - base_row = row + stlh_cnt - 1; - - if (wp != NULL) - { - out_regions = &wp->w_stl_click; - out_count = &wp->w_stl_click_count; - base_col = wp->w_wincol; - } - else - { - // 'tabline': store regions in global state since there is no - // associated window. - out_regions = &tabline_stl_click; - out_count = &tabline_stl_click_count; - base_col = firstwin->w_wincol; - } - - // Count the click regions. - for (n = 0; clicktab[n].start != NULL; n++) - click_count++; - - // Free old click regions. - if (*out_regions != NULL) - { - for (n = 0; n < *out_count; n++) - vim_free((*out_regions)[n].funcname); - VIM_CLEAR(*out_regions); - } - *out_count = 0; - - if (click_count > 0) - { - stl_click_region_T *regions; - int rcount = 0; - - regions = ALLOC_MULT(stl_click_region_T, click_count); - if (regions != NULL) - { - char_u *cur_funcname = NULL; - int cur_minwid = 0; - int region_start = base_col; - - // Walk through click records converting buffer positions - // to screen columns. - len = 0; - p = buf; - for (n = 0; clicktab[n].start != NULL; n++) - { - len += vim_strnsize(p, - (int)(clicktab[n].start - p)); - p = clicktab[n].start; - - // Close previous region if there was one. - if (cur_funcname != NULL) - { - regions[rcount].row = base_row; - regions[rcount].col_start = region_start; - regions[rcount].col_end = base_col + len; - regions[rcount].funcname = - vim_strsave(cur_funcname); - regions[rcount].minwid = cur_minwid; - regions[rcount].tabnr = 0; - rcount++; - } - - cur_funcname = clicktab[n].funcname; - cur_minwid = clicktab[n].minwid; - region_start = base_col + len; - } - - // Close final region if it extends to the end. - if (cur_funcname != NULL) - { - regions[rcount].row = base_row; - regions[rcount].col_start = region_start; - regions[rcount].col_end = base_col + maxwidth; - regions[rcount].funcname = - vim_strsave(cur_funcname); - regions[rcount].minwid = cur_minwid; - regions[rcount].tabnr = 0; - rcount++; - } - - *out_regions = regions; - *out_count = rcount; - } - } - - // Free the funcname strings allocated by build_stl_str_hl_local(). - for (n = 0; clicktab[n].start != NULL; n++) - vim_free(clicktab[n].funcname); - } - theend: if (override_success) pop_highlight_overrides(); diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim index 79e857cf5..6128e08b6 100644 --- a/src/testdir/test_statusline.vim +++ b/src/testdir/test_statusline.vim @@ -817,6 +817,44 @@ func Test_statusline_click_multiple_regions() let &laststatus = save_ls endfunc +" Click on a region in any row of a multi-line statusline (issue #20116). +func Test_statusline_click_multiline() + let save_mouse = &mouse + let save_stl = &statusline + let save_ls = &laststatus + let save_stlo = &statuslineopt + set mouse=a + set laststatus=2 + + " First row contains the click region, second row is filled with fillchar. + set statusline=%[StlClickTestFunc][Click]%[]%@\ \ \ \ + set statuslineopt=maxheight:2,fixedheight + redraw! + + let stl_row = win_screenpos(0)[0] + winheight(0) + + " Click on [Click] in the first row of the multi-line statusline. + call test_setmouse(stl_row, 2) + call feedkeys("\<LeftMouse>\<LeftRelease>", 'xt') + call assert_true(exists('g:stl_click_info')) + call assert_equal(0, g:stl_click_info.minwid) + unlet! g:stl_click_info + + " A click region on the second row should also be recognized. + set statusline=row1%@%2[StlClickTestFunc][Click2]%[] + redraw! + call test_setmouse(stl_row + 1, 2) + call feedkeys("\<LeftMouse>\<LeftRelease>", 'xt') + call assert_true(exists('g:stl_click_info')) + call assert_equal(2, g:stl_click_info.minwid) + unlet! g:stl_click_info + + let &mouse = save_mouse + let &statusline = save_stl + let &laststatus = save_ls + let &statuslineopt = save_stlo +endfunc + func Test_statusline_click_region_extends_to_end() let save_mouse = &mouse let save_stl = &statusline diff --git a/src/version.c b/src/version.c index 34f3dd850..b64fd60b8 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 441, /**/ 440, /**/ -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1wJzh4-005tO0-Ax%40256bit.org.
