aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/autoload/remote/define.vim6
-rw-r--r--runtime/doc/autocmd.txt4
-rw-r--r--runtime/doc/diagnostic.txt2
-rw-r--r--runtime/doc/vim_diff.txt1
-rw-r--r--runtime/lua/vim/lsp.lua6
-rw-r--r--runtime/lua/vim/lsp/handlers.lua4
-rw-r--r--runtime/lua/vim/lsp/util.lua157
-rw-r--r--runtime/lua/vim/treesitter/query.lua38
-rw-r--r--src/nvim/api/buffer.c1
-rw-r--r--src/nvim/aucmd.c1
-rw-r--r--src/nvim/auevents.lua1
-rw-r--r--src/nvim/autocmd.c1
-rw-r--r--src/nvim/buffer.c1
-rw-r--r--src/nvim/change.c287
-rw-r--r--src/nvim/channel.c1
-rw-r--r--src/nvim/charset.c1
-rw-r--r--src/nvim/cursor.c1
-rw-r--r--src/nvim/diff.c2
-rw-r--r--src/nvim/digraph.c1
-rw-r--r--src/nvim/edit.c11
-rw-r--r--src/nvim/eval.c1
-rw-r--r--src/nvim/eval/funcs.c2
-rw-r--r--src/nvim/eval/typval.c2
-rw-r--r--src/nvim/eval/userfunc.c1
-rw-r--r--src/nvim/event/rstream.c1
-rw-r--r--src/nvim/ex_cmds.c2
-rw-r--r--src/nvim/ex_cmds2.c1
-rw-r--r--src/nvim/ex_docmd.c2
-rw-r--r--src/nvim/ex_getln.c1
-rw-r--r--src/nvim/ex_session.c1
-rw-r--r--src/nvim/file_search.c1
-rw-r--r--src/nvim/fileio.c2
-rw-r--r--src/nvim/fold.c1
-rw-r--r--src/nvim/getchar.c11
-rw-r--r--src/nvim/globals.h1
-rw-r--r--src/nvim/indent.c1
-rw-r--r--src/nvim/indent_c.c1
-rw-r--r--src/nvim/input.c255
-rw-r--r--src/nvim/input.h9
-rw-r--r--src/nvim/lua/executor.c1
-rw-r--r--src/nvim/lua/stdlib.c1
-rw-r--r--src/nvim/main.c1
-rw-r--r--src/nvim/mbyte.c2
-rw-r--r--src/nvim/memline.c2
-rw-r--r--src/nvim/memory.c1
-rw-r--r--src/nvim/menu.c1
-rw-r--r--src/nvim/message.c47
-rw-r--r--src/nvim/misc1.c912
-rw-r--r--src/nvim/misc1.h10
-rw-r--r--src/nvim/mouse.c27
-rw-r--r--src/nvim/move.c2
-rw-r--r--src/nvim/msgpack_rpc/channel.c1
-rw-r--r--src/nvim/normal.c1
-rw-r--r--src/nvim/ops.c1
-rw-r--r--src/nvim/option.c1
-rw-r--r--src/nvim/os/fs.c1
-rw-r--r--src/nvim/os/input.c1
-rw-r--r--src/nvim/os/shell.c113
-rw-r--r--src/nvim/os/signal.c1
-rw-r--r--src/nvim/os/users.c60
-rw-r--r--src/nvim/os_unix.c1
-rw-r--r--src/nvim/path.c1
-rw-r--r--src/nvim/quickfix.c1
-rw-r--r--src/nvim/regexp.c1
-rw-r--r--src/nvim/runtime.c1
-rw-r--r--src/nvim/screen.c1
-rw-r--r--src/nvim/search.c6
-rw-r--r--src/nvim/spell.c2
-rw-r--r--src/nvim/spellfile.c12
-rw-r--r--src/nvim/strings.c1
-rw-r--r--src/nvim/syntax.c1
-rw-r--r--src/nvim/tag.c2
-rw-r--r--src/nvim/terminal.c1
-rw-r--r--src/nvim/ui.c39
-rw-r--r--src/nvim/undo.c2
-rw-r--r--src/nvim/window.c1
-rw-r--r--test/functional/api/extmark_spec.lua6
-rw-r--r--test/functional/autocmd/searchwrapped_spec.lua53
-rw-r--r--test/functional/treesitter/parser_spec.lua37
-rw-r--r--test/unit/os/shell_spec.lua1
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