diff options
Diffstat (limited to 'src/nvim/buffer.c')
-rw-r--r-- | src/nvim/buffer.c | 697 |
1 files changed, 256 insertions, 441 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index b50c764ac3..b3bbdce9d9 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -53,6 +53,7 @@ #include "nvim/indent_c.h" #include "nvim/main.h" #include "nvim/mark.h" +#include "nvim/extmark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" @@ -80,12 +81,6 @@ #include "nvim/os/input.h" #include "nvim/buffer_updates.h" -typedef enum { - kBLSUnchanged = 0, - kBLSChanged = 1, - kBLSDeleted = 2, -} BufhlLineStatus; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "buffer.c.generated.h" #endif @@ -191,14 +186,17 @@ int open_buffer( } } - /* - * if there is no memfile at all, exit - * This is OK, since there are no changes to lose. - */ + // If there is no memfile at all, exit. + // This is OK, since there are no changes to lose. if (curbuf == NULL) { EMSG(_("E82: Cannot allocate any buffer, exiting...")); + + // Don't try to do any saving, with "curbuf" NULL almost nothing + // will work. + v_dying = 2; getout(2); } + EMSG(_("E83: Cannot allocate buffer, using other one...")); enter_buffer(curbuf); if (old_tw != curbuf->b_p_tw) { @@ -410,11 +408,11 @@ bool buf_valid(buf_T *buf) /// caller should get a new buffer very soon! /// The 'bufhidden' option can force freeing and deleting. /// @param abort_if_last -/// If TRUE, do not close the buffer if autocommands cause +/// If true, do not close the buffer if autocommands cause /// there to be only one window with this buffer. e.g. when /// ":quit" is supposed to close the window but autocommands /// close all other windows. -void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) +void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) { bool unload_buf = (action != 0); bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE); @@ -768,6 +766,9 @@ static void free_buffer(buf_T *buf) unref_var_dict(buf->b_vars); aubuflocal_remove(buf); tv_dict_unref(buf->additional_data); + xfree(buf->b_prompt_text); + callback_free(&buf->b_prompt_callback); + callback_free(&buf->b_prompt_interrupt); clear_fmark(&buf->b_last_cursor); clear_fmark(&buf->b_last_insert); clear_fmark(&buf->b_last_change); @@ -816,7 +817,7 @@ static void free_buffer_stuff(buf_T *buf, int free_flags) } uc_clear(&buf->b_ucmds); // clear local user commands buf_delete_signs(buf, (char_u *)"*"); // delete any signs - bufhl_clear_all(buf); // delete any highligts + extmark_free_all(buf); // delete any extmarks 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_CLEAR(buf->b_start_fenc); @@ -929,7 +930,7 @@ void handle_swap_exists(bufref_T *old_curbuf) // User selected Recover at ATTENTION prompt. msg_scroll = true; - ml_recover(); + ml_recover(false); MSG_PUTS("\n"); // don't overwrite the last message cmdline_row = msg_row; do_modelines(0); @@ -1236,7 +1237,7 @@ do_buffer( return FAIL; } } else { - EMSG2(_("E89: %s will be killed(add ! to override)"), + EMSG2(_("E89: %s will be killed (add ! to override)"), (char *)buf->b_fname); return FAIL; } @@ -1583,10 +1584,12 @@ void enter_buffer(buf_T *buf) open_buffer(false, NULL, 0); } else { - if (!msg_silent) { + if (!msg_silent && !shortmess(SHM_FILEINFO)) { need_fileinfo = true; // display file info after redraw } - (void)buf_check_timestamp(curbuf, false); // check if file changed + // check if file changed + (void)buf_check_timestamp(curbuf, false); + curwin->w_topline = 1; curwin->w_topfill = 0; apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf); @@ -1618,6 +1621,7 @@ void enter_buffer(buf_T *buf) if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { (void)did_set_spelllang(curwin); } + curbuf->b_last_used = time(NULL); redraw_later(NOT_VALID); } @@ -1750,6 +1754,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, false, curbuf); } if (aborting()) { // autocmds may abort script processing + xfree(ffname); return NULL; } if (buf == curbuf) { @@ -1879,6 +1884,10 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) } } + buf->b_prompt_callback.type = kCallbackNone; + buf->b_prompt_interrupt.type = kCallbackNone; + buf->b_prompt_text = NULL; + return buf; } @@ -1947,6 +1956,7 @@ void free_buf_options(buf_T *buf, int free_p_ff) clear_string_option(&buf->b_p_path); clear_string_option(&buf->b_p_tags); clear_string_option(&buf->b_p_tc); + clear_string_option(&buf->b_p_tfu); clear_string_option(&buf->b_p_dict); clear_string_option(&buf->b_p_tsr); clear_string_option(&buf->b_p_qe); @@ -2244,6 +2254,23 @@ int buflist_findpat( return match; } +typedef struct { + buf_T *buf; + char_u *match; +} bufmatch_T; + +/// Compare functions for qsort() below, that compares b_last_used. +static int +buf_time_compare(const void *s1, const void *s2) +{ + buf_T *buf1 = *(buf_T **)s1; + buf_T *buf2 = *(buf_T **)s2; + + if (buf1->b_last_used == buf2->b_last_used) { + return 0; + } + return buf1->b_last_used > buf2->b_last_used ? -1 : 1; +} /* * Find all buffer names that match. @@ -2257,6 +2284,7 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options) char_u *p; int attempt; char_u *patc; + bufmatch_T *matches = NULL; *num_file = 0; // return values in case of FAIL *file = NULL; @@ -2307,7 +2335,13 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options) } else { p = vim_strsave(p); } - (*file)[count++] = p; + if (matches != NULL) { + matches[count].buf = buf; + matches[count].match = p; + count++; + } else { + (*file)[count++] = p; + } } } } @@ -2316,6 +2350,10 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options) } if (round == 1) { *file = xmalloc((size_t)count * sizeof(**file)); + + if (options & WILD_BUFLASTUSED) { + matches = xmalloc((size_t)count * sizeof(*matches)); + } } } vim_regfree(regmatch.regprog); @@ -2328,6 +2366,25 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options) xfree(patc); } + if (matches != NULL) { + if (count > 1) { + qsort(matches, (size_t)count, sizeof(bufmatch_T), buf_time_compare); + } + + // if the current buffer is first in the list, place it at the end + if (matches[0].buf == curbuf) { + for (int i = 1; i < count; i++) { + (*file)[i-1] = matches[i].match; + } + (*file)[count-1] = matches[0].match; + } else { + for (int i = 0; i < count; i++) { + (*file)[i] = matches[i].match; + } + } + xfree(matches); + } + *num_file = count; return count == 0 ? FAIL : OK; } @@ -2588,11 +2645,35 @@ linenr_T buflist_findlnum(buf_T *buf) // List all known file names (for :files and :buffers command). void buflist_list(exarg_T *eap) { - buf_T *buf; + buf_T *buf = firstbuf; int len; int i; - for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next) { + garray_T buflist; + buf_T **buflist_data = NULL, **p; + + if (vim_strchr(eap->arg, 't')) { + ga_init(&buflist, sizeof(buf_T *), 50); + for (buf = firstbuf; buf != NULL; buf = buf->b_next) { + ga_grow(&buflist, 1); + ((buf_T **)buflist.ga_data)[buflist.ga_len++] = buf; + } + + qsort(buflist.ga_data, (size_t)buflist.ga_len, + sizeof(buf_T *), buf_time_compare); + + p = buflist_data = (buf_T **)buflist.ga_data; + buf = *p; + } + + for (; + buf != NULL && !got_int; + buf = buflist_data + ? (++p < buflist_data + buflist.ga_len ? *p : NULL) + : buf->b_next) { + const bool is_terminal = buf->terminal; + const bool job_running = buf->terminal && terminal_running(buf->terminal); + // skip unspecified buffers if ((!buf->b_p_bl && !eap->forceit && !strchr((char *)eap->arg, 'u')) || (strchr((char *)eap->arg, 'u') && buf->b_p_bl) @@ -2602,6 +2683,8 @@ void buflist_list(exarg_T *eap) && (buf->b_ml.ml_mfp == NULL || buf->b_nwindows == 0)) || (strchr((char *)eap->arg, 'h') && (buf->b_ml.ml_mfp == NULL || buf->b_nwindows != 0)) + || (strchr((char *)eap->arg, 'R') && (!is_terminal || !job_running)) + || (strchr((char *)eap->arg, 'F') && (!is_terminal || job_running)) || (strchr((char *)eap->arg, '-') && buf->b_p_ma) || (strchr((char *)eap->arg, '=') && !buf->b_p_ro) || (strchr((char *)eap->arg, 'x') && !(buf->b_flags & BF_READERR)) @@ -2648,13 +2731,22 @@ void buflist_list(exarg_T *eap) do { IObuff[len++] = ' '; } while (--i > 0 && len < IOSIZE - 18); - vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), - _("line %" PRId64), - buf == curbuf ? (int64_t)curwin->w_cursor.lnum - : (int64_t)buflist_findlnum(buf)); + if (vim_strchr(eap->arg, 't') && buf->b_last_used) { + add_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used); + } else { + vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), + _("line %" PRId64), + buf == curbuf ? (int64_t)curwin->w_cursor.lnum + : (int64_t)buflist_findlnum(buf)); + } + msg_outtrans(IObuff); line_breakcheck(); } + + if (buflist_data) { + ga_clear(&buflist); + } } /* @@ -2689,7 +2781,7 @@ setfname( buf_T *buf, char_u *ffname, char_u *sfname, - int message // give message when buffer already exists + bool message // give message when buffer already exists ) { buf_T *obuf = NULL; @@ -2992,28 +3084,29 @@ fileinfo( } vim_snprintf_add((char *)buffer, IOSIZE, "\"%s%s%s%s%s%s", - curbufIsChanged() ? (shortmess(SHM_MOD) - ? " [+]" : _(" [Modified]")) : " ", - (curbuf->b_flags & BF_NOTEDITED) - && !bt_dontwrite(curbuf) - ? _("[Not edited]") : "", - (curbuf->b_flags & BF_NEW) - && !bt_dontwrite(curbuf) - ? _("[New file]") : "", - (curbuf->b_flags & BF_READERR) ? _("[Read errors]") : "", - curbuf->b_p_ro ? (shortmess(SHM_RO) ? _("[RO]") - : _("[readonly]")) : "", - (curbufIsChanged() || (curbuf->b_flags & BF_WRITE_MASK) - || curbuf->b_p_ro) ? - " " : ""); - /* With 32 bit longs and more than 21,474,836 lines multiplying by 100 - * causes an overflow, thus for large numbers divide instead. */ - if (curwin->w_cursor.lnum > 1000000L) + curbufIsChanged() + ? (shortmess(SHM_MOD) ? " [+]" : _(" [Modified]")) : " ", + (curbuf->b_flags & BF_NOTEDITED) && !bt_dontwrite(curbuf) + ? _("[Not edited]") : "", + (curbuf->b_flags & BF_NEW) && !bt_dontwrite(curbuf) + ? new_file_message() : "", + (curbuf->b_flags & BF_READERR) + ? _("[Read errors]") : "", + curbuf->b_p_ro + ? (shortmess(SHM_RO) ? _("[RO]") : _("[readonly]")) : "", + (curbufIsChanged() + || (curbuf->b_flags & BF_WRITE_MASK) + || curbuf->b_p_ro) + ? " " : ""); + // With 32 bit longs and more than 21,474,836 lines multiplying by 100 + // causes an overflow, thus for large numbers divide instead. + if (curwin->w_cursor.lnum > 1000000L) { n = (int)(((long)curwin->w_cursor.lnum) / ((long)curbuf->b_ml.ml_line_count / 100L)); - else + } else { n = (int)(((long)curwin->w_cursor.lnum * 100L) / (long)curbuf->b_ml.ml_line_count); + } if (curbuf->b_ml.ml_flags & ML_EMPTY) { vim_snprintf_add((char *)buffer, IOSIZE, "%s", _(no_lines_msg)); } else if (p_ru) { @@ -3070,18 +3163,15 @@ void col_print(char_u *buf, size_t buflen, int col, int vcol) } } -/* - * put file name in title bar of window and in icon title - */ - static char_u *lasttitle = NULL; static char_u *lasticon = NULL; + +// Put the title name in the title bar and icon of the window. void maketitle(void) { - char_u *t_str = NULL; - char_u *i_name; - char_u *i_str = NULL; + char_u *title_str = NULL; + char_u *icon_str = NULL; int maxlen = 0; int len; int mustset; @@ -3095,7 +3185,7 @@ void maketitle(void) need_maketitle = false; if (!p_title && !p_icon && lasttitle == NULL && lasticon == NULL) { - return; + return; // nothing to do } if (p_title) { @@ -3116,14 +3206,14 @@ void maketitle(void) build_stl_str_hl(curwin, (char_u *)buf, sizeof(buf), p_titlestring, use_sandbox, 0, maxlen, NULL, NULL); - t_str = (char_u *)buf; + title_str = (char_u *)buf; if (called_emsg) { set_string_option_direct((char_u *)"titlestring", -1, (char_u *)"", OPT_FREE, SID_ERROR); } called_emsg |= save_called_emsg; } else { - t_str = p_titlestring; + title_str = p_titlestring; } } else { // Format: "fname + (path) (1 of 2) - VIM". @@ -3207,16 +3297,16 @@ void maketitle(void) trunc_string((char_u *)buf, (char_u *)buf, maxlen, sizeof(buf)); } } - t_str = (char_u *)buf; + title_str = (char_u *)buf; #undef SPACE_FOR_FNAME #undef SPACE_FOR_DIR #undef SPACE_FOR_ARGNR } } - mustset = ti_change(t_str, &lasttitle); + mustset = value_change(title_str, &lasttitle); if (p_icon) { - i_str = (char_u *)buf; + icon_str = (char_u *)buf; if (*p_iconstring != NUL) { if (stl_syntax & STL_IN_ICON) { int use_sandbox = false; @@ -3224,37 +3314,40 @@ void maketitle(void) use_sandbox = was_set_insecurely((char_u *)"iconstring", 0); called_emsg = false; - build_stl_str_hl(curwin, i_str, sizeof(buf), - p_iconstring, use_sandbox, - 0, 0, NULL, NULL); - if (called_emsg) + build_stl_str_hl(curwin, icon_str, sizeof(buf), + p_iconstring, use_sandbox, + 0, 0, NULL, NULL); + if (called_emsg) { set_string_option_direct((char_u *)"iconstring", -1, - (char_u *)"", OPT_FREE, SID_ERROR); + (char_u *)"", OPT_FREE, SID_ERROR); + } called_emsg |= save_called_emsg; - } else - i_str = p_iconstring; + } else { + icon_str = p_iconstring; + } } else { + char_u *buf_p; if (buf_spname(curbuf) != NULL) { - i_name = buf_spname(curbuf); + buf_p = buf_spname(curbuf); } else { // use file name only in icon - i_name = path_tail(curbuf->b_ffname); + buf_p = path_tail(curbuf->b_ffname); } - *i_str = NUL; + *icon_str = NUL; // Truncate name at 100 bytes. - len = (int)STRLEN(i_name); + len = (int)STRLEN(buf_p); if (len > 100) { len -= 100; if (has_mbyte) { - len += (*mb_tail_off)(i_name, i_name + len) + 1; + len += (*mb_tail_off)(buf_p, buf_p + len) + 1; } - i_name += len; + buf_p += len; } - STRCPY(i_str, i_name); - trans_characters(i_str, IOSIZE); + STRCPY(icon_str, buf_p); + trans_characters(icon_str, IOSIZE); } } - mustset |= ti_change(i_str, &lasticon); + mustset |= value_change(icon_str, &lasticon); if (mustset) { resettitle(); @@ -3268,8 +3361,8 @@ void maketitle(void) /// @param str desired title string /// @param[in,out] last current title string // -/// @return true when "*last" changed. -static bool ti_change(char_u *str, char_u **last) +/// @return true if resettitle() is to be called. +static bool value_change(char_u *str, char_u **last) FUNC_ATTR_WARN_UNUSED_RESULT { if ((str == NULL) != (*last == NULL) @@ -3277,10 +3370,11 @@ static bool ti_change(char_u *str, char_u **last) xfree(*last); if (str == NULL) { *last = NULL; + resettitle(); } else { *last = vim_strsave(str); + return true; } - return true; } return false; } @@ -3370,7 +3464,8 @@ int build_stl_str_hl( } type; } items[STL_MAX_ITEM]; #define TMPLEN 70 - char_u tmp[TMPLEN]; + char_u buf_tmp[TMPLEN]; + char_u win_tmp[TMPLEN]; char_u *usefmt = fmt; const int save_must_redraw = must_redraw; const int save_redr_type = curwin->w_redr_type; @@ -3378,10 +3473,18 @@ int build_stl_str_hl( // When the format starts with "%!" then evaluate it as an expression and // use the result as the actual format string. if (fmt[0] == '%' && fmt[1] == '!') { + typval_T tv = { + .v_type = VAR_NUMBER, + .vval.v_number = wp->handle, + }; + set_var(S_LEN("g:statusline_winid"), &tv, false); + usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox); if (usefmt == NULL) { usefmt = fmt; } + + do_unlet(S_LEN("g:statusline_winid"), true); } if (fillchar == 0) { @@ -3391,14 +3494,27 @@ int build_stl_str_hl( fillchar = '-'; } + // The cursor in windows other than the current one isn't always + // up-to-date, esp. because of autocommands and timers. + linenr_T lnum = wp->w_cursor.lnum; + if (lnum > wp->w_buffer->b_ml.ml_line_count) { + lnum = wp->w_buffer->b_ml.ml_line_count; + wp->w_cursor.lnum = lnum; + } + // Get line & check if empty (cursorpos will show "0-1"). - char_u *line_ptr = ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, false); + const char_u *line_ptr = ml_get_buf(wp->w_buffer, lnum, false); bool empty_line = (*line_ptr == NUL); // Get the byte value now, in case we need it below. This is more // efficient than making a copy of the line. int byteval; - if (wp->w_cursor.col > (colnr_T)STRLEN(line_ptr)) { + const size_t len = STRLEN(line_ptr); + if (wp->w_cursor.col > (colnr_T)len) { + // Line may have changed since checking the cursor column, or the lnum + // was adjusted above. + wp->w_cursor.col = (colnr_T)len; + wp->w_cursor.coladd = 0; byteval = 0; } else { byteval = utf_ptr2char(line_ptr + wp->w_cursor.col); @@ -3530,8 +3646,20 @@ int build_stl_str_hl( } } if (n == curitem && group_start_userhl == group_end_userhl) { + // empty group out_p = t; group_len = 0; + for (n = groupitems[groupdepth] + 1; n < curitem; n++) { + // do not use the highlighting from the removed group + if (items[n].type == Highlight) { + items[n].type = Empty; + } + // adjust the start position of TabPage to the next + // item position + if (items[n].type == TabPage) { + items[n].start = out_p; + } + } } } @@ -3791,22 +3919,31 @@ int build_stl_str_hl( // { Evaluate the expression // Store the current buffer number as a string variable - vim_snprintf((char *)tmp, sizeof(tmp), "%d", curbuf->b_fnum); - set_internal_string_var((char_u *)"g:actual_curbuf", tmp); + vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum); + set_internal_string_var((char_u *)"g:actual_curbuf", buf_tmp); + vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->handle); + set_internal_string_var((char_u *)"g:actual_curwin", win_tmp); buf_T *const save_curbuf = curbuf; win_T *const save_curwin = curwin; + const int save_VIsual_active = VIsual_active; curwin = wp; curbuf = wp->w_buffer; + // Visual mode is only valid in the current window. + if (curwin != save_curwin) { + VIsual_active = false; + } // Note: The result stored in `t` is unused. str = eval_to_string_safe(out_p, &t, use_sandbox); curwin = save_curwin; curbuf = save_curbuf; + VIsual_active = save_VIsual_active; // Remove the variable we just stored do_unlet(S_LEN("g:actual_curbuf"), true); + do_unlet(S_LEN("g:actual_curwin"), true); // } @@ -3865,8 +4002,8 @@ int build_stl_str_hl( // Store the position percentage in our temporary buffer. // Note: We cannot store the value in `num` because // `get_rel_pos` can return a named position. Ex: "Top" - get_rel_pos(wp, tmp, TMPLEN); - str = tmp; + get_rel_pos(wp, buf_tmp, TMPLEN); + str = buf_tmp; break; case STL_ARGLISTSTAT: @@ -3876,19 +4013,19 @@ int build_stl_str_hl( // at the end of the null-terminated string. // Setting the first byte to null means it will place the argument // number string at the beginning of the buffer. - tmp[0] = 0; + buf_tmp[0] = 0; // Note: The call will only return true if it actually - // appended data to the `tmp` buffer. - if (append_arg_number(wp, tmp, (int)sizeof(tmp), false)) { - str = tmp; + // appended data to the `buf_tmp` buffer. + if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), false)) { + str = buf_tmp; } break; case STL_KEYMAP: fillable = false; - if (get_keymap_str(wp, (char_u *)"<%s>", tmp, TMPLEN)) { - str = tmp; + if (get_keymap_str(wp, (char_u *)"<%s>", buf_tmp, TMPLEN)) { + str = buf_tmp; } break; case STL_PAGENUM: @@ -3945,9 +4082,9 @@ int build_stl_str_hl( // (including the brackets and null terminating character) if (*wp->w_buffer->b_p_ft != NUL && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3) { - vim_snprintf((char *)tmp, sizeof(tmp), "[%s]", - wp->w_buffer->b_p_ft); - str = tmp; + vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "[%s]", + wp->w_buffer->b_p_ft); + str = buf_tmp; } break; @@ -3959,13 +4096,13 @@ int build_stl_str_hl( // (including the comma and null terminating character) if (*wp->w_buffer->b_p_ft != NUL && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) { - vim_snprintf((char *)tmp, sizeof(tmp), ",%s", - wp->w_buffer->b_p_ft); + vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), ",%s", + wp->w_buffer->b_p_ft); // Uppercase the file extension - for (char_u *t = tmp; *t != 0; t++) { + for (char_u *t = buf_tmp; *t != 0; t++) { *t = (char_u)TOUPPER_LOC(*t); } - str = tmp; + str = buf_tmp; } break; } @@ -4629,7 +4766,8 @@ do_arg_all( if (i < alist->al_ga.ga_len && (AARGLIST(alist)[i].ae_fnum == buf->b_fnum || path_full_compare(alist_name(&AARGLIST(alist)[i]), - buf->b_ffname, true) & kEqualFiles)) { + buf->b_ffname, + true, true) & kEqualFiles)) { int weight = 1; if (old_curtab == curtab) { @@ -4812,6 +4950,12 @@ do_arg_all( xfree(opened); } +// Return TRUE if "buf" is a prompt buffer. +int bt_prompt(buf_T *buf) +{ + return buf != NULL && buf->b_p_bt[0] == 'p'; +} + /* * Open a window for a number of buffers. */ @@ -5179,6 +5323,13 @@ bool bt_help(const buf_T *const buf) return buf != NULL && buf->b_help; } +// Return true if "buf" is a normal buffer, 'buftype' is empty. +bool bt_normal(const buf_T *const buf) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return buf != NULL && buf->b_p_bt[0] == NUL; +} + // Return true if "buf" is the quickfix buffer. bool bt_quickfix(const buf_T *const buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT @@ -5199,14 +5350,18 @@ bool bt_nofile(const buf_T *const buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f') - || buf->b_p_bt[0] == 'a' || buf->terminal); + || buf->b_p_bt[0] == 'a' + || buf->terminal + || buf->b_p_bt[0] == 'p'); } // Return true if "buf" is a "nowrite", "nofile" or "terminal" buffer. bool bt_dontwrite(const buf_T *const buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return buf != NULL && (buf->b_p_bt[0] == 'n' || buf->terminal); + return buf != NULL && (buf->b_p_bt[0] == 'n' + || buf->terminal + || buf->b_p_bt[0] == 'p'); } bool bt_dontwrite_msg(const buf_T *const buf) @@ -5259,6 +5414,9 @@ char_u *buf_spname(buf_T *buf) if (buf->b_fname != NULL) { return buf->b_fname; } + if (bt_prompt(buf)) { + return (char_u *)_("[Prompt]"); + } return (char_u *)_("[Scratch]"); } if (buf->b_fname == NULL) { @@ -5323,349 +5481,6 @@ int buf_signcols(buf_T *buf) return buf->b_signcols; } -// bufhl: plugin highlights associated with a buffer - -/// Get reference to line in kbtree_t -/// -/// @param b the three -/// @param line the linenumber to lookup -/// @param put if true, put a new line when not found -/// if false, return NULL when not found -BufhlLine *bufhl_tree_ref(BufhlInfo *b, linenr_T line, bool put) -{ - BufhlLine t = BUFHLLINE_INIT(line); - - // kp_put() only works if key is absent, try get first - BufhlLine **pp = kb_get(bufhl, b, &t); - if (pp) { - return *pp; - } else if (!put) { - return NULL; - } - - BufhlLine *p = xmalloc(sizeof(*p)); - *p = (BufhlLine)BUFHLLINE_INIT(line); - kb_put(bufhl, b, p); - return p; -} - -/// 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) -{ - if (src_id == 0) { - src_id = (int)nvim_create_namespace((String)STRING_INIT); - } - if (hl_id <= 0) { - // no highlight group or invalid line, just return src_id - return src_id; - } - - BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, lnum, true); - - BufhlItem *hlentry = kv_pushp(lineinfo->items); - 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) { - redraw_buf_line_later(buf, lnum); - } - return src_id; -} - -/// Add highlighting to a buffer, bounded by two cursor positions, -/// with an offset. -/// -/// @param buf 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 Highlight group id -/// @param pos_start Cursor position to start the hightlighting at -/// @param pos_end Cursor position to end the highlighting at -/// @param offset Move the whole highlighting this many columns to the right -void bufhl_add_hl_pos_offset(buf_T *buf, - int src_id, - int hl_id, - lpos_T pos_start, - lpos_T pos_end, - colnr_T offset) -{ - colnr_T hl_start = 0; - colnr_T hl_end = 0; - - for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum ++) { - if (pos_start.lnum < lnum && lnum < pos_end.lnum) { - hl_start = offset; - hl_end = MAXCOL; - } else if (lnum == pos_start.lnum && lnum < pos_end.lnum) { - hl_start = pos_start.col + offset + 1; - hl_end = MAXCOL; - } else if (pos_start.lnum < lnum && lnum == pos_end.lnum) { - hl_start = offset; - hl_end = pos_end.col + offset; - } else if (pos_start.lnum == lnum && pos_end.lnum == lnum) { - hl_start = pos_start.col + offset + 1; - hl_end = pos_end.col + offset; - } - (void)bufhl_add_hl(buf, src_id, hl_id, lnum, hl_start, hl_end); - } -} - -int bufhl_add_virt_text(buf_T *buf, - int src_id, - linenr_T lnum, - VirtText virt_text) -{ - if (src_id == 0) { - src_id = (int)nvim_create_namespace((String)STRING_INIT); - } - - BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, lnum, true); - - bufhl_clear_virttext(&lineinfo->virt_text); - if (kv_size(virt_text) > 0) { - lineinfo->virt_text_src = src_id; - lineinfo->virt_text = virt_text; - } else { - lineinfo->virt_text_src = 0; - // currently not needed, but allow a future caller with - // 0 size and non-zero capacity - kv_destroy(virt_text); - } - - if (0 < lnum && lnum <= buf->b_ml.ml_line_count) { - redraw_buf_line_later(buf, lnum); - } - return src_id; -} - -static void bufhl_clear_virttext(VirtText *text) -{ - for (size_t i = 0; i < kv_size(*text); i++) { - xfree(kv_A(*text, i).text); - } - kv_destroy(*text); - *text = (VirtText)KV_INITIAL_VALUE; -} - -/// 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) -{ - kbitr_t(bufhl) itr; - BufhlLine *l, t = BUFHLLINE_INIT(line_start); - if (!kb_itr_get(bufhl, &buf->b_bufhl_info, &t, &itr)) { - kb_itr_next(bufhl, &buf->b_bufhl_info, &itr); - } - for (; kb_itr_valid(&itr); kb_itr_next(bufhl, &buf->b_bufhl_info, &itr)) { - l = kb_itr_key(&itr); - linenr_T line = l->line; - if (line > line_end) { - break; - } - if (line_start <= line) { - BufhlLineStatus status = bufhl_clear_line(l, src_id, line); - if (status != kBLSUnchanged) { - redraw_buf_line_later(buf, line); - } - if (status == kBLSDeleted) { - kb_del_itr(bufhl, &buf->b_bufhl_info, &itr); - xfree(l); - } - } - } -} - -/// 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 BufhlLineStatus bufhl_clear_line(BufhlLine *lineinfo, int src_id, - linenr_T lnum) -{ - BufhlLineStatus changed = kBLSUnchanged; - size_t oldsize = kv_size(lineinfo->items); - if (src_id < 0) { - kv_size(lineinfo->items) = 0; - } else { - size_t newidx = 0; - for (size_t i = 0; i < kv_size(lineinfo->items); i++) { - if (kv_A(lineinfo->items, i).src_id != src_id) { - if (i != newidx) { - kv_A(lineinfo->items, newidx) = kv_A(lineinfo->items, i); - } - newidx++; - } - } - kv_size(lineinfo->items) = newidx; - } - if (kv_size(lineinfo->items) != oldsize) { - changed = kBLSChanged; - } - - if (kv_size(lineinfo->virt_text) != 0 - && (src_id < 0 || src_id == lineinfo->virt_text_src)) { - bufhl_clear_virttext(&lineinfo->virt_text); - lineinfo->virt_text_src = 0; - changed = kBLSChanged; - } - - if (kv_size(lineinfo->items) == 0 && kv_size(lineinfo->virt_text) == 0) { - kv_destroy(lineinfo->items); - return kBLSDeleted; - } - return changed; -} - - -/// Remove all highlights and free the highlight data -void bufhl_clear_all(buf_T *buf) -{ - bufhl_clear_line_range(buf, -1, 1, MAXLNUM); - kb_destroy(bufhl, (&buf->b_bufhl_info)); - kb_init(&buf->b_bufhl_info); - kv_destroy(buf->b_bufhl_move_space); - kv_init(buf->b_bufhl_move_space); -} - -/// 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, - bool end_temp) -{ - kbitr_t(bufhl) itr; - BufhlLine *l, t = BUFHLLINE_INIT(line1); - if (end_temp && amount < 0) { - // Move all items from b_bufhl_move_space to the btree. - for (size_t i = 0; i < kv_size(buf->b_bufhl_move_space); i++) { - l = kv_A(buf->b_bufhl_move_space, i); - l->line += amount; - kb_put(bufhl, &buf->b_bufhl_info, l); - } - kv_size(buf->b_bufhl_move_space) = 0; - return; - } - - if (!kb_itr_get(bufhl, &buf->b_bufhl_info, &t, &itr)) { - kb_itr_next(bufhl, &buf->b_bufhl_info, &itr); - } - for (; kb_itr_valid(&itr); kb_itr_next(bufhl, &buf->b_bufhl_info, &itr)) { - l = kb_itr_key(&itr); - if (l->line >= line1 && l->line <= line2) { - if (end_temp && amount > 0) { - kb_del_itr(bufhl, &buf->b_bufhl_info, &itr); - kv_push(buf->b_bufhl_move_space, l); - } - if (amount == MAXLNUM) { - if (bufhl_clear_line(l, -1, l->line) == kBLSDeleted) { - kb_del_itr(bufhl, &buf->b_bufhl_info, &itr); - xfree(l); - } else { - assert(false); - } - } else { - l->line += amount; - } - } else if (l->line > line2) { - if (amount_after == 0) { - break; - } - l->line += amount_after; - } - } -} - - -/// 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, BufhlLineInfo *info) -{ - BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, lnum, false); - if (!lineinfo) { - return false; - } - info->valid_to = -1; - info->line = lineinfo; - return true; -} - -/// 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(BufhlLineInfo *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->line->items); i++) { - BufhlItem entry = kv_A(info->line->items, 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. */ |