diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/buffer_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/drawscreen.c | 368 | ||||
-rw-r--r-- | src/nvim/eval.c | 10 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 1 | ||||
-rw-r--r-- | src/nvim/eval/vars.c | 15 | ||||
-rw-r--r-- | src/nvim/map.c | 3 | ||||
-rw-r--r-- | src/nvim/map_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/ops.c | 411 | ||||
-rw-r--r-- | src/nvim/ops.h | 12 | ||||
-rw-r--r-- | src/nvim/option.c | 4 | ||||
-rw-r--r-- | src/nvim/option_vars.h | 3 | ||||
-rw-r--r-- | src/nvim/options.lua | 58 | ||||
-rw-r--r-- | src/nvim/po/da.po | 2 | ||||
-rw-r--r-- | src/nvim/po/fr.po | 2 | ||||
-rw-r--r-- | src/nvim/po/tr.po | 2 | ||||
-rw-r--r-- | src/nvim/po/uk.po | 2 | ||||
-rw-r--r-- | src/nvim/shada.c | 4 | ||||
-rw-r--r-- | src/nvim/yankmap.c | 45 | ||||
-rw-r--r-- | src/nvim/yankmap.h | 25 |
19 files changed, 686 insertions, 283 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 1e5086309c..9653b5e09c 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -528,6 +528,7 @@ struct file_buffer { Callback b_ofu_cb; ///< 'omnifunc' callback char *b_p_tfu; ///< 'tagfunc' Callback b_tfu_cb; ///< 'tagfunc' callback + char *b_p_urf; ///< 'userregfunc' int b_p_eof; ///< 'endoffile' int b_p_eol; ///< 'endofline' int b_p_fixeol; ///< 'fixendofline' diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 1626e46cf6..145229bacc 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -59,7 +59,6 @@ #include <stdlib.h> #include <string.h> -#include "klib/kvec.h" #include "nvim/api/private/defs.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" @@ -120,6 +119,8 @@ #include "nvim/vim_defs.h" #include "nvim/window.h" +#include "klib/kvec.h" + /// corner value flags for hsep_connected and vsep_connected typedef enum { WC_TOP_LEFT = 0, @@ -179,12 +180,8 @@ bool default_grid_alloc(void) // Allocation of the screen buffers is done only when the size changes and // when Rows and Columns have been set and we have started doing full // screen stuff. - if ((default_grid.chars != NULL - && Rows == default_grid.rows - && Columns == default_grid.cols) - || Rows == 0 - || Columns == 0 - || (!full_screen && default_grid.chars == NULL)) { + if ((default_grid.chars != NULL && Rows == default_grid.rows && Columns == default_grid.cols) + || Rows == 0 || Columns == 0 || (!full_screen && default_grid.chars == NULL)) { resizing = false; return false; } @@ -202,8 +199,8 @@ bool default_grid_alloc(void) grid_alloc(&default_grid, Rows, Columns, true, true); stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size); - tab_page_click_defs = stl_alloc_click_defs(tab_page_click_defs, Columns, - &tab_page_click_defs_size); + tab_page_click_defs + = stl_alloc_click_defs(tab_page_click_defs, Columns, &tab_page_click_defs_size); default_grid.comp_height = Rows; default_grid.comp_width = Columns; @@ -226,8 +223,7 @@ void screenclear(void) // blank out the default grid for (int i = 0; i < default_grid.rows; i++) { - grid_clear_line(&default_grid, default_grid.line_offset[i], - default_grid.cols, true); + grid_clear_line(&default_grid, default_grid.line_offset[i], default_grid.cols, true); } ui_call_grid_clear(1); // clear the display @@ -274,7 +270,7 @@ void screen_resize(int width, int height) return; } - if (width < 0 || height < 0) { // just checking... + if (width < 0 || height < 0) { // just checking... return; } @@ -315,9 +311,9 @@ void screen_resize(int width, int height) RedrawingDisabled++; - win_new_screensize(); // fit the windows in the new sized screen + win_new_screensize(); // fit the windows in the new sized screen - comp_col(); // recompute columns for shown command and ruler + comp_col(); // recompute columns for shown command and ruler RedrawingDisabled--; @@ -411,8 +407,7 @@ void check_screensize(void) /// Return true if redrawing should currently be done. bool redrawing(void) { - return !RedrawingDisabled - && !(p_lz && char_avail() && !KeyTyped && !do_redraw); + return !RedrawingDisabled && !(p_lz && char_avail() && !KeyTyped && !do_redraw); } /// Redraw the parts of the screen that is marked for redraw. @@ -483,8 +478,7 @@ int update_screen(void) if (msg_grid.chars) { // non-displayed part of msg_grid is considered invalid. for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) { - grid_clear_line(&msg_grid, msg_grid.line_offset[i], - msg_grid.cols, i < p_ch); + grid_clear_line(&msg_grid, msg_grid.line_offset[i], msg_grid.cols, i < p_ch); } } msg_grid.throttled = false; @@ -494,8 +488,7 @@ int update_screen(void) if (type == UPD_NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) { was_invalidated = ui_comp_set_screen_valid(false); for (int i = valid; i < Rows - p_ch; i++) { - grid_clear_line(&default_grid, default_grid.line_offset[i], - Columns, false); + grid_clear_line(&default_grid, default_grid.line_offset[i], Columns, false); } FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_floating) { @@ -541,10 +534,10 @@ int update_screen(void) hl_changed = true; } - if (type == UPD_CLEAR) { // first clear screen - screenclear(); // will reset clear_cmdline - // and set UPD_NOT_VALID for each window - cmdline_screen_cleared(); // clear external cmdline state + if (type == UPD_CLEAR) { // first clear screen + screenclear(); // will reset clear_cmdline + // and set UPD_NOT_VALID for each window + cmdline_screen_cleared(); // clear external cmdline state type = UPD_NOT_VALID; // must_redraw may be set indirectly, avoid another redraw later must_redraw = 0; @@ -568,7 +561,7 @@ int update_screen(void) redraw_tabline = true; } - if (clear_cmdline) { // going to clear cmdline (done below) + if (clear_cmdline) { // going to clear cmdline (done below) msg_check_for_delay(false); } @@ -577,8 +570,9 @@ 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_p_stc) - ? number_width(curwin) : 0)) { + && 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; } @@ -600,8 +594,7 @@ int update_screen(void) buf_T *buf = wp->w_buffer; if (buf->b_mod_set) { - if (buf->b_mod_tick_syn < display_tick - && syntax_present(wp)) { + if (buf->b_mod_tick_syn < display_tick && syntax_present(wp)) { syn_stack_apply_changes(buf); buf->b_mod_tick_syn = display_tick; } @@ -765,8 +758,8 @@ static void win_redr_border(win_T *wp) } if (wp->w_config.title) { - int title_col = win_get_bordertext_col(icol, wp->w_config.title_width, - wp->w_config.title_pos); + int title_col + = win_get_bordertext_col(icol, wp->w_config.title_width, wp->w_config.title_pos); win_redr_bordertext(wp, wp->w_config.title_chunks, title_col); } if (adj[1]) { @@ -801,8 +794,8 @@ static void win_redr_border(win_T *wp) } if (wp->w_config.footer) { - int footer_col = win_get_bordertext_col(icol, wp->w_config.footer_width, - wp->w_config.footer_pos); + int footer_col + = win_get_bordertext_col(icol, wp->w_config.footer_width, wp->w_config.footer_pos); win_redr_bordertext(wp, wp->w_config.footer_chunks, footer_col); } if (adj[1]) { @@ -832,8 +825,7 @@ void setcursor_mayforce(bool force) // With 'rightleft' set and the cursor on a double-wide character, // position it on the leftmost column. col = curwin->w_width_inner - curwin->w_wcol - - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2 - && vim_isprintc(gchar_cursor())) ? 2 : 1); + - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2 && vim_isprintc(gchar_cursor())) ? 2 : 1); } grid_adjust(&grid, &row, &col); @@ -847,22 +839,19 @@ void setcursor_mayforce(bool force) void show_cursor_info_later(bool force) { int state = get_real_state(); - int empty_line = (State & MODE_INSERT) == 0 - && *ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum) == NUL; + int empty_line + = (State & MODE_INSERT) == 0 && *ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum) == NUL; // Only draw when something changed. validate_virtcol_win(curwin); - if (force - || curwin->w_cursor.lnum != curwin->w_stl_cursor.lnum + if (force || curwin->w_cursor.lnum != curwin->w_stl_cursor.lnum || curwin->w_cursor.col != curwin->w_stl_cursor.col || curwin->w_virtcol != curwin->w_stl_virtcol || curwin->w_cursor.coladd != curwin->w_stl_cursor.coladd || curwin->w_topline != curwin->w_stl_topline || curwin->w_buffer->b_ml.ml_line_count != curwin->w_stl_line_count - || curwin->w_topfill != curwin->w_stl_topfill - || empty_line != curwin->w_stl_empty - || reg_recording != curwin->w_stl_recording - || state != curwin->w_stl_state + || curwin->w_topfill != curwin->w_stl_topfill || empty_line != curwin->w_stl_empty + || reg_recording != curwin->w_stl_recording || state != curwin->w_stl_state || (VIsual_active && VIsual_mode != curwin->w_stl_visual_mode)) { if (curwin->w_status_height || global_stl_height()) { curwin->w_redr_status = true; @@ -874,8 +863,7 @@ void show_cursor_info_later(bool force) curwin->w_redr_status = true; } - if ((p_icon && (stl_syntax & STL_IN_ICON)) - || (p_title && (stl_syntax & STL_IN_TITLE))) { + if ((p_icon && (stl_syntax & STL_IN_ICON)) || (p_title && (stl_syntax & STL_IN_TITLE))) { need_maketitle = true; } } @@ -927,9 +915,7 @@ int showmode(void) msg_grid_validate(); bool do_mode = ((p_smd && msg_silent == 0) - && ((State & MODE_TERMINAL) - || (State & MODE_INSERT) - || restart_edit != NUL + && ((State & MODE_TERMINAL) || (State & MODE_INSERT) || restart_edit != NUL || VIsual_active)); bool can_show_mode = (p_ch != 0 || ui_has(kUIMessages)); @@ -951,7 +937,7 @@ int showmode(void) // Position on the last line in the window, column 0 msg_pos_mode(); - int attr = HL_ATTR(HLF_CM); // Highlight mode + int attr = HL_ATTR(HLF_CM); // Highlight mode // When the screen is too narrow to show the entire mode message, // avoid scrolling and truncate instead. @@ -1007,8 +993,8 @@ int showmode(void) msg_puts_attr(_(" REVERSE"), attr); } msg_puts_attr(_(" INSERT"), attr); - } else if (restart_edit == 'I' || restart_edit == 'i' - || restart_edit == 'a' || restart_edit == 'A') { + } else if (restart_edit == 'I' || restart_edit == 'i' || restart_edit == 'a' + || restart_edit == 'A') { if (curbuf->terminal) { msg_puts_attr(_(" (terminal)"), attr); } else { @@ -1022,8 +1008,7 @@ int showmode(void) if (State & MODE_LANGMAP) { if (curwin->w_p_arab) { msg_puts_attr(_(" Arabic"), attr); - } else if (get_keymap_str(curwin, " (%s)", - NameBuff, MAXPATHL)) { + } else if (get_keymap_str(curwin, " (%s)", NameBuff, MAXPATHL)) { msg_puts_attr(NameBuff, attr); } } @@ -1036,21 +1021,25 @@ int showmode(void) // Don't concatenate separate words to avoid translation // problems. - switch ((VIsual_select ? 4 : 0) - + (VIsual_mode == Ctrl_V) * 2 - + (VIsual_mode == 'V')) { + switch ((VIsual_select ? 4 : 0) + (VIsual_mode == Ctrl_V) * 2 + (VIsual_mode == 'V')) { case 0: - p = N_(" VISUAL"); break; + p = N_(" VISUAL"); + break; case 1: - p = N_(" VISUAL LINE"); break; + p = N_(" VISUAL LINE"); + break; case 2: - p = N_(" VISUAL BLOCK"); break; + p = N_(" VISUAL BLOCK"); + break; case 4: - p = N_(" SELECT"); break; + p = N_(" SELECT"); + break; case 5: - p = N_(" SELECT LINE"); break; + p = N_(" SELECT LINE"); + break; default: - p = N_(" SELECT BLOCK"); break; + p = N_(" SELECT BLOCK"); + break; } msg_puts_attr(_(p), attr); } @@ -1059,9 +1048,8 @@ int showmode(void) need_clear = true; } - if (reg_recording != 0 - && edit_submode == NULL // otherwise it gets too long - ) { + if (reg_recording != 0 && edit_submode == NULL // otherwise it gets too long + ) { recording_mode(attr); need_clear = true; } @@ -1070,12 +1058,12 @@ int showmode(void) if (need_clear || clear_cmdline || redraw_mode) { msg_clr_eos(); } - msg_didout = false; // overwrite this message + msg_didout = false; // overwrite this message length = msg_col; msg_col = 0; msg_no_more = false; lines_left = save_lines_left; - need_wait_return = nwr_save; // never ask for hit-return for this + need_wait_return = nwr_save; // never ask for hit-return for this } else if (clear_cmdline && msg_silent == 0) { // Clear the whole command line. Will reset "clear_cmdline". msg_clr_cmdline(); @@ -1154,12 +1142,14 @@ static void recording_mode(int attr) } msg_puts_attr(_("recording"), attr); - char s[4]; - snprintf(s, ARRAY_SIZE(s), " @%c", reg_recording); + char reg_str[8]; + reg_str[utf_char2bytes(reg_recording, reg_str)] = 0; + char s[16]; + snprintf(s, ARRAY_SIZE(s), " @%s", reg_str); msg_puts_attr(s, attr); } -#define COL_RULER 17 // columns needed by standard ruler +#define COL_RULER 17 // columns needed by standard ruler /// Compute columns for ruler and shown command. 'sc_col' is also used to /// decide what the maximum length of a message on the status line can be. @@ -1180,17 +1170,15 @@ void comp_col(void) } if (p_sc && *p_sloc == 'l') { sc_col += SHOWCMD_COLS; - if (!p_ru || last_has_status) { // no need for separating space + if (!p_ru || last_has_status) { // no need for separating space sc_col++; } } - assert(sc_col >= 0 - && INT_MIN + sc_col <= Columns); + assert(sc_col >= 0 && INT_MIN + sc_col <= Columns); sc_col = Columns - sc_col; - assert(ru_col >= 0 - && INT_MIN + ru_col <= Columns); + assert(ru_col >= 0 && INT_MIN + ru_col <= Columns); ru_col = Columns - ru_col; - if (sc_col <= 0) { // screen too narrow, will become a mess + if (sc_col <= 0) { // screen too narrow, will become a mess sc_col = 1; } if (ru_col <= 0) { @@ -1235,8 +1223,7 @@ static bool win_redraw_signcols(win_T *wp) static bool hsep_connected(win_T *wp, WindowCorner corner) { bool before = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT); - int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) - ? wp->w_winrow - 1 : W_ENDROW(wp); + int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) ? wp->w_winrow - 1 : W_ENDROW(wp); frame_T *fr = wp->w_frame; while (fr->fr_parent != NULL) { @@ -1270,8 +1257,8 @@ static bool hsep_connected(win_T *wp, WindowCorner corner) static bool vsep_connected(win_T *wp, WindowCorner corner) { bool before = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT); - int sep_col = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) - ? wp->w_wincol - 1 : W_ENDCOL(wp); + int sep_col + = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) ? wp->w_wincol - 1 : W_ENDCOL(wp); frame_T *fr = wp->w_frame; while (fr->fr_parent != NULL) { @@ -1436,19 +1423,19 @@ static void draw_sep_connectors_win(win_T *wp) /// bot: from bot_start to last row (when scrolled up) static void win_update(win_T *wp) { - int top_end = 0; // Below last row of the top area that needs - // updating. 0 when no top area updating. - int mid_start = 999; // first row of the mid area that needs - // updating. 999 when no mid area updating. - int mid_end = 0; // Below last row of the mid area that needs - // updating. 0 when no mid area updating. - int bot_start = 999; // first row of the bot area that needs - // updating. 999 when no bot area updating - bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit - bool top_to_mod = false; // redraw above mod_top - - int bot_scroll_start = 999; // first line that needs to be redrawn due to - // scrolling. only used for EOB + int top_end = 0; // Below last row of the top area that needs + // updating. 0 when no top area updating. + int mid_start = 999; // first row of the mid area that needs + // updating. 999 when no mid area updating. + int mid_end = 0; // Below last row of the mid area that needs + // updating. 0 when no mid area updating. + int bot_start = 999; // first row of the bot area that needs + // updating. 999 when no bot area updating + bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit + bool top_to_mod = false; // redraw above mod_top + + int bot_scroll_start = 999; // first line that needs to be redrawn due to + // scrolling. only used for EOB static bool recursive = false; // being called recursively @@ -1457,9 +1444,10 @@ static void win_update(win_T *wp) DID_NONE = 1, // didn't update a line DID_LINE = 2, // updated a normal line DID_FOLD = 3, // updated a folded line - } did_update = DID_NONE; + } did_update + = DID_NONE; - linenr_T syntax_last_parsed = 0; // last parsed text line + linenr_T syntax_last_parsed = 0; // last parsed text line linenr_T mod_top = 0; linenr_T mod_bot = 0; @@ -1567,14 +1555,12 @@ static void win_update(win_T *wp) // previous line invalid. Simple solution: redraw all visible // lines above the change. // Same for a match pattern. - if (screen_search_hl.rm.regprog != NULL - && re_multiline(screen_search_hl.rm.regprog)) { + if (screen_search_hl.rm.regprog != NULL && re_multiline(screen_search_hl.rm.regprog)) { top_to_mod = true; } else { const matchitem_T *cur = wp->w_match_head; while (cur != NULL) { - if (cur->mit_match.regprog != NULL - && re_multiline(cur->mit_match.regprog)) { + if (cur->mit_match.regprog != NULL && re_multiline(cur->mit_match.regprog)) { top_to_mod = true; break; } @@ -1667,13 +1653,11 @@ static void win_update(win_T *wp) // 2: wp->w_topline is below wp->w_lines[0].wl_lnum: may scroll up // 3: wp->w_topline is wp->w_lines[0].wl_lnum: find first entry in // w_lines[] that needs updating. - if ((type == UPD_VALID || type == UPD_SOME_VALID - || type == UPD_INVERTED || type == UPD_INVERTED_ALL) + if ((type == UPD_VALID || type == UPD_SOME_VALID || type == UPD_INVERTED + || type == UPD_INVERTED_ALL) && !wp->w_botfill && !wp->w_old_botfill) { - if (mod_top != 0 - && wp->w_topline == mod_top - && (!wp->w_lines[0].wl_valid - || wp->w_topline == wp->w_lines[0].wl_lnum)) { + if (mod_top != 0 && wp->w_topline == mod_top + && (!wp->w_lines[0].wl_valid || wp->w_topline == wp->w_lines[0].wl_lnum)) { // w_topline is the first changed line and window is not scrolled, // the scrolling from changed lines will be done further down. } else if (wp->w_lines[0].wl_valid @@ -1696,7 +1680,7 @@ static void win_update(win_T *wp) } else { j = wp->w_lines[0].wl_lnum - wp->w_topline; } - if (j < wp->w_grid.rows - 2) { // not too far off + if (j < wp->w_grid.rows - 2) { // not too far off int i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1, true); // insert extra lines for previously invisible filler lines if (wp->w_lines[0].wl_lnum != wp->w_topline) { @@ -1742,8 +1726,7 @@ static void win_update(win_T *wp) int j = -1; int row = 0; for (int i = 0; i < wp->w_lines_valid; i++) { - if (wp->w_lines[i].wl_valid - && wp->w_lines[i].wl_lnum == wp->w_topline) { + if (wp->w_lines[i].wl_valid && wp->w_lines[i].wl_lnum == wp->w_topline) { j = i; break; } @@ -1782,8 +1765,7 @@ static void win_update(win_T *wp) wp->w_lines[idx] = wp->w_lines[j]; // stop at line that didn't fit, unless it is still // valid (no lines deleted) - if (row > 0 && bot_start + row - + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) { + if (row > 0 && bot_start + row + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) { wp->w_lines_valid = idx + 1; break; } @@ -1799,8 +1781,8 @@ static void win_update(win_T *wp) // Correct the first entry for filler lines at the top // when it won't get updated below. if (win_may_fill(wp) && bot_start > 0) { - wp->w_lines[0].wl_size = (uint16_t)(plines_win_nofill(wp, wp->w_topline, true) - + wp->w_topfill); + wp->w_lines[0].wl_size + = (uint16_t)(plines_win_nofill(wp, wp->w_topline, true) + wp->w_topfill); } } } @@ -1863,15 +1845,13 @@ static void win_update(win_T *wp) } else { from = wp->w_old_cursor_lnum; to = curwin->w_cursor.lnum; - if (from == 0) { // Visual mode just started + if (from == 0) { // Visual mode just started from = to; } } - if (VIsual.lnum != wp->w_old_visual_lnum - || VIsual.col != wp->w_old_visual_col) { - if (wp->w_old_visual_lnum < from - && wp->w_old_visual_lnum != 0) { + if (VIsual.lnum != wp->w_old_visual_lnum || VIsual.col != wp->w_old_visual_col) { + if (wp->w_old_visual_lnum < from && wp->w_old_visual_lnum != 0) { from = wp->w_old_visual_lnum; } if (wp->w_old_visual_lnum > to) { @@ -1927,8 +1907,7 @@ static void win_update(win_T *wp) } } - if (fromc != wp->w_old_cursor_fcol - || toc != wp->w_old_cursor_lcol) { + if (fromc != wp->w_old_cursor_fcol || toc != wp->w_old_cursor_lcol) { if (from > VIsual.lnum) { from = VIsual.lnum; } @@ -1982,7 +1961,7 @@ static void win_update(win_T *wp) } else { mid_start = 0; } - while (lnum < from && idx < wp->w_lines_valid) { // find start + while (lnum < from && idx < wp->w_lines_valid) { // find start if (wp->w_lines[idx].wl_valid) { mid_start += wp->w_lines[idx].wl_size; } else if (!scrolled_down) { @@ -1997,9 +1976,8 @@ static void win_update(win_T *wp) } srow += mid_start; mid_end = wp->w_grid.rows; - for (; idx < wp->w_lines_valid; idx++) { // find end - if (wp->w_lines[idx].wl_valid - && wp->w_lines[idx].wl_lnum >= to + 1) { + for (; idx < wp->w_lines_valid; idx++) { // find end + if (wp->w_lines[idx].wl_valid && wp->w_lines[idx].wl_lnum >= to + 1) { // Only update until first row of this line mid_end = srow; break; @@ -2043,12 +2021,12 @@ static void win_update(win_T *wp) } // Update all the window rows. - int idx = 0; // first entry in w_lines[].wl_size - int row = 0; // current window row to display - int srow = 0; // starting row of the current line + int idx = 0; // first entry in w_lines[].wl_size + int row = 0; // current window row to display + int srow = 0; // starting row of the current line - bool eof = false; // if true, we hit the end of the file - bool didline = false; // if true, we finished the last line + bool eof = false; // if true, we hit the end of the file + bool didline = false; // if true, we finished the last line while (true) { // stop updating when reached the end of the window (check for _past_ // the end of the window is at the end of the loop) @@ -2074,27 +2052,19 @@ static void win_update(win_T *wp) // When syntax folding is being used, the saved syntax states will // already have been updated, we can't see where the syntax state is // the same again, just update until the end of the window. - if (row < top_end - || (row >= mid_start && row < mid_end) - || top_to_mod - || idx >= wp->w_lines_valid - || (row + wp->w_lines[idx].wl_size > bot_start) + if (row < top_end || (row >= mid_start && row < mid_end) || top_to_mod + || idx >= wp->w_lines_valid || (row + wp->w_lines[idx].wl_size > bot_start) || (mod_top != 0 && (lnum == mod_top || (lnum >= mod_top - && (lnum < mod_bot - || did_update == DID_FOLD - || (did_update == DID_LINE - && syntax_present(wp) - && ((foldmethodIsSyntax(wp) - && hasAnyFolding(wp)) + && (lnum < mod_bot || did_update == DID_FOLD + || (did_update == DID_LINE && syntax_present(wp) + && ((foldmethodIsSyntax(wp) && hasAnyFolding(wp)) || syntax_check_changed(lnum))) // match in fixed position might need redraw // if lines were inserted or deleted - || (wp->w_match_head != NULL - && buf->b_mod_xlines != 0))))) - || lnum == wp->w_cursorline - || lnum == wp->w_last_cursorline) { + || (wp->w_match_head != NULL && buf->b_mod_xlines != 0))))) + || lnum == wp->w_cursorline || lnum == wp->w_last_cursorline) { if (lnum == mod_top) { top_to_mod = false; } @@ -2104,9 +2074,7 @@ static void win_update(win_T *wp) // Don't do this when the change continues until the end. // Don't scroll when dollar_vcol >= 0, keep the "$". // Don't scroll when redrawing the top, scrolled already above. - if (lnum == mod_top - && mod_bot != MAXLNUM - && !(dollar_vcol >= 0 && mod_bot == mod_top + 1) + if (lnum == mod_top && mod_bot != MAXLNUM && !(dollar_vcol >= 0 && mod_bot == mod_top + 1) && row >= top_end) { int old_rows = 0; linenr_T l; @@ -2118,18 +2086,15 @@ static void win_update(win_T *wp) for (i = idx; i < wp->w_lines_valid; i++) { // Only valid lines have a meaningful wl_lnum. Invalid // lines are part of the changed area. - if (wp->w_lines[i].wl_valid - && wp->w_lines[i].wl_lnum == mod_bot) { + if (wp->w_lines[i].wl_valid && wp->w_lines[i].wl_lnum == mod_bot) { break; } old_rows += wp->w_lines[i].wl_size; - if (wp->w_lines[i].wl_valid - && wp->w_lines[i].wl_lastlnum + 1 == mod_bot) { + if (wp->w_lines[i].wl_valid && wp->w_lines[i].wl_lastlnum + 1 == mod_bot) { // Must have found the last valid entry above mod_bot. // Add following invalid entries. i++; - while (i < wp->w_lines_valid - && !wp->w_lines[i].wl_valid) { + while (i < wp->w_lines_valid && !wp->w_lines[i].wl_valid) { old_rows += wp->w_lines[i++].wl_size; } break; @@ -2212,8 +2177,7 @@ static void win_update(win_T *wp) } wp->w_lines[j] = wp->w_lines[i]; // stop at a line that won't fit - if (x + (int)wp->w_lines[j].wl_size - > wp->w_grid.rows) { + if (x + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) { wp->w_lines_valid = j + 1; break; } @@ -2223,8 +2187,8 @@ static void win_update(win_T *wp) if (bot_start > x) { bot_start = x; } - } else { // j > i - // move entries in w_lines[] downwards + } else { // j > i + // move entries in w_lines[] downwards j -= i; wp->w_lines_valid += (linenr_T)j; if (wp->w_lines_valid > wp->w_grid.rows) { @@ -2249,25 +2213,20 @@ static void win_update(win_T *wp) // When lines are folded, display one line for all of them. // Otherwise, display normally (can be several display lines when // 'wrap' is on). - foldinfo_T foldinfo = wp->w_p_cul && lnum == wp->w_cursor.lnum - ? cursorline_fi : fold_info(wp, lnum); - - if (foldinfo.fi_lines == 0 - && idx < wp->w_lines_valid - && wp->w_lines[idx].wl_valid - && wp->w_lines[idx].wl_lnum == lnum - && lnum > wp->w_topline + foldinfo_T foldinfo + = wp->w_p_cul && lnum == wp->w_cursor.lnum ? cursorline_fi : fold_info(wp, lnum); + + if (foldinfo.fi_lines == 0 && idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid + && wp->w_lines[idx].wl_lnum == lnum && lnum > wp->w_topline && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE)) - && srow + wp->w_lines[idx].wl_size > wp->w_grid.rows - && win_get_fill(wp, lnum) == 0) { + && srow + wp->w_lines[idx].wl_size > wp->w_grid.rows && win_get_fill(wp, lnum) == 0) { // This line is not going to fit. Don't draw anything here, // will draw "@ " lines below. row = wp->w_grid.rows + 1; } else { prepare_search_hl(wp, &screen_search_hl, lnum); // Let the syntax stuff know we skipped a few lines. - if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum - && syntax_present(wp)) { + if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum && syntax_present(wp)) { syntax_end_parsing(wp, syntax_last_parsed + 1); } @@ -2275,8 +2234,8 @@ static void win_update(win_T *wp) // Display one line spellvars_T zero_spv = { 0 }; - row = win_line(wp, lnum, srow, wp->w_grid.rows, 0, - display_buf_line ? &spv : &zero_spv, foldinfo); + row = win_line(wp, lnum, srow, wp->w_grid.rows, 0, display_buf_line ? &spv : &zero_spv, + foldinfo); if (display_buf_line) { syntax_last_parsed = lnum; @@ -2299,7 +2258,7 @@ static void win_update(win_T *wp) wp->w_lines[idx].wl_lnum = lnum; wp->w_lines[idx].wl_valid = true; - if (row > wp->w_grid.rows) { // past end of grid + if (row > wp->w_grid.rows) { // past end of grid // we may need the size of that too long line later on if (dollar_vcol == -1) { wp->w_lines[idx].wl_size = (uint16_t)plines_win(wp, lnum, true); @@ -2319,8 +2278,8 @@ static void win_update(win_T *wp) // the text doesn't need to be redrawn, but the number column does. if ((wp->w_p_nu && mod_top != 0 && lnum >= mod_bot && buf->b_mod_xlines != 0) || (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum)) { - foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum - ? cursorline_fi : fold_info(wp, lnum); + foldinfo_T info + = wp->w_p_cul && lnum == wp->w_cursor.lnum ? cursorline_fi : fold_info(wp, lnum); win_line(wp, lnum, srow, wp->w_grid.rows, wp->w_lines[idx].wl_size, &spv, info); } @@ -2336,7 +2295,7 @@ static void win_update(win_T *wp) // 'statuscolumn' width has changed or errored, start from the top. if (wp->w_redr_statuscol) { -redr_statuscol: + redr_statuscol: wp->w_redr_statuscol = false; idx = 0; row = 0; @@ -2386,7 +2345,7 @@ redr_statuscol: // Window ends in filler lines. wp->w_botline = lnum; wp->w_filler_rows = wp->w_grid.rows - srow; - } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate" + } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate" // Last line isn't finished: Display "@@@" in the last screen line. grid_line_start(&wp->w_grid, wp->w_grid.rows - 1); grid_line_fill(0, MIN(wp->w_grid.cols, 3), wp->w_p_fcs_chars.lastline, at_attr); @@ -2394,19 +2353,18 @@ redr_statuscol: grid_line_flush(); set_empty_rows(wp, srow); wp->w_botline = lnum; - } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline" + } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline" // Last line isn't finished: Display "@@@" at the end. // If this would split a doublewidth char in two, we need to display "@@@@" instead grid_line_start(&wp->w_grid, wp->w_grid.rows - 1); int width = grid_line_getchar(MAX(wp->w_grid.cols - 3, 0), NULL) == NUL ? 4 : 3; - grid_line_fill(MAX(wp->w_grid.cols - width, 0), wp->w_grid.cols, - wp->w_p_fcs_chars.lastline, at_attr); + grid_line_fill(MAX(wp->w_grid.cols - width, 0), wp->w_grid.cols, wp->w_p_fcs_chars.lastline, + at_attr); grid_line_flush(); set_empty_rows(wp, srow); wp->w_botline = lnum; } else { - win_draw_end(wp, wp->w_p_fcs_chars.lastline, true, srow, - wp->w_grid.rows, HLF_AT); + win_draw_end(wp, wp->w_p_fcs_chars.lastline, true, srow, wp->w_grid.rows, HLF_AT); set_empty_rows(wp, srow); wp->w_botline = lnum; } @@ -2442,9 +2400,7 @@ redr_statuscol: lastline = 0; } - win_draw_end(wp, wp->w_p_fcs_chars.eob, false, MAX(lastline, row), - wp->w_grid.rows, - HLF_EOB); + win_draw_end(wp, wp->w_p_fcs_chars.eob, false, MAX(lastline, row), wp->w_grid.rows, HLF_EOB); set_empty_rows(wp, row); } @@ -2462,9 +2418,9 @@ redr_statuscol: // Send win_extmarks if needed for (size_t n = 0; n < kv_size(win_extmark_arr); n++) { - ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle, - kv_A(win_extmark_arr, n).ns_id, (Integer)kv_A(win_extmark_arr, n).mark_id, - kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col); + ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle, kv_A(win_extmark_arr, n).ns_id, + (Integer)kv_A(win_extmark_arr, n).mark_id, kv_A(win_extmark_arr, n).win_row, + kv_A(win_extmark_arr, n).win_col); } if (dollar_vcol == -1) { @@ -2522,11 +2478,9 @@ void win_scroll_lines(win_T *wp, int row, int line_count) } if (line_count < 0) { - grid_del_lines(&wp->w_grid, row, -line_count, - wp->w_grid.rows, 0, wp->w_grid.cols); + grid_del_lines(&wp->w_grid, row, -line_count, wp->w_grid.rows, 0, wp->w_grid.cols); } else { - grid_ins_lines(&wp->w_grid, row, line_count, - wp->w_grid.rows, 0, wp->w_grid.cols); + grid_ins_lines(&wp->w_grid, row, line_count, wp->w_grid.rows, 0, wp->w_grid.cols); } } @@ -2645,7 +2599,7 @@ void redraw_later(win_T *wp, int type) if (type >= UPD_NOT_VALID) { wp->w_lines_valid = 0; } - if (must_redraw < type) { // must_redraw is the maximum of all windows + if (must_redraw < type) { // must_redraw is the maximum of all windows must_redraw = type; } } @@ -2705,11 +2659,10 @@ void redraw_buf_line_later(buf_T *buf, linenr_T line, bool force) } } -void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline) +void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf - && lastline >= wp->w_topline && firstline < wp->w_botline) { + if (wp->w_buffer == buf && lastline >= wp->w_topline && firstline < wp->w_botline) { if (wp->w_redraw_top == 0 || wp->w_redraw_top > firstline) { wp->w_redraw_top = firstline; } @@ -2726,9 +2679,7 @@ void redraw_buf_status_later(buf_T *buf) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer == buf - && (wp->w_status_height - || (wp == curwin && global_stl_height()) - || wp->w_winbar_height)) { + && (wp->w_status_height || (wp == curwin && global_stl_height()) || wp->w_winbar_height)) { wp->w_redr_status = true; set_must_redraw(UPD_VALID); } @@ -2741,8 +2692,7 @@ void status_redraw_all(void) bool is_stl_global = global_stl_height() != 0; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if ((!is_stl_global && wp->w_status_height) || wp == curwin - || wp->w_winbar_height) { + if ((!is_stl_global && wp->w_status_height) || wp == curwin || wp->w_winbar_height) { wp->w_redr_status = true; redraw_later(wp, UPD_VALID); } @@ -2761,8 +2711,9 @@ void status_redraw_buf(buf_T *buf) bool is_stl_global = global_stl_height() != 0; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf && ((!is_stl_global && wp->w_status_height) - || (is_stl_global && wp == curwin) || wp->w_winbar_height)) { + if (wp->w_buffer == buf + && ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin) + || wp->w_winbar_height)) { wp->w_redr_status = true; redraw_later(wp, UPD_VALID); } @@ -2792,8 +2743,7 @@ void redraw_statuslines(void) } /// Redraw all status lines at the bottom of frame "frp". -void win_redraw_last_status(const frame_T *frp) - FUNC_ATTR_NONNULL_ARG(1) +void win_redraw_last_status(const frame_T *frp) FUNC_ATTR_NONNULL_ARG(1) { if (frp->fr_layout == FR_LEAF) { frp->fr_win->w_redr_status = true; @@ -2817,11 +2767,9 @@ void win_redraw_last_status(const frame_T *frp) /// Used to remove the "$" from a change command. /// Note that when also inserting/deleting lines w_redraw_top and w_redraw_bot /// may become invalid and the whole window will have to be redrawn. -void redrawWinline(win_T *wp, linenr_T lnum) - FUNC_ATTR_NONNULL_ALL +void redrawWinline(win_T *wp, linenr_T lnum) FUNC_ATTR_NONNULL_ALL { - if (lnum >= wp->w_topline - && lnum < wp->w_botline) { + if (lnum >= wp->w_topline && lnum < wp->w_botline) { if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) { wp->w_redraw_top = lnum; } @@ -2834,8 +2782,7 @@ void redrawWinline(win_T *wp, linenr_T lnum) /// Return true if the cursor line in window "wp" may be concealed, according /// to the 'concealcursor' option. -bool conceal_cursor_line(const win_T *wp) - FUNC_ATTR_NONNULL_ALL +bool conceal_cursor_line(const win_T *wp) FUNC_ATTR_NONNULL_ALL { int c; @@ -2859,8 +2806,7 @@ bool conceal_cursor_line(const win_T *wp) /// Whether cursorline is drawn in a special way /// /// If true, both old and new cursorline will need to be redrawn when moving cursor within windows. -bool win_cursorline_standout(const win_T *wp) - FUNC_ATTR_NONNULL_ALL +bool win_cursorline_standout(const win_T *wp) FUNC_ATTR_NONNULL_ALL { return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp)); } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3d224bfa0f..7e3060100c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3186,12 +3186,10 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // Register contents: @r. case '@': (*arg)++; + int regname = mb_cptr2char_adv((const char**) arg); if (evaluate) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = get_reg_contents(**arg, kGRegExprSrc); - } - if (**arg != NUL) { - (*arg)++; + rettv->vval.v_string = get_reg_contents(regname, kGRegExprSrc); } break; @@ -4511,7 +4509,7 @@ bool garbage_collect(bool testing) // registers (ShaDa additional data) { - const void *reg_iter = NULL; + iter_register_T reg_iter = ITER_REGISTER_NULL; do { yankreg_T reg; char name = NUL; @@ -4520,7 +4518,7 @@ bool garbage_collect(bool testing) if (name != NUL) { ABORTING(set_ref_dict)(reg.additional_data, copyID); } - } while (reg_iter != NULL); + } while (reg_iter != ITER_REGISTER_NULL); } // global marks (ShaDa additional data) diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 2f9472f158..d7237d6443 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3367,6 +3367,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) "xattr", #endif "nvim", + "rneovim", }; // XXX: eval_has_provider() may shell out :( diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index e016c65d90..91ac60d8ea 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -557,7 +557,7 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon) static const char *skip_var_one(const char *arg) { if (*arg == '@' && arg[1] != NUL) { - return arg + 2; + return arg + 1 + utfc_ptr2len(arg + 1); } return find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); @@ -863,16 +863,20 @@ static char *ex_let_register(char *arg, typval_T *const tv, const bool is_const, char *arg_end = NULL; arg++; + + int regname = utf_ptr2char(arg); + int mblen = utf_ptr2len(arg); + if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { semsg(_(e_letwrong), op); } else if (endchars != NULL - && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + 1))) == NULL) { + && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + mblen))) == NULL) { emsg(_(e_letunexp)); } else { char *ptofree = NULL; const char *p = tv_get_string_chk(tv); if (p != NULL && op != NULL && *op == '.') { - char *s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); + char *s = get_reg_contents(*arg == '@' ? '"' : regname, kGRegExprSrc); if (s != NULL) { ptofree = concat_str(s, p); p = ptofree; @@ -880,8 +884,9 @@ static char *ex_let_register(char *arg, typval_T *const tv, const bool is_const, } } if (p != NULL) { - write_reg_contents(*arg == '@' ? '"' : *arg, p, (ssize_t)strlen(p), false); - arg_end = arg + 1; + write_reg_contents(*arg == '@' ? '"' : regname, + p, (ssize_t)strlen(p), false); + arg_end = arg + mblen; } xfree(ptofree); } diff --git a/src/nvim/map.c b/src/nvim/map.c index be6bf58daa..d7d1a00158 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -118,6 +118,9 @@ void mh_clear(MapHash *h) #define VAL_NAME(x) quasiquote(x, ptr_t) #include "nvim/map_value_impl.c.h" #undef VAL_NAME +#define VAL_NAME(x) quasiquote(x, int) +#include "nvim/map_value_impl.c.h" +#undef VAL_NAME #undef KEY_NAME #define KEY_NAME(x) x##cstr_t diff --git a/src/nvim/map_defs.h b/src/nvim/map_defs.h index 836b1447c2..36c851497d 100644 --- a/src/nvim/map_defs.h +++ b/src/nvim/map_defs.h @@ -153,6 +153,7 @@ KEY_DECLS(HlEntry) KEY_DECLS(ColorKey) MAP_DECLS(int, int) +MAP_DECLS(ptr_t, int) MAP_DECLS(int, ptr_t) MAP_DECLS(cstr_t, ptr_t) MAP_DECLS(cstr_t, int) diff --git a/src/nvim/ops.c b/src/nvim/ops.c index fccc663e1a..9b969a2337 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -26,6 +26,7 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/userfunc.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_getln.h" @@ -73,8 +74,24 @@ #include "nvim/undo.h" #include "nvim/vim_defs.h" #include "nvim/window.h" +#include "nvim/yankmap.h" -static yankreg_T y_regs[NUM_REGISTERS] = { 0 }; +struct yank_registers { + yankmap_T inner; +}; + +yank_registers_T y_regs; + +static yankreg_T *get_reg(yank_registers_T *regs, int idx) +{ + return yankmap_get(®s->inner, idx); + +} + +static yankreg_T *get_global_reg(int idx) +{ + return get_reg(&y_regs, idx); +} static yankreg_T *y_previous = NULL; // ptr to last written yankreg @@ -759,6 +776,24 @@ char *get_expr_line_src(void) return xstrdup(expr_line); } + +int get_userreg(int regname) +{ + if ((regname >= 'a' && regname <= 'z') + || (regname >= 'A' && regname <= 'Z') + || (regname >= '0' && regname <= '9') + || (regname <= 127 && strchr("\"-:.%#=*+_/", regname)) + || regname == Ctrl_F + || regname == Ctrl_P + || regname == Ctrl_W + || regname == Ctrl_A + || (regname + USER_REGISTERS_START) < regname) { + return -1; + } + + return regname + USER_REGISTERS_START; +} + /// @return whether `regname` is a valid name of a yank register. /// /// @note: There is no check for 0 (default register), caller should do this. @@ -775,12 +810,156 @@ bool valid_yank_reg(int regname, bool writing) || regname == '-' || regname == '_' || regname == '*' - || regname == '+') { + || regname == '+' + || get_userreg(regname) != -1) { return true; } return false; } +static int call_userreg_put(const char* urf, int regname, typval_T* out) +{ + char regname_str[5]; + int len; + + len = utf_char2len(regname); + regname_str[len] = 0; + utf_char2bytes(regname, regname_str); + + typval_T args[3]; + args[0].v_type = VAR_STRING; + args[1].v_type = VAR_STRING; + args[2].v_type = VAR_NUMBER; + + args[0].vval.v_string = "put"; + args[1].vval.v_string = regname_str; + args[2].vval.v_number = 0; + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.fe_evaluate = true; + + return call_func( + urf, + -1, + out, + /* argcount_in = */ 3, + args, + &funcexe); +} + +// Converts a typval returned from the userregfunction to a register. +static void typval_to_yankreg(yankreg_T* yankreg, typval_T* val) +{ + if (!yankreg || !val) { + return; + } + + char* type; + dict_T* dict; + typval_T tv; + size_t i; + size_t sz; + + free_register(yankreg); + memset(yankreg, 0, sizeof(*yankreg)); + + switch (val->v_type) { + + case VAR_DICT: + dict = val->vval.v_dict; + type = tv_dict_get_string(dict, "type", false); + + if (!strcmp(type, "block")) { + yankreg->y_width = (int) tv_dict_get_number(dict, "width"); + yankreg->y_type = kMTBlockWise; + } else if (!strcmp(type, "line")) { + yankreg->y_type = kMTLineWise; + } else { + yankreg->y_type = kMTCharWise; + } + + if (tv_dict_get_tv(dict, "lines", &tv) == OK) { + if (tv.v_type == VAR_STRING) { + yankreg->y_array = (char**) xcalloc(sizeof(char*), 1); + yankreg->y_array[0] = strdup(tv.vval.v_string); + } else if (tv.v_type == VAR_LIST) { + yankreg->y_array = + (char**) xcalloc(sizeof(char*), (size_t) tv_list_len(tv.vval.v_list)); + + i = 0; + TV_LIST_ITER_CONST(tv.vval.v_list, li, { + if (li->li_tv.v_type == VAR_STRING) { + yankreg->y_array[i] = strdup(tv_get_string(&li->li_tv)); + } else { + yankreg->y_array[i] = NULL; + } + ++ i; + }); + + yankreg->y_size = i; + } + } else { + yankreg->y_array = NULL; + } + + if (tv_dict_get_tv(dict, "additional_data", &tv) == OK) { + if (tv.v_type == VAR_DICT) { + yankreg->additional_data = tv.vval.v_dict; + } + } + break; + + case VAR_LIST: + yankreg->y_type = kMTLineWise; + sz = (size_t) tv_list_len(val->vval.v_list); + yankreg->y_array = (char**) xcalloc(sizeof(char*), sz); + yankreg->y_size = sz; + i = 0; + TV_LIST_ITER_CONST(val->vval.v_list, li, { + yankreg->y_array[i] = strdup(tv_get_string(&li->li_tv)); + i ++; + }); + break; + + default: + yankreg->y_type = kMTCharWise; + yankreg->y_size = 1; + + if (val->vval.v_string) { + yankreg->y_array = (char**) xcalloc(sizeof(char*), 1); + yankreg->y_array[0] = strdup(tv_get_string(val)); + } else { + yankreg->y_array = NULL; + } + + break; + + } + + yankreg->timestamp = os_time(); +} + +static void copy_userreg(yankreg_T* into, int regname) +{ + if (!into) { + return; + } + + if (!curbuf->b_p_urf || strlen(curbuf->b_p_urf) == 0) { + return; + } + + typval_T* ret = xmalloc(sizeof(typval_T)); + + if (call_userreg_put(curbuf->b_p_urf, regname, ret) == FAIL) { + return; + } + + typval_to_yankreg(into, ret); + + tv_free(ret); +} + /// @return yankreg_T to use, according to the value of `regname`. /// Cannot handle the '_' (black hole) register. /// Must only be called with a valid register name! @@ -824,7 +1003,11 @@ yankreg_T *get_yank_register(int regname, int mode) if (i == -1) { i = 0; } - reg = &y_regs[i]; + reg = get_global_reg(i); + if (get_userreg(regname) != -1 && mode != YREG_YANK) { + // If the mode is not yank, copy the userreg data to the reg. + copy_userreg(reg, regname); + } if (mode == YREG_YANK) { // remember the written register for unnamed paste @@ -850,7 +1033,7 @@ yankreg_T *copy_register(int name) if (copy->y_size == 0) { copy->y_array = NULL; } else { - copy->y_array = xcalloc(copy->y_size, sizeof(char *)); + copy->y_array = (char**) xcalloc(copy->y_size, sizeof(char *)); for (size_t i = 0; i < copy->y_size; i++) { copy->y_array[i] = xstrdup(reg->y_array[i]); } @@ -881,8 +1064,7 @@ int do_record(int c) if (reg_recording == 0) { // start recording - // registers 0-9, a-z and " are allowed - if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) { + if (c < 0) { retval = FAIL; } else { reg_recording = c; @@ -907,9 +1089,10 @@ int do_record(int c) } // Name of requested register, or empty string for unnamed operation. - char buf[NUMBUFLEN + 2]; - buf[0] = (char)regname; - buf[1] = NUL; + char buf[NUMBUFLEN + 5]; + int len = utf_char2len(regname); + utf_char2bytes(regname, buf); + buf[len] = NUL; tv_dict_add_str(dict, S_LEN("regname"), buf); tv_dict_set_keys_readonly(dict); @@ -986,6 +1169,9 @@ static int stuff_yank(int regname, char *p) reg->y_type = kMTCharWise; } reg->timestamp = os_time(); + if (get_userreg(regname) != -1) { + return eval_yank_userreg(curbuf->b_p_urf, regname, reg); + } return OK; } @@ -1294,6 +1480,90 @@ int insert_reg(int regname, bool literally_arg) return retval; } +/// Converts a yankreg to a dict which can be used as an argument to the +// userregfunc. +static dict_T* yankreg_to_dict(yankreg_T* yankreg) { + dict_T *const dict = tv_dict_alloc(); + dict->dv_refcount = 1; + tv_dict_add_nr(dict, S_LEN("width"), yankreg->y_width); + + const char* type; + + switch(yankreg->y_type) { + case kMTLineWise: + type = "line"; + break; + case kMTCharWise: + type = "char"; + break; + case kMTBlockWise: + type = "block"; + break; + default: + type = "unknown"; + } + + tv_dict_add_str(dict, S_LEN("type"), type); + if (yankreg->additional_data) { + tv_dict_add_dict(dict, S_LEN("additional_data"), yankreg->additional_data); + } + + list_T *const lines = tv_list_alloc((long)yankreg->y_size); + + size_t i; + for (i = 0; i < yankreg->y_size; ++ i) { + tv_list_append_string( + lines, yankreg->y_array[i], (long)strlen(yankreg->y_array[i])); + } + + tv_dict_add_list(dict, S_LEN("lines"), lines); + + return dict; +} + +/* + * Executes the yank() function on a user-defined register to set the contents + * of that register. + */ +static int eval_yank_userreg(const char *ufn, int regname, yankreg_T *reg) +{ + if (!reg) + return -1; + + int ret, len; + char regname_str[5]; + + len = (*utf_char2len)(regname); + regname_str[len] = 0; + utf_char2bytes(regname, regname_str); + + typval_T args[4]; + args[0].v_type = VAR_STRING; + args[1].v_type = VAR_STRING; + args[2].v_type = VAR_DICT; + args[3].v_type = VAR_UNKNOWN; + + args[0].vval.v_string = "yank"; + args[1].vval.v_string = regname_str; + args[2].vval.v_dict = yankreg_to_dict(reg); + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.fe_evaluate = true; + + typval_T* out = xmalloc(sizeof(typval_T)); + return call_func( + ufn, + -1, + out, + /* argcount_in = */ 3, + args, + &funcexe + ); + + tv_free(out); + return ret; +} + /// If "regname" is a special register, return true and store a pointer to its /// value in "argp". /// @@ -1377,6 +1647,9 @@ bool get_spec_reg(int regname, char **argp, bool *allocated, bool errmsg) case '_': // black hole: always empty *argp = ""; return true; + + default: + break; } return false; @@ -1423,14 +1696,14 @@ bool cmdline_paste_reg(int regname, bool literally_arg, bool remcr) /// Shift the delete registers: "9 is cleared, "8 becomes "9, etc. static void shift_delete_registers(bool y_append) { - free_register(&y_regs[9]); // free register "9 + free_register(get_global_reg(9)); // free register "9 for (int n = 9; n > 1; n--) { - y_regs[n] = y_regs[n - 1]; + *get_global_reg(n) = *get_global_reg(n - 1); } if (!y_append) { - y_previous = &y_regs[1]; + y_previous = get_global_reg(1); } - y_regs[1].y_array = NULL; // set register "1 to empty + get_global_reg(1)->y_array = NULL; // set register "1 to empty } /// Handle a delete operation. @@ -1523,7 +1796,7 @@ int op_delete(oparg_T *oap) if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) { shift_delete_registers(is_append_register(oap->regname)); - reg = &y_regs[1]; + reg = get_global_reg(1); op_yank_reg(oap, false, reg, false); did_yank = true; } @@ -2526,11 +2799,20 @@ int op_change(oparg_T *oap) return retval; } + +/* + * set all the yank registers to empty (called from main()) + */ +void init_yank(void) +{ + init_yankmap(&y_regs.inner); +} + #if defined(EXITFREE) void clear_registers(void) { for (int i = 0; i < NUM_REGISTERS; i++) { - free_register(&y_regs[i]); + free_register(get_global_reg(i)); } } @@ -2576,6 +2858,14 @@ bool op_yank(oparg_T *oap, bool message) yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK); op_yank_reg(oap, message, reg, is_append_register(oap->regname)); + + if (get_userreg(oap->regname) != -1) { + if (eval_yank_userreg(curbuf->b_p_urf, oap->regname, reg) == -1) { + beep_flush(); + return false; + } + } + set_clipboard(oap->regname, reg); do_autocmd_textyankpost(oap, reg); @@ -2699,7 +2989,11 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) if (oap->regname == NUL) { *namebuf = NUL; } else { - vim_snprintf(namebuf, sizeof(namebuf), _(" into \"%c"), oap->regname); + char buf[5]; + int len = (*utf_char2len) (oap->regname); + utf_char2bytes(oap->regname, buf); + buf[len] = 0; + vim_snprintf(namebuf, sizeof(namebuf), _(" into \"%s"), buf); } // redisplay now, so message is not deleted @@ -2769,6 +3063,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) FUNC_ATTR_NONNULL_ALL { static bool recursive = false; + int len; if (recursive || !has_event(EVENT_TEXTYANKPOST)) { // No autocommand was defined, or we yanked from this autocommand. @@ -2790,13 +3085,14 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) tv_dict_add_list(dict, S_LEN("regcontents"), list); // Register type. - char buf[NUMBUFLEN + 2]; + char buf[NUMBUFLEN + 6]; format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); tv_dict_add_str(dict, S_LEN("regtype"), buf); // Name of requested register, or empty string for unnamed operation. - buf[0] = (char)oap->regname; - buf[1] = NUL; + len = utf_char2len(oap->regname); + buf[len] = 0; + utf_char2bytes(oap->regname, buf); tv_dict_add_str(dict, S_LEN("regname"), buf); // Motion type: inclusive or exclusive. @@ -3051,6 +3347,10 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) reg = get_yank_register(regname, YREG_PASTE); } + if (get_userreg(regname) != -1) { + copy_userreg(reg, regname); + } + y_type = reg->y_type; y_width = reg->y_width; y_size = reg->y_size; @@ -3728,7 +4028,7 @@ int get_register_name(int num) /// @return the index of the register "" points to. int get_unname_register(void) { - return y_previous == NULL ? -1 : (int)(y_previous - &y_regs[0]); + return yankmap_find(&y_regs.inner, y_previous); } /// ":dis" and ":registers": Display the contents of the yank registers. @@ -3765,10 +4065,10 @@ void ex_display(exarg_T *eap) if (y_previous != NULL) { yb = y_previous; } else { - yb = &(y_regs[0]); + yb = get_global_reg(0); } } else { - yb = &(y_regs[i]); + yb = get_global_reg(i); } get_clipboard(name, &yb, true); @@ -5020,6 +5320,10 @@ static yankreg_T *init_write_reg(int name, yankreg_T **old_y_previous, bool must static void finish_write_reg(int name, yankreg_T *reg, yankreg_T *old_y_previous) { + if (get_userreg(name) != -1) { + eval_yank_userreg(curbuf->b_p_urf, name, reg); + } + // Send text of clipboard register to the clipboard. set_clipboard(name, reg); @@ -6472,7 +6776,7 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) } if (explicit_cb_reg) { - target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER]; + target = get_global_reg(*name == '*' ? STAR_REGISTER : PLUS_REGISTER); if (writing && (cb_flags & (*name == '*' ? CB_UNNAMED : CB_UNNAMEDPLUS))) { clipboard_needs_update = false; } @@ -6489,10 +6793,10 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) if (cb_flags & CB_UNNAMEDPLUS) { *name = (cb_flags & CB_UNNAMED && writing) ? '"' : '+'; - target = &y_regs[PLUS_REGISTER]; + target = get_global_reg(PLUS_REGISTER); } else { *name = '*'; - target = &y_regs[STAR_REGISTER]; + target = get_global_reg(STAR_REGISTER); } goto end; } @@ -6805,11 +7109,11 @@ static inline bool reg_empty(const yankreg_T *const reg) /// Iterate over global registers. /// /// @see op_register_iter -const void *op_global_reg_iter(const void *const iter, char *const name, yankreg_T *const reg, - bool *is_unnamed) +iter_register_T op_global_reg_iter(iter_register_T iter, char *const name, + yankreg_T *const reg, bool *is_unnamed) FUNC_ATTR_NONNULL_ARG(2, 3, 4) FUNC_ATTR_WARN_UNUSED_RESULT { - return op_reg_iter(iter, y_regs, name, reg, is_unnamed); + return op_reg_iter(iter, &y_regs, name, reg, is_unnamed); } /// Iterate over registers `regs`. @@ -6821,30 +7125,33 @@ const void *op_global_reg_iter(const void *const iter, char *const name, yankreg /// /// @return Pointer that must be passed to next `op_register_iter` call or /// NULL if iteration is over. -const void *op_reg_iter(const void *const iter, const yankreg_T *const regs, char *const name, - yankreg_T *const reg, bool *is_unnamed) +iter_register_T op_reg_iter(iter_register_T iter, yank_registers_T *regs, + char *const name, yankreg_T *const reg, + bool *is_unnamed) FUNC_ATTR_NONNULL_ARG(3, 4, 5) FUNC_ATTR_WARN_UNUSED_RESULT { *name = NUL; - const yankreg_T *iter_reg = (iter == NULL - ? &(regs[0]) - : (const yankreg_T *const)iter); - while (iter_reg - &(regs[0]) < NUM_SAVED_REGISTERS && reg_empty(iter_reg)) { - iter_reg++; + int iter_idx = (int)(iter == ITER_REGISTER_NULL ? 0 : iter - 1); + + while (iter_idx < NUM_SAVED_REGISTERS && reg_empty(get_reg(regs, iter_idx))) { + ++iter_idx; } - if (iter_reg - &(regs[0]) == NUM_SAVED_REGISTERS || reg_empty(iter_reg)) { - return NULL; + + if (iter_idx >= NUM_SAVED_REGISTERS || reg_empty(get_reg(regs, iter_idx))) { + return ITER_REGISTER_NULL; } - int iter_off = (int)(iter_reg - &(regs[0])); - *name = (char)get_register_name(iter_off); - *reg = *iter_reg; - *is_unnamed = (iter_reg == y_previous); - while (++iter_reg - &(regs[0]) < NUM_SAVED_REGISTERS) { - if (!reg_empty(iter_reg)) { - return (void *)iter_reg; + + *reg = *get_reg(regs, iter_idx); + *name = (char) get_register_name(iter_idx); + *is_unnamed = (get_reg(regs, iter_idx) == y_previous); + + while (++iter_idx < NUM_SAVED_REGISTERS) { + if (!reg_empty(get_reg(regs, iter_idx))) { + return iter_idx + 1; } } - return NULL; + + return ITER_REGISTER_NULL; } /// Get a number of non-empty registers @@ -6852,8 +7159,8 @@ size_t op_reg_amount(void) FUNC_ATTR_WARN_UNUSED_RESULT { size_t ret = 0; - for (size_t i = 0; i < NUM_SAVED_REGISTERS; i++) { - if (!reg_empty(y_regs + i)) { + for (int i = 0; i < NUM_SAVED_REGISTERS; i++) { + if (!reg_empty(get_global_reg(i))) { ret++; } } @@ -6873,11 +7180,11 @@ bool op_reg_set(const char name, const yankreg_T reg, bool is_unnamed) if (i == -1) { return false; } - free_register(&y_regs[i]); - y_regs[i] = reg; + free_register(get_global_reg(i)); + *get_global_reg(i) = reg; if (is_unnamed) { - y_previous = &y_regs[i]; + y_previous = get_global_reg(i); } return true; } @@ -6893,7 +7200,7 @@ const yankreg_T *op_reg_get(const char name) if (i == -1) { return NULL; } - return &y_regs[i]; + return get_global_reg(i); } /// Set the previous yank register @@ -6908,7 +7215,7 @@ bool op_reg_set_previous(const char name) return false; } - y_previous = &y_regs[i]; + y_previous = get_global_reg(i); return true; } diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 1a708fab03..643d2a2deb 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -38,6 +38,7 @@ typedef int (*Indenter)(void); /// flags for do_put() enum { + ITER_REGISTER_NULL = 0, PUT_FIXINDENT = 1, ///< make indent look nice PUT_CURSEND = 2, ///< leave cursor after end of new text PUT_CURSLINE = 4, ///< leave cursor on last line of new text @@ -61,6 +62,7 @@ enum { STAR_REGISTER = 37, PLUS_REGISTER = 38, NUM_REGISTERS = 39, + USER_REGISTERS_START = 39 }; /// Operator IDs; The order must correspond to opchars[] in ops.c! @@ -120,6 +122,8 @@ typedef enum { YREG_YANK, YREG_PUT, } yreg_mode_t; +/// Returns a reference to a user-defined register. +int get_userreg(int regname); static inline int op_reg_index(int regname) REAL_FATTR_CONST; @@ -144,13 +148,17 @@ static inline int op_reg_index(const int regname) } else if (regname == '+') { return PLUS_REGISTER; } else { - return -1; + return get_userreg(regname); } } +struct yank_registers; +typedef struct yank_registers yank_registers_T; + +typedef size_t iter_register_T; + static inline bool is_literal_register(int regname) REAL_FATTR_CONST; - /// @see get_yank_register /// @return true when register should be inserted literally /// (selection or clipboard) diff --git a/src/nvim/option.c b/src/nvim/option.c index 0ac65ed95d..d5df8385f8 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4700,6 +4700,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) return &(buf->b_p_cfu); case PV_OFU: return &(buf->b_p_ofu); + case PV_URF: + return &(buf->b_p_urf); case PV_EOF: return &(buf->b_p_eof); case PV_EOL: @@ -5127,6 +5129,8 @@ void buf_copy_options(buf_T *buf, int flags) set_buflocal_cfu_callback(buf); buf->b_p_ofu = xstrdup(p_ofu); COPY_OPT_SCTX(buf, BV_OFU); + buf->b_p_urf = xstrdup(p_urf); + COPY_OPT_SCTX(buf, BV_URF); set_buflocal_ofu_callback(buf); buf->b_p_tfu = xstrdup(p_tfu); COPY_OPT_SCTX(buf, BV_TFU); diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h index 175f2af896..69d8f0833d 100644 --- a/src/nvim/option_vars.h +++ b/src/nvim/option_vars.h @@ -728,6 +728,7 @@ EXTERN char *p_udir; ///< 'undodir' EXTERN int p_udf; ///< 'undofile' EXTERN OptInt p_ul; ///< 'undolevels' EXTERN OptInt p_ur; ///< 'undoreload' +EXTERN char* p_urf; ///< 'userregfunction' EXTERN OptInt p_uc; ///< 'updatecount' EXTERN OptInt p_ut; ///< 'updatetime' EXTERN char *p_shada; ///< 'shada' @@ -779,7 +780,7 @@ EXTERN int p_wa; ///< 'writeany' EXTERN int p_wb; ///< 'writebackup' EXTERN OptInt p_wd; ///< 'writedelay' EXTERN int p_cdh; ///< 'cdhome' - + /// // Value for b_p_ul indicating the global value must be used. #define NO_LOCAL_UNDOLEVEL (-123456) diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 72f9ff849d..632732d7b7 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -9237,6 +9237,64 @@ return { varname = 'p_ut', }, { + abbreviation='urf', + full_name='userregfunc', + desc= [=[ + This option specifies a function to be used to handle any registers + that Neovim does not natively handle. This option unlocks all + characters to be used as registers by the user. + + The 'userregfunc' function is called each time a user register is read + from or written to. + + The 'userregfunc' function must take the following parameters: + + {action} The action being done on this register (either 'yank' + or 'put' + + {register} The string holding the name of the register. This + is always a single character, though multi-byte + characters are allowed. + + {content} If the action is 'yank' this is the content being + yanked into the register. The content is a dictionary + with the following items: + + {lines} The lines being yanked, as a list. + + {type} The type of yank, either "line", "char", or + "block" + + {width} The width in case of "block" mode. + + {additional_data} Additional data. (can be returned in + put mode). + + In case the action is 'put', the 'userregfunc' function should return + the content to place in that location. The content can either be a + string, in which case "char" mode is inferred, or it can return a + dictionary of the same template that populates 'content'. + + A very simple example of a 'userregfunc' function that behaves exactly + like traditional registers would look like: > + + let s:contents = {} + function! MyUserregFunction(action, register, content) abort + if a:action == "put" + return get(s:contents, a:register, "") + else + let s:contents[a:register] = a:content + endif + endfunction + set userregfunc=MyUserregFunction +< + ]=], + short_desc=N_("Function used to define behavior of user-defined registers."), + type='string', scope={'buffer'}, + varname='p_urf', + defaults={if_true=""} + }, + { abbreviation = 'vsts', cb = 'did_set_varsofttabstop', defaults = { if_true = '' }, diff --git a/src/nvim/po/da.po b/src/nvim/po/da.po index 0345d3e243..ba0301589c 100644 --- a/src/nvim/po/da.po +++ b/src/nvim/po/da.po @@ -4049,7 +4049,7 @@ msgid "freeing %ld lines" msgstr "frigør %ld linjer" #, c-format -msgid " into \"%c" +msgid " into \"%s" msgstr " i \"%c" #, c-format diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po index d9058326d5..fae6fe2297 100644 --- a/src/nvim/po/fr.po +++ b/src/nvim/po/fr.po @@ -4313,7 +4313,7 @@ msgid "freeing %ld lines" msgstr "libration de %ld lignes" #, c-format -msgid " into \"%c" +msgid " into \"%s" msgstr " dans \"%c" #, c-format diff --git a/src/nvim/po/tr.po b/src/nvim/po/tr.po index f3c55fe9ab..08b97ec180 100644 --- a/src/nvim/po/tr.po +++ b/src/nvim/po/tr.po @@ -4208,7 +4208,7 @@ msgstr[0] "%<PRId64> satır değiştirildi" msgstr[1] "%<PRId64> satır değiştirildi" #, c-format -msgid " into \"%c" +msgid " into \"%s" msgstr " \"%c" #, c-format diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po index abc77071c4..e62de86b09 100644 --- a/src/nvim/po/uk.po +++ b/src/nvim/po/uk.po @@ -5375,7 +5375,7 @@ msgstr[1] "Змінено %<PRId64> рядки" msgstr[2] "Змінено %<PRId64> рядків" #, c-format -msgid " into \"%c" +msgid " into \"%s" msgstr " у \"%c" #, c-format diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 490ab2b050..0b148993f8 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -2382,7 +2382,7 @@ static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse, static inline void shada_initialize_registers(WriteMergerState *const wms, int max_reg_lines) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE { - const void *reg_iter = NULL; + iter_register_T reg_iter = ITER_REGISTER_NULL; const bool limit_reg_lines = max_reg_lines >= 0; do { yankreg_T reg; @@ -2413,7 +2413,7 @@ static inline void shada_initialize_registers(WriteMergerState *const wms, int m } } }; - } while (reg_iter != NULL); + } while (reg_iter != ITER_REGISTER_NULL); } /// Replace numbered mark in WriteMergerState diff --git a/src/nvim/yankmap.c b/src/nvim/yankmap.c new file mode 100644 index 0000000000..591bcffe33 --- /dev/null +++ b/src/nvim/yankmap.c @@ -0,0 +1,45 @@ +#include "nvim/yankmap.h" + +#include "nvim/memory.h" + +void init_yankmap(yankmap_T* map) +{ + memset(map, 0, sizeof(yankmap_T)); + + map->reg_to_yankreg = (Map(int, ptr_t))MAP_INIT; + map->yankreg_to_reg = (Map(ptr_t, int))MAP_INIT; + // map_init(int, ptr_t, &map->reg_to_yankreg); + // map_init(ptr_t, int, &map->yankreg_to_reg); +} + +yankreg_T* yankmap_get(yankmap_T* yankmap, int reg) +{ + bool is_new = false; + yankreg_T** ret + = (yankreg_T**)map_put_ref(int, ptr_t)(&yankmap->reg_to_yankreg, reg, NULL, &is_new); + + if (ret) { + if (is_new) { + *ret = xcalloc(sizeof(yankreg_T), 1); + } + + /* Add the back-reference */ + int* ref = map_put_ref(ptr_t, int)(&yankmap->yankreg_to_reg, *ret, NULL, NULL); + *ref = reg; + + return *ret; + } + + return NULL; +} + +int yankmap_find(yankmap_T* yankmap, yankreg_T* yankreg) +{ + int* ref = map_ref(ptr_t, int)(&yankmap->yankreg_to_reg, yankreg, NULL); + + if (ref) { + return *ref; + } + + return -1; +} diff --git a/src/nvim/yankmap.h b/src/nvim/yankmap.h new file mode 100644 index 0000000000..4468f3a016 --- /dev/null +++ b/src/nvim/yankmap.h @@ -0,0 +1,25 @@ +#ifndef YANK_TRIE_H_ +#define YANK_TRIE_H_ + +#include <stdbool.h> + +#include "nvim/map_defs.h" +#include "nvim/ops.h" + +typedef struct { + /* Register name to yank register. */ + Map(int, ptr_t) reg_to_yankreg; + + /* Yank register to register name. */ + Map(ptr_t, int) yankreg_to_reg; +} yankmap_T; + +void init_yankmap(yankmap_T* yankmap); + +yankreg_T* yankmap_get(yankmap_T* yankmap, int reg); + +yankreg_T* yankmap_put(yankmap_T* yankmap, int index); + +int yankmap_find(yankmap_T* yankmap, yankreg_T* yankreg); + +#endif |