diff options
80 files changed, 1118 insertions, 1062 deletions
diff --git a/runtime/autoload/remote/define.vim b/runtime/autoload/remote/define.vim index 2aec96e365..82e5164d85 100644 --- a/runtime/autoload/remote/define.vim +++ b/runtime/autoload/remote/define.vim @@ -240,7 +240,11 @@ function! s:GetAutocmdPrefix(name, opts) endif if has_key(a:opts, 'nested') && a:opts.nested - call add(rv, 'nested') + call add(rv, '++nested') + endif + + if has_key(a:opts, 'once') && a:opts.once + call add(rv, '++once') endif return join(rv, ' ') diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 879a34bcaa..242631d98c 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -824,6 +824,10 @@ RemoteReply When a reply from a Vim that functions as Note that even if an autocommand is defined, the reply should be read with |remote_read()| to consume it. + *SearchWrapped* +SearchWrapped After making a search with |n| or |N| if the + search wraps around the document back to + the start/finish respectively. *SessionLoadPost* SessionLoadPost After loading the session file created using the |:mksession| command. diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index d02510a829..0893f1f343 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -41,6 +41,7 @@ requires a namespace. *diagnostic-structure* A diagnostic is a Lua table with the following keys: + bufnr: Buffer number lnum: The starting line of the diagnostic end_lnum: The final line of the diagnostic col: The starting column of the diagnostic @@ -48,6 +49,7 @@ A diagnostic is a Lua table with the following keys: severity: The severity of the diagnostic |vim.diagnostic.severity| message: The diagnostic text source: The source of the diagnostic + user_data: Arbitrary data plugins or users can add Diagnostics use the same indexing as the rest of the Nvim API (i.e. 0-based rows and columns). |api-indexing| diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 956cb3e624..bc59ea785e 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -180,6 +180,7 @@ Commands: |:match| can be invoked before highlight group is defined Events: + |SearchWrapped| |Signal| |TabNewEntered| |TermClose| diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 95bc4635b0..72a84dcc53 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1493,11 +1493,7 @@ local function adjust_start_col(lnum, line, items, encoding) end end if min_start_char then - if encoding == 'utf-8' then - return min_start_char - else - return vim.str_byteindex(line, min_start_char, encoding == 'utf-16') - end + return util._str_byteindex_enc(line, min_start_char, encoding) else return nil end diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 22089aaaa6..c974d1a6b4 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -285,7 +285,7 @@ local function location_handler(_, result, ctx, _) util.jump_to_location(result[1]) if #result > 1 then - util.set_qflist(util.locations_to_items(result)) + vim.fn.setqflist({}, ' ', {title = 'LSP locations', items = util.locations_to_items(result)}) api.nvim_command("copen") end else @@ -379,7 +379,7 @@ local make_call_hierarchy_handler = function(direction) }) end end - util.set_qflist(items) + vim.fn.setqflist({}, ' ', {title = 'LSP call hierarchy', items = items}) api.nvim_command("copen") end end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index a3a91a34f7..b11925df53 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -90,6 +90,42 @@ local function split_lines(value) return split(value, '\n', true) end +--- Convert byte index to `encoding` index. +--- Convenience wrapper around vim.str_utfindex +---@param line string line to be indexed +---@param index number byte index (utf-8), or `nil` for length +---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 +---@return number `encoding` index of `index` in `line` +function M._str_utfindex_enc(line, index, encoding) + if encoding ~= 'utf-8' then + local col32, col16 = vim.str_utfindex(line, index) + if encoding == 'utf-32' then + return col32 + else + return col16 + end + else + return index + end +end + +--- Convert UTF index to `encoding` index. +--- Convenience wrapper around vim.str_byteindex +---Alternative to vim.str_byteindex that takes an encoding. +---@param line string line to be indexed +---@param index number UTF index +---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 +---@return number byte (utf-8) index of `encoding` index `index` in `line` +function M._str_byteindex_enc(line, index, encoding) + if encoding ~= 'utf-8' then + return vim.str_byteindex(line, index, not encoding or encoding ~= 'utf-32') + else + return index + end +end + +local _str_utfindex_enc = M._str_utfindex_enc +local _str_byteindex_enc = M._str_byteindex_enc --- Replaces text in a range with new text. --- --- CAUTION: Changes in-place! @@ -237,6 +273,7 @@ end ---@private --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position --- Returns a zero-indexed column, since set_lines() does the conversion to +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 --- 1-indexed local function get_line_byte_from_position(bufnr, position, offset_encoding) -- LSP's line and characters are 0-indexed @@ -247,13 +284,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) if col > 0 then local line = get_line(bufnr, position.line) local ok, result - - if offset_encoding == "utf-16" or not offset_encoding then - ok, result = pcall(vim.str_byteindex, line, col, true) - elseif offset_encoding == "utf-32" then - ok, result = pcall(vim.str_byteindex, line, col, false) - end - + ok, result = pcall(_str_byteindex_enc, line, col, offset_encoding) if ok then return result end @@ -325,12 +356,15 @@ end --- Applies a list of text edits to a buffer. ---@param text_edits table list of `TextEdit` objects ---@param bufnr number Buffer id +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to encoding of first client of `bufnr` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit -function M.apply_text_edits(text_edits, bufnr) +function M.apply_text_edits(text_edits, bufnr, offset_encoding) validate { text_edits = { text_edits, 't', false }; bufnr = { bufnr, 'number', false }; + offset_encoding = { offset_encoding, 'string', true }; } + offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) if not next(text_edits) then return end if not api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) @@ -367,8 +401,7 @@ function M.apply_text_edits(text_edits, bufnr) -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here. local has_eol_text_edit = false local max = vim.api.nvim_buf_line_count(bufnr) - -- TODO handle offset_encoding - local _, len = vim.str_utfindex(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '') + local len = _str_utfindex_enc(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '', nil, offset_encoding) text_edits = vim.tbl_map(function(text_edit) if max <= text_edit.range.start.line then text_edit.range.start.line = max - 1 @@ -1432,11 +1465,11 @@ do --[[ References ]] --- ---@param bufnr number Buffer id ---@param references table List of `DocumentHighlight` objects to highlight - ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32", or nil. Defaults to utf-16 + ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32", or nil. Defaults to `offset_encoding` of first client of `bufnr` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight function M.buf_highlight_references(bufnr, references, offset_encoding) validate { bufnr = {bufnr, 'n', true} } - offset_encoding = offset_encoding or 'utf-16' + offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) for _, reference in ipairs(references) do local start_line, start_char = reference["range"]["start"]["line"], reference["range"]["start"]["character"] local end_line, end_char = reference["range"]["end"]["line"], reference["range"]["end"]["character"] @@ -1649,30 +1682,61 @@ function M.try_trim_markdown_code_blocks(lines) return 'markdown' end -local str_utfindex = vim.str_utfindex ---@private -local function make_position_param() - local row, col = unpack(api.nvim_win_get_cursor(0)) +---@param window (optional, number): window handle or 0 for current, defaults to current +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` +local function make_position_param(window, offset_encoding) + window = window or 0 + local buf = vim.api.nvim_win_get_buf(window) + local row, col = unpack(api.nvim_win_get_cursor(window)) + offset_encoding = offset_encoding or M._get_offset_encoding(buf) row = row - 1 - local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] + local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1] if not line then return { line = 0; character = 0; } end - -- TODO handle offset_encoding - local _ - _, col = str_utfindex(line, col) + + col = _str_utfindex_enc(line, col, offset_encoding) + return { line = row; character = col; } end --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. --- +---@param window (optional, number): window handle or 0 for current, defaults to current +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` ---@returns `TextDocumentPositionParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams -function M.make_position_params() +function M.make_position_params(window, offset_encoding) + window = window or 0 + local buf = vim.api.nvim_win_get_buf(window) + offset_encoding = offset_encoding or M._get_offset_encoding(buf) return { - textDocument = M.make_text_document_params(); - position = make_position_param() + textDocument = M.make_text_document_params(buf); + position = make_position_param(window, offset_encoding) + } +end + +--- Utility function for getting the encoding of the first LSP client on the given buffer. +---@param bufnr (number) buffer handle or 0 for current, defaults to current +---@returns (string) encoding first client if there is one, nil otherwise +function M._get_offset_encoding(bufnr) + validate { + bufnr = {bufnr, 'n', true}; } + + local offset_encoding + + for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do + local this_offset_encoding = client.offset_encoding or "utf-16" + if not offset_encoding then + offset_encoding = this_offset_encoding + elseif offset_encoding ~= this_offset_encoding then + vim.notify("warning: multiple different client offset_encodings detected for buffer, this is not supported yet", vim.log.levels.WARN) + end + end + + return offset_encoding end --- Using the current position in the current buffer, creates an object that @@ -1680,12 +1744,16 @@ end --- `textDocument/codeAction`, `textDocument/colorPresentation`, --- `textDocument/rangeFormatting`. --- +---@param window (optional, number): window handle or 0 for current, defaults to current +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`current_position`, end = `current_position` } } -function M.make_range_params() - local position = make_position_param() +function M.make_range_params(window, offset_encoding) + local buf = vim.api.nvim_win_get_buf(window) + offset_encoding = offset_encoding or M._get_offset_encoding(buf) + local position = make_position_param(window, offset_encoding) return { - textDocument = M.make_text_document_params(), + textDocument = M.make_text_document_params(buf), range = { start = position; ["end"] = position; } } end @@ -1697,27 +1765,29 @@ end ---Defaults to the start of the last visual selection. ---@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. +---@param bufnr (optional, number): buffer handle or 0 for current, defaults to current +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `bufnr` ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`start_position`, end = `end_position` } } -function M.make_given_range_params(start_pos, end_pos) +function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) validate { start_pos = {start_pos, 't', true}; end_pos = {end_pos, 't', true}; + offset_encoding = {offset_encoding, 's', true}; } - local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<')) - local B = list_extend({}, end_pos or api.nvim_buf_get_mark(0, '>')) + bufnr = bufnr or 0 + offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) + local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<')) + local B = list_extend({}, end_pos or api.nvim_buf_get_mark(bufnr, '>')) -- convert to 0-index A[1] = A[1] - 1 B[1] = B[1] - 1 - -- account for encoding. - -- TODO handle offset_encoding + -- account for offset_encoding. if A[2] > 0 then - local _, char = M.character_offset(0, A[1], A[2]) - A = {A[1], char} + A = {A[1], M.character_offset(bufnr, A[1], A[2], offset_encoding)} end if B[2] > 0 then - local _, char = M.character_offset(0, B[1], B[2]) - B = {B[1], char} + B = {B[1], M.character_offset(bufnr, B[1], B[2], offset_encoding)} end -- we need to offset the end character position otherwise we loose the last -- character of the selection, as LSP end position is exclusive @@ -1726,7 +1796,7 @@ function M.make_given_range_params(start_pos, end_pos) B[2] = B[2] + 1 end return { - textDocument = M.make_text_document_params(), + textDocument = M.make_text_document_params(bufnr), range = { start = {line = A[1], character = A[2]}, ['end'] = {line = B[1], character = B[2]} @@ -1736,10 +1806,11 @@ end --- Creates a `TextDocumentIdentifier` object for the current buffer. --- +---@param bufnr (optional, number): Buffer handle, defaults to current ---@returns `TextDocumentIdentifier` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier -function M.make_text_document_params() - return { uri = vim.uri_from_bufnr(0) } +function M.make_text_document_params(bufnr) + return { uri = vim.uri_from_bufnr(bufnr or 0) } end --- Create the workspace params @@ -1782,14 +1853,16 @@ end ---@param buf buffer id (0 for current) ---@param row 0-indexed line ---@param col 0-indexed byte offset in line ----@returns (number, number) UTF-32 and UTF-16 index of the character in line {row} column {col} in buffer {buf} -function M.character_offset(bufnr, row, col) - local line = get_line(bufnr, row) +---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `buf` +---@returns (number, number) `offset_encoding` index of the character in line {row} column {col} in buffer {buf} +function M.character_offset(buf, row, col, offset_encoding) + local line = get_line(buf, row) + offset_encoding = offset_encoding or M._get_offset_encoding(buf) -- If the col is past the EOL, use the line length. if col > #line then - return str_utfindex(line) + return _str_utfindex_enc(line, nil, offset_encoding) end - return str_utfindex(line, col) + return _str_utfindex_enc(line, col, offset_encoding) end --- Helper function to return nested values in language server settings diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index b8255e61ed..ebed502c92 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -164,22 +164,36 @@ function M.parse_query(lang, query) return self end --- TODO(vigoux): support multiline nodes too - --- Gets the text corresponding to a given node --- ---@param node the node ----@param bsource The buffer or string from which the node is extracted +---@param source The buffer or string from which the node is extracted function M.get_node_text(node, source) local start_row, start_col, start_byte = node:start() local end_row, end_col, end_byte = node:end_() if type(source) == "number" then - if start_row ~= end_row then + local lines + local eof_row = a.nvim_buf_line_count(source) + if start_row >= eof_row then return nil end - local line = a.nvim_buf_get_lines(source, start_row, start_row+1, true)[1] - return string.sub(line, start_col+1, end_col) + + if end_col == 0 then + lines = a.nvim_buf_get_lines(source, start_row, end_row, true) + end_col = -1 + else + lines = a.nvim_buf_get_lines(source, start_row, end_row + 1, true) + end + + if #lines == 1 then + lines[1] = string.sub(lines[1], start_col+1, end_col) + else + lines[1] = string.sub(lines[1], start_col+1) + lines[#lines] = string.sub(lines[#lines], 1, end_col) + end + + return table.concat(lines, "\n") elseif type(source) == "string" then return source:sub(start_byte+1, end_byte) end @@ -211,11 +225,6 @@ local predicate_handlers = { ["lua-match?"] = function(match, _, source, predicate) local node = match[predicate[2]] local regex = predicate[3] - local start_row, _, end_row, _ = node:range() - if start_row ~= end_row then - return false - end - return string.find(M.get_node_text(node, source), regex) end, @@ -239,13 +248,8 @@ local predicate_handlers = { return function(match, _, source, pred) local node = match[pred[2]] - local start_row, start_col, end_row, end_col = node:range() - if start_row ~= end_row then - return false - end - local regex = compiled_vim_regexes[pred[3]] - return regex:match_line(source, start_row, start_col, end_col) + return regex:match_str(M.get_node_text(node, source)) end end)(), diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 718743ed9c..7988bff25a 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -29,7 +29,6 @@ #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/ops.h" #include "nvim/undo.h" diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c index a236b47027..d7f73fa4a1 100644 --- a/src/nvim/aucmd.c +++ b/src/nvim/aucmd.c @@ -8,7 +8,6 @@ #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/main.h" -#include "nvim/misc1.h" #include "nvim/os/os.h" #include "nvim/ui.h" #include "nvim/vim.h" diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 1daae85c5e..6227b08b26 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -76,6 +76,7 @@ return { 'QuickFixCmdPre', -- before :make, :grep etc. 'QuitPre', -- before :quit 'RemoteReply', -- upon string reception from a remote vim + 'SearchWrapped', -- after the search wrapped around 'SessionLoadPost', -- after loading a session file 'ShellCmdPost', -- after ":!cmd" 'ShellFilterPost', -- after ":1,2!cmd", ":w !cmd", ":r !cmd". diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 0248230e15..3780cad1d6 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -15,7 +15,6 @@ #include "nvim/ex_docmd.h" #include "nvim/fileio.h" #include "nvim/getchar.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/regexp.h" diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 297d9743df..89baea83f8 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -57,7 +57,6 @@ #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/os/input.h" diff --git a/src/nvim/change.c b/src/nvim/change.c index 96fbaa9166..1dbbfff024 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -19,7 +19,6 @@ #include "nvim/indent_c.h" #include "nvim/mark.h" #include "nvim/memline.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/plines.h" @@ -1863,3 +1862,289 @@ void del_lines(long nlines, bool undo) // adjust marks, mark the buffer as changed and prepare for displaying deleted_lines_mark(first, n); } + +/// Returns the length in bytes of the prefix of the given string which introduces a comment. +/// +/// If this string is not a comment then 0 is returned. +/// When "flags" is not NULL, it is set to point to the flags of the recognized comment leader. +/// "backward" must be true for the "O" command. +/// If "include_space" is set, include trailing whitespace while calculating the length. +int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_space) +{ + int i, j; + int result; + int got_com = false; + int found_one; + char_u part_buf[COM_MAX_LEN]; // buffer for one option part + char_u *string; // pointer to comment string + char_u *list; + int middle_match_len = 0; + char_u *prev_list; + char_u *saved_flags = NULL; + + result = i = 0; + while (ascii_iswhite(line[i])) { // leading white space is ignored + i++; + } + + // Repeat to match several nested comment strings. + while (line[i] != NUL) { + // scan through the 'comments' option for a match + found_one = false; + for (list = curbuf->b_p_com; *list;) { + // Get one option part into part_buf[]. Advance "list" to next + // one. Put "string" at start of string. + if (!got_com && flags != NULL) { + *flags = list; // remember where flags started + } + prev_list = list; + (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ","); + string = vim_strchr(part_buf, ':'); + if (string == NULL) { // missing ':', ignore this part + continue; + } + *string++ = NUL; // isolate flags from string + + // If we found a middle match previously, use that match when this + // is not a middle or end. + if (middle_match_len != 0 + && vim_strchr(part_buf, COM_MIDDLE) == NULL + && vim_strchr(part_buf, COM_END) == NULL) { + break; + } + + // When we already found a nested comment, only accept further + // nested comments. + if (got_com && vim_strchr(part_buf, COM_NEST) == NULL) { + continue; + } + + // When 'O' flag present and using "O" command skip this one. + if (backward && vim_strchr(part_buf, COM_NOBACK) != NULL) { + continue; + } + + // Line contents and string must match. + // When string starts with white space, must have some white space + // (but the amount does not need to match, there might be a mix of + // TABs and spaces). + if (ascii_iswhite(string[0])) { + if (i == 0 || !ascii_iswhite(line[i - 1])) { + continue; // missing white space + } + while (ascii_iswhite(string[0])) { + string++; + } + } + for (j = 0; string[j] != NUL && string[j] == line[i + j]; j++) { + } + if (string[j] != NUL) { + continue; // string doesn't match + } + // When 'b' flag used, there must be white space or an + // end-of-line after the string in the line. + if (vim_strchr(part_buf, COM_BLANK) != NULL + && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) { + continue; + } + + // We have found a match, stop searching unless this is a middle + // comment. The middle comment can be a substring of the end + // comment in which case it's better to return the length of the + // end comment and its flags. Thus we keep searching with middle + // and end matches and use an end match if it matches better. + if (vim_strchr(part_buf, COM_MIDDLE) != NULL) { + if (middle_match_len == 0) { + middle_match_len = j; + saved_flags = prev_list; + } + continue; + } + if (middle_match_len != 0 && j > middle_match_len) { + // Use this match instead of the middle match, since it's a + // longer thus better match. + middle_match_len = 0; + } + + if (middle_match_len == 0) { + i += j; + } + found_one = true; + break; + } + + if (middle_match_len != 0) { + // Use the previously found middle match after failing to find a + // match with an end. + if (!got_com && flags != NULL) { + *flags = saved_flags; + } + i += middle_match_len; + found_one = true; + } + + // No match found, stop scanning. + if (!found_one) { + break; + } + + result = i; + + // Include any trailing white space. + while (ascii_iswhite(line[i])) { + i++; + } + + if (include_space) { + result = i; + } + + // If this comment doesn't nest, stop here. + got_com = true; + if (vim_strchr(part_buf, COM_NEST) == NULL) { + break; + } + } + return result; +} + +/// Return the offset at which the last comment in line starts. If there is no +/// comment in the whole line, -1 is returned. +/// +/// When "flags" is not null, it is set to point to the flags describing the +/// recognized comment leader. +int get_last_leader_offset(char_u *line, char_u **flags) +{ + int result = -1; + int i, j; + int lower_check_bound = 0; + char_u *string; + char_u *com_leader; + char_u *com_flags; + char_u *list; + int found_one; + char_u part_buf[COM_MAX_LEN]; // buffer for one option part + + // Repeat to match several nested comment strings. + i = (int)STRLEN(line); + while (--i >= lower_check_bound) { + // scan through the 'comments' option for a match + found_one = false; + for (list = curbuf->b_p_com; *list;) { + char_u *flags_save = list; + + // Get one option part into part_buf[]. Advance list to next one. + // put string at start of string. + (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ","); + string = vim_strchr(part_buf, ':'); + if (string == NULL) { // If everything is fine, this cannot actually + // happen. + continue; + } + *string++ = NUL; // Isolate flags from string. + com_leader = string; + + // Line contents and string must match. + // When string starts with white space, must have some white space + // (but the amount does not need to match, there might be a mix of + // TABs and spaces). + if (ascii_iswhite(string[0])) { + if (i == 0 || !ascii_iswhite(line[i - 1])) { + continue; + } + while (ascii_iswhite(*string)) { + string++; + } + } + for (j = 0; string[j] != NUL && string[j] == line[i + j]; j++) { + // do nothing + } + if (string[j] != NUL) { + continue; + } + + // When 'b' flag used, there must be white space or an + // end-of-line after the string in the line. + if (vim_strchr(part_buf, COM_BLANK) != NULL + && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) { + continue; + } + + if (vim_strchr(part_buf, COM_MIDDLE) != NULL) { + // For a middlepart comment, only consider it to match if + // everything before the current position in the line is + // whitespace. Otherwise we would think we are inside a + // comment if the middle part appears somewhere in the middle + // of the line. E.g. for C the "*" appears often. + for (j = 0; j <= i && ascii_iswhite(line[j]); j++) { + } + if (j < i) { + continue; + } + } + + // We have found a match, stop searching. + found_one = true; + + if (flags) { + *flags = flags_save; + } + com_flags = flags_save; + + break; + } + + if (found_one) { + char_u part_buf2[COM_MAX_LEN]; // buffer for one option part + int len1, len2, off; + + result = i; + // If this comment nests, continue searching. + if (vim_strchr(part_buf, COM_NEST) != NULL) { + continue; + } + + lower_check_bound = i; + + // Let's verify whether the comment leader found is a substring + // of other comment leaders. If it is, let's adjust the + // lower_check_bound so that we make sure that we have determined + // the comment leader correctly. + + while (ascii_iswhite(*com_leader)) { + com_leader++; + } + len1 = (int)STRLEN(com_leader); + + for (list = curbuf->b_p_com; *list;) { + char_u *flags_save = list; + + (void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ","); + if (flags_save == com_flags) { + continue; + } + string = vim_strchr(part_buf2, ':'); + string++; + while (ascii_iswhite(*string)) { + string++; + } + len2 = (int)STRLEN(string); + if (len2 == 0) { + continue; + } + + // Now we have to verify whether string ends with a substring + // beginning the com_leader. + for (off = (len2 > i ? i : len2); off > 0 && off + len1 > len2;) { + off--; + if (!STRNCMP(string + off, com_leader, len2 - off)) { + if (i - off < lower_check_bound) { + lower_check_bound = i - off; + } + } + } + } + } + } + return result; +} diff --git a/src/nvim/channel.c b/src/nvim/channel.c index a662f3a951..cd5134fe5f 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -10,7 +10,6 @@ #include "nvim/event/socket.h" #include "nvim/fileio.h" #include "nvim/lua/executor.h" -#include "nvim/misc1.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/os/shell.h" diff --git a/src/nvim/charset.c b/src/nvim/charset.c index eb0903b594..599d662993 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -21,7 +21,6 @@ #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/os_unix.h" diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 4e1d7f9d78..6e2c6232d7 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -14,7 +14,6 @@ #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/plines.h" #include "nvim/screen.h" diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 1f8acd8c79..0233b3a5ab 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -29,7 +29,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" @@ -39,6 +38,7 @@ #include "nvim/screen.h" #include "nvim/strings.h" #include "nvim/undo.h" +#include "nvim/ui.h" #include "nvim/vim.h" #include "nvim/window.h" #include "xdiff/xdiff.h" diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index d1dd9b8309..8eda173cac 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -20,7 +20,6 @@ #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/normal.h" #include "nvim/os/input.h" #include "nvim/screen.h" diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 9bfb8a9d4a..2135d0bcd2 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -35,7 +35,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" @@ -832,6 +831,16 @@ static int insert_execute(VimState *state, int key) return insert_handle_key(s); } + +/// Return true when need to go to Insert mode because of 'insertmode'. +/// +/// Don't do this when still processing a command or a mapping. +/// Don't do this when inside a ":normal" command. +bool goto_im(void) +{ + return p_im && stuff_empty() && typebuf_typed(); +} + static int insert_handle_key(InsertState *s) { // The big switch to handle a character in insert mode. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d45aa8194c..86384bc5b2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -35,7 +35,6 @@ #include "nvim/lua/executor.h" #include "nvim/mark.h" #include "nvim/memline.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/ops.h" #include "nvim/option.h" diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 33ca4016cf..5252c940f7 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -33,12 +33,12 @@ #include "nvim/if_cscope.h" #include "nvim/indent.h" #include "nvim/indent_c.h" +#include "nvim/input.h" #include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/mark.h" #include "nvim/math.h" #include "nvim/memline.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/msgpack_rpc/channel.h" diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index dfb2500b49..11bbaaed9c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -32,8 +32,6 @@ #include "nvim/pos.h" #include "nvim/types.h" #include "nvim/vim.h" -// TODO(ZyX-I): Move line_breakcheck out of misc1 -#include "nvim/misc1.h" // For line_breakcheck #include "nvim/os/fileio.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 9478a8441b..eb241eb8ae 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -17,7 +17,6 @@ #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" -#include "nvim/misc1.h" #include "nvim/os/input.h" #include "nvim/regexp.h" #include "nvim/search.h" diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 755307781c..3c43d1f98d 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -13,7 +13,6 @@ #include "nvim/log.h" #include "nvim/memory.h" #include "nvim/main.h" -#include "nvim/misc1.h" #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index f95fe84f69..c0cb17fa61 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -39,6 +39,7 @@ #include "nvim/getchar.h" #include "nvim/highlight.h" #include "nvim/indent.h" +#include "nvim/input.h" #include "nvim/log.h" #include "nvim/main.h" #include "nvim/mark.h" @@ -46,7 +47,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 2dc098df8c..33f9477608 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -35,7 +35,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4c040bff7f..9f0f8d93a3 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -39,6 +39,7 @@ #include "nvim/globals.h" #include "nvim/hardcopy.h" #include "nvim/if_cscope.h" +#include "nvim/input.h" #include "nvim/keymap.h" #include "nvim/lua/executor.h" #include "nvim/main.h" @@ -48,7 +49,6 @@ #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 475f22d061..ba2238ace2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -48,7 +48,6 @@ #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/ops.h" diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index f80a63560c..a37cad9f2d 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -28,7 +28,6 @@ #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/keymap.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/os/input.h" diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 5953a574f3..d31021b3ef 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -57,7 +57,6 @@ #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/fs_defs.h" #include "nvim/os/input.h" diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 24428c2d9a..f8cf341836 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -30,12 +30,12 @@ #include "nvim/getchar.h" #include "nvim/hashtab.h" #include "nvim/iconv.h" +#include "nvim/input.h" #include "nvim/mbyte.h" #include "nvim/memfile.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 7806d3eada..b1d4321d4c 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -28,7 +28,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/ops.h" #include "nvim/option.h" diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 27e8cb36af..05c38a5233 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -29,6 +29,7 @@ #include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" +#include "nvim/input.h" #include "nvim/keymap.h" #include "nvim/lua/executor.h" #include "nvim/main.h" @@ -36,7 +37,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" @@ -458,6 +458,15 @@ void flush_buffers(flush_buffers_T flush_typeahead) } } +/// flush map and typeahead buffers and give a warning for an error +void beep_flush(void) +{ + if (emsg_silent == 0) { + flush_buffers(FLUSH_MINIMAL); + vim_beep(BO_ERROR); + } +} + /* * The previous contents of the redo buffer is kept in old_redobuffer. * This is used for the CTRL-O <.> command in insert mode. diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 938e9c7db7..dfbc80066e 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -202,7 +202,6 @@ EXTERN bool msg_did_scroll INIT(= false); EXTERN char_u *keep_msg INIT(= NULL); // msg to be shown after redraw EXTERN int keep_msg_attr INIT(= 0); // highlight attr for keep_msg -EXTERN bool keep_msg_more INIT(= false); // keep_msg was set by msgmore() EXTERN bool need_fileinfo INIT(= false); // do fileinfo() after redraw EXTERN int msg_scroll INIT(= false); // msg_start() will scroll EXTERN bool msg_didout INIT(= false); // msg_outstr() was used in line diff --git a/src/nvim/indent.c b/src/nvim/indent.c index f49aff6643..8cc5bc2436 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -17,7 +17,6 @@ #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/plines.h" diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 3e3e07e9d6..faa9b38cf7 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -8,7 +8,6 @@ #include "nvim/vim.h" #include "nvim/ascii.h" -#include "nvim/misc1.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/edit.h" diff --git a/src/nvim/input.c b/src/nvim/input.c new file mode 100644 index 0000000000..2f7c5c2c16 --- /dev/null +++ b/src/nvim/input.c @@ -0,0 +1,255 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// input.c: high level functions for prompting the user or input +// like yes/no or number prompts. + +#include <inttypes.h> +#include <stdbool.h> + +#include "nvim/func_attr.h" +#include "nvim/getchar.h" +#include "nvim/mbyte.h" +#include "nvim/memory.h" +#include "nvim/input.h" +#include "nvim/mouse.h" +#include "nvim/os/input.h" +#include "nvim/ui.h" +#include "nvim/vim.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "input.c.generated.h" +#endif + +/// Ask for a reply from the user, 'y' or 'n' +/// +/// No other characters are accepted, the message is repeated until a valid +/// reply is entered or <C-c> is hit. +/// +/// @param[in] str Prompt: question to ask user. Is always followed by +/// " (y/n)?". +/// @param[in] direct Determines what function to use to get user input. If +/// true then ui_inchar() will be used, otherwise vgetc(). +/// I.e. when direct is true then characters are obtained +/// directly from the user without buffers involved. +/// +/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt. +int ask_yesno(const char *const str, const bool direct) +{ + const int save_State = State; + + no_wait_return++; + State = CONFIRM; // Mouse behaves like with :confirm. + setmouse(); // Disable mouse in xterm. + no_mapping++; + + int r = ' '; + while (r != 'y' && r != 'n') { + // Same highlighting as for wait_return. + smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str); + if (direct) { + r = get_keystroke(NULL); + } else { + r = plain_vgetc(); + } + if (r == Ctrl_C || r == ESC) { + r = 'n'; + } + msg_putchar(r); // Show what you typed. + ui_flush(); + } + no_wait_return--; + State = save_State; + setmouse(); + no_mapping--; + + return r; +} + +/// Get a key stroke directly from the user. +/// +/// Ignores mouse clicks and scrollbar events, except a click for the left +/// button (used at the more prompt). +/// Doesn't use vgetc(), because it syncs undo and eats mapped characters. +/// Disadvantage: typeahead is ignored. +/// Translates the interrupt character for unix to ESC. +int get_keystroke(MultiQueue *events) +{ + char_u *buf = NULL; + int buflen = 150; + int maxlen; + int len = 0; + int n; + int save_mapped_ctrl_c = mapped_ctrl_c; + int waited = 0; + + mapped_ctrl_c = 0; // mappings are not used here + for (;;) { + // flush output before waiting + ui_flush(); + // Leave some room for check_termcode() to insert a key code into (max + // 5 chars plus NUL). And fix_input_buffer() can triple the number of + // bytes. + maxlen = (buflen - 6 - len) / 3; + if (buf == NULL) { + buf = xmalloc((size_t)buflen); + } else if (maxlen < 10) { + // Need some more space. This might happen when receiving a long + // escape sequence. + buflen += 100; + buf = xrealloc(buf, (size_t)buflen); + maxlen = (buflen - 6 - len) / 3; + } + + // First time: blocking wait. Second time: wait up to 100ms for a + // terminal code to complete. + n = os_inchar(buf + len, maxlen, len == 0 ? -1L : 100L, 0, events); + if (n > 0) { + // Replace zero and CSI by a special key code. + n = fix_input_buffer(buf + len, n); + len += n; + waited = 0; + } else if (len > 0) { + waited++; // keep track of the waiting time + } + if (n > 0) { // found a termcode: adjust length + len = n; + } + if (len == 0) { // nothing typed yet + continue; + } + + // Handle modifier and/or special key code. + n = buf[0]; + if (n == K_SPECIAL) { + n = TO_SPECIAL(buf[1], buf[2]); + if (buf[1] == KS_MODIFIER + || n == K_IGNORE + || (is_mouse_key(n) && n != K_LEFTMOUSE)) { + if (buf[1] == KS_MODIFIER) { + mod_mask = buf[2]; + } + len -= 3; + if (len > 0) { + memmove(buf, buf + 3, (size_t)len); + } + continue; + } + break; + } + if (MB_BYTE2LEN(n) > len) { + // more bytes to get. + continue; + } + buf[len >= buflen ? buflen - 1 : len] = NUL; + n = utf_ptr2char(buf); + break; + } + xfree(buf); + + mapped_ctrl_c = save_mapped_ctrl_c; + return n; +} + +/// Get a number from the user. +/// When "mouse_used" is not NULL allow using the mouse. +/// +/// @param colon allow colon to abort +int get_number(int colon, int *mouse_used) +{ + int n = 0; + int c; + int typed = 0; + + if (mouse_used != NULL) { + *mouse_used = false; + } + + // When not printing messages, the user won't know what to type, return a + // zero (as if CR was hit). + if (msg_silent != 0) { + return 0; + } + + no_mapping++; + for (;;) { + ui_cursor_goto(msg_row, msg_col); + c = safe_vgetc(); + if (ascii_isdigit(c)) { + n = n * 10 + c - '0'; + msg_putchar(c); + typed++; + } else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H) { + if (typed > 0) { + msg_puts("\b \b"); + typed--; + } + n /= 10; + } else if (mouse_used != NULL && c == K_LEFTMOUSE) { + *mouse_used = true; + n = mouse_row + 1; + break; + } else if (n == 0 && c == ':' && colon) { + stuffcharReadbuff(':'); + if (!exmode_active) { + cmdline_row = msg_row; + } + skip_redraw = true; // skip redraw once + do_redraw = false; + break; + } else if (c == Ctrl_C || c == ESC || c == 'q') { + n = 0; + break; + } else if (c == CAR || c == NL) { + break; + } + } + no_mapping--; + return n; +} + +/// Ask the user to enter a number. +/// +/// When "mouse_used" is not NULL allow using the mouse and in that case return +/// the line number. +int prompt_for_number(int *mouse_used) +{ + int i; + int save_cmdline_row; + int save_State; + + // When using ":silent" assume that <CR> was entered. + if (mouse_used != NULL) { + msg_puts(_("Type number and <Enter> or click with the mouse " + "(q or empty cancels): ")); + } else { + msg_puts(_("Type number and <Enter> (q or empty cancels): ")); + } + + // Set the state such that text can be selected/copied/pasted and we still + // get mouse events. + save_cmdline_row = cmdline_row; + cmdline_row = 0; + save_State = State; + State = ASKMORE; // prevents a screen update when using a timer + // May show different mouse shape. + setmouse(); + + i = get_number(true, mouse_used); + if (KeyTyped) { + // don't call wait_return() now + if (msg_row > 0) { + cmdline_row = msg_row - 1; + } + need_wait_return = false; + msg_didany = false; + msg_didout = false; + } else { + cmdline_row = save_cmdline_row; + } + State = save_State; + // May need to restore mouse shape. + setmouse(); + + return i; +} diff --git a/src/nvim/input.h b/src/nvim/input.h new file mode 100644 index 0000000000..7975f21215 --- /dev/null +++ b/src/nvim/input.h @@ -0,0 +1,9 @@ +#ifndef NVIM_INPUT_H +#define NVIM_INPUT_H + +#include "nvim/vim.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "input.h.generated.h" +#endif +#endif // NVIM_INPUT_H diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index a899ca63ac..b09d133495 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -31,7 +31,6 @@ #include "nvim/map.h" #include "nvim/memline.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/os/os.h" #include "nvim/screen.h" diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index b5553060a1..b746e03625 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -34,7 +34,6 @@ #include "nvim/map.h" #include "nvim/memline.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/os/os.h" #include "nvim/regexp.h" diff --git a/src/nvim/main.c b/src/nvim/main.c index 29510e26ff..cbd1f53727 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -39,7 +39,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 42117bc762..ce44f6c619 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -42,13 +42,13 @@ #include "nvim/eval.h" #include "nvim/fileio.h" #include "nvim/func_attr.h" +#include "nvim/getchar.h" #include "nvim/iconv.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/os.h" #include "nvim/path.h" diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 40da4b48f8..08521c0dc3 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -51,6 +51,7 @@ #include "nvim/fileio.h" #include "nvim/func_attr.h" #include "nvim/getchar.h" +#include "nvim/input.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/mbyte.h" @@ -58,7 +59,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 3d621ebbb7..677ff8f522 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -17,7 +17,6 @@ #include "nvim/memfile.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/sign.h" #include "nvim/ui.h" #include "nvim/vim.h" diff --git a/src/nvim/menu.c b/src/nvim/menu.c index d596b31062..ac4d52c392 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -22,7 +22,6 @@ #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/screen.h" #include "nvim/state.h" #include "nvim/strings.h" diff --git a/src/nvim/message.c b/src/nvim/message.c index eaf7e2622a..befca8c76b 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -23,12 +23,12 @@ #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/highlight.h" +#include "nvim/input.h" #include "nvim/keymap.h" #include "nvim/main.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/normal.h" #include "nvim/ops.h" @@ -76,6 +76,8 @@ static int msg_hist_len = 0; static FILE *verbose_fd = NULL; static int verbose_did_open = FALSE; +bool keep_msg_more = false; // keep_msg was set by msgmore() + /* * When writing messages to the screen, there are many different situations. * A number of variables is used to remember the current state: @@ -1298,6 +1300,49 @@ void set_keep_msg(char *s, int attr) keep_msg_attr = attr; } +void msgmore(long n) +{ + long pn; + + if (global_busy // no messages now, wait until global is finished + || !messaging()) { // 'lazyredraw' set, don't do messages now + return; + } + + // We don't want to overwrite another important message, but do overwrite + // a previous "more lines" or "fewer lines" message, so that "5dd" and + // then "put" reports the last action. + if (keep_msg != NULL && !keep_msg_more) { + return; + } + + if (n > 0) { + pn = n; + } else { + pn = -n; + } + + if (pn > p_report) { + if (n > 0) { + vim_snprintf(msg_buf, MSG_BUF_LEN, + NGETTEXT("%ld more line", "%ld more lines", pn), + pn); + } else { + vim_snprintf(msg_buf, MSG_BUF_LEN, + NGETTEXT("%ld line less", "%ld fewer lines", pn), + pn); + } + if (got_int) { + xstrlcat(msg_buf, _(" (Interrupted)"), MSG_BUF_LEN); + } + if (msg(msg_buf)) { + set_keep_msg(msg_buf, 0); + keep_msg_more = true; + } + } +} + + void msg_ext_set_kind(const char *msg_kind) { // Don't change the label of an existing batch: diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c deleted file mode 100644 index 3873a5c49a..0000000000 --- a/src/nvim/misc1.c +++ /dev/null @@ -1,912 +0,0 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - -/* - * misc1.c: functions that didn't seem to fit elsewhere - */ - -#include <assert.h> -#include <inttypes.h> -#include <limits.h> -#include <stdbool.h> -#include <string.h> - -#include "nvim/ascii.h" -#include "nvim/buffer.h" -#include "nvim/buffer_updates.h" -#include "nvim/charset.h" -#include "nvim/cursor.h" -#include "nvim/diff.h" -#include "nvim/edit.h" -#include "nvim/eval.h" -#include "nvim/event/stream.h" -#include "nvim/ex_cmds.h" -#include "nvim/ex_docmd.h" -#include "nvim/ex_getln.h" -#include "nvim/fileio.h" -#include "nvim/fold.h" -#include "nvim/func_attr.h" -#include "nvim/garray.h" -#include "nvim/getchar.h" -#include "nvim/indent.h" -#include "nvim/indent_c.h" -#include "nvim/main.h" -#include "nvim/mbyte.h" -#include "nvim/memline.h" -#include "nvim/memory.h" -#include "nvim/message.h" -#include "nvim/misc1.h" -#include "nvim/mouse.h" -#include "nvim/move.h" -#include "nvim/option.h" -#include "nvim/os/input.h" -#include "nvim/os/os.h" -#include "nvim/os/shell.h" -#include "nvim/os/signal.h" -#include "nvim/os/time.h" -#include "nvim/os_unix.h" -#include "nvim/quickfix.h" -#include "nvim/regexp.h" -#include "nvim/screen.h" -#include "nvim/search.h" -#include "nvim/state.h" -#include "nvim/strings.h" -#include "nvim/tag.h" -#include "nvim/ui.h" -#include "nvim/undo.h" -#include "nvim/vim.h" -#include "nvim/window.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "misc1.c.generated.h" -#endif -// All user names (for ~user completion as done by shell). -static garray_T ga_users = GA_EMPTY_INIT_VALUE; - -/* - * get_leader_len() returns the length in bytes of the prefix of the given - * string which introduces a comment. If this string is not a comment then - * 0 is returned. - * When "flags" is not NULL, it is set to point to the flags of the recognized - * comment leader. - * "backward" must be true for the "O" command. - * If "include_space" is set, include trailing whitespace while calculating the - * length. - */ -int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_space) -{ - int i, j; - int result; - int got_com = FALSE; - int found_one; - char_u part_buf[COM_MAX_LEN]; // buffer for one option part - char_u *string; // pointer to comment string - char_u *list; - int middle_match_len = 0; - char_u *prev_list; - char_u *saved_flags = NULL; - - result = i = 0; - while (ascii_iswhite(line[i])) { // leading white space is ignored - ++i; - } - - /* - * Repeat to match several nested comment strings. - */ - while (line[i] != NUL) { - /* - * scan through the 'comments' option for a match - */ - found_one = FALSE; - for (list = curbuf->b_p_com; *list;) { - // Get one option part into part_buf[]. Advance "list" to next - // one. Put "string" at start of string. - if (!got_com && flags != NULL) { - *flags = list; // remember where flags started - } - prev_list = list; - (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ","); - string = vim_strchr(part_buf, ':'); - if (string == NULL) { // missing ':', ignore this part - continue; - } - *string++ = NUL; // isolate flags from string - - // If we found a middle match previously, use that match when this - // is not a middle or end. - if (middle_match_len != 0 - && vim_strchr(part_buf, COM_MIDDLE) == NULL - && vim_strchr(part_buf, COM_END) == NULL) { - break; - } - - // When we already found a nested comment, only accept further - // nested comments. - if (got_com && vim_strchr(part_buf, COM_NEST) == NULL) { - continue; - } - - // When 'O' flag present and using "O" command skip this one. - if (backward && vim_strchr(part_buf, COM_NOBACK) != NULL) { - continue; - } - - // Line contents and string must match. - // When string starts with white space, must have some white space - // (but the amount does not need to match, there might be a mix of - // TABs and spaces). - if (ascii_iswhite(string[0])) { - if (i == 0 || !ascii_iswhite(line[i - 1])) { - continue; // missing white space - } - while (ascii_iswhite(string[0])) { - ++string; - } - } - for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j) { - } - if (string[j] != NUL) { - continue; // string doesn't match - } - // When 'b' flag used, there must be white space or an - // end-of-line after the string in the line. - if (vim_strchr(part_buf, COM_BLANK) != NULL - && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) { - continue; - } - - // We have found a match, stop searching unless this is a middle - // comment. The middle comment can be a substring of the end - // comment in which case it's better to return the length of the - // end comment and its flags. Thus we keep searching with middle - // and end matches and use an end match if it matches better. - if (vim_strchr(part_buf, COM_MIDDLE) != NULL) { - if (middle_match_len == 0) { - middle_match_len = j; - saved_flags = prev_list; - } - continue; - } - if (middle_match_len != 0 && j > middle_match_len) { - // Use this match instead of the middle match, since it's a - // longer thus better match. - middle_match_len = 0; - } - - if (middle_match_len == 0) { - i += j; - } - found_one = TRUE; - break; - } - - if (middle_match_len != 0) { - // Use the previously found middle match after failing to find a - // match with an end. - if (!got_com && flags != NULL) { - *flags = saved_flags; - } - i += middle_match_len; - found_one = TRUE; - } - - // No match found, stop scanning. - if (!found_one) { - break; - } - - result = i; - - // Include any trailing white space. - while (ascii_iswhite(line[i])) { - ++i; - } - - if (include_space) { - result = i; - } - - // If this comment doesn't nest, stop here. - got_com = TRUE; - if (vim_strchr(part_buf, COM_NEST) == NULL) { - break; - } - } - return result; -} - -/* - * Return the offset at which the last comment in line starts. If there is no - * comment in the whole line, -1 is returned. - * - * When "flags" is not null, it is set to point to the flags describing the - * recognized comment leader. - */ -int get_last_leader_offset(char_u *line, char_u **flags) -{ - int result = -1; - int i, j; - int lower_check_bound = 0; - char_u *string; - char_u *com_leader; - char_u *com_flags; - char_u *list; - int found_one; - char_u part_buf[COM_MAX_LEN]; // buffer for one option part - - /* - * Repeat to match several nested comment strings. - */ - i = (int)STRLEN(line); - while (--i >= lower_check_bound) { - /* - * scan through the 'comments' option for a match - */ - found_one = FALSE; - for (list = curbuf->b_p_com; *list;) { - char_u *flags_save = list; - - /* - * Get one option part into part_buf[]. Advance list to next one. - * put string at start of string. - */ - (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ","); - string = vim_strchr(part_buf, ':'); - if (string == NULL) { // If everything is fine, this cannot actually - // happen. - continue; - } - *string++ = NUL; // Isolate flags from string. - com_leader = string; - - /* - * Line contents and string must match. - * When string starts with white space, must have some white space - * (but the amount does not need to match, there might be a mix of - * TABs and spaces). - */ - if (ascii_iswhite(string[0])) { - if (i == 0 || !ascii_iswhite(line[i - 1])) { - continue; - } - while (ascii_iswhite(*string)) { - string++; - } - } - for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j) { - // do nothing - } - if (string[j] != NUL) { - continue; - } - - /* - * When 'b' flag used, there must be white space or an - * end-of-line after the string in the line. - */ - if (vim_strchr(part_buf, COM_BLANK) != NULL - && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) { - continue; - } - - if (vim_strchr(part_buf, COM_MIDDLE) != NULL) { - // For a middlepart comment, only consider it to match if - // everything before the current position in the line is - // whitespace. Otherwise we would think we are inside a - // comment if the middle part appears somewhere in the middle - // of the line. E.g. for C the "*" appears often. - for (j = 0; j <= i && ascii_iswhite(line[j]); j++) { - } - if (j < i) { - continue; - } - } - - /* - * We have found a match, stop searching. - */ - found_one = TRUE; - - if (flags) { - *flags = flags_save; - } - com_flags = flags_save; - - break; - } - - if (found_one) { - char_u part_buf2[COM_MAX_LEN]; // buffer for one option part - int len1, len2, off; - - result = i; - /* - * If this comment nests, continue searching. - */ - if (vim_strchr(part_buf, COM_NEST) != NULL) { - continue; - } - - lower_check_bound = i; - - // Let's verify whether the comment leader found is a substring - // of other comment leaders. If it is, let's adjust the - // lower_check_bound so that we make sure that we have determined - // the comment leader correctly. - - while (ascii_iswhite(*com_leader)) { - ++com_leader; - } - len1 = (int)STRLEN(com_leader); - - for (list = curbuf->b_p_com; *list;) { - char_u *flags_save = list; - - (void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ","); - if (flags_save == com_flags) { - continue; - } - string = vim_strchr(part_buf2, ':'); - ++string; - while (ascii_iswhite(*string)) { - ++string; - } - len2 = (int)STRLEN(string); - if (len2 == 0) { - continue; - } - - // Now we have to verify whether string ends with a substring - // beginning the com_leader. - for (off = (len2 > i ? i : len2); off > 0 && off + len1 > len2;) { - --off; - if (!STRNCMP(string + off, com_leader, len2 - off)) { - if (i - off < lower_check_bound) { - lower_check_bound = i - off; - } - } - } - } - } - } - return result; -} - -/// Ask for a reply from the user, 'y' or 'n' -/// -/// No other characters are accepted, the message is repeated until a valid -/// reply is entered or <C-c> is hit. -/// -/// @param[in] str Prompt: question to ask user. Is always followed by -/// " (y/n)?". -/// @param[in] direct Determines what function to use to get user input. If -/// true then ui_inchar() will be used, otherwise vgetc(). -/// I.e. when direct is true then characters are obtained -/// directly from the user without buffers involved. -/// -/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt. -int ask_yesno(const char *const str, const bool direct) -{ - const int save_State = State; - - no_wait_return++; - State = CONFIRM; // Mouse behaves like with :confirm. - setmouse(); // Disable mouse in xterm. - no_mapping++; - - int r = ' '; - while (r != 'y' && r != 'n') { - // Same highlighting as for wait_return. - smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str); - if (direct) { - r = get_keystroke(NULL); - } else { - r = plain_vgetc(); - } - if (r == Ctrl_C || r == ESC) { - r = 'n'; - } - msg_putchar(r); // Show what you typed. - ui_flush(); - } - no_wait_return--; - State = save_State; - setmouse(); - no_mapping--; - - return r; -} - -/* - * Return TRUE if "c" is a mouse key. - */ -int is_mouse_key(int c) -{ - return c == K_LEFTMOUSE - || c == K_LEFTMOUSE_NM - || c == K_LEFTDRAG - || c == K_LEFTRELEASE - || c == K_LEFTRELEASE_NM - || c == K_MOUSEMOVE - || c == K_MIDDLEMOUSE - || c == K_MIDDLEDRAG - || c == K_MIDDLERELEASE - || c == K_RIGHTMOUSE - || c == K_RIGHTDRAG - || c == K_RIGHTRELEASE - || c == K_MOUSEDOWN - || c == K_MOUSEUP - || c == K_MOUSELEFT - || c == K_MOUSERIGHT - || c == K_X1MOUSE - || c == K_X1DRAG - || c == K_X1RELEASE - || c == K_X2MOUSE - || c == K_X2DRAG - || c == K_X2RELEASE; -} - -/* - * Get a key stroke directly from the user. - * Ignores mouse clicks and scrollbar events, except a click for the left - * button (used at the more prompt). - * Doesn't use vgetc(), because it syncs undo and eats mapped characters. - * Disadvantage: typeahead is ignored. - * Translates the interrupt character for unix to ESC. - */ -int get_keystroke(MultiQueue *events) -{ - char_u *buf = NULL; - int buflen = 150; - int maxlen; - int len = 0; - int n; - int save_mapped_ctrl_c = mapped_ctrl_c; - int waited = 0; - - mapped_ctrl_c = 0; // mappings are not used here - for (;;) { - // flush output before waiting - ui_flush(); - // Leave some room for check_termcode() to insert a key code into (max - // 5 chars plus NUL). And fix_input_buffer() can triple the number of - // bytes. - maxlen = (buflen - 6 - len) / 3; - if (buf == NULL) { - buf = xmalloc((size_t)buflen); - } else if (maxlen < 10) { - // Need some more space. This might happen when receiving a long - // escape sequence. - buflen += 100; - buf = xrealloc(buf, (size_t)buflen); - maxlen = (buflen - 6 - len) / 3; - } - - // First time: blocking wait. Second time: wait up to 100ms for a - // terminal code to complete. - n = os_inchar(buf + len, maxlen, len == 0 ? -1L : 100L, 0, events); - if (n > 0) { - // Replace zero and CSI by a special key code. - n = fix_input_buffer(buf + len, n); - len += n; - waited = 0; - } else if (len > 0) { - ++waited; // keep track of the waiting time - } - if (n > 0) { // found a termcode: adjust length - len = n; - } - if (len == 0) { // nothing typed yet - continue; - } - - // Handle modifier and/or special key code. - n = buf[0]; - if (n == K_SPECIAL) { - n = TO_SPECIAL(buf[1], buf[2]); - if (buf[1] == KS_MODIFIER - || n == K_IGNORE - || (is_mouse_key(n) && n != K_LEFTMOUSE)) { - if (buf[1] == KS_MODIFIER) { - mod_mask = buf[2]; - } - len -= 3; - if (len > 0) { - memmove(buf, buf + 3, (size_t)len); - } - continue; - } - break; - } - if (MB_BYTE2LEN(n) > len) { - // more bytes to get. - continue; - } - buf[len >= buflen ? buflen - 1 : len] = NUL; - n = utf_ptr2char(buf); - break; - } - xfree(buf); - - mapped_ctrl_c = save_mapped_ctrl_c; - return n; -} - -/// Get a number from the user. -/// When "mouse_used" is not NULL allow using the mouse. -/// -/// @param colon allow colon to abort -int get_number(int colon, int *mouse_used) -{ - int n = 0; - int c; - int typed = 0; - - if (mouse_used != NULL) { - *mouse_used = FALSE; - } - - // When not printing messages, the user won't know what to type, return a - // zero (as if CR was hit). - if (msg_silent != 0) { - return 0; - } - - no_mapping++; - for (;;) { - ui_cursor_goto(msg_row, msg_col); - c = safe_vgetc(); - if (ascii_isdigit(c)) { - n = n * 10 + c - '0'; - msg_putchar(c); - ++typed; - } else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H) { - if (typed > 0) { - msg_puts("\b \b"); - --typed; - } - n /= 10; - } else if (mouse_used != NULL && c == K_LEFTMOUSE) { - *mouse_used = TRUE; - n = mouse_row + 1; - break; - } else if (n == 0 && c == ':' && colon) { - stuffcharReadbuff(':'); - if (!exmode_active) { - cmdline_row = msg_row; - } - skip_redraw = true; // skip redraw once - do_redraw = false; - break; - } else if (c == Ctrl_C || c == ESC || c == 'q') { - n = 0; - break; - } else if (c == CAR || c == NL) { - break; - } - } - no_mapping--; - return n; -} - -/* - * Ask the user to enter a number. - * When "mouse_used" is not NULL allow using the mouse and in that case return - * the line number. - */ -int prompt_for_number(int *mouse_used) -{ - int i; - int save_cmdline_row; - int save_State; - - // When using ":silent" assume that <CR> was entered. - if (mouse_used != NULL) { - msg_puts(_("Type number and <Enter> or click with the mouse " - "(q or empty cancels): ")); - } else { - msg_puts(_("Type number and <Enter> (q or empty cancels): ")); - } - - /* Set the state such that text can be selected/copied/pasted and we still - * get mouse events. */ - save_cmdline_row = cmdline_row; - cmdline_row = 0; - save_State = State; - State = ASKMORE; // prevents a screen update when using a timer - // May show different mouse shape. - setmouse(); - - i = get_number(TRUE, mouse_used); - if (KeyTyped) { - // don't call wait_return() now - if (msg_row > 0) { - cmdline_row = msg_row - 1; - } - need_wait_return = false; - msg_didany = false; - msg_didout = false; - } else { - cmdline_row = save_cmdline_row; - } - State = save_State; - // May need to restore mouse shape. - setmouse(); - - return i; -} - -void msgmore(long n) -{ - long pn; - - if (global_busy // no messages now, wait until global is finished - || !messaging()) { // 'lazyredraw' set, don't do messages now - return; - } - - // We don't want to overwrite another important message, but do overwrite - // a previous "more lines" or "fewer lines" message, so that "5dd" and - // then "put" reports the last action. - if (keep_msg != NULL && !keep_msg_more) { - return; - } - - if (n > 0) { - pn = n; - } else { - pn = -n; - } - - if (pn > p_report) { - if (n > 0) { - vim_snprintf(msg_buf, MSG_BUF_LEN, - NGETTEXT("%ld more line", "%ld more lines", pn), - pn); - } else { - vim_snprintf(msg_buf, MSG_BUF_LEN, - NGETTEXT("%ld line less", "%ld fewer lines", pn), - pn); - } - if (got_int) { - xstrlcat(msg_buf, _(" (Interrupted)"), MSG_BUF_LEN); - } - if (msg(msg_buf)) { - set_keep_msg(msg_buf, 0); - keep_msg_more = true; - } - } -} - -/* - * flush map and typeahead buffers and give a warning for an error - */ -void beep_flush(void) -{ - if (emsg_silent == 0) { - flush_buffers(FLUSH_MINIMAL); - vim_beep(BO_ERROR); - } -} - -// Give a warning for an error -// val is one of the BO_ values, e.g., BO_OPER -void vim_beep(unsigned val) -{ - called_vim_beep = true; - - if (emsg_silent == 0) { - if (!((bo_flags & val) || (bo_flags & BO_ALL))) { - static int beeps = 0; - static uint64_t start_time = 0; - - // Only beep up to three times per half a second, - // otherwise a sequence of beeps would freeze Vim. - if (start_time == 0 || os_hrtime() - start_time > 500000000u) { - beeps = 0; - start_time = os_hrtime(); - } - beeps++; - if (beeps <= 3) { - if (p_vb) { - ui_call_visual_bell(); - } else { - ui_call_bell(); - } - } - } - - // When 'debug' contains "beep" produce a message. If we are sourcing - // a script or executing a function give the user a hint where the beep - // comes from. - if (vim_strchr(p_debug, 'e') != NULL) { - msg_source(HL_ATTR(HLF_W)); - msg_attr(_("Beep!"), HL_ATTR(HLF_W)); - } - } -} - -#if defined(EXITFREE) - -void free_users(void) -{ - ga_clear_strings(&ga_users); -} - -#endif - -/* - * Find all user names for user completion. - * Done only once and then cached. - */ -static void init_users(void) -{ - static int lazy_init_done = FALSE; - - if (lazy_init_done) { - return; - } - - lazy_init_done = TRUE; - - os_get_usernames(&ga_users); -} - -/* - * Function given to ExpandGeneric() to obtain an user names. - */ -char_u *get_users(expand_T *xp, int idx) -{ - init_users(); - if (idx < ga_users.ga_len) { - return ((char_u **)ga_users.ga_data)[idx]; - } - return NULL; -} - -/* - * Check whether name matches a user name. Return: - * 0 if name does not match any user name. - * 1 if name partially matches the beginning of a user name. - * 2 is name fully matches a user name. - */ -int match_user(char_u *name) -{ - int n = (int)STRLEN(name); - int result = 0; - - init_users(); - for (int i = 0; i < ga_users.ga_len; i++) { - if (STRCMP(((char_u **)ga_users.ga_data)[i], name) == 0) { - return 2; // full match - } - if (STRNCMP(((char_u **)ga_users.ga_data)[i], name, n) == 0) { - result = 1; // partial match - } - } - return result; -} - -/// os_call_shell() wrapper. Handles 'verbose', :profile, and v:shell_error. -/// Invalidates cached tags. -/// -/// @return shell command exit code -int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg) -{ - int retval; - proftime_T wait_time; - - if (p_verbose > 3) { - verbose_enter(); - smsg(_("Executing command: \"%s\""), cmd == NULL ? p_sh : cmd); - msg_putchar('\n'); - verbose_leave(); - } - - if (do_profiling == PROF_YES) { - prof_child_enter(&wait_time); - } - - if (*p_sh == NUL) { - emsg(_(e_shellempty)); - retval = -1; - } else { - // The external command may update a tags file, clear cached tags. - tag_freematch(); - - retval = os_call_shell(cmd, opts, extra_shell_arg); - } - - set_vim_var_nr(VV_SHELL_ERROR, (varnumber_T)retval); - if (do_profiling == PROF_YES) { - prof_child_exit(&wait_time); - } - - return retval; -} - -/// Get the stdout of an external command. -/// If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not -/// NULL store the length there. -/// -/// @param cmd command to execute -/// @param infile optional input file name -/// @param flags can be kShellOptSilent or 0 -/// @param ret_len length of the stdout -/// -/// @return an allocated string, or NULL for error. -char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, size_t *ret_len) -{ - char_u *buffer = NULL; - - if (check_secure()) { - return NULL; - } - - // get a name for the temp file - char_u *tempname = vim_tempname(); - if (tempname == NULL) { - emsg(_(e_notmp)); - return NULL; - } - - // Add the redirection stuff - char_u *command = make_filter_cmd(cmd, infile, tempname); - - /* - * Call the shell to execute the command (errors are ignored). - * Don't check timestamps here. - */ - ++no_check_timestamps; - call_shell(command, kShellOptDoOut | kShellOptExpand | flags, NULL); - --no_check_timestamps; - - xfree(command); - - // read the names from the file into memory - FILE *fd = os_fopen((char *)tempname, READBIN); - - if (fd == NULL) { - semsg(_(e_notopen), tempname); - goto done; - } - - fseek(fd, 0L, SEEK_END); - size_t len = (size_t)ftell(fd); // get size of temp file - fseek(fd, 0L, SEEK_SET); - - buffer = xmalloc(len + 1); - size_t i = fread((char *)buffer, 1, len, fd); - fclose(fd); - os_remove((char *)tempname); - if (i != len) { - semsg(_(e_notread), tempname); - XFREE_CLEAR(buffer); - } else if (ret_len == NULL) { - // Change NUL into SOH, otherwise the string is truncated. - for (i = 0; i < len; ++i) { - if (buffer[i] == NUL) { - buffer[i] = 1; - } - } - - buffer[len] = NUL; // make sure the buffer is terminated - } else { - *ret_len = len; - } - -done: - xfree(tempname); - return buffer; -} - -/* - * Return TRUE when need to go to Insert mode because of 'insertmode'. - * Don't do this when still processing a command or a mapping. - * Don't do this when inside a ":normal" command. - */ -int goto_im(void) -{ - return p_im && stuff_empty() && typebuf_typed(); -} diff --git a/src/nvim/misc1.h b/src/nvim/misc1.h deleted file mode 100644 index 14ca361073..0000000000 --- a/src/nvim/misc1.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef NVIM_MISC1_H -#define NVIM_MISC1_H - -#include "nvim/os/shell.h" -#include "nvim/vim.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "misc1.h.generated.h" -#endif -#endif // NVIM_MISC1_H diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 386094e509..5d007fb173 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -10,7 +10,6 @@ #include "nvim/diff.h" #include "nvim/fold.h" #include "nvim/memline.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/os_unix.h" @@ -31,6 +30,32 @@ static linenr_T orig_topline = 0; static int orig_topfill = 0; +/// Return true if "c" is a mouse key. +bool is_mouse_key(int c) +{ + return c == K_LEFTMOUSE + || c == K_LEFTMOUSE_NM + || c == K_LEFTDRAG + || c == K_LEFTRELEASE + || c == K_LEFTRELEASE_NM + || c == K_MOUSEMOVE + || c == K_MIDDLEMOUSE + || c == K_MIDDLEDRAG + || c == K_MIDDLERELEASE + || c == K_RIGHTMOUSE + || c == K_RIGHTDRAG + || c == K_RIGHTRELEASE + || c == K_MOUSEDOWN + || c == K_MOUSEUP + || c == K_MOUSELEFT + || c == K_MOUSERIGHT + || c == K_X1MOUSE + || c == K_X1DRAG + || c == K_X1RELEASE + || c == K_X2MOUSE + || c == K_X2DRAG + || c == K_X2RELEASE; +} /// Move the cursor to the specified row and column on the screen. /// Change current window if necessary. Returns an integer with the /// CURSOR_MOVED bit set if the cursor has moved or unset otherwise. diff --git a/src/nvim/move.c b/src/nvim/move.c index d80e63e79d..0a672000e4 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -23,9 +23,9 @@ #include "nvim/diff.h" #include "nvim/edit.h" #include "nvim/fold.h" +#include "nvim/getchar.h" #include "nvim/mbyte.h" #include "nvim/memline.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/plines.h" diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index a1a1f0f8c0..299651ee97 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -24,7 +24,6 @@ #include "nvim/map.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/os/input.h" diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 9332c55b5f..95a521c0d8 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -40,7 +40,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 52c382028e..9bc63477e9 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -38,7 +38,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" diff --git a/src/nvim/option.c b/src/nvim/option.c index 05929193b8..2ceb1bd992 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -55,7 +55,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index e9963516fc..24c7678633 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -21,7 +21,6 @@ #include "nvim/assert.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/os.h" #include "nvim/os/os_defs.h" diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index fc9bbbc8b0..3790eba212 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -17,7 +17,6 @@ #include "nvim/main.h" #include "nvim/mbyte.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/os/input.h" #include "nvim/state.h" diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 6ef0aa1091..e618b2788b 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -12,6 +12,7 @@ #include "nvim/event/libuv_process.h" #include "nvim/event/loop.h" #include "nvim/event/rstream.h" +#include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/fileio.h" #include "nvim/lib/kvec.h" @@ -20,7 +21,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option_defs.h" #include "nvim/os/shell.h" #include "nvim/os/signal.h" @@ -28,6 +28,7 @@ #include "nvim/screen.h" #include "nvim/strings.h" #include "nvim/types.h" +#include "nvim/tag.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -681,6 +682,116 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args) return exitcode; } +/// os_call_shell() wrapper. Handles 'verbose', :profile, and v:shell_error. +/// Invalidates cached tags. +/// +/// @return shell command exit code +int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg) +{ + int retval; + proftime_T wait_time; + + if (p_verbose > 3) { + verbose_enter(); + smsg(_("Executing command: \"%s\""), cmd == NULL ? p_sh : cmd); + msg_putchar('\n'); + verbose_leave(); + } + + if (do_profiling == PROF_YES) { + prof_child_enter(&wait_time); + } + + if (*p_sh == NUL) { + emsg(_(e_shellempty)); + retval = -1; + } else { + // The external command may update a tags file, clear cached tags. + tag_freematch(); + + retval = os_call_shell(cmd, opts, extra_shell_arg); + } + + set_vim_var_nr(VV_SHELL_ERROR, (varnumber_T)retval); + if (do_profiling == PROF_YES) { + prof_child_exit(&wait_time); + } + + return retval; +} + +/// Get the stdout of an external command. +/// If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not +/// NULL store the length there. +/// +/// @param cmd command to execute +/// @param infile optional input file name +/// @param flags can be kShellOptSilent or 0 +/// @param ret_len length of the stdout +/// +/// @return an allocated string, or NULL for error. +char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, size_t *ret_len) +{ + char_u *buffer = NULL; + + if (check_secure()) { + return NULL; + } + + // get a name for the temp file + char_u *tempname = vim_tempname(); + if (tempname == NULL) { + emsg(_(e_notmp)); + return NULL; + } + + // Add the redirection stuff + char_u *command = make_filter_cmd(cmd, infile, tempname); + + // Call the shell to execute the command (errors are ignored). + // Don't check timestamps here. + no_check_timestamps++; + call_shell(command, kShellOptDoOut | kShellOptExpand | flags, NULL); + no_check_timestamps--; + + xfree(command); + + // read the names from the file into memory + FILE *fd = os_fopen((char *)tempname, READBIN); + + if (fd == NULL) { + semsg(_(e_notopen), tempname); + goto done; + } + + fseek(fd, 0L, SEEK_END); + size_t len = (size_t)ftell(fd); // get size of temp file + fseek(fd, 0L, SEEK_SET); + + buffer = xmalloc(len + 1); + size_t i = fread((char *)buffer, 1, len, fd); + fclose(fd); + os_remove((char *)tempname); + if (i != len) { + semsg(_(e_notread), tempname); + XFREE_CLEAR(buffer); + } else if (ret_len == NULL) { + // Change NUL into SOH, otherwise the string is truncated. + for (i = 0; i < len; i++) { + if (buffer[i] == NUL) { + buffer[i] = 1; + } + } + + buffer[len] = NUL; // make sure the buffer is terminated + } else { + *ret_len = len; + } + +done: + xfree(tempname); + return buffer; +} /// os_system - synchronously execute a command in the shell /// /// example: diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index 0d125ec964..a8bf68a1a2 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -18,7 +18,6 @@ #include "nvim/main.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/os/signal.h" #include "nvim/vim.h" diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c index 9952e2b387..e0ce3fec31 100644 --- a/src/nvim/os/users.c +++ b/src/nvim/os/users.c @@ -18,6 +18,9 @@ # include <lm.h> #endif +// All user names (for ~user completion as done by shell). +static garray_T ga_users = GA_EMPTY_INIT_VALUE; + // Add a user name to the list of users in garray_T *users. // Do nothing if user name is NULL or empty. static void add_user(garray_T *users, char *user, bool need_copy) @@ -157,3 +160,60 @@ char *os_get_user_directory(const char *name) return NULL; } + +#if defined(EXITFREE) + +void free_users(void) +{ + ga_clear_strings(&ga_users); +} + +#endif + +/// Find all user names for user completion. +/// +/// Done only once and then cached. +static void init_users(void) +{ + static int lazy_init_done = false; + + if (lazy_init_done) { + return; + } + + lazy_init_done = true; + + os_get_usernames(&ga_users); +} + +/// Given to ExpandGeneric() to obtain an user names. +char_u *get_users(expand_T *xp, int idx) +{ + init_users(); + if (idx < ga_users.ga_len) { + return ((char_u **)ga_users.ga_data)[idx]; + } + return NULL; +} + +/// Check whether name matches a user name. +/// +/// @return 0 if name does not match any user name. +/// 1 if name partially matches the beginning of a user name. +/// 2 is name fully matches a user name. +int match_user(char_u *name) +{ + int n = (int)STRLEN(name); + int result = 0; + + init_users(); + for (int i = 0; i < ga_users.ga_len; i++) { + if (STRCMP(((char_u **)ga_users.ga_data)[i], name) == 0) { + return 2; // full match + } + if (STRNCMP(((char_u **)ga_users.ga_data)[i], name, n) == 0) { + result = 1; // partial match + } + } + return result; +} diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index 9396a5896a..1398dba0e4 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -20,7 +20,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/os/input.h" diff --git a/src/nvim/path.c b/src/nvim/path.c index 01ac88d537..674d67e21a 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -18,7 +18,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 6a192d148f..2204e2a62a 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -27,7 +27,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index aa8d1503fc..45e580dbee 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -64,7 +64,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/os/input.h" #include "nvim/plines.h" #include "nvim/garray.h" diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index be365d4ab8..1c04cb16b3 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -12,7 +12,6 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/lua/executor.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/os.h" #include "nvim/runtime.h" diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 0d06c45229..a938a3b062 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -98,7 +98,6 @@ #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" diff --git a/src/nvim/search.c b/src/nvim/search.c index f45d709b91..906c9a6f47 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -14,6 +14,7 @@ #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/charset.h" +#include "nvim/change.h" #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" @@ -31,7 +32,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" @@ -1354,6 +1354,10 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, } retval = 1; // pattern found + if (sia && sia->sa_wrapped) { + apply_autocmds(EVENT_SEARCHWRAPPED, NULL, NULL, false, NULL); + } + /* * Add character and/or line offset */ diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 85d1e139bf..bd31e98faa 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -94,12 +94,12 @@ #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/hashtab.h" +#include "nvim/input.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/normal.h" #include "nvim/option.h" #include "nvim/os/input.h" diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index c65fcc1180..42bb3c61a5 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -237,7 +237,6 @@ #include "nvim/fileio.h" #include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" @@ -4447,10 +4446,10 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname) putc(SN_PREFCOND, fd); // <sectionID> putc(SNF_REQUIRED, fd); // <sectionflags> - size_t l = (size_t)write_spell_prefcond(NULL, &spin->si_prefcond); + size_t l = (size_t)write_spell_prefcond(NULL, &spin->si_prefcond, &fwv); put_bytes(fd, l, 4); // <sectionlen> - write_spell_prefcond(fd, &spin->si_prefcond); + write_spell_prefcond(fd, &spin->si_prefcond, &fwv); } // SN_REP: <repcount> <rep> ... @@ -5794,7 +5793,7 @@ static int set_spell_finish(spelltab_T *new_st) // Write the table with prefix conditions to the .spl file. // When "fd" is NULL only count the length of what is written. -static int write_spell_prefcond(FILE *fd, garray_T *gap) +static int write_spell_prefcond(FILE *fd, garray_T *gap, size_t *fwv) { assert(gap->ga_len >= 0); @@ -5802,8 +5801,7 @@ static int write_spell_prefcond(FILE *fd, garray_T *gap) put_bytes(fd, (uintmax_t)gap->ga_len, 2); // <prefcondcnt> } size_t totlen = 2 + (size_t)gap->ga_len; // <prefcondcnt> and <condlen> bytes - size_t x = 1; // collect return value of fwrite() - for (int i = 0; i < gap->ga_len; ++i) { + for (int i = 0; i < gap->ga_len; i++) { // <prefcond> : <condlen> <condstr> char_u *p = ((char_u **)gap->ga_data)[i]; if (p != NULL) { @@ -5811,7 +5809,7 @@ static int write_spell_prefcond(FILE *fd, garray_T *gap) if (fd != NULL) { assert(len <= INT_MAX); fputc((int)len, fd); - x &= fwrite(p, len, 1, fd); + *fwv &= fwrite(p, len, 1, fd); } totlen += len; } else if (fd != NULL) { diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 27f93fe4ce..e2a8108c45 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -31,7 +31,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/ops.h" #include "nvim/option.h" diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index a39f78b751..a9447165c2 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -32,7 +32,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 483d2df778..a10a2a0c32 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -25,11 +25,11 @@ #include "nvim/fold.h" #include "nvim/garray.h" #include "nvim/if_cscope.h" +#include "nvim/input.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/os/input.h" diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 2d32b6bac4..04068a3cb8 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -64,7 +64,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/option.h" diff --git a/src/nvim/ui.c b/src/nvim/ui.c index aad72af025..1aadaf5c9d 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -23,7 +23,6 @@ #include "nvim/main.h" #include "nvim/mbyte.h" #include "nvim/memory.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" @@ -300,6 +299,44 @@ void ui_busy_stop(void) } } +/// Emit a bell or visualbell as a warning +/// +/// val is one of the BO_ values, e.g., BO_OPER +void vim_beep(unsigned val) +{ + called_vim_beep = true; + + if (emsg_silent == 0) { + if (!((bo_flags & val) || (bo_flags & BO_ALL))) { + static int beeps = 0; + static uint64_t start_time = 0; + + // Only beep up to three times per half a second, + // otherwise a sequence of beeps would freeze Vim. + if (start_time == 0 || os_hrtime() - start_time > 500000000u) { + beeps = 0; + start_time = os_hrtime(); + } + beeps++; + if (beeps <= 3) { + if (p_vb) { + ui_call_visual_bell(); + } else { + ui_call_bell(); + } + } + } + + // When 'debug' contains "beep" produce a message. If we are sourcing + // a script or executing a function give the user a hint where the beep + // comes from. + if (vim_strchr(p_debug, 'e') != NULL) { + msg_source(HL_ATTR(HLF_W)); + msg_attr(_("Beep!"), HL_ATTR(HLF_W)); + } + } +} + void ui_attach_impl(UI *ui, uint64_t chanid) { if (ui_count == MAX_UI_COUNT) { diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 3e7eb6d01e..d18f35a43a 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -93,12 +93,12 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/garray.h" +#include "nvim/getchar.h" #include "nvim/lib/kvec.h" #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/os/os.h" diff --git a/src/nvim/window.c b/src/nvim/window.c index be963d8374..c711f462d1 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -30,7 +30,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/misc1.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index 45a01be620..a8f538b951 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -420,7 +420,7 @@ describe('API/extmarks', function() end) it('marks move with open line', function() - -- open_line in misc1.c + -- open_line in change.c -- testing marks below are also moved feed("yyP") set_extmark(ns, marks[1], 0, 4) @@ -489,7 +489,7 @@ describe('API/extmarks', function() end) it('marks move with line splits (using enter)', function() - -- open_line in misc1.c + -- open_line in change.c -- testing marks below are also moved feed("yyP") set_extmark(ns, marks[1], 0, 4) @@ -500,7 +500,7 @@ describe('API/extmarks', function() end) it('marks at last line move on insert new line', function() - -- open_line in misc1.c + -- open_line in change.c set_extmark(ns, marks[1], 0, 4) feed('0i<cr><esc>') check_undo_redo(ns, marks[1], 0, 4, 1, 4) diff --git a/test/functional/autocmd/searchwrapped_spec.lua b/test/functional/autocmd/searchwrapped_spec.lua new file mode 100644 index 0000000000..46c2c99b3d --- /dev/null +++ b/test/functional/autocmd/searchwrapped_spec.lua @@ -0,0 +1,53 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local command = helpers.command +local curbufmeths = helpers.curbufmeths +local eq = helpers.eq +local eval = helpers.eval +local feed = helpers.feed + +describe('autocmd SearchWrapped', function() + before_each(function() + clear() + command('set ignorecase') + command('let g:test = 0') + command('autocmd! SearchWrapped * let g:test += 1') + curbufmeths.set_lines(0, 1, false, { + 'The quick brown fox', + 'jumps over the lazy dog'}) + end) + + it('gets triggered when search wraps the end', function() + feed('/the<Return>') + eq(0, eval('g:test')) + + feed('n') + eq(1, eval('g:test')) + + feed('nn') + eq(2, eval('g:test')) + end) + + it('gets triggered when search wraps in reverse order', function() + feed('/the<Return>') + eq(0, eval('g:test')) + + feed('NN') + eq(1, eval('g:test')) + + feed('NN') + eq(2, eval('g:test')) + end) + + it('does not get triggered on failed searches', function() + feed('/blargh<Return>') + eq(0, eval('g:test')) + + feed('NN') + eq(0, eval('g:test')) + + feed('NN') + eq(0, eval('g:test')) + end) +end) diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 1138cfbf4c..911fa017ab 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -227,6 +227,43 @@ void ui_refresh(void) }, res) end) + it('supports getting text of multiline node', function() + if pending_c_parser(pending) then return end + insert(test_text) + local res = exec_lua([[ + local parser = vim.treesitter.get_parser(0, "c") + local tree = parser:parse()[1] + return vim.treesitter.get_node_text(tree:root(), 0) + ]]) + eq(test_text, res) + + local res2 = exec_lua([[ + local parser = vim.treesitter.get_parser(0, "c") + local root = parser:parse()[1]:root() + return vim.treesitter.get_node_text(root:child(0):child(0), 0) + ]]) + eq('void', res2) + end) + + it('support getting text where start of node is past EOF', function() + local text = [[ +def run + a = <<~E +end]] + insert(text) + local result = exec_lua([[ + local fake_node = {} + function fake_node:start() + return 3, 0, 23 + end + function fake_node:end_() + return 3, 0, 23 + end + return vim.treesitter.get_node_text(fake_node, 0) == nil + ]]) + eq(true, result) + end) + it('can match special regex characters like \\ * + ( with `vim-match?`', function() insert('char* astring = "\\n"; (1 + 1) * 2 != 2;') diff --git a/test/unit/os/shell_spec.lua b/test/unit/os/shell_spec.lua index a73fc8e47e..29a2b78491 100644 --- a/test/unit/os/shell_spec.lua +++ b/test/unit/os/shell_spec.lua @@ -4,7 +4,6 @@ local cimported = helpers.cimport( './src/nvim/os/shell.h', './src/nvim/option_defs.h', './src/nvim/main.h', - './src/nvim/misc1.h', './src/nvim/memory.h' ) local ffi, eq = helpers.ffi, helpers.eq |