aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/options.c6
-rw-r--r--src/nvim/eval.c15
-rw-r--r--src/nvim/eval/funcs.c41
-rw-r--r--src/nvim/ex_docmd.c5
-rw-r--r--src/nvim/getchar.c3
-rw-r--r--src/nvim/indent.c6
-rw-r--r--src/nvim/syntax.c2
-rw-r--r--src/nvim/testdir/test_cmdline.vim8
-rw-r--r--src/nvim/testdir/test_lispwords.vim12
-rw-r--r--src/nvim/testdir/test_syntax.vim88
-rw-r--r--src/nvim/ui.c5
-rw-r--r--src/nvim/ui_compositor.c13
12 files changed, 169 insertions, 35 deletions
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 3067a6e6b4..19ce25f676 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -43,6 +43,9 @@ static int validate_option_value_args(Dict(option) *opts, int *scope, int *opt_t
if (opts->win.type == kObjectTypeInteger) {
*opt_type = SREQ_WIN;
*from = find_window_by_handle((int)opts->win.data.integer, err);
+ if (ERROR_SET(err)) {
+ return FAIL;
+ }
} else if (HAS_KEY(opts->win)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: win");
return FAIL;
@@ -52,6 +55,9 @@ static int validate_option_value_args(Dict(option) *opts, int *scope, int *opt_t
*scope = OPT_LOCAL;
*opt_type = SREQ_BUF;
*from = find_buffer_by_handle((int)opts->buf.data.integer, err);
+ if (ERROR_SET(err)) {
+ return FAIL;
+ }
} else if (HAS_KEY(opts->buf)) {
api_set_error(err, kErrorTypeValidation, "invalid value for key: buf");
return FAIL;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index a4f56b47e6..43d6ed558f 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -49,6 +49,7 @@
#include "nvim/sign.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
+#include "nvim/ui_compositor.h"
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/window.h"
@@ -6849,19 +6850,19 @@ void return_register(int regname, typval_T *rettv)
rettv->vval.v_string = xstrdup(buf);
}
-void screenchar_adjust_grid(ScreenGrid **grid, int *row, int *col)
+void screenchar_adjust(ScreenGrid **grid, int *row, int *col)
{
// TODO(bfredl): this is a hack for legacy tests which use screenchar()
// to check printed messages on the screen (but not floats etc
// as these are not legacy features). If the compositor is refactored to
// have its own buffer, this should just read from it instead.
msg_scroll_flush();
- if (msg_grid.chars && msg_grid.comp_index > 0 && *row >= msg_grid.comp_row
- && *row < (msg_grid.rows + msg_grid.comp_row)
- && *col < msg_grid.cols) {
- *grid = &msg_grid;
- *row -= msg_grid.comp_row;
- }
+
+ *grid = ui_comp_get_grid_at_coord(*row, *col);
+
+ // Make `row` and `col` relative to the grid
+ *row -= (*grid)->comp_row;
+ *col -= (*grid)->comp_col;
}
/// Set line or list of lines in buffer "buf".
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 2225076a0a..8e49d1b9a8 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -7766,6 +7766,9 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
break;
}
}
+
+ // clear the start flag to avoid getting stuck here
+ options &= ~SEARCH_START;
}
if (subpatnum != FAIL) {
@@ -8035,14 +8038,15 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int c;
+ ScreenGrid *grid;
int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
- if (row < 0 || row >= default_grid.rows
- || col < 0 || col >= default_grid.cols) {
+
+ screenchar_adjust(&grid, &row, &col);
+
+ if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
c = -1;
} else {
- ScreenGrid *grid = &default_grid;
- screenchar_adjust_grid(&grid, &row, &col);
c = grid->attrs[grid->line_offset[row] + col];
}
rettv->vval.v_number = c;
@@ -8053,14 +8057,15 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int c;
+ ScreenGrid *grid;
int row = tv_get_number_chk(&argvars[0], NULL) - 1;
int col = tv_get_number_chk(&argvars[1], NULL) - 1;
- if (row < 0 || row >= default_grid.rows
- || col < 0 || col >= default_grid.cols) {
+
+ screenchar_adjust(&grid, &row, &col);
+
+ if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
c = -1;
} else {
- ScreenGrid *grid = &default_grid;
- screenchar_adjust_grid(&grid, &row, &col);
c = utf_ptr2char((char *)grid->chars[grid->line_offset[row] + col]);
}
rettv->vval.v_number = c;
@@ -8069,15 +8074,16 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "screenchars()" function
static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
+ ScreenGrid *grid;
int row = tv_get_number_chk(&argvars[0], NULL) - 1;
int col = tv_get_number_chk(&argvars[1], NULL) - 1;
- if (row < 0 || row >= default_grid.rows
- || col < 0 || col >= default_grid.cols) {
+
+ screenchar_adjust(&grid, &row, &col);
+
+ if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
tv_list_alloc_ret(rettv, 0);
return;
}
- ScreenGrid *grid = &default_grid;
- screenchar_adjust_grid(&grid, &row, &col);
int pcc[MAX_MCO];
int c = utfc_ptr2char(grid->chars[grid->line_offset[row] + col], pcc);
int composing_len = 0;
@@ -8136,14 +8142,17 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_string = NULL;
rettv->v_type = VAR_STRING;
+
+ ScreenGrid *grid;
int row = tv_get_number_chk(&argvars[0], NULL) - 1;
int col = tv_get_number_chk(&argvars[1], NULL) - 1;
- if (row < 0 || row >= default_grid.rows
- || col < 0 || col >= default_grid.cols) {
+
+ screenchar_adjust(&grid, &row, &col);
+
+ if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
return;
}
- ScreenGrid *grid = &default_grid;
- screenchar_adjust_grid(&grid, &row, &col);
+
rettv->vval.v_string = (char *)vim_strsave(grid->chars[grid->line_offset[row] + col]);
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index a65e89a9f5..49db5c3716 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -2826,10 +2826,13 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent)
curwin->w_cursor.lnum = eap->line2;
// Don't leave the cursor on an illegal line or column, but do
- // accept zero as address, so 0;/PATTERN/ works correctly.
+ // accept zero as address, so 0;/PATTERN/ works correctly
+ // (where zero usually means to use the first line).
// Check the cursor position before returning.
if (eap->line2 > 0) {
check_cursor();
+ } else {
+ check_cursor_col();
}
need_check_cursor = true;
}
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 7f783fd72f..57d25e5c45 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2122,6 +2122,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
const bool save_may_garbage_collect = may_garbage_collect;
const int save_cursor_row = ui_current_row();
const int save_cursor_col = ui_current_col();
+ const handle_T save_cursor_grid = ui_cursor_grid();
const int prev_did_emsg = did_emsg;
vgetc_busy = 0;
@@ -2135,7 +2136,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// The mapping may do anything, but we expect it to take care of
// redrawing. Do put the cursor back where it was.
- ui_cursor_goto(save_cursor_row, save_cursor_col);
+ ui_grid_cursor_goto(save_cursor_grid, save_cursor_row, save_cursor_col);
ui_flush();
// If an error was displayed and the expression returns an empty
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 010d2fe869..d71b3adb5c 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -697,8 +697,10 @@ int get_lisp_indent(void)
&& lisp_match(that + 1)) {
amount += 2;
} else {
- that++;
- amount++;
+ if (*that != NUL) {
+ that++;
+ amount++;
+ }
firsttry = amount;
while (ascii_iswhite(*that)) {
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 47305b6709..0d43458b5b 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -2381,8 +2381,6 @@ static void update_si_attr(int idx)
} else {
sip->si_attr = CUR_STATE(idx - 1).si_attr;
sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
- sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
- sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
if (sip->si_cont_list == NULL) {
sip->si_flags |= HL_TRANS_CONT;
sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index b2c752376f..d26c80077d 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -636,6 +636,14 @@ func Test_illegal_address2()
call delete('Xtest.vim')
endfunc
+func Test_mark_from_line_zero()
+ " this was reading past the end of the first (empty) line
+ new
+ norm oxxxx
+ call assert_fails("0;'(", 'E20:')
+ bwipe!
+endfunc
+
func Test_cmdline_complete_wildoptions()
help
call feedkeys(":tag /\<c-a>\<c-b>\"\<cr>", 'tx')
diff --git a/src/nvim/testdir/test_lispwords.vim b/src/nvim/testdir/test_lispwords.vim
index ff710b2716..4144fb0521 100644
--- a/src/nvim/testdir/test_lispwords.vim
+++ b/src/nvim/testdir/test_lispwords.vim
@@ -1,4 +1,5 @@
-" Tests for 'lispwords' settings being global-local
+" Tests for 'lispwords' settings being global-local.
+" And other lisp indent stuff.
set nocompatible viminfo+=nviminfo
@@ -85,4 +86,13 @@ func Test_lisp_indent()
set nolisp
endfunc
+func Test_lisp_indent_works()
+ " This was reading beyond the end of the line
+ new
+ exe "norm a\tü(\<CR>="
+ set lisp
+ norm ==
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 6bef61ae8f..9f50b3c241 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -745,8 +745,9 @@ func Test_search_syntax_skip()
1
call search('VIM', 'w', '', 0, 'synIDattr(synID(line("."), col("."), 1), "name") =~? "comment"')
call assert_equal('Another Text for VIM', getline('.'))
+
1
- call search('VIM', 'w', '', 0, 'synIDattr(synID(line("."), col("."), 1), "name") !~? "string"')
+ call search('VIM', 'cw', '', 0, 'synIDattr(synID(line("."), col("."), 1), "name") !~? "string"')
call assert_equal(' let a = "VIM"', getline('.'))
" Skip argument using Lambda.
@@ -755,29 +756,106 @@ func Test_search_syntax_skip()
call assert_equal('Another Text for VIM', getline('.'))
1
- call search('VIM', 'w', '', 0, { -> synIDattr(synID(line("."), col("."), 1), "name") !~? "string"})
+ call search('VIM', 'cw', '', 0, { -> synIDattr(synID(line("."), col("."), 1), "name") !~? "string"})
call assert_equal(' let a = "VIM"', getline('.'))
" Skip argument using funcref.
func InComment()
return synIDattr(synID(line("."), col("."), 1), "name") =~? "comment"
endfunc
- func InString()
+ func NotInString()
return synIDattr(synID(line("."), col("."), 1), "name") !~? "string"
endfunc
+
1
call search('VIM', 'w', '', 0, function('InComment'))
call assert_equal('Another Text for VIM', getline('.'))
1
- call search('VIM', 'w', '', 0, function('InString'))
+ call search('VIM', 'cw', '', 0, function('NotInString'))
call assert_equal(' let a = "VIM"', getline('.'))
delfunc InComment
- delfunc InString
+ delfunc NotInString
bwipe!
endfunc
+func Test_syn_contained_transparent()
+ " Comments starting with "Regression:" show the result when the highlighting
+ " span of the containing item is assigned to the contained region.
+ syntax on
+
+ let l:case = "Transparent region contained in region"
+ new
+ syntax region X start=/\[/ end=/\]/ contained transparent
+ syntax region Y start=/(/ end=/)/ contains=X
+
+ call setline(1, "==(--[~~]--)==")
+ let l:expected = " YYYYYYYYYY "
+ eval AssertHighlightGroups(1, 1, l:expected, 1, l:case)
+ syntax clear Y X
+ bw!
+
+ let l:case = "Transparent region extends region"
+ new
+ syntax region X start=/\[/ end=/\]/ contained transparent
+ syntax region Y start=/(/ end=/)/ end=/e/ contains=X
+
+ call setline(1, "==(--[~~e~~]--)==")
+ let l:expected = " YYYYYYYYYYYYY "
+ " Regression: " YYYYYYY YYY "
+ eval AssertHighlightGroups(1, 1, l:expected, 1, l:case)
+ syntax clear Y X
+ bw!
+
+ let l:case = "Nested transparent regions extend region"
+ new
+ syntax region X start=/\[/ end=/\]/ contained transparent
+ syntax region Y start=/(/ end=/)/ end=/e/ contains=X
+
+ call setline(1, "==(--[~~e~~[~~e~~]~~e~~]--)==")
+ let l:expected = " YYYYYYYYYYYYYYYYYYYYYYYYY "
+ " Regression: " YYYYYYY YYYYYYYYY "
+ eval AssertHighlightGroups(1, 1, l:expected, 1, l:case)
+ syntax clear Y X
+ bw!
+
+ let l:case = "Transparent region contained in match"
+ new
+ syntax region X start=/\[/ end=/\]/ contained transparent
+ syntax match Y /(.\{-})/ contains=X
+
+ call setline(1, "==(--[~~]--)==")
+ let l:expected = " YYYYYYYYYY "
+ eval AssertHighlightGroups(1, 1, l:expected, 1, l:case)
+ syntax clear Y X
+ bw!
+
+ let l:case = "Transparent region extends match"
+ new
+ syntax region X start=/\[/ end=/\]/ contained transparent
+ syntax match Y /(.\{-}[e)]/ contains=X
+
+ call setline(1, "==(--[~~e~~]--)==")
+ let l:expected = " YYYYYYYYYY "
+ " Regression: " YYYYYYY "
+ eval AssertHighlightGroups(1, 1, l:expected, 1, l:case)
+ syntax clear Y X
+ bw!
+
+ let l:case = "Nested transparent regions extend match"
+ new
+ syntax region X start=/\[/ end=/\]/ contained transparent
+ syntax match Y /(.\{-}[e)]/ contains=X
+
+ call setline(1, "==(--[~~e~~[~~e~~]~~e~~]--)==")
+ let l:expected = " YYYYYYYYYYYYYYYYYYYYYY "
+ " Regression: " YYYYYYY YYYYYY "
+ eval AssertHighlightGroups(1, 1, l:expected, 1, l:case)
+ syntax clear Y X
+ bw!
+endfunc
+
func Test_syn_include_contains_TOP()
let l:case = "TOP in included syntax means its group list name"
new
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 96ce2c4cb1..a49e9df9ee 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -495,6 +495,11 @@ int ui_current_col(void)
return cursor_col;
}
+handle_T ui_cursor_grid(void)
+{
+ return cursor_grid_handle;
+}
+
void ui_flush(void)
{
cmdline_ui_flush();
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index 3ce2b80ea9..5df70d0d8e 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -300,6 +300,19 @@ ScreenGrid *ui_comp_mouse_focus(int row, int col)
return NULL;
}
+/// Compute which grid is on top at supplied screen coordinates
+ScreenGrid *ui_comp_get_grid_at_coord(int row, int col)
+{
+ for (ssize_t i = (ssize_t)kv_size(layers) - 1; i > 0; i--) {
+ ScreenGrid *grid = kv_A(layers, i);
+ if (row >= grid->comp_row && row < grid->comp_row + grid->rows
+ && col >= grid->comp_col && col < grid->comp_col + grid->cols) {
+ return grid;
+ }
+ }
+ return &default_grid;
+}
+
/// Baseline implementation. This is always correct, but we can sometimes
/// do something more efficient (where efficiency means smaller deltas to
/// the downstream UI.)