diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 6ba1834d82..3009177d88 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -201,6 +201,7 @@ EVENTS • |CmdlineLeavePre| triggered before preparing to leave the command line. • New `append` paremeter for |ui-messages| `msg_show` event. • New `msg_id` paremeter for |ui-messages| `msg_show` event. +• 'rulerformat' is emitted as `msg_ruler` when not part of the statusline. • Creating or updating a progress message with |nvim_echo()| triggers a |Progress| event. HIGHLIGHTS diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 400298af35..b86a9b45ae 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2141,8 +2141,8 @@ DictAs(eval_statusline_ret) nvim_eval_statusline(String str, Dict(eval_statuslin if (opts->use_winbar) { fillchar = wp->w_p_fcs_chars.wbr; } else { - int attr; - fillchar = fillchar_status(&attr, wp); + hlf_T group; + fillchar = fillchar_status(&group, wp); } } diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 40c2fa5c64..6ec418a93b 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -513,17 +513,13 @@ static int wildmenu_match_len(expand_T *xp, char *s) /// @param matches list of matches static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int match, bool showtail) { - int len; - int clen; // length in screen cells - int attr; - int i; bool highlight = true; char *selstart = NULL; int selstart_col = 0; char *selend = NULL; static int first_match = 0; bool add_left = false; - int l; + int i, l; if (matches == NULL) { // interrupted completion? return; @@ -536,7 +532,7 @@ static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int m highlight = false; } // count 1 for the ending ">" - clen = wildmenu_match_len(xp, SHOW_MATCH(match)) + 3; + int clen = wildmenu_match_len(xp, SHOW_MATCH(match)) + 3; // length in screen cells if (match == 0) { first_match = 0; } else if (match < first_match) { @@ -577,7 +573,10 @@ static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int m } } - schar_T fillchar = fillchar_status(&attr, curwin); + int len; + hlf_T group; + schar_T fillchar = fillchar_status(&group, curwin); + int attr = win_hl_attr(curwin, (int)group); if (first_match == 0) { *buf = NUL; diff --git a/src/nvim/option.c b/src/nvim/option.c index a78bceadef..560dfc7a4e 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2094,6 +2094,7 @@ static const char *did_set_laststatus(optset_T *args) win_comp_pos(); } + status_redraw_curbuf(); last_status(false); // (re)set last window status line. return NULL; } diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index d452b0efaa..a806a0c73f 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -66,7 +66,6 @@ typedef enum { /// If inversion is possible we use it. Else '=' characters are used. void win_redr_status(win_T *wp) { - int attr; bool is_stl_global = global_stl_height() > 0; static bool busy = false; @@ -92,15 +91,16 @@ void win_redr_status(win_T *wp) redraw_custom_statusline(wp); } + hlf_T group = HLF_C; // May need to draw the character below the vertical separator. if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing()) { schar_T fillchar; if (stl_connected(wp)) { - fillchar = fillchar_status(&attr, wp); + fillchar = fillchar_status(&group, wp); } else { - attr = win_hl_attr(wp, HLF_C); fillchar = wp->w_p_fcs_chars.vert; } + int attr = win_hl_attr(wp, (int)group); grid_line_start(&default_gridview, W_ENDROW(wp)); grid_line_put_schar(W_ENDCOL(wp), fillchar, attr); grid_line_flush(); @@ -208,15 +208,17 @@ void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_r } } -/// Redraw the status line, window bar or ruler of window "wp". -/// When "wp" is NULL redraw the tab pages line from 'tabline'. -static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) +/// Redraw the status line, window bar, ruler or tabline. +/// @param wp target window, NULL for 'tabline' +/// @param draw_winbar redraw 'winbar' +/// @param draw_ruler redraw 'rulerformat' +/// @param ui_event emit UI-event instead of drawing +static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler, bool ui_event) { static bool entered = false; - int attr; - int row; int col = 0; - int maxwidth; + int attr, row, maxwidth; + hlf_T group; schar_T fillchar; char buf[MAXPATHL]; char transbuf[MAXPATHL]; @@ -243,7 +245,8 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) stl = p_tal; row = 0; fillchar = schar_from_ascii(' '); - attr = HL_ATTR(HLF_TPF); + group = HLF_TPF; + attr = HL_ATTR(group); maxwidth = Columns; opt_idx = kOptTabline; } else if (draw_winbar) { @@ -259,14 +262,15 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) } fillchar = wp->w_p_fcs_chars.wbr; - attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC); + group = (wp == curwin) ? HLF_WBR : HLF_WBRNC; + attr = win_hl_attr(wp, (int)group); maxwidth = wp->w_view_width; stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); wp->w_winbar_click_defs = stl_alloc_click_defs(wp->w_winbar_click_defs, maxwidth, &wp->w_winbar_click_defs_size); } else { row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); - fillchar = fillchar_status(&attr, wp); + fillchar = fillchar_status(&group, wp); const bool in_status_line = wp->w_status_height != 0 || is_stl_global; maxwidth = in_status_line && !is_stl_global ? wp->w_width : Columns; stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); @@ -297,7 +301,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) grid = grid_adjust(&msg_grid_adj, &row, &col); maxwidth--; // writing in last column may cause scrolling fillchar = schar_from_ascii(' '); - attr = HL_ATTR(HLF_MSG); + group = HLF_MSG; } } else { opt_idx = kOptStatusline; @@ -305,6 +309,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) opt_scope = ((*wp->w_p_stl != NUL) ? OPT_LOCAL : 0); } + attr = win_hl_attr(wp, (int)group); if (in_status_line && !is_stl_global) { col += wp->w_wincol; } @@ -332,31 +337,57 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) int len = (int)strlen(buf); int start_col = col; - // Draw each snippet with the specified highlighting. - screengrid_line_start(grid, row, 0); + if (!ui_event) { + // Draw each snippet with the specified highlighting. + screengrid_line_start(grid, row, 0); + } - int curattr = attr; char *p = buf; - for (int n = 0; hltab[n].start != NULL; n++) { - int textlen = (int)(hltab[n].start - p); - // Make all characters printable. - size_t tsize = transstr_buf(p, textlen, transbuf, sizeof transbuf, true); - col += grid_line_puts(col, transbuf, (int)tsize, curattr); - p = hltab[n].start; - - if (hltab[n].userhl == 0) { - curattr = attr; - } else if (hltab[n].userhl < 0) { - curattr = hl_combine_attr(attr, syn_id2attr(-hltab[n].userhl)); - } else if (wp != NULL && wp != curwin && wp->w_status_height != 0) { - curattr = highlight_stlnc[hltab[n].userhl - 1]; + int curattr = attr; + int curgroup = (int)group; + Array content = ARRAY_DICT_INIT; + for (stl_hlrec_t *sp = hltab;; sp++) { + int textlen = (int)(sp->start ? sp->start - p : buf + len - p); + // Make all characters printable. Use an empty string instead of p, if p is beyond buf + len. + size_t tsize = transstr_buf(p >= buf + len ? "" : p, textlen, transbuf, sizeof transbuf, true); + if (!ui_event) { + col += grid_line_puts(col, transbuf, (int)tsize, curattr); } else { - curattr = highlight_user[hltab[n].userhl - 1]; + Array chunk = ARRAY_DICT_INIT; + ADD(chunk, INTEGER_OBJ(curattr)); + ADD(chunk, STRING_OBJ(cbuf_as_string(xmemdupz(transbuf, tsize), tsize))); + ADD(chunk, INTEGER_OBJ(curgroup)); + ADD(content, ARRAY_OBJ(chunk)); + } + p = sp->start; + + if (p == NULL) { + break; + } else if (sp->userhl == 0) { + curattr = attr; + curgroup = (int)group; + } else if (sp->userhl < 0) { + curattr = syn_id2attr(-sp->userhl); + curgroup = -sp->userhl; + } else { + int *userhl = (wp != NULL && wp != curwin && wp->w_status_height != 0) + ? highlight_stlnc : highlight_user; + char userbuf[5] = "User"; + userbuf[4] = (char)sp->userhl + '0'; + curattr = userhl[sp->userhl - 1]; + curgroup = syn_name2id_len(userbuf, 5); + } + if (curattr != attr) { + curattr = hl_combine_attr(attr, curattr); } } - // Make sure to use an empty string instead of p, if p is beyond buf + len. - size_t tsize = transstr_buf(p >= buf + len ? "" : p, -1, transbuf, sizeof transbuf, true); - col += grid_line_puts(col, transbuf, (int)tsize, curattr); + + if (ui_event) { + ui_call_msg_ruler(content); + api_free_array(content); + goto theend; + } + int maxcol = start_col + maxwidth; // fill up with "fillchar" @@ -389,7 +420,7 @@ void win_redr_winbar(win_T *wp) if (wp->w_winbar_height == 0 || !redrawing()) { // Do nothing. } else if (*p_wbr != NUL || *wp->w_p_wbr != NUL) { - win_redr_custom(wp, true, false); + win_redr_custom(wp, true, false, false); } entered = false; } @@ -423,44 +454,22 @@ void redraw_ruler(void) // Don't draw the ruler while doing insert-completion, it might overwrite // the (long) mode message. - if (wp->w_status_height == 0 && !is_stl_global) { - if (edit_submode != NULL) { - return; - } - } - - if (*p_ruf && p_ch > 0 && !ui_has(kUIMessages)) { - win_redr_custom(wp, false, true); + if (wp->w_status_height == 0 && !is_stl_global && edit_submode != NULL) { return; } - // Check if not in Insert mode and the line is empty (will show "0-1"). - int empty_line = (State & MODE_INSERT) == 0 - && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) == NUL; - - int width; - schar_T fillchar; - int attr; - int off; - bool part_of_status = false; - - if (wp->w_status_height) { - fillchar = fillchar_status(&attr, wp); - off = wp->w_wincol; - width = wp->w_width; - part_of_status = true; - } else if (is_stl_global) { - fillchar = fillchar_status(&attr, wp); - off = 0; - width = Columns; - part_of_status = true; - } else { - fillchar = schar_from_ascii(' '); - attr = HL_ATTR(HLF_MSG); - width = Columns; - off = 0; + bool part_of_status = wp->w_status_height || is_stl_global; + if (*p_ruf && (p_ch > 0 || (ui_has(kUIMessages) && !part_of_status))) { + win_redr_custom(wp, false, true, ui_has(kUIMessages)); + return; } + hlf_T group = HLF_MSG; + int off = wp->w_status_height ? wp->w_wincol : 0; + int width = wp->w_status_height ? wp->w_width : Columns; + schar_T fillchar = part_of_status ? fillchar_status(&group, wp) : schar_from_ascii(' '); + int attr = win_hl_attr(wp, (int)group); + // In list mode virtcol needs to be recomputed colnr_T virtcol = wp->w_virtcol; if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) { @@ -469,6 +478,10 @@ void redraw_ruler(void) wp->w_p_list = true; } + // Check if not in Insert mode and the line is empty (will show "0-1"). + int empty_line = (State & MODE_INSERT) == 0 + && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) == NUL; + #define RULER_BUF_LEN 70 char buffer[RULER_BUF_LEN]; @@ -542,13 +555,13 @@ void redraw_ruler(void) } /// Get the character to use in a status line. Get its attributes in "*attr". -schar_T fillchar_status(int *attr, win_T *wp) +schar_T fillchar_status(hlf_T *group, win_T *wp) { if (wp == curwin) { - *attr = win_hl_attr(wp, HLF_S); + *group = HLF_S; return wp->w_p_fcs_chars.stl; } else { - *attr = win_hl_attr(wp, HLF_SNC); + *group = HLF_SNC; return wp->w_p_fcs_chars.stlnc; } } @@ -566,7 +579,7 @@ void redraw_custom_statusline(win_T *wp) } entered = true; - win_redr_custom(wp, false, false); + win_redr_custom(wp, false, false, false); entered = false; } @@ -644,7 +657,7 @@ void draw_tabline(void) // Use the 'tabline' option if it's set. if (*p_tal != NUL) { - win_redr_custom(NULL, false, false); + win_redr_custom(NULL, false, false, false); } else { int tabcount = 0; int col = 0; diff --git a/src/nvim/statusline.h b/src/nvim/statusline.h index 964f871272..04b876ef2b 100644 --- a/src/nvim/statusline.h +++ b/src/nvim/statusline.h @@ -2,6 +2,7 @@ #include +#include "nvim/highlight_defs.h" #include "nvim/macros_defs.h" #include "nvim/option_defs.h" // IWYU pragma: keep #include "nvim/statusline_defs.h" // IWYU pragma: keep diff --git a/test/functional/legacy/statusline_spec.lua b/test/functional/legacy/statusline_spec.lua index 0bc77fe67e..cad7165270 100644 --- a/test/functional/legacy/statusline_spec.lua +++ b/test/functional/legacy/statusline_spec.lua @@ -12,6 +12,15 @@ describe('statusline', function() before_each(function() screen = Screen.new(50, 7) + screen:add_extra_attr_ids({ + [100] = { + background = Screen.colors.Red, + foreground = Screen.colors.Gray100, + reverse = true, + bold = true, + }, + [101] = { foreground = Screen.colors.Blue, reverse = true, bold = true }, + }) end) it('is updated in cmdline mode when using window-local statusline vim-patch:8.2.2737', function() @@ -51,7 +60,7 @@ describe('statusline', function() screen:expect([[ ^ | {1:~ }|*4 - {9: