diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/vim.c | 1 | ||||
-rw-r--r-- | src/nvim/buffer.c | 4 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 28 | ||||
-rw-r--r-- | src/nvim/drawline.c | 138 | ||||
-rw-r--r-- | src/nvim/drawscreen.c | 16 | ||||
-rw-r--r-- | src/nvim/eval.c | 2 | ||||
-rw-r--r-- | src/nvim/eval.h | 2 | ||||
-rw-r--r-- | src/nvim/mouse.c | 26 | ||||
-rw-r--r-- | src/nvim/mouse.h | 15 | ||||
-rw-r--r-- | src/nvim/move.c | 3 | ||||
-rw-r--r-- | src/nvim/option.c | 13 | ||||
-rw-r--r-- | src/nvim/option.h | 2 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 8 | ||||
-rw-r--r-- | src/nvim/options.lua | 9 | ||||
-rw-r--r-- | src/nvim/optionstr.c | 9 | ||||
-rw-r--r-- | src/nvim/screen.c | 10 | ||||
-rw-r--r-- | src/nvim/statusline.c | 84 | ||||
-rw-r--r-- | src/nvim/window.c | 3 |
18 files changed, 335 insertions, 38 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 1e8964d912..65b08ecade 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2238,6 +2238,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * fillchar, maxwidth, hltab_ptr, + NULL, NULL); PUT(result, "width", INTEGER_OBJ(width)); diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index e6d0dcb8cf..1bba146caf 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3236,7 +3236,7 @@ void maketitle(void) if (*p_titlestring != NUL) { if (stl_syntax & STL_IN_TITLE) { build_stl_str_hl(curwin, buf, sizeof(buf), p_titlestring, - "titlestring", 0, 0, maxlen, NULL, NULL); + "titlestring", 0, 0, maxlen, NULL, NULL, NULL); title_str = buf; } else { title_str = p_titlestring; @@ -3342,7 +3342,7 @@ void maketitle(void) if (*p_iconstring != NUL) { if (stl_syntax & STL_IN_ICON) { build_stl_str_hl(curwin, icon_str, sizeof(buf), p_iconstring, - "iconstring", 0, 0, 0, NULL, NULL); + "iconstring", 0, 0, 0, NULL, NULL, NULL); } else { icon_str = p_iconstring; } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index df4ebdffc0..7442e60024 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -204,6 +204,8 @@ typedef struct { #define w_p_cc w_onebuf_opt.wo_cc // 'colorcolumn' char *wo_sbr; #define w_p_sbr w_onebuf_opt.wo_sbr // 'showbreak' + char *wo_stc; +#define w_p_stc w_onebuf_opt.wo_stc // 'statuscolumn' char *wo_stl; #define w_p_stl w_onebuf_opt.wo_stl // 'statusline' char *wo_wbr; @@ -1300,6 +1302,7 @@ struct window_S { linenr_T w_redraw_bot; // when != 0: last line needing redraw bool w_redr_status; // if true statusline/winbar must be redrawn bool w_redr_border; // if true border must be redrawn + bool w_redr_statuscol; // if true 'statuscolumn' must be redrawn // remember what is shown in the ruler for this window (if 'ruler' set) pos_T w_ru_cursor; // cursor position shown in ruler @@ -1404,6 +1407,31 @@ struct window_S { StlClickDefinition *w_winbar_click_defs; // Size of the w_winbar_click_defs array size_t w_winbar_click_defs_size; + + // Status column click definitions + StlClickDefinition *w_statuscol_click_defs; + // Size of the w_statuscol_click_defs array + size_t w_statuscol_click_defs_size; +}; + +/// Struct to hold info for 'statuscolumn' +typedef struct statuscol statuscol_T; + +struct statuscol { + int width; // width of the status column + int cur_attr; // current attributes in text + int num_attr; // attributes used for line number + int fold_attr; // attributes used for fold column + int sign_attr[SIGN_SHOW_MAX]; // attributes used for signs + int truncate; // truncated width + bool draw; // draw statuscolumn or not + char fold_text[10]; // text in fold column (%C) + char *sign_text[SIGN_SHOW_MAX]; // text in sign column (%s) + char text[MAXPATHL]; // text in status column + char *textp; // current position in text + size_t text_len; // length of text + stl_hlrec_t *hlrec; // highlight groups + stl_hlrec_t *hlrecp; // current highlight group }; /// Macros defined in Vim, but not in Neovim diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 924251a679..d3e8545b88 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -21,6 +21,7 @@ #include "nvim/decoration_provider.h" #include "nvim/diff.h" #include "nvim/drawline.h" +#include "nvim/eval.h" #include "nvim/extmark_defs.h" #include "nvim/fold.h" #include "nvim/garray.h" @@ -43,6 +44,7 @@ #include "nvim/sign.h" #include "nvim/spell.h" #include "nvim/state.h" +#include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/terminal.h" @@ -60,6 +62,7 @@ typedef enum { WL_FOLD, // 'foldcolumn' WL_SIGN, // column for signs WL_NR, // line number + WL_STC, // 'statuscolumn' WL_BRI, // 'breakindent' WL_SBR, // 'showbreak' or 'diff' WL_LINE, // text in the line @@ -395,6 +398,90 @@ static int get_sign_attrs(buf_T *buf, linenr_T lnum, SignTextAttrs *sattrs, int return num_signs; } +static void get_statuscol_str(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines, + int cul_attr, int sign_num_attr, SignTextAttrs *sattrs, + foldinfo_T foldinfo, char_u *extra, statuscol_T *stcp) +{ + long relnum; + bool wrapped = row != startrow + filler_lines; + bool use_cul = use_cursor_line_sign(wp, lnum); + + // Set num, fold and sign text and attrs, empty when wrapped + if (row == startrow) { + relnum = labs(get_cursor_rel_lnum(wp, lnum)); + stcp->num_attr = sign_num_attr ? sign_num_attr + : get_line_number_attr(wp, lnum, row, startrow, filler_lines); + + if (compute_foldcolumn(wp, 0)) { + size_t n = fill_foldcolumn((char_u *)stcp->fold_text, wp, foldinfo, lnum); + stcp->fold_text[n] = NUL; + stcp->fold_attr = win_hl_attr(wp, use_cul ? HLF_CLF : HLF_FC); + } + } + + int i = 0; + for (; i < wp->w_scwidth; i++) { + SignTextAttrs *sattr = wrapped ? NULL : sign_get_attr(i, sattrs, wp->w_scwidth); + stcp->sign_text[i] = sattr && sattr->text ? sattr->text : " "; + stcp->sign_attr[i] = use_cul && cul_attr ? cul_attr : sattr ? sattr->hl_attr_id : 0; + } + stcp->sign_text[i] = NULL; + + int width = build_statuscol_str(wp, row == startrow, wrapped, lnum, relnum, + stcp->width, ' ', stcp->text, &stcp->hlrec, stcp); + // Force a redraw in case of error or when truncated + if (*wp->w_p_stc == NUL || (stcp->truncate > 0 && wp->w_nrwidth < MAX_NUMBERWIDTH)) { + if (stcp->truncate) { // Avoid truncating 'statuscolumn' + wp->w_nrwidth = MIN(MAX_NUMBERWIDTH, wp->w_nrwidth + stcp->truncate); + wp->w_nrwidth_width = wp->w_nrwidth; + } else { // 'statuscolumn' reset due to error + wp->w_nrwidth_line_count = 0; + wp->w_nrwidth = (wp->w_p_nu || wp->w_p_rnu) * number_width(wp); + } + wp->w_redr_statuscol = true; + return; + } + + // Reset text/highlight pointer and current attr for new line + stcp->textp = stcp->text; + stcp->hlrecp = stcp->hlrec; + stcp->cur_attr = stcp->num_attr; + stcp->text_len = strlen(stcp->text); + + int fill = stcp->width - width; + if (fill > 0) { + // Fill up with ' ' + memset(&stcp->text[stcp->text_len], ' ', (size_t)fill); + stcp->text_len += (size_t)fill; + stcp->text[stcp->text_len] = NUL; + } +} + +static void get_statuscol_display_info(LineDrawState *draw_state, int *char_attr, int *n_extrap, + int *c_extrap, int *c_finalp, char_u *extra, char **pp_extra, + statuscol_T *stcp) +{ + *c_extrap = NUL; + *c_finalp = NUL; + do { + *draw_state = WL_STC; + *char_attr = stcp->cur_attr; + *pp_extra = stcp->textp; + *n_extrap = stcp->hlrecp->start ? (int)(stcp->hlrecp->start - stcp->textp) + : (int)strlen(*pp_extra); + // Prepare for next highlight section if not yet at the end + if (stcp->textp + *n_extrap < stcp->text + stcp->text_len) { + int hl = stcp->hlrecp->userhl; + stcp->textp = stcp->hlrecp->start; + stcp->cur_attr = hl < 0 ? syn_id2attr(-stcp->hlrecp->userhl) + : hl > 0 ? hl : stcp->num_attr; + stcp->hlrecp++; + *draw_state = WL_STC - 1; + } + // Skip over empty highlight sections + } while (*n_extrap == 0 && stcp->textp < stcp->text + stcp->text_len); +} + /// Return true if CursorLineNr highlight is to be used for the number column. /// /// - 'cursorline' must be set @@ -1098,6 +1185,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, extra_check = true; } + statuscol_T statuscol = { 0 }; + if (*wp->w_p_stc != NUL) { + // Draw the 'statuscolumn' if option is set. + statuscol.draw = true; + statuscol.width = win_col_off(wp); + } + int sign_idx = 0; // Repeat for the whole displayed line. for (;;) { @@ -1125,9 +1219,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } } + // Skip fold, sign and number states if 'statuscolumn' is set. + if (draw_state == WL_FOLD - 1 && n_extra == 0 && statuscol.draw) { + draw_state = WL_STC - 1; + } + if (draw_state == WL_FOLD - 1 && n_extra == 0) { int fdc = compute_foldcolumn(wp, 0); - draw_state = WL_FOLD; if (fdc > 0) { // Draw the 'foldcolumn'. Allocate a buffer, "extra" may @@ -1217,7 +1315,23 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } } - if (draw_state == WL_NR && n_extra == 0) { + if (draw_state == WL_STC - 1 && n_extra == 0) { + draw_state = WL_STC; + // Draw the 'statuscolumn' if option is set. + if (statuscol.draw) { + if (statuscol.text_len == 0) { + get_statuscol_str(wp, lnum, row, startrow, filler_lines, cul_attr, + sign_num_attr, sattrs, foldinfo, extra, &statuscol); + if (wp->w_redr_statuscol) { + return 0; + } + } + get_statuscol_display_info(&draw_state, &char_attr, &n_extra, &c_extra, + &c_final, extra, &p_extra, &statuscol); + } + } + + if (draw_state == WL_STC && n_extra == 0) { win_col_offset = off; } @@ -1349,7 +1463,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, && wp == curwin && lnum == wp->w_cursor.lnum && vcol >= (long)wp->w_virtcol) - || (number_only && draw_state > WL_NR)) + || (number_only && draw_state > WL_STC)) && filler_todo <= 0) { draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row); grid_put_linebuf(grid, row, 0, col, -grid->cols, wp->w_p_rl, wp, bg_attr, false); @@ -2214,7 +2328,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, && wp->w_p_list && (wp->w_p_wrap ? (wp->w_skipcol > 0 && row == 0) : wp->w_leftcol > 0) && filler_todo <= 0 - && draw_state > WL_NR + && draw_state > WL_STC && c != NUL) { c = wp->w_p_lcs_chars.prec; lcs_prec_todo = NUL; @@ -2508,7 +2622,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, col++; // UTF-8: Put a 0 in the second screen char. linebuf_char[off][0] = 0; - if (draw_state > WL_NR && filler_todo <= 0) { + if (draw_state > WL_STC && filler_todo <= 0) { vcol++; } // When "tocol" is halfway through a character, set it to the end of @@ -2591,7 +2705,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // Only advance the "vcol" when after the 'number' or 'relativenumber' // column. - if (draw_state > WL_NR + if (draw_state > WL_STC && filler_todo <= 0) { vcol++; } @@ -2601,7 +2715,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } // restore attributes after "predeces" in 'listchars' - if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0) { + if (draw_state > WL_STC && n_attr3 > 0 && --n_attr3 == 0) { char_attr = saved_attr3; } @@ -2697,6 +2811,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, if (filler_todo <= 0) { need_showbreak = true; } + if (statuscol.draw) { + if (row == startrow + 1 || row == startrow + filler_lines) { + // Re-evaluate 'statuscolumn' for the first wrapped row and non filler line + statuscol.text_len = 0; + } else { // Otherwise just reset the text/hlrec pointers + statuscol.textp = statuscol.text; + statuscol.hlrecp = statuscol.hlrec; + } // Fall back to default columns if the 'n' flag isn't in 'cpo' + statuscol.draw = vim_strchr(p_cpo, CPO_NUMCOL) == NULL; + } filler_todo--; // When the filler lines are actually below the last line of the // file, don't draw the line itself, break here. diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index de6bdda71e..568dbe99c0 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -522,7 +522,7 @@ int update_screen(void) // TODO(bfredl): special casing curwin here is SÅ JÄVLA BULL. // Either this should be done for all windows or not at all. if (curwin->w_redr_type < UPD_NOT_VALID - && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu) + && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu || *curwin->w_p_stc) ? number_width(curwin) : 0)) { curwin->w_redr_type = UPD_NOT_VALID; } @@ -1032,7 +1032,7 @@ static void win_update(win_T *wp, DecorProviders *providers) // Force redraw when width of 'number' or 'relativenumber' column // changes. - int nrwidth = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0; + int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0; if (wp->w_nrwidth != nrwidth) { type = UPD_NOT_VALID; wp->w_nrwidth = nrwidth; @@ -1823,6 +1823,18 @@ static void win_update(win_T *wp, DecorProviders *providers) did_update = DID_NONE; } + // 'statuscolumn' width has changed or errored, start from the top. + if (wp->w_redr_statuscol) { + wp->w_redr_statuscol = false; + idx = 0; + row = 0; + lnum = wp->w_topline; + wp->w_lines_valid = 0; + wp->w_valid &= ~VALID_WCOL; + decor_providers_invoke_win(wp, providers, &line_providers, &provider_err); + continue; + } + if (lnum > buf->b_ml.ml_line_count) { eof = true; break; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a112a2e447..17eb08cee6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -267,6 +267,8 @@ static struct vimvar { VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), + VV(VV_RELNUM, "relnum", VAR_NUMBER, VV_RO), + VV(VV_WRAP, "wrap", VAR_BOOL, VV_RO), }; #undef VV diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 61c1363d54..d67414f12f 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -165,6 +165,8 @@ typedef enum { VV__NULL_DICT, // Dictionary with NULL value. For test purposes only. VV__NULL_BLOB, // Blob with NULL value. For test purposes only. VV_LUA, + VV_RELNUM, + VV_WRAP, } VimVarIndex; /// All recognized msgpack types diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index b4d6a533f1..88103d1888 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -291,6 +291,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) pos_T start_visual; bool moved; // Has cursor moved? bool in_winbar; // mouse in window bar + bool in_statuscol; // mouse in status column bool in_status_line; // mouse in status line static bool in_tab_line = false; // mouse clicked in tab line bool in_sep_line; // mouse in vertical separator line @@ -645,10 +646,11 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) moved = (jump_flags & CURSOR_MOVED); in_winbar = (jump_flags & MOUSE_WINBAR); + in_statuscol = (jump_flags & MOUSE_STATUSCOL); in_status_line = (jump_flags & IN_STATUS_LINE); in_sep_line = (jump_flags & IN_SEP_LINE); - if ((in_winbar || in_status_line) && is_click) { + if ((in_winbar || in_status_line || in_statuscol) && is_click) { // Handle click event on window bar or status lin int click_grid = mouse_grid; int click_row = mouse_row; @@ -659,7 +661,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) } StlClickDefinition *click_defs = in_status_line ? wp->w_status_click_defs - : wp->w_winbar_click_defs; + : in_winbar ? wp->w_winbar_click_defs + : wp->w_statuscol_click_defs; if (in_status_line && global_stl_height() > 0) { // global statusline is displayed for the current window, @@ -682,8 +685,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) } return false; - } else if (in_winbar) { - // A drag or release event in the window bar has no side effects. + } else if (in_winbar || in_statuscol) { + // A drag or release event in the window bar and status column has no side effects. return false; } @@ -1027,6 +1030,7 @@ int jump_to_mouse(int flags, bool *inclusive, int which_button) static bool on_status_line = false; static bool on_sep_line = false; static bool on_winbar = false; + static bool on_statuscol = false; static int prev_row = -1; static int prev_col = -1; static int did_drag = false; // drag was noticed @@ -1069,6 +1073,9 @@ retnomove: if (on_winbar) { return IN_OTHER_WIN | MOUSE_WINBAR; } + if (on_statuscol) { + return IN_OTHER_WIN | MOUSE_STATUSCOL; + } if (flags & MOUSE_MAY_STOP_VIS) { end_visual_mode(); redraw_curbuf_later(UPD_INVERTED); // delete the inversion @@ -1103,6 +1110,10 @@ retnomove: ? wp->w_winbar_height != 0 : false; + on_statuscol = grid == (col < win_col_off(wp)) + ? *wp->w_p_stc != NUL + : false; + on_sep_line = grid == DEFAULT_GRID_HANDLE && col >= wp->w_width ? col - wp->w_width + 1 == 1 : false; @@ -1130,6 +1141,10 @@ retnomove: return IN_OTHER_WIN | MOUSE_WINBAR; } + if (on_statuscol) { + return IN_OTHER_WIN | MOUSE_STATUSCOL; + } + fdc = win_fdccol_count(wp); dragwin = NULL; @@ -1230,6 +1245,9 @@ retnomove: } else if (on_winbar && which_button == MOUSE_RIGHT) { // After a click on the window bar don't start Visual mode. return IN_OTHER_WIN | MOUSE_WINBAR; + } else if (on_statuscol && which_button == MOUSE_RIGHT) { + // After a click on the status column don't start Visual mode. + return IN_OTHER_WIN | MOUSE_STATUSCOL; } else { // keep_window_focus must be true // before moving the cursor for a left click, stop Visual mode diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h index d493a479b1..8b2d7e0acd 100644 --- a/src/nvim/mouse.h +++ b/src/nvim/mouse.h @@ -9,17 +9,18 @@ #include "nvim/window.h" /// jump_to_mouse() returns one of first five these values, possibly with -/// some of the other four added. +/// some of the other five added. enum { IN_UNKNOWN = 0, IN_BUFFER = 1, - IN_STATUS_LINE = 2, ///< on status or command line - IN_SEP_LINE = 4, ///< on vertical separator line - IN_OTHER_WIN = 8, ///< in other window but can't go there + IN_STATUS_LINE = 2, ///< on status or command line + IN_SEP_LINE = 4, ///< on vertical separator line + IN_OTHER_WIN = 8, ///< in other window but can't go there CURSOR_MOVED = 0x100, - MOUSE_FOLD_CLOSE = 0x200, ///< clicked on '-' in fold column - MOUSE_FOLD_OPEN = 0x400, ///< clicked on '+' in fold column - MOUSE_WINBAR = 0x800, ///< in window toolbar + MOUSE_FOLD_CLOSE = 0x200, ///< clicked on '-' in fold column + MOUSE_FOLD_OPEN = 0x400, ///< clicked on '+' in fold column + MOUSE_WINBAR = 0x800, ///< in window toolbar + MOUSE_STATUSCOL = 0x1000, ///< in 'statuscolumn' }; /// flags for jump_to_mouse() diff --git a/src/nvim/move.c b/src/nvim/move.c index 227e19bca3..0d2a38b041 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -649,7 +649,8 @@ void validate_cursor_col(void) // fold column and sign column (these don't move when scrolling horizontally). int win_col_off(win_T *wp) { - return ((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0) + return ((wp->w_p_nu || wp->w_p_rnu || (*wp->w_p_stc != NUL)) ? + (number_width(wp) + (*wp->w_p_stc == NUL)) : 0) + (cmdwin_type == 0 || wp != curwin ? 0 : 1) + win_fdccol_count(wp) + (win_signcol_count(wp) * win_signcol_width(wp)); diff --git a/src/nvim/option.c b/src/nvim/option.c index a6ec0a08c2..fe905a5a05 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2094,6 +2094,9 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va if (curwin->w_p_spell) { errmsg = did_set_spelllang(curwin); } + } else if (((int *)varp == &curwin->w_p_nu || (int *)varp == &curwin->w_p_rnu) + && *curwin->w_p_stc != NUL) { // '(relative)number' + 'statuscolumn' + curwin->w_nrwidth_line_count = 0; } if ((int *)varp == &curwin->w_p_arab) { @@ -2316,7 +2319,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, } else if (pp == &curwin->w_p_nuw || pp == &curwin->w_allbuf_opt.wo_nuw) { if (value < 1) { errmsg = e_positive; - } else if (value > 20) { + } else if (value > MAX_NUMBERWIDTH) { errmsg = e_invarg; } } else if (pp == &curbuf->b_p_iminsert || pp == &p_iminsert) { @@ -3630,6 +3633,9 @@ void unset_global_local_option(char *name, void *from) clear_string_option(&((win_T *)from)->w_p_ve); ((win_T *)from)->w_ve_flags = 0; break; + case PV_STC: + clear_string_option(&((win_T *)from)->w_p_stc); + break; } } @@ -4014,6 +4020,8 @@ static char_u *get_varp(vimoption_T *p) return (char_u *)&(curwin->w_p_winhl); case PV_WINBL: return (char_u *)&(curwin->w_p_winbl); + case PV_STC: + return (char_u *)&(curwin->w_p_stc); default: iemsg(_("E356: get_varp ERROR")); } @@ -4102,6 +4110,7 @@ void copy_winopt(winopt_T *from, winopt_T *to) to->wo_scl = copy_option_val(from->wo_scl); to->wo_winhl = copy_option_val(from->wo_winhl); to->wo_winbl = from->wo_winbl; + to->wo_stc = copy_option_val(from->wo_stc); // Copy the script context so that we know were the value was last set. memmove(to->wo_script_ctx, from->wo_script_ctx, sizeof(to->wo_script_ctx)); @@ -4139,6 +4148,7 @@ static void check_winopt(winopt_T *wop) check_string_option(&wop->wo_fcs); check_string_option(&wop->wo_ve); check_string_option(&wop->wo_wbr); + check_string_option(&wop->wo_stc); } /// Free the allocated memory inside a winopt_T. @@ -4165,6 +4175,7 @@ void clear_winopt(winopt_T *wop) clear_string_option(&wop->wo_fcs); clear_string_option(&wop->wo_ve); clear_string_option(&wop->wo_wbr); + clear_string_option(&wop->wo_stc); } void didset_window_options(win_T *wp, bool valid_cursor) diff --git a/src/nvim/option.h b/src/nvim/option.h index 6744678044..636257bfe8 100644 --- a/src/nvim/option.h +++ b/src/nvim/option.h @@ -19,6 +19,8 @@ typedef enum { #define BCO_ALWAYS 2 // always copy the options #define BCO_NOHELP 4 // don't touch the help related options +#define MAX_NUMBERWIDTH 20 // used for 'numberwidth' and 'statuscolumn' + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "option.h.generated.h" #endif diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 699a57c2aa..56fdb6de84 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -337,6 +337,8 @@ enum { STL_ARGLISTSTAT = 'a', ///< Argument list status as (x of y). STL_PAGENUM = 'N', ///< Page number (when printing). STL_SHOWCMD = 'S', ///< 'showcmd' buffer + STL_FOLDCOL = 'C', ///< Fold column for 'statuscolumn' + STL_SIGNCOL = 's', ///< Sign column for 'statuscolumn' STL_VIM_EXPR = '{', ///< Start of expression to substitute. STL_SEPARATE = '=', ///< Separation between alignment sections. STL_TRUNCMARK = '<', ///< Truncation mark if line is too long. @@ -354,8 +356,9 @@ enum { STL_HELPFLAG, STL_HELPFLAG_ALT, STL_FILETYPE, STL_FILETYPE_ALT, \ STL_PREVIEWFLAG, STL_PREVIEWFLAG_ALT, STL_MODIFIED, STL_MODIFIED_ALT, \ STL_QUICKFIX, STL_PERCENTAGE, STL_ALTPERCENT, STL_ARGLISTSTAT, STL_PAGENUM, \ - STL_SHOWCMD, STL_VIM_EXPR, STL_SEPARATE, STL_TRUNCMARK, STL_USER_HL, \ - STL_HIGHLIGHT, STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \ + STL_SHOWCMD, STL_FOLDCOL, STL_SIGNCOL, STL_VIM_EXPR, STL_SEPARATE, \ + STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, STL_TABPAGENR, STL_TABCLOSENR, \ + STL_CLICK_FUNC, STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \ 0, \ }) @@ -955,6 +958,7 @@ enum { WV_CULOPT, WV_CC, WV_SBR, + WV_STC, WV_STL, WV_WFH, WV_WFW, diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 8c7cf94465..387ccd0888 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2298,6 +2298,15 @@ return { defaults={if_true=false} }, { + full_name='statuscolumn', abbreviation='stc', + short_desc=N_("custom format for the status column"), + type='string', scope={'window'}, + redraw={'current_window'}, + secure=true, + alloced=true, + defaults={if_true=""} + }, + { full_name='statusline', abbreviation='stl', short_desc=N_("custom format for the status line"), type='string', scope={'global', 'window'}, diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 0c9d428ce7..a97e9b7c7e 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -1177,12 +1177,14 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf redraw_titles(); } } else if (gvarp == &p_stl || gvarp == &p_wbr || varp == &p_tal - || varp == &p_ruf) { - // 'statusline', 'winbar', 'tabline' or 'rulerformat' + || varp == &p_ruf || varp == &curwin->w_p_stc) { + // 'statusline', 'winbar', 'tabline', 'rulerformat' or 'statuscolumn' int wid; if (varp == &p_ruf) { // reset ru_wid first ru_wid = 0; + } else if (varp == &curwin->w_p_stc) { + curwin->w_nrwidth_line_count = 0; } s = *varp; if (varp == &p_ruf && *s == '%') { @@ -1197,7 +1199,8 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf errmsg = check_stl_option(p_ruf); } } else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') { - // check 'statusline', 'winbar' or 'tabline' only if it doesn't start with "%!" + // check 'statusline', 'winbar', 'tabline' or 'statuscolumn' + // only if it doesn't start with "%!" errmsg = check_stl_option(s); } if (varp == &p_ruf && errmsg == NULL) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 52ab2ad25e..afdb04ebd8 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -780,6 +780,16 @@ int number_width(win_T *wp) } wp->w_nrwidth_line_count = lnum; + // make best estimate for 'statuscolumn' + if (*wp->w_p_stc != NUL) { + char buf[MAXPATHL]; + wp->w_nrwidth_width = 0; + n = build_statuscol_str(wp, true, false, lnum, 0, 0, NUL, buf, NULL, NULL); + n = MAX(n, (wp->w_p_nu || wp->w_p_rnu) * (int)wp->w_p_nuw); + wp->w_nrwidth_width = MIN(n, MAX_NUMBERWIDTH); + return wp->w_nrwidth_width; + } + n = 0; do { lnum /= 10; diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 0f28eae0d8..d548c285fb 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -367,8 +367,9 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) // Make a copy, because the statusline may include a function call that // might change the option value and free the memory. stl = xstrdup(stl); - width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, - opt_scope, fillchar, maxwidth, &hltab, &tabtab); + width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, opt_scope, + fillchar, maxwidth, &hltab, &tabtab, NULL); + xfree(stl); ewp->w_p_crb = p_crb_save; @@ -867,6 +868,32 @@ void draw_tabline(void) redraw_tabline = false; } +int build_statuscol_str(win_T *wp, bool setnum, bool wrap, linenr_T lnum, long relnum, int maxwidth, + int fillchar, char *buf, stl_hlrec_t **hlrec, statuscol_T *stcp) +{ + if (setnum) { + set_vim_var_nr(VV_LNUM, lnum); + set_vim_var_nr(VV_RELNUM, relnum); + } + set_vim_var_bool(VV_WRAP, wrap); + + StlClickRecord *clickrec; + char *stc = xstrdup(wp->w_p_stc); + int width = build_stl_str_hl(wp, buf, MAXPATHL, stc, "statuscolumn", OPT_LOCAL, + fillchar, maxwidth, hlrec, &clickrec, stcp); + xfree(stc); + + // Allocate and fill click def array if width has changed + if (wp->w_status_click_defs_size != (size_t)width) { + stl_clear_click_defs(wp->w_statuscol_click_defs, wp->w_statuscol_click_defs_size); + wp->w_statuscol_click_defs = stl_alloc_click_defs(wp->w_statuscol_click_defs, width, + &wp->w_statuscol_click_defs_size); + stl_fill_click_defs(wp->w_statuscol_click_defs, clickrec, buf, width, false); + } + + return width; +} + /// Build a string from the status line items in "fmt". /// Return length of string in screen cells. /// @@ -890,11 +917,13 @@ void draw_tabline(void) /// @param fillchar Character to use when filling empty space in the statusline /// @param maxwidth The maximum width to make the statusline /// @param hltab HL attributes (can be NULL) -/// @param tabtab Tab clicks definition (can be NULL). +/// @param tabtab Tab clicks definition (can be NULL) +/// @param stcp Status column attributes (can be NULL) /// /// @return The final width of the statusline int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_name, int opt_scope, - int fillchar, int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab) + int fillchar, int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab, + statuscol_T *stcp) { static size_t stl_items_len = 20; // Initial value, grows as needed. static stl_item_t *stl_items = NULL; @@ -1466,8 +1495,9 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n } case STL_LINE: - num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) - ? 0L : (long)(wp->w_cursor.lnum); + // Overload %l with v:lnum for 'statuscolumn' + num = strcmp(opt_name, "statuscolumn") == 0 ? get_vim_var_nr(VV_LNUM) + : (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? 0L : (long)(wp->w_cursor.lnum); break; case STL_NUMLINES: @@ -1565,9 +1595,14 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n case STL_ROFLAG: case STL_ROFLAG_ALT: - itemisflag = true; - if (wp->w_buffer->b_p_ro) { - str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]"); + // Overload %r with v:relnum for 'statuscolumn' + if (strcmp(opt_name, "statuscolumn") == 0) { + num = get_vim_var_nr(VV_RELNUM); + } else { + itemisflag = true; + if (wp->w_buffer->b_p_ro) { + str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]"); + } } break; @@ -1579,6 +1614,33 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n } break; + case STL_FOLDCOL: // 'C' for 'statuscolumn' + case STL_SIGNCOL: { // 's' for 'statuscolumn' + if (stcp == NULL) { + break; + } + + bool fold = opt == STL_FOLDCOL; + *buf_tmp = NUL; + for (int i = 0; i <= 9; i++) { + char *p = fold ? stcp->fold_text : stcp->sign_text[i]; + if ((!p || !*p) && *buf_tmp == NUL) { + break; + } + stl_items[curitem].type = Highlight; + stl_items[curitem].start = out_p + strlen(buf_tmp); + stl_items[curitem].minwid = !p || (fold && i) ? 0 : fold ? stcp->fold_attr + : stcp->sign_attr[i]; + curitem++; + if (!p || (fold && i)) { + str = buf_tmp; + break; + } + STRCAT(buf_tmp, p); + } + break; + } + case STL_FILETYPE: // Copy the filetype if it is not null and the formatted string will fit // in the temporary buffer @@ -1850,6 +1912,10 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // What follows is post-processing to handle alignment and highlighting. int width = vim_strsize(out); + // Return truncated width for 'statuscolumn' + if (stcp != NULL && width > maxwidth) { + stcp->truncate = width - maxwidth; + } if (maxwidth > 0 && width > maxwidth) { // Result is too long, must truncate somewhere. int item_idx = 0; diff --git a/src/nvim/window.c b/src/nvim/window.c index 4e569ce11f..e836fa50fb 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5094,6 +5094,9 @@ static void win_free(win_T *wp, tabpage_T *tp) stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); xfree(wp->w_winbar_click_defs); + stl_clear_click_defs(wp->w_statuscol_click_defs, wp->w_statuscol_click_defs_size); + xfree(wp->w_statuscol_click_defs); + // Remove the window from the b_wininfo lists, it may happen that the // freed memory is re-used for another window. FOR_ALL_BUFFERS(buf) { |