aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer_defs.h1
-rw-r--r--src/nvim/drawscreen.c368
-rw-r--r--src/nvim/eval.c10
-rw-r--r--src/nvim/eval/funcs.c1
-rw-r--r--src/nvim/eval/vars.c15
-rw-r--r--src/nvim/map.c3
-rw-r--r--src/nvim/map_defs.h1
-rw-r--r--src/nvim/ops.c411
-rw-r--r--src/nvim/ops.h12
-rw-r--r--src/nvim/option.c4
-rw-r--r--src/nvim/option_vars.h3
-rw-r--r--src/nvim/options.lua58
-rw-r--r--src/nvim/po/da.po2
-rw-r--r--src/nvim/po/fr.po2
-rw-r--r--src/nvim/po/tr.po2
-rw-r--r--src/nvim/po/uk.po2
-rw-r--r--src/nvim/shada.c4
-rw-r--r--src/nvim/yankmap.c45
-rw-r--r--src/nvim/yankmap.h25
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(&regs->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