diff options
Diffstat (limited to 'src/nvim/buffer.c')
-rw-r--r-- | src/nvim/buffer.c | 343 |
1 files changed, 310 insertions, 33 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index a6e3fedd3f..62ab7495da 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -22,6 +22,7 @@ #include "nvim/api/private/handle.h" #include "nvim/ascii.h" +#include "nvim/assert.h" #include "nvim/vim.h" #include "nvim/buffer.h" #include "nvim/charset.h" @@ -579,16 +580,17 @@ free_buffer_stuff ( ) { if (free_options) { - clear_wininfo(buf); /* including window-local options */ - free_buf_options(buf, TRUE); + clear_wininfo(buf); // including window-local options + free_buf_options(buf, true); ga_clear(&buf->b_s.b_langp); } - vars_clear(&buf->b_vars->dv_hashtab); /* free all internal variables */ + vars_clear(&buf->b_vars->dv_hashtab); // free all internal variables hash_init(&buf->b_vars->dv_hashtab); - uc_clear(&buf->b_ucmds); /* clear local user commands */ - buf_delete_signs(buf); /* delete any signs */ - map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */ - map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */ + uc_clear(&buf->b_ucmds); // clear local user commands + buf_delete_signs(buf); // delete any signs + bufhl_clear_all(buf); // delete any highligts + map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings + map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs xfree(buf->b_start_fenc); buf->b_start_fenc = NULL; } @@ -1600,21 +1602,28 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit) col = 0; if (options & GETF_SWITCH) { - /* If 'switchbuf' contains "useopen": jump to first window containing - * "buf" if one exists */ - if (swb_flags & SWB_USEOPEN) + // If 'switchbuf' contains "useopen": jump to first window containing + // "buf" if one exists + if (swb_flags & SWB_USEOPEN) { wp = buf_jump_open_win(buf); - /* If 'switchbuf' contains "usetab": jump to first window in any tab - * page containing "buf" if one exists */ - if (wp == NULL && (swb_flags & SWB_USETAB)) + } + + // If 'switchbuf' contains "usetab": jump to first window in any tab + // page containing "buf" if one exists + if (wp == NULL && (swb_flags & SWB_USETAB)) { wp = buf_jump_open_tab(buf); - /* If 'switchbuf' contains "split" or "newtab" and the current buffer - * isn't empty: open new window */ - if (wp == NULL && (swb_flags & (SWB_SPLIT | SWB_NEWTAB)) && !bufempty()) { - if (swb_flags & SWB_NEWTAB) /* Open in a new tab */ + } + + // If 'switchbuf' contains "split", "vsplit" or "newtab" and the + // current buffer isn't empty: open new tab or window + if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB)) + && !bufempty()) { + if (swb_flags & SWB_NEWTAB) { tabpage_new(); - else if (win_split(0, 0) == FAIL) /* Open in a new window */ + } else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0) + == FAIL) { return FAIL; + } RESET_BINDING(curwin); } } @@ -1733,12 +1742,15 @@ int buflist_findpat( int toggledollar; if (pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#')) { - if (*pattern == '%') + if (*pattern == '%') { match = curbuf->b_fnum; - else + } else { match = curwin->w_alt_fnum; - if (diffmode && !diff_mode_buf(buflist_findnr(match))) + } + buf_T *found_buf = buflist_findnr(match); + if (diffmode && !(found_buf && diff_mode_buf(found_buf))) { match = -1; + } } /* * Try four ways of matching a listed buffer: @@ -2826,7 +2838,7 @@ typedef enum { /// @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 page nrs (can be NULL) +/// @param tabtab Tab clicks definition (can be NULL). /// /// @return The final width of the statusline int build_stl_str_hl( @@ -2838,13 +2850,15 @@ int build_stl_str_hl( int fillchar, int maxwidth, struct stl_hlrec *hltab, - struct stl_hlrec *tabtab + StlClickRecord *tabtab ) { int groupitem[STL_MAX_ITEM]; struct stl_item { // Where the item starts in the status line output buffer - char_u *start; + char_u *start; + // Function to run for ClickFunc items. + char *cmd; // The minimum width of the item int minwid; // The maximum width of the item @@ -2856,10 +2870,10 @@ int build_stl_str_hl( Middle, Highlight, TabPage, + ClickFunc, Trunc - } type; - } item[STL_MAX_ITEM]; - + } type; + } item[STL_MAX_ITEM]; #define TMPLEN 70 char_u tmp[TMPLEN]; char_u *usefmt = fmt; @@ -3164,6 +3178,24 @@ int build_stl_str_hl( continue; } + if (*fmt_p == STL_CLICK_FUNC) { + fmt_p++; + char *t = (char *) fmt_p; + while (*fmt_p != STL_CLICK_FUNC && *fmt_p) { + fmt_p++; + } + if (*fmt_p != STL_CLICK_FUNC) { + break; + } + item[curitem].type = ClickFunc; + item[curitem].start = out_p; + item[curitem].cmd = xmemdupz(t, (size_t) (((char *) fmt_p - t))); + item[curitem].minwid = minwid; + fmt_p++; + curitem++; + continue; + } + // Denotes the end of the minwid // the maxwid may follow immediately after if (*fmt_p == '.') { @@ -3281,6 +3313,7 @@ int build_stl_str_hl( } break; } + case STL_LINE: num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? 0L : (long)(wp->w_cursor.lnum); @@ -3821,16 +3854,37 @@ int build_stl_str_hl( // Store the info about tab pages labels. if (tabtab != NULL) { - struct stl_hlrec *sp = tabtab; + StlClickRecord *cur_tab_rec = tabtab; for (long l = 0; l < itemcnt; l++) { if (item[l].type == TabPage) { - sp->start = item[l].start; - sp->userhl = item[l].minwid; - sp++; + cur_tab_rec->start = (char *) item[l].start; + if (item[l].minwid == 0) { + cur_tab_rec->def.type = kStlClickDisabled; + cur_tab_rec->def.tabnr = 0; + } else { + int tabnr = item[l].minwid; + if (item[l].minwid > 0) { + cur_tab_rec->def.type = kStlClickTabSwitch; + } else { + cur_tab_rec->def.type = kStlClickTabClose; + tabnr = -tabnr; + } + cur_tab_rec->def.tabnr = tabnr; + } + cur_tab_rec->def.func = NULL; + cur_tab_rec++; + } else if (item[l].type == ClickFunc) { + cur_tab_rec->start = (char *) item[l].start; + cur_tab_rec->def.type = kStlClickFuncRun; + cur_tab_rec->def.tabnr = item[l].minwid; + cur_tab_rec->def.func = item[l].cmd; + cur_tab_rec++; } } - sp->start = NULL; - sp->userhl = 0; + cur_tab_rec->start = NULL; + cur_tab_rec->def.type = kStlClickDisabled; + cur_tab_rec->def.tabnr = 0; + cur_tab_rec->def.func = NULL; } return width; @@ -3852,6 +3906,11 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen) above = wp->w_topline - 1; above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill; + if (wp->w_topline == 1 && wp->w_topfill >= 1) { + // All buffer lines are displayed and there is an indication + // of filler lines, that can be considered seeing all lines. + above = 0; + } below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1; if (below <= 0) STRLCPY(buf, (above == 0 ? _("All") : _("Bot")), buflen); @@ -4812,6 +4871,224 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a } } +// bufhl: plugin highlights associated with a buffer + +/// Adds a highlight to buffer. +/// +/// Unlike matchaddpos() highlights follow changes to line numbering (as lines +/// are inserted/removed above the highlighted line), like signs and marks do. +/// +/// When called with "src_id" set to 0, a unique source id is generated and +/// returned. Succesive calls can pass it in as "src_id" to add new highlights +/// to the same source group. All highlights in the same group can be cleared +/// at once. If the highlight never will be manually deleted pass in -1 for +/// "src_id" +/// +/// if "hl_id" or "lnum" is invalid no highlight is added, but a new src_id +/// is still returned. +/// +/// @param buf The buffer to add highlights to +/// @param src_id src_id to use or 0 to use a new src_id group, +/// or -1 for ungrouped highlight. +/// @param hl_id Id of the highlight group to use +/// @param lnum The line to highlight +/// @param col_start First column to highlight +/// @param col_end The last column to highlight, +/// or -1 to highlight to end of line +/// @return The src_id that was used +int bufhl_add_hl(buf_T *buf, + int src_id, + int hl_id, + linenr_T lnum, + colnr_T col_start, + colnr_T col_end) { + static int next_src_id = 1; + if (src_id == 0) { + src_id = next_src_id++; + } + if (hl_id <= 0) { + // no highlight group or invalid line, just return src_id + return src_id; + } + if (!buf->b_bufhl_info) { + buf->b_bufhl_info = map_new(linenr_T, bufhl_vec_T)(); + } + bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(buf->b_bufhl_info, + lnum, true); + + bufhl_hl_item_T *hlentry = kv_pushp(bufhl_hl_item_T, *lineinfo); + hlentry->src_id = src_id; + hlentry->hl_id = hl_id; + hlentry->start = col_start; + hlentry->stop = col_end; + + if (0 < lnum && lnum <= buf->b_ml.ml_line_count) { + changed_lines_buf(buf, lnum, lnum+1, 0); + redraw_buf_later(buf, VALID); + } + return src_id; +} + +/// Clear bufhl highlights from a given source group and range of lines. +/// +/// @param buf The buffer to remove highlights from +/// @param src_id highlight source group to clear, or -1 to clear all groups. +/// @param line_start first line to clear +/// @param line_end last line to clear or MAXLNUM to clear to end of file. +void bufhl_clear_line_range(buf_T *buf, + int src_id, + linenr_T line_start, + linenr_T line_end) { + if (!buf->b_bufhl_info) { + return; + } + linenr_T line; + linenr_T first_changed = MAXLNUM, last_changed = -1; + // In the case line_start - line_end << bufhl_info->size + // it might be better to reverse this, i e loop over the lines + // to clear on. + bufhl_vec_T unused; + map_foreach(buf->b_bufhl_info, line, unused, { + (void)unused; + if (line_start <= line && line <= line_end) { + if (bufhl_clear_line(buf->b_bufhl_info, src_id, line)) { + if (line > last_changed) { + last_changed = line; + } + if (line < first_changed) { + first_changed = line; + } + } + } + }) + + if (last_changed != -1) { + changed_lines_buf(buf, first_changed, last_changed+1, 0); + redraw_buf_later(buf, VALID); + } +} + +/// Clear bufhl highlights from a given source group and given line +/// +/// @param bufhl_info The highlight info for the buffer +/// @param src_id Highlight source group to clear, or -1 to clear all groups. +/// @param lnum Linenr where the highlight should be cleared +static bool bufhl_clear_line(bufhl_info_T *bufhl_info, int src_id, int lnum) { + bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(bufhl_info, + lnum, false); + size_t oldsize = kv_size(*lineinfo); + if (src_id < 0) { + kv_size(*lineinfo) = 0; + } else { + size_t newind = 0; + for (size_t i = 0; i < kv_size(*lineinfo); i++) { + if (kv_A(*lineinfo, i).src_id != src_id) { + if (i != newind) { + kv_A(*lineinfo, newind) = kv_A(*lineinfo, i); + } + newind++; + } + } + kv_size(*lineinfo) = newind; + } + + if (kv_size(*lineinfo) == 0) { + kv_destroy(*lineinfo); + map_del(linenr_T, bufhl_vec_T)(bufhl_info, lnum); + } + return kv_size(*lineinfo) != oldsize; +} + +/// Remove all highlights and free the highlight data +void bufhl_clear_all(buf_T* buf) { + if (!buf->b_bufhl_info) { + return; + } + bufhl_clear_line_range(buf, -1, 1, MAXLNUM); + map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info); + buf->b_bufhl_info = NULL; +} + +/// Adjust a placed highlight for inserted/deleted lines. +void bufhl_mark_adjust(buf_T* buf, + linenr_T line1, + linenr_T line2, + long amount, + long amount_after) { + if (!buf->b_bufhl_info) { + return; + } + + bufhl_info_T *newmap = map_new(linenr_T, bufhl_vec_T)(); + linenr_T line; + bufhl_vec_T lineinfo; + map_foreach(buf->b_bufhl_info, line, lineinfo, { + if (line >= line1 && line <= line2) { + if (amount == MAXLNUM) { + bufhl_clear_line(buf->b_bufhl_info, -1, line); + continue; + } else { + line += amount; + } + } else if (line > line2) { + line += amount_after; + } + map_put(linenr_T, bufhl_vec_T)(newmap, line, lineinfo); + }); + map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info); + buf->b_bufhl_info = newmap; +} + + +/// Get highlights to display at a specific line +/// +/// @param buf The buffer handle +/// @param lnum The line number +/// @param[out] info The highligts for the line +/// @return true if there was highlights to display +bool bufhl_start_line(buf_T *buf, linenr_T lnum, bufhl_lineinfo_T *info) { + if (!buf->b_bufhl_info) { + return false; + } + + info->valid_to = -1; + info->entries = map_get(linenr_T, bufhl_vec_T)(buf->b_bufhl_info, lnum); + return kv_size(info->entries) > 0; +} + +/// get highlighting at column col +/// +/// It is is assumed this will be called with +/// non-decreasing column nrs, so that it is +/// possible to only recalculate highlights +/// at endpoints. +/// +/// @param info The info returned by bufhl_start_line +/// @param col The column to get the attr for +/// @return The highilight attr to display at the column +int bufhl_get_attr(bufhl_lineinfo_T *info, colnr_T col) { + if (col <= info->valid_to) { + return info->current; + } + int attr = 0; + info->valid_to = MAXCOL; + for (size_t i = 0; i < kv_size(info->entries); i++) { + bufhl_hl_item_T entry = kv_A(info->entries, i); + if (entry.start <= col && col <= entry.stop) { + int entry_attr = syn_id2attr(entry.hl_id); + attr = hl_combine_attr(attr, entry_attr); + if (entry.stop < info->valid_to) { + info->valid_to = entry.stop; + } + } else if (col < entry.start && entry.start-1 < info->valid_to) { + info->valid_to = entry.start-1; + } + } + info->current = attr; + return attr; +} + + /* * Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed. */ |