diff options
-rw-r--r-- | runtime/doc/api.txt | 32 | ||||
-rw-r--r-- | runtime/ftplugin/query.lua | 6 | ||||
-rw-r--r-- | runtime/indent/query.lua | 6 | ||||
-rw-r--r-- | runtime/lua/vim/filetype.lua | 2 | ||||
-rw-r--r-- | src/nvim/api/autocmd.c | 291 | ||||
-rw-r--r-- | src/nvim/api/extmark.c | 4 | ||||
-rw-r--r-- | src/nvim/api/keysets.lua | 8 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.h | 5 | ||||
-rw-r--r-- | src/nvim/edit.c | 8 | ||||
-rw-r--r-- | src/nvim/move.c | 13 | ||||
-rw-r--r-- | src/nvim/normal.c | 7 | ||||
-rw-r--r-- | src/nvim/screen.c | 8 | ||||
-rw-r--r-- | test/functional/api/autocmd_spec.lua | 91 | ||||
-rw-r--r-- | test/functional/ui/syntax_conceal_spec.lua | 53 |
14 files changed, 418 insertions, 116 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 0a8a3e9a2c..2b9aef2d77 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2453,9 +2453,9 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) local pos = a.nvim_win_get_cursor(0) local ns = a.nvim_create_namespace('my-plugin') -- Create new extmark at line 1, column 1. - local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {}) + local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, {}) -- Create new extmark at line 3, column 1. - local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {}) + local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, {}) -- Get extmarks only from line 3. local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) -- Get all marks in this buffer + namespace. @@ -3151,6 +3151,34 @@ nvim_tabpage_set_var({tabpage}, {name}, {value}) ============================================================================== Autocmd Functions *api-autocmd* +nvim_clear_autocmd({*opts}) *nvim_clear_autocmd()* + Clear all autocommands that match the corresponding {opts}. To + delete a particular autocmd, see |nvim_del_autocmd|. + + Parameters: ~ + {opts} Parameters + • event: (string|table) Examples: + • event: "pat1" + • event: { "pat1" } + • event: { "pat1", "pat2", "pat3" } + + • pattern: (string|table) + • pattern or patterns to match exactly. + • For example, if you have `*.py` as that + pattern for the autocmd, you must pass + `*.py` exactly to clear it. `test.py` will + not match the pattern. + + • defaults to clearing all patterns. + • NOTE: Cannot be used with {buffer} + + • buffer: (bufnr) + • clear only |autocmd-buflocal| autocommands. + • NOTE: Cannot be used with {pattern} + + • group: (string|int) The augroup name or id. + • NOTE: If not passed, will only delete autocmds not in any group. + nvim_create_augroup({name}, {*opts}) *nvim_create_augroup()* Create or get an autocommand group |autocmd-groups|. diff --git a/runtime/ftplugin/query.lua b/runtime/ftplugin/query.lua new file mode 100644 index 0000000000..c1694961af --- /dev/null +++ b/runtime/ftplugin/query.lua @@ -0,0 +1,6 @@ +-- Neovim filetype plugin file +-- Language: Tree-sitter query +-- Last Change: 2022 Mar 29 + +-- it's a lisp! +vim.cmd [[ runtime! ftplugin/lisp.vim ]] diff --git a/runtime/indent/query.lua b/runtime/indent/query.lua new file mode 100644 index 0000000000..55cb73e62b --- /dev/null +++ b/runtime/indent/query.lua @@ -0,0 +1,6 @@ +-- Neovim indent file +-- Language: Tree-sitter query +-- Last Change: 2022 Mar 29 + +-- it's a lisp! +vim.cmd [[ runtime! indent/lisp.vim ]] diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index ab71de54f8..2194db95a9 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1424,6 +1424,8 @@ local pattern = { return "git" end end, + -- Neovim only + [".*/queries/.*%.scm"] = "query", -- tree-sitter queries -- END PATTERN } -- luacheck: pop diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 66ac394498..f896671287 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -392,9 +392,6 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc FUNC_API_SINCE(9) { int64_t autocmd_id = -1; - - const char_u pattern_buflocal[BUFLOCAL_PAT_LEN]; - int au_group = AUGROUP_DEFAULT; char *desc = NULL; Array patterns = ARRAY_DICT_INIT; @@ -404,7 +401,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc Callback cb = CALLBACK_NONE; - if (!unpack_string_or_array(&event_array, &event, "event", err)) { + if (!unpack_string_or_array(&event_array, &event, "event", true, err)) { goto cleanup; } @@ -466,84 +463,13 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc bool is_once = api_object_to_bool(opts->once, "once", false, err); bool is_nested = api_object_to_bool(opts->nested, "nested", false, err); - switch (opts->group.type) { - case kObjectTypeNil: - break; - case kObjectTypeString: - au_group = augroup_find(opts->group.data.string.data); - if (au_group == AUGROUP_ERROR) { - api_set_error(err, - kErrorTypeValidation, - "invalid augroup: %s", opts->group.data.string.data); - goto cleanup; - } - break; - case kObjectTypeInteger: - au_group = (int)opts->group.data.integer; - char *name = augroup_name(au_group); - if (!augroup_exists(name)) { - api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); - goto cleanup; - } - break; - default: - api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); + int au_group = get_augroup_from_object(opts->group, err); + if (au_group == AUGROUP_ERROR) { goto cleanup; } - if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { - api_set_error(err, kErrorTypeValidation, - "cannot pass both: 'pattern' and 'buffer' for the same autocmd"); + if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, opts->buffer, err)) { goto cleanup; - } else if (opts->pattern.type != kObjectTypeNil) { - Object *v = &opts->pattern; - - if (v->type == kObjectTypeString) { - char_u *pat = (char_u *)v->data.string.data; - size_t patlen = aucmd_pattern_length(pat); - while (patlen) { - ADD(patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); - - pat = aucmd_next_pattern(pat, patlen); - patlen = aucmd_pattern_length(pat); - } - } else if (v->type == kObjectTypeArray) { - if (!check_autocmd_string_array(patterns, "pattern", err)) { - goto cleanup; - } - - Array array = v->data.array; - for (size_t i = 0; i < array.size; i++) { - char_u *pat = (char_u *)array.items[i].data.string.data; - size_t patlen = aucmd_pattern_length(pat); - while (patlen) { - ADD(patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); - - pat = aucmd_next_pattern(pat, patlen); - patlen = aucmd_pattern_length(pat); - } - } - } else { - api_set_error(err, - kErrorTypeValidation, - "'pattern' must be a string"); - goto cleanup; - } - } else if (opts->buffer.type != kObjectTypeNil) { - if (opts->buffer.type != kObjectTypeInteger) { - api_set_error(err, - kErrorTypeValidation, - "'buffer' must be an integer"); - goto cleanup; - } - - buf_T *buf = find_buffer_by_handle((Buffer)opts->buffer.data.integer, err); - if (ERROR_SET(err)) { - goto cleanup; - } - - snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); - ADD(patterns, STRING_OBJ(cstr_to_string((char *)pattern_buflocal))); } if (opts->desc.type != kObjectTypeNil) { @@ -621,6 +547,94 @@ void nvim_del_autocmd(Integer id, Error *err) } } +/// Clear all autocommands that match the corresponding {opts}. To delete +/// a particular autocmd, see |nvim_del_autocmd|. +/// @param opts Parameters +/// - event: (string|table) +/// Examples: +/// - event: "pat1" +/// - event: { "pat1" } +/// - event: { "pat1", "pat2", "pat3" } +/// - pattern: (string|table) +/// - pattern or patterns to match exactly. +/// - For example, if you have `*.py` as that pattern for the autocmd, +/// you must pass `*.py` exactly to clear it. `test.py` will not +/// match the pattern. +/// - defaults to clearing all patterns. +/// - NOTE: Cannot be used with {buffer} +/// - buffer: (bufnr) +/// - clear only |autocmd-buflocal| autocommands. +/// - NOTE: Cannot be used with {pattern} +/// - group: (string|int) The augroup name or id. +/// - NOTE: If not passed, will only delete autocmds *not* in any group. +/// +void nvim_clear_autocmd(Dict(clear_autocmd) *opts, Error *err) + FUNC_API_SINCE(9) +{ + // TODO(tjdevries): Future improvements: + // - once: (boolean) - Only clear autocmds with once. See |autocmd-once| + // - nested: (boolean) - Only clear autocmds with nested. See |autocmd-nested| + // - group: Allow passing "*" or true or something like that to force doing all + // autocmds, regardless of their group. + + Array patterns = ARRAY_DICT_INIT; + Array event_array = ARRAY_DICT_INIT; + + if (!unpack_string_or_array(&event_array, &opts->event, "event", false, err)) { + goto cleanup; + } + + if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { + api_set_error(err, kErrorTypeValidation, + "Cannot use both 'pattern' and 'buffer'"); + goto cleanup; + } + + int au_group = get_augroup_from_object(opts->group, err); + if (au_group == AUGROUP_ERROR) { + goto cleanup; + } + + if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, opts->buffer, err)) { + goto cleanup; + } + + // When we create the autocmds, we want to say that they are all matched, so that's * + // but when we clear them, we want to say that we didn't pass a pattern, so that's NUL + if (patterns.size == 0) { + ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING(""))); + } + + // If we didn't pass any events, that means clear all events. + if (event_array.size == 0) { + FOR_ALL_AUEVENTS(event) { + FOREACH_ITEM(patterns, pat_object, { + char_u *pat = (char_u *)pat_object.data.string.data; + if (!clear_autocmd(event, pat, au_group, err)) { + goto cleanup; + } + }); + } + } else { + FOREACH_ITEM(event_array, event_str, { + GET_ONE_EVENT(event_nr, event_str, cleanup); + + FOREACH_ITEM(patterns, pat_object, { + char_u *pat = (char_u *)pat_object.data.string.data; + if (!clear_autocmd(event_nr, pat, au_group, err)) { + goto cleanup; + } + }); + }); + } + +cleanup: + api_free_array(event_array); + api_free_array(patterns); + + return; +} + /// Create or get an autocommand group |autocmd-groups|. /// /// To get an existing group id, do: @@ -723,7 +737,7 @@ void nvim_exec_autocmd(Object event, Dict(exec_autocmd) *opts, Error *err) Array event_array = ARRAY_DICT_INIT; - if (!unpack_string_or_array(&event_array, &event, "event", err)) { + if (!unpack_string_or_array(&event_array, &event, "event", true, err)) { goto cleanup; } @@ -822,7 +836,7 @@ static bool check_autocmd_string_array(Array arr, char *k, Error *err) return true; } -static bool unpack_string_or_array(Array *array, Object *v, char *k, Error *err) +static bool unpack_string_or_array(Array *array, Object *v, char *k, bool required, Error *err) { if (v->type == kObjectTypeString) { ADD(*array, copy_object(*v)); @@ -832,10 +846,119 @@ static bool unpack_string_or_array(Array *array, Object *v, char *k, Error *err) } *array = copy_array(v->data.array); } else { - api_set_error(err, - kErrorTypeValidation, - "'%s' must be an array or a string.", - k); + if (required) { + api_set_error(err, + kErrorTypeValidation, + "'%s' must be an array or a string.", + k); + return false; + } + } + + return true; +} + +// Returns AUGROUP_ERROR if there was a problem with {group} +static int get_augroup_from_object(Object group, Error *err) +{ + int au_group = AUGROUP_ERROR; + + switch (group.type) { + case kObjectTypeNil: + return AUGROUP_DEFAULT; + case kObjectTypeString: + au_group = augroup_find(group.data.string.data); + if (au_group == AUGROUP_ERROR) { + api_set_error(err, + kErrorTypeValidation, + "invalid augroup: %s", group.data.string.data); + + return AUGROUP_ERROR; + } + + return au_group; + case kObjectTypeInteger: + au_group = (int)group.data.integer; + char *name = augroup_name(au_group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); + return AUGROUP_ERROR; + } + + return au_group; + default: + api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); + return AUGROUP_ERROR; + } +} + +static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Object buffer, + Error *err) +{ + const char_u pattern_buflocal[BUFLOCAL_PAT_LEN]; + + if (pattern.type != kObjectTypeNil && buffer.type != kObjectTypeNil) { + api_set_error(err, kErrorTypeValidation, + "cannot pass both: 'pattern' and 'buffer' for the same autocmd"); + return false; + } else if (pattern.type != kObjectTypeNil) { + Object *v = &pattern; + + if (v->type == kObjectTypeString) { + char_u *pat = (char_u *)v->data.string.data; + size_t patlen = aucmd_pattern_length(pat); + while (patlen) { + ADD(*patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); + + pat = aucmd_next_pattern(pat, patlen); + patlen = aucmd_pattern_length(pat); + } + } else if (v->type == kObjectTypeArray) { + if (!check_autocmd_string_array(*patterns, "pattern", err)) { + return false; + } + + Array array = v->data.array; + for (size_t i = 0; i < array.size; i++) { + char_u *pat = (char_u *)array.items[i].data.string.data; + size_t patlen = aucmd_pattern_length(pat); + while (patlen) { + ADD(*patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); + + pat = aucmd_next_pattern(pat, patlen); + patlen = aucmd_pattern_length(pat); + } + } + } else { + api_set_error(err, + kErrorTypeValidation, + "'pattern' must be a string"); + return false; + } + } else if (buffer.type != kObjectTypeNil) { + if (buffer.type != kObjectTypeInteger) { + api_set_error(err, + kErrorTypeValidation, + "'buffer' must be an integer"); + return false; + } + + buf_T *buf = find_buffer_by_handle((Buffer)buffer.data.integer, err); + if (ERROR_SET(err)) { + return false; + } + + snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); + ADD(*patterns, STRING_OBJ(cstr_to_string((char *)pattern_buflocal))); + } + + return true; +} + +static bool clear_autocmd(event_T event, char_u *pat, int au_group, Error *err) +{ + if (do_autocmd_event(event, pat, false, false, (char_u *)"", true, au_group) == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to clear autocmd"); return false; } diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 797b64e2af..8dca37a321 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -259,9 +259,9 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// local pos = a.nvim_win_get_cursor(0) /// local ns = a.nvim_create_namespace('my-plugin') /// -- Create new extmark at line 1, column 1. -/// local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {}) +/// local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, {}) /// -- Create new extmark at line 3, column 1. -/// local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {}) +/// local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, {}) /// -- Get extmarks only from line 3. /// local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) /// -- Get all marks in this buffer + namespace. diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 6a29b0fb59..520bb7fbc6 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -123,14 +123,20 @@ return { "nocombine"; }; -- Autocmds + clear_autocmd = { + "buffer"; + "event"; + "group"; + "pattern"; + }; create_autocmd = { "buffer"; "callback"; "command"; "desc"; "group"; - "once"; "nested"; + "once"; "pattern"; }; exec_autocmd = { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index bc7c2e6a60..650349cde7 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -140,8 +140,9 @@ typedef struct { // Useful macro for executing some `code` for each item in an array. #define FOREACH_ITEM(a, __foreach_item, code) \ - for (size_t __foreach_i = 0; __foreach_i < (a).size; __foreach_i++) { \ - Object __foreach_item = (a).items[__foreach_i]; \ + for (size_t (__foreach_item ## _index) = 0; (__foreach_item ## _index) < (a).size; \ + (__foreach_item ## _index)++) { \ + Object __foreach_item = (a).items[__foreach_item ## _index]; \ code; \ } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index c087948810..815d57121b 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1480,8 +1480,6 @@ bool edit(int cmdchar, bool startln, long count) /// @param ready not busy with something static void ins_redraw(bool ready) { - bool conceal_cursor_moved = false; - if (char_avail()) { return; } @@ -1504,7 +1502,6 @@ static void ins_redraw(bool ready) update_curswant(); ins_apply_autocmds(EVENT_CURSORMOVEDI); } - conceal_cursor_moved = true; curwin->w_last_cursormoved = curwin->w_cursor; } @@ -1560,11 +1557,6 @@ static void ins_redraw(bool ready) curbuf->b_changed_invalid = false; } - if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin) - && conceal_cursor_moved) { - redrawWinline(curwin, curwin->w_cursor.lnum); - } - pum_check_clear(); if (must_redraw) { update_screen(0); diff --git a/src/nvim/move.c b/src/nvim/move.c index 9b35a7da0a..5e02e355bf 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -96,12 +96,12 @@ static void comp_botline(win_T *wp) } /// Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set. +/// Also when concealing is on and 'concealcursor' is not active. void redraw_for_cursorline(win_T *wp) FUNC_ATTR_NONNULL_ALL { - if ((wp->w_p_rnu || win_cursorline_standout(wp)) - && (wp->w_valid & VALID_CROW) == 0 - && !pum_visible()) { + if ((wp->w_valid & VALID_CROW) == 0 && !pum_visible() + && (wp->w_p_rnu || win_cursorline_standout(wp))) { // win_line() will redraw the number column and cursorline only. redraw_later(wp, VALID); } @@ -109,6 +109,7 @@ void redraw_for_cursorline(win_T *wp) /// Redraw when w_virtcol changes and 'cursorcolumn' is set or 'cursorlineopt' /// contains "screenline". +/// Also when concealing is on and 'concealcursor' is active. static void redraw_for_cursorcolumn(win_T *wp) FUNC_ATTR_NONNULL_ALL { @@ -121,6 +122,12 @@ static void redraw_for_cursorcolumn(win_T *wp) redraw_later(wp, VALID); } } + // If the cursor moves horizontally when 'concealcursor' is active, then the + // current line needs to be redrawn in order to calculate the correct + // cursor position. + if ((wp->w_valid & VALID_VIRTCOL) == 0 && wp->w_p_cole > 0 && conceal_cursor_line(wp)) { + redrawWinline(wp, wp->w_cursor.lnum); + } } /* diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 6c7595d1a4..72e80952ff 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1288,13 +1288,6 @@ static void normal_redraw(NormalState *s) update_topline(curwin); validate_cursor(); - // If the cursor moves horizontally when 'concealcursor' is active, then the - // current line needs to be redrawn in order to calculate the correct - // cursor position. - if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin)) { - redrawWinline(curwin, curwin->w_cursor.lnum); - } - if (VIsual_active) { update_curbuf(INVERTED); // update inverted part } else if (must_redraw) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 262f17cd21..f922a50260 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -667,15 +667,11 @@ void conceal_check_cursor_line(void) /// 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. -/// TODO(bfredl): VIsual_active shouldn't be needed, but is used to fix a glitch -/// caused by scrolling. +/// 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 { - return wp->w_p_cul - || (wp->w_p_cole > 0 && (VIsual_active || !conceal_cursor_line(wp))); + return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp)); } /* diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua index 25220f7e74..9fc0066819 100644 --- a/test/functional/api/autocmd_spec.lua +++ b/test/functional/api/autocmd_spec.lua @@ -980,4 +980,95 @@ describe('autocmd api', function() eq(0, #meths.get_autocmds { event = 'BufReadPost' }) end) end) + + describe('nvim_clear_autocmd', function() + it('should clear based on event + pattern', function() + command('autocmd InsertEnter *.py :echo "Python can be cool sometimes"') + command('autocmd InsertEnter *.txt :echo "Text Files Are Cool"') + + local search = { event = "InsertEnter", pattern = "*.txt" } + local before_delete = meths.get_autocmds(search) + eq(1, #before_delete) + + local before_delete_all = meths.get_autocmds { event = search.event } + eq(2, #before_delete_all) + + meths.clear_autocmd(search) + local after_delete = meths.get_autocmds(search) + eq(0, #after_delete) + + local after_delete_all = meths.get_autocmds { event = search.event } + eq(1, #after_delete_all) + end) + + it('should clear based on event', function() + command('autocmd InsertEnter *.py :echo "Python can be cool sometimes"') + command('autocmd InsertEnter *.txt :echo "Text Files Are Cool"') + + local search = { event = "InsertEnter"} + local before_delete = meths.get_autocmds(search) + eq(2, #before_delete) + + meths.clear_autocmd(search) + local after_delete = meths.get_autocmds(search) + eq(0, #after_delete) + end) + + it('should clear based on pattern', function() + command('autocmd InsertEnter *.TestPat1 :echo "Enter 1"') + command('autocmd InsertLeave *.TestPat1 :echo "Leave 1"') + command('autocmd InsertEnter *.TestPat2 :echo "Enter 2"') + command('autocmd InsertLeave *.TestPat2 :echo "Leave 2"') + + local search = { pattern = "*.TestPat1"} + local before_delete = meths.get_autocmds(search) + eq(2, #before_delete) + local before_delete_events = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } } + eq(4, #before_delete_events) + + meths.clear_autocmd(search) + local after_delete = meths.get_autocmds(search) + eq(0, #after_delete) + + local after_delete_events = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } } + eq(2, #after_delete_events) + end) + + it('should allow clearing by buffer', function() + command('autocmd! InsertEnter') + command('autocmd InsertEnter <buffer> :echo "Enter Buffer"') + command('autocmd InsertEnter *.TestPat1 :echo "Enter Pattern"') + + local search = { event = "InsertEnter" } + local before_delete = meths.get_autocmds(search) + eq(2, #before_delete) + + meths.clear_autocmd { buffer = 0 } + local after_delete = meths.get_autocmds(search) + eq(1, #after_delete) + eq("*.TestPat1", after_delete[1].pattern) + end) + + it('should allow clearing by buffer and group', function() + command('augroup TestNvimClearAutocmds') + command(' au!') + command(' autocmd InsertEnter <buffer> :echo "Enter Buffer"') + command(' autocmd InsertEnter *.TestPat1 :echo "Enter Pattern"') + command('augroup END') + + local search = { event = "InsertEnter", group = "TestNvimClearAutocmds" } + local before_delete = meths.get_autocmds(search) + eq(2, #before_delete) + + -- Doesn't clear without passing group. + meths.clear_autocmd { buffer = 0 } + local without_group = meths.get_autocmds(search) + eq(2, #without_group) + + -- Doest clear with passing group. + meths.clear_autocmd { buffer = 0, group = search.group } + local with_group = meths.get_autocmds(search) + eq(1, #with_group) + end) + end) end) diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua index 4e1852162f..92e5a5dd94 100644 --- a/test/functional/ui/syntax_conceal_spec.lua +++ b/test/functional/ui/syntax_conceal_spec.lua @@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen') local clear, feed, command = helpers.clear, helpers.feed, helpers.command local eq = helpers.eq local insert = helpers.insert +local poke_eventloop = helpers.poke_eventloop describe('Screen', function() local screen @@ -911,7 +912,57 @@ describe('Screen', function() {0:~ }| | ]]} - eq(grid_lines, {{2, 0, {{'c', 0, 3}}}}) + eq({{2, 0, {{'c', 0, 3}}}}, grid_lines) + end) + + it('K_EVENT should not cause extra redraws with concealcursor #13196', function() + command('set conceallevel=1') + command('set concealcursor=nv') + command('set redrawdebug+=nodelta') + + insert([[ + aaa + bbb + ccc + ]]) + screen:expect{grid=[[ + aaa | + bbb | + ccc | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + + -- XXX: hack to get notifications, and check only a single line is + -- updated. Could use next_msg() also. + local orig_handle_grid_line = screen._handle_grid_line + local grid_lines = {} + function screen._handle_grid_line(self, grid, row, col, items) + table.insert(grid_lines, {row, col, items}) + orig_handle_grid_line(self, grid, row, col, items) + end + feed('k') + screen:expect{grid=[[ + aaa | + bbb | + ^ccc | + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + eq({{2, 0, {{'c', 0, 3}}}}, grid_lines) + poke_eventloop() -- causes K_EVENT key + screen:expect_unchanged() + eq({{2, 0, {{'c', 0, 3}}}}, grid_lines) end) -- Copy of Test_cursor_column_in_concealed_line_after_window_scroll in |