aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/lsp.txt2
-rw-r--r--runtime/doc/mbyte.txt2
-rw-r--r--runtime/doc/news.txt1
-rw-r--r--runtime/doc/quickfix.txt2
-rw-r--r--runtime/doc/syntax.txt10
-rw-r--r--runtime/ftplugin/glsl.lua1
-rw-r--r--runtime/lua/vim/_comment.lua11
-rw-r--r--runtime/lua/vim/glob.lua11
-rw-r--r--runtime/lua/vim/lsp/util.lua14
-rw-r--r--runtime/lua/vim/shared.lua78
-rw-r--r--runtime/syntax/java.vim4
-rw-r--r--src/nvim/api/options.c9
-rw-r--r--src/nvim/bufwrite.c2
-rw-r--r--src/nvim/channel.c50
-rw-r--r--src/nvim/event/defs.h16
-rw-r--r--src/nvim/event/process.c3
-rw-r--r--src/nvim/event/rstream.c163
-rw-r--r--src/nvim/event/stream.c3
-rw-r--r--src/nvim/ex_docmd.c18
-rw-r--r--src/nvim/lua/stdlib.c100
-rw-r--r--src/nvim/msgpack_rpc/channel.c36
-rw-r--r--src/nvim/option.c6
-rw-r--r--src/nvim/os/fileio.c2
-rw-r--r--src/nvim/os/fileio_defs.h1
-rw-r--r--src/nvim/os/input.c16
-rw-r--r--src/nvim/os/shell.c102
-rw-r--r--src/nvim/rbuffer.c230
-rw-r--r--src/nvim/rbuffer.h71
-rw-r--r--src/nvim/rbuffer_defs.h45
-rw-r--r--src/nvim/tui/input.c146
-rw-r--r--src/nvim/tui/input.h9
-rw-r--r--test/functional/lua/glob_spec.lua13
-rw-r--r--test/functional/lua/with_spec.lua292
-rw-r--r--test/functional/plugin/lsp_spec.lua8
-rw-r--r--test/unit/fixtures/rbuffer.c28
-rw-r--r--test/unit/fixtures/rbuffer.h9
-rw-r--r--test/unit/rbuffer_spec.lua340
37 files changed, 791 insertions, 1063 deletions
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 4ddf82706b..ca9dfd0350 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -1983,7 +1983,9 @@ locations_to_items({locations}, {offset_encoding})
(`table[]`) A list of objects with the following fields:
• {filename} (`string`)
• {lnum} (`integer`) 1-indexed line number
+ • {end_lnum} (`integer`) 1-indexed end line number
• {col} (`integer`) 1-indexed column
+ • {end_col} (`integer`) 1-indexed end column
• {text} (`string`)
• {user_data} (`lsp.Location|lsp.LocationLink`)
diff --git a/runtime/doc/mbyte.txt b/runtime/doc/mbyte.txt
index 0a7e0baad3..a8c5670352 100644
--- a/runtime/doc/mbyte.txt
+++ b/runtime/doc/mbyte.txt
@@ -686,7 +686,7 @@ You might want to select the font used for the menus. Unfortunately this
doesn't always work. See the system specific remarks below, and 'langmenu'.
-USING UTF-8 IN X-Windows *utf-8-in-xwindows*
+USING UTF-8 IN X-WINDOWS *utf-8-in-xwindows*
You need to specify a font to be used. For double-wide characters another
font is required, which is exactly twice as wide. There are three ways to do
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index b316cc7b04..2ff6b0302c 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -113,6 +113,7 @@ LSP
• Completion side effects (including snippet expansion, execution of commands
and application of additional text edits) is now built-in.
+• |vim.lsp.util.locations_to_items()| sets `end_col` and `end_lnum` fields.
LUA
diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt
index 5d3c0cbdc2..897e503fc4 100644
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -1296,7 +1296,7 @@ passed to make, say :make html or :make pdf.
Additional arguments can be passed to pandoc:
- either by appending them to make, say `:make html --self-contained` .
-- or setting them in `b:pandoc_compiler_args` or `g:pandoc_compiler_args`
+- or setting them in `b:pandoc_compiler_args` or `g:pandoc_compiler_args`.
PERL *quickfix-perl* *compiler-perl*
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index 1e3dfe1e32..61028c791d 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -466,14 +466,14 @@ ASTRO *astro.vim* *ft-astro-syntax*
Configuration
The following variables control certain syntax highlighting features.
-You can add them to your .vimrc: >
+You can add them to your .vimrc.
+
+To enables TypeScript and TSX for ".astro" files (default "disable"): >
let g:astro_typescript = "enable"
<
-Enables TypeScript and TSX for ".astro" files. Default Value: "disable" >
+To enables Stylus for ".astro" files (default "disable"): >
let g:astro_stylus = "enable"
<
-Enables Stylus for ".astro" files. Default Value: "disable"
-
NOTE: You need to install an external plugin to support stylus in astro files.
@@ -1437,7 +1437,7 @@ Note: Syntax folding might slow down syntax highlighting significantly,
especially for large files.
-HTML/OS (by Aestiva) *htmlos.vim* *ft-htmlos-syntax*
+HTML/OS (BY AESTIVA) *htmlos.vim* *ft-htmlos-syntax*
The coloring scheme for HTML/OS works as follows:
diff --git a/runtime/ftplugin/glsl.lua b/runtime/ftplugin/glsl.lua
new file mode 100644
index 0000000000..f398d66a63
--- /dev/null
+++ b/runtime/ftplugin/glsl.lua
@@ -0,0 +1 @@
+vim.bo.commentstring = '// %s'
diff --git a/runtime/lua/vim/_comment.lua b/runtime/lua/vim/_comment.lua
index 044cd69716..efe289b3e1 100644
--- a/runtime/lua/vim/_comment.lua
+++ b/runtime/lua/vim/_comment.lua
@@ -194,14 +194,9 @@ local function toggle_lines(line_start, line_end, ref_position)
-- - Debatable for highlighting in text area (like LSP semantic tokens).
-- Mostly because it causes flicker as highlighting is preserved during
-- comment toggling.
- package.loaded['vim._comment']._lines = vim.tbl_map(f, lines)
- local lua_cmd = string.format(
- 'vim.api.nvim_buf_set_lines(0, %d, %d, false, package.loaded["vim._comment"]._lines)',
- line_start - 1,
- line_end
- )
- vim.cmd.lua({ lua_cmd, mods = { lockmarks = true } })
- package.loaded['vim._comment']._lines = nil
+ vim._with({ lockmarks = true }, function()
+ vim.api.nvim_buf_set_lines(0, line_start - 1, line_end, false, vim.tbl_map(f, lines))
+ end)
end
--- Operator which toggles user-supplied range of lines
diff --git a/runtime/lua/vim/glob.lua b/runtime/lua/vim/glob.lua
index ad4a915a94..6de2bc3e94 100644
--- a/runtime/lua/vim/glob.lua
+++ b/runtime/lua/vim/glob.lua
@@ -29,8 +29,10 @@ function M.to_lpeg(pattern)
return patt
end
- local function add(acc, a)
- return acc + a
+ local function condlist(conds, after)
+ return vim.iter(conds):fold(P(false), function(acc, cond)
+ return acc + cond * after
+ end)
end
local function mul(acc, m)
@@ -63,15 +65,14 @@ function M.to_lpeg(pattern)
* C(P('!') ^ -1)
* Ct(Ct(C(P(1)) * P('-') * C(P(1) - P(']'))) ^ 1 * P(']'))
/ class,
- CondList = P('{') * Cf(V('Cond') * (P(',') * V('Cond')) ^ 0, add) * P('}'),
+ CondList = P('{') * Ct(V('Cond') * (P(',') * V('Cond')) ^ 0) * P('}') * V('Pattern') / condlist,
-- TODO: '*' inside a {} condition is interpreted literally but should probably have the same
-- wildcard semantics it usually has.
-- Fixing this is non-trivial because '*' should match non-greedily up to "the rest of the
-- pattern" which in all other cases is the entire succeeding part of the pattern, but at the end of a {}
-- condition means "everything after the {}" where several other options separated by ',' may
-- exist in between that should not be matched by '*'.
- Cond = Cf((V('Ques') + V('Class') + V('CondList') + (V('Literal') - S(',}'))) ^ 1, mul)
- + Cc(P(0)),
+ Cond = Cf((V('Ques') + V('Class') + V('Literal') - S(',}')) ^ 1, mul) + Cc(P(0)),
Literal = P(1) / P,
End = P(-1) * Cc(P(-1)),
})
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 3d61af8b15..088d57b6d6 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1722,7 +1722,9 @@ end)
---@inlinedoc
---@field filename string
---@field lnum integer 1-indexed line number
+---@field end_lnum integer 1-indexed end line number
---@field col integer 1-indexed column
+---@field end_col integer 1-indexed end column
---@field text string
---@field user_data lsp.Location|lsp.LocationLink
@@ -1749,7 +1751,7 @@ function M.locations_to_items(locations, offset_encoding)
end
local items = {}
- ---@type table<string, {start: lsp.Position, location: lsp.Location|lsp.LocationLink}[]>
+ ---@type table<string, {start: lsp.Position, end: lsp.Position, location: lsp.Location|lsp.LocationLink}[]>
local grouped = setmetatable({}, {
__index = function(t, k)
local v = {}
@@ -1761,7 +1763,7 @@ function M.locations_to_items(locations, offset_encoding)
-- locations may be Location or LocationLink
local uri = d.uri or d.targetUri
local range = d.range or d.targetSelectionRange
- table.insert(grouped[uri], { start = range.start, location = d })
+ table.insert(grouped[uri], { start = range.start, ['end'] = range['end'], location = d })
end
---@type string[]
@@ -1776,6 +1778,9 @@ function M.locations_to_items(locations, offset_encoding)
local line_numbers = {}
for _, temp in ipairs(rows) do
table.insert(line_numbers, temp.start.line)
+ if temp.start.line ~= temp['end'].line then
+ table.insert(line_numbers, temp['end'].line)
+ end
end
-- get all the lines for this uri
@@ -1783,13 +1788,18 @@ function M.locations_to_items(locations, offset_encoding)
for _, temp in ipairs(rows) do
local pos = temp.start
+ local end_pos = temp['end']
local row = pos.line
+ local end_row = end_pos.line
local line = lines[row] or ''
local col = M._str_byteindex_enc(line, pos.character, offset_encoding)
+ local end_col = M._str_byteindex_enc(lines[end_row] or '', end_pos.character, offset_encoding)
table.insert(items, {
filename = filename,
lnum = row + 1,
+ end_lnum = end_row + 1,
col = col + 1,
+ end_col = end_col + 1,
text = line,
user_data = temp.location,
})
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 9d54a80144..7fd29d5f7b 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -1139,4 +1139,82 @@ function vim._defer_require(root, mod)
})
end
+--- @nodoc
+--- @class vim.context.mods
+--- @field buf? integer
+--- @field emsg_silent? boolean
+--- @field hide? boolean
+--- @field horizontal? boolean
+--- @field keepalt? boolean
+--- @field keepjumps? boolean
+--- @field keepmarks? boolean
+--- @field keeppatterns? boolean
+--- @field lockmarks? boolean
+--- @field noautocmd? boolean
+--- @field options? table<string, any>
+--- @field sandbox? boolean
+--- @field silent? boolean
+--- @field unsilent? boolean
+--- @field win? integer
+
+--- Executes function `f` with the given context specification.
+---
+--- @param context vim.context.mods
+function vim._with(context, f)
+ vim.validate('context', context, 'table')
+ vim.validate('f', f, 'function')
+
+ vim.validate('context.buf', context.buf, 'number', true)
+ vim.validate('context.emsg_silent', context.emsg_silent, 'boolean', true)
+ vim.validate('context.hide', context.hide, 'boolean', true)
+ vim.validate('context.horizontal', context.horizontal, 'boolean', true)
+ vim.validate('context.keepalt', context.keepalt, 'boolean', true)
+ vim.validate('context.keepjumps', context.keepjumps, 'boolean', true)
+ vim.validate('context.keepmarks', context.keepmarks, 'boolean', true)
+ vim.validate('context.keeppatterns', context.keeppatterns, 'boolean', true)
+ vim.validate('context.lockmarks', context.lockmarks, 'boolean', true)
+ vim.validate('context.noautocmd', context.noautocmd, 'boolean', true)
+ vim.validate('context.options', context.options, 'table', true)
+ vim.validate('context.sandbox', context.sandbox, 'boolean', true)
+ vim.validate('context.silent', context.silent, 'boolean', true)
+ vim.validate('context.unsilent', context.unsilent, 'boolean', true)
+ vim.validate('context.win', context.win, 'number', true)
+
+ -- Check buffer exists
+ if context.buf then
+ if not vim.api.nvim_buf_is_valid(context.buf) then
+ error('Invalid buffer id: ' .. context.buf)
+ end
+ end
+
+ -- Check window exists
+ if context.win then
+ if not vim.api.nvim_win_is_valid(context.win) then
+ error('Invalid window id: ' .. context.win)
+ end
+ end
+
+ -- Store original options
+ local previous_options ---@type table<string, any>
+ if context.options then
+ previous_options = {}
+ for k, v in pairs(context.options) do
+ previous_options[k] =
+ vim.api.nvim_get_option_value(k, { win = context.win, buf = context.buf })
+ vim.api.nvim_set_option_value(k, v, { win = context.win, buf = context.buf })
+ end
+ end
+
+ local retval = { vim._with_c(context, f) }
+
+ -- Restore original options
+ if previous_options then
+ for k, v in pairs(previous_options) do
+ vim.api.nvim_set_option_value(k, v, { win = context.win, buf = context.buf })
+ end
+ end
+
+ return unpack(retval)
+end
+
return vim
diff --git a/runtime/syntax/java.vim b/runtime/syntax/java.vim
index 5ba724d24e..f5910a8557 100644
--- a/runtime/syntax/java.vim
+++ b/runtime/syntax/java.vim
@@ -3,7 +3,7 @@
" Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com>
" Former Maintainer: Claudio Fleiner <claudio@fleiner.com>
" Repository: https://github.com/zzzyxwvut/java-vim.git
-" Last Change: 2024 May 30
+" Last Change: 2024 Jun 08
" Please check :help java.vim for comments on some of the options available.
@@ -215,7 +215,7 @@ syn keyword javaLabelVarType contained var
syn keyword javaLabelCastType contained char byte short int
" Allow for the contingency of the enclosing region not being able to
" _keep_ its _end_, e.g. case ':':.
-syn region javaLabelWhenClause contained transparent matchgroup=javaLabel start="\<when\>" matchgroup=NONE end=":"me=e-1 end="->"me=e-2 contains=TOP,javaExternal
+syn region javaLabelWhenClause contained transparent matchgroup=javaLabel start="\<when\>" matchgroup=NONE end=":"me=e-1 end="->"me=e-2 contains=TOP,javaExternal,javaLambdaDef
syn match javaLabelNumber contained "\<0\>[lL]\@!"
syn match javaLabelNumber contained "\<\%(0\%([xX]\x\%(_*\x\)*\|_*\o\%(_*\o\)*\|[bB][01]\%(_*[01]\)*\)\|[1-9]\%(_*\d\)*\)\>[lL]\@!"
hi def link javaLabelDefault javaLabel
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index d9bc0ccc92..5adaff8449 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -54,6 +54,10 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
}
if (HAS_KEY_X(opts, buf)) {
+ VALIDATE(!(HAS_KEY_X(opts, scope) && *scope == OPT_GLOBAL), "%s",
+ "cannot use both global 'scope' and 'buf'", {
+ return FAIL;
+ });
*scope = OPT_LOCAL;
*req_scope = kOptReqBuf;
*from = find_buffer_by_handle(opts->buf, err);
@@ -68,11 +72,6 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
return FAIL;
});
- VALIDATE((!HAS_KEY_X(opts, scope) || !HAS_KEY_X(opts, buf)), "%s",
- "cannot use both 'scope' and 'buf'", {
- return FAIL;
- });
-
VALIDATE((!HAS_KEY_X(opts, win) || !HAS_KEY_X(opts, buf)),
"%s", "cannot use both 'buf' and 'win'", {
return FAIL;
diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c
index 6fbcb2dbb8..5522ab1ca3 100644
--- a/src/nvim/bufwrite.c
+++ b/src/nvim/bufwrite.c
@@ -1066,7 +1066,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
bool whole = (start == 1 && end == buf->b_ml.ml_line_count);
bool write_undo_file = false;
context_sha256_T sha_ctx;
- unsigned bkc = get_bkc_value(buf);
+ unsigned bkc = get_bkc_flags(buf);
if (fname == NULL || *fname == NUL) { // safety check
return FAIL;
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 05225cecd0..5f9bfc3a73 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -39,8 +39,6 @@
#include "nvim/os/os_defs.h"
#include "nvim/os/shell.h"
#include "nvim/path.h"
-#include "nvim/rbuffer.h"
-#include "nvim/rbuffer_defs.h"
#include "nvim/terminal.h"
#include "nvim/types_defs.h"
@@ -432,7 +430,7 @@ Channel *channel_job_start(char **argv, const char *exepath, CallbackReader on_s
wstream_init(&proc->in, 0);
}
if (has_out) {
- rstream_init(&proc->out, 0);
+ rstream_init(&proc->out);
}
if (rpc) {
@@ -447,7 +445,7 @@ Channel *channel_job_start(char **argv, const char *exepath, CallbackReader on_s
if (has_err) {
callback_reader_start(&chan->on_stderr, "stderr");
- rstream_init(&proc->err, 0);
+ rstream_init(&proc->err);
rstream_start(&proc->err, on_job_stderr, chan);
}
@@ -484,7 +482,7 @@ uint64_t channel_connect(bool tcp, const char *address, bool rpc, CallbackReader
channel->stream.socket.s.internal_close_cb = close_cb;
channel->stream.socket.s.internal_data = channel;
wstream_init(&channel->stream.socket.s, 0);
- rstream_init(&channel->stream.socket, 0);
+ rstream_init(&channel->stream.socket);
if (rpc) {
rpc_start(channel);
@@ -509,7 +507,7 @@ void channel_from_connection(SocketWatcher *watcher)
channel->stream.socket.s.internal_close_cb = close_cb;
channel->stream.socket.s.internal_data = channel;
wstream_init(&channel->stream.socket.s, 0);
- rstream_init(&channel->stream.socket, 0);
+ rstream_init(&channel->stream.socket);
rpc_start(channel);
channel_create_event(channel, watcher->addr);
}
@@ -554,7 +552,7 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, const char **err
dup2(STDERR_FILENO, STDIN_FILENO);
}
#endif
- rstream_init_fd(&main_loop, &channel->stream.stdio.in, stdin_dup_fd, 0);
+ rstream_init_fd(&main_loop, &channel->stream.stdio.in, stdin_dup_fd);
wstream_init_fd(&main_loop, &channel->stream.stdio.out, stdout_dup_fd, 0);
if (rpc) {
@@ -648,51 +646,38 @@ static inline list_T *buffer_to_tv_list(const char *const buf, const size_t len)
return l;
}
-void on_channel_data(RStream *stream, RBuffer *buf, size_t count, void *data, bool eof)
+size_t on_channel_data(RStream *stream, const char *buf, size_t count, void *data, bool eof)
{
Channel *chan = data;
- on_channel_output(stream, chan, buf, eof, &chan->on_data);
+ return on_channel_output(stream, chan, buf, count, eof, &chan->on_data);
}
-void on_job_stderr(RStream *stream, RBuffer *buf, size_t count, void *data, bool eof)
+size_t on_job_stderr(RStream *stream, const char *buf, size_t count, void *data, bool eof)
{
Channel *chan = data;
- on_channel_output(stream, chan, buf, eof, &chan->on_stderr);
+ return on_channel_output(stream, chan, buf, count, eof, &chan->on_stderr);
}
-static void on_channel_output(RStream *stream, Channel *chan, RBuffer *buf, bool eof,
- CallbackReader *reader)
+static size_t on_channel_output(RStream *stream, Channel *chan, const char *buf, size_t count,
+ bool eof, CallbackReader *reader)
{
- size_t count;
- char *output = rbuffer_read_ptr(buf, &count);
-
if (chan->term) {
- if (!eof) {
- char *p = output;
- char *end = output + count;
+ if (count) {
+ const char *p = buf;
+ const char *end = buf + count;
while (p < end) {
// Don't pass incomplete UTF-8 sequences to libvterm. #16245
// Composing chars can be passed separately, so utf_ptr2len_len() is enough.
int clen = utf_ptr2len_len(p, (int)(end - p));
if (clen > end - p) {
- count = (size_t)(p - output);
+ count = (size_t)(p - buf);
break;
}
p += clen;
}
}
- terminal_receive(chan->term, output, count);
- }
-
- if (count) {
- rbuffer_consumed(buf, count);
- }
- // Move remaining data to start of buffer, so the buffer can never wrap around.
- rbuffer_reset(buf);
-
- if (callback_reader_set(*reader)) {
- ga_concat_len(&reader->buffer, output, count);
+ terminal_receive(chan->term, buf, count);
}
if (eof) {
@@ -700,8 +685,11 @@ static void on_channel_output(RStream *stream, Channel *chan, RBuffer *buf, bool
}
if (callback_reader_set(*reader)) {
+ ga_concat_len(&reader->buffer, buf, count);
schedule_channel_event(chan);
}
+
+ return count;
}
/// schedule the necessary callbacks to be invoked as a deferred event
diff --git a/src/nvim/event/defs.h b/src/nvim/event/defs.h
index 8563006159..41690ead88 100644
--- a/src/nvim/event/defs.h
+++ b/src/nvim/event/defs.h
@@ -6,7 +6,6 @@
#include <uv.h>
#include "nvim/eval/typval_defs.h"
-#include "nvim/rbuffer_defs.h"
#include "nvim/types_defs.h"
enum { EVENT_HANDLER_MAX_ARGC = 10, };
@@ -59,11 +58,13 @@ typedef struct rstream RStream;
/// Type of function called when the RStream buffer is filled with data
///
/// @param stream The Stream instance
-/// @param buf The associated RBuffer instance
+/// @param read_data data that was read
/// @param count Number of bytes that was read.
/// @param data User-defined data
/// @param eof If the stream reached EOF.
-typedef void (*stream_read_cb)(RStream *stream, RBuffer *buf, size_t count, void *data, bool eof);
+/// @return number of bytes which were consumed
+typedef size_t (*stream_read_cb)(RStream *stream, const char *read_data, size_t count, void *data,
+ bool eof);
/// Type of function called when the Stream has information about a write
/// request.
@@ -102,11 +103,16 @@ struct stream {
struct rstream {
Stream s;
bool did_eof;
- RBuffer *buffer;
+ bool want_read;
+ bool pending_read;
+ bool paused_full;
+ char *buffer; // ARENA_BLOCK_SIZE
+ char *read_pos;
+ char *write_pos;
uv_buf_t uvbuf;
stream_read_cb read_cb;
size_t num_bytes;
- size_t fpos;
+ int64_t fpos;
};
#define ADDRESS_MAX_SIZE 256
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index 710376cd62..70fc31ba21 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -18,7 +18,6 @@
#include "nvim/os/pty_process.h"
#include "nvim/os/shell.h"
#include "nvim/os/time.h"
-#include "nvim/rbuffer_defs.h"
#include "nvim/ui_client.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -355,7 +354,7 @@ static void flush_stream(Process *proc, RStream *stream)
int err = uv_recv_buffer_size((uv_handle_t *)&stream->s.uv.pipe,
&system_buffer_size);
if (err) {
- system_buffer_size = (int)rbuffer_capacity(stream->buffer);
+ system_buffer_size = ARENA_BLOCK_SIZE;
}
size_t max_bytes = stream->num_bytes + (size_t)system_buffer_size;
diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c
index 6c7fa20bd8..71290d0c0d 100644
--- a/src/nvim/event/rstream.c
+++ b/src/nvim/event/rstream.c
@@ -11,38 +11,44 @@
#include "nvim/macros_defs.h"
#include "nvim/main.h"
#include "nvim/os/os_defs.h"
-#include "nvim/rbuffer.h"
-#include "nvim/rbuffer_defs.h"
#include "nvim/types_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/rstream.c.generated.h"
#endif
-void rstream_init_fd(Loop *loop, RStream *stream, int fd, size_t bufsize)
+void rstream_init_fd(Loop *loop, RStream *stream, int fd)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
stream_init(loop, &stream->s, fd, NULL);
- rstream_init(stream, bufsize);
+ rstream_init(stream);
}
-void rstream_init_stream(RStream *stream, uv_stream_t *uvstream, size_t bufsize)
+void rstream_init_stream(RStream *stream, uv_stream_t *uvstream)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
stream_init(NULL, &stream->s, -1, uvstream);
- rstream_init(stream, bufsize);
+ rstream_init(stream);
}
-void rstream_init(RStream *stream, size_t bufsize)
+void rstream_init(RStream *stream)
FUNC_ATTR_NONNULL_ARG(1)
{
stream->fpos = 0;
stream->read_cb = NULL;
stream->num_bytes = 0;
- stream->buffer = rbuffer_new(bufsize);
- stream->buffer->data = stream;
- stream->buffer->full_cb = on_rbuffer_full;
- stream->buffer->nonfull_cb = on_rbuffer_nonfull;
+ stream->buffer = alloc_block();
+ stream->read_pos = stream->write_pos = stream->buffer;
+}
+
+void rstream_start_inner(RStream *stream)
+ FUNC_ATTR_NONNULL_ARG(1)
+{
+ if (stream->s.uvstream) {
+ uv_read_start(stream->s.uvstream, alloc_cb, read_cb);
+ } else {
+ uv_idle_start(&stream->s.uv.idle, fread_idle_cb);
+ }
}
/// Starts watching for events from a `Stream` instance.
@@ -53,17 +59,16 @@ void rstream_start(RStream *stream, stream_read_cb cb, void *data)
{
stream->read_cb = cb;
stream->s.cb_data = data;
- if (stream->s.uvstream) {
- uv_read_start(stream->s.uvstream, alloc_cb, read_cb);
- } else {
- uv_idle_start(&stream->s.uv.idle, fread_idle_cb);
+ stream->want_read = true;
+ if (!stream->paused_full) {
+ rstream_start_inner(stream);
}
}
/// Stops watching for events from a `Stream` instance.
///
/// @param stream The `Stream` instance
-void rstream_stop(RStream *stream)
+void rstream_stop_inner(RStream *stream)
FUNC_ATTR_NONNULL_ALL
{
if (stream->s.uvstream) {
@@ -73,16 +78,14 @@ void rstream_stop(RStream *stream)
}
}
-static void on_rbuffer_full(RBuffer *buf, void *data)
-{
- rstream_stop(data);
-}
-
-static void on_rbuffer_nonfull(RBuffer *buf, void *data)
+/// Stops watching for events from a `Stream` instance.
+///
+/// @param stream The `Stream` instance
+void rstream_stop(RStream *stream)
+ FUNC_ATTR_NONNULL_ALL
{
- RStream *stream = data;
- assert(stream->read_cb);
- rstream_start(stream, stream->read_cb, stream->s.cb_data);
+ rstream_stop_inner(stream);
+ stream->want_read = false;
}
// Callbacks used by libuv
@@ -91,10 +94,9 @@ static void on_rbuffer_nonfull(RBuffer *buf, void *data)
static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
{
RStream *stream = handle->data;
- // `uv_buf_t.len` happens to have different size on Windows.
- size_t write_count;
- buf->base = rbuffer_write_ptr(stream->buffer, &write_count);
- buf->len = UV_BUF_LEN(write_count);
+ buf->base = stream->write_pos;
+ // `uv_buf_t.len` happens to have different size on Windows (as a treat)
+ buf->len = UV_BUF_LEN(rstream_space(stream));
}
/// Callback invoked by libuv after it copies the data into the buffer provided
@@ -108,21 +110,21 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf)
// cnt == 0 means libuv asked for a buffer and decided it wasn't needed:
// http://docs.libuv.org/en/latest/stream.html#c.uv_read_start.
//
- // We don't need to do anything with the RBuffer because the next call
+ // We don't need to do anything with the buffer because the next call
// to `alloc_cb` will return the same unused pointer (`rbuffer_produced`
// won't be called)
if (cnt == UV_ENOBUFS || cnt == 0) {
return;
} else if (cnt == UV_EOF && uvstream->type == UV_TTY) {
// The TTY driver might signal EOF without closing the stream
- invoke_read_cb(stream, 0, true);
+ invoke_read_cb(stream, true);
} else {
DLOG("closing Stream (%p): %s (%s)", (void *)stream,
uv_err_name((int)cnt), os_strerror((int)cnt));
// Read error or EOF, either way stop the stream and invoke the callback
// with eof == true
uv_read_stop(uvstream);
- invoke_read_cb(stream, 0, true);
+ invoke_read_cb(stream, true);
}
return;
}
@@ -130,10 +132,13 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf)
// at this point we're sure that cnt is positive, no error occurred
size_t nread = (size_t)cnt;
stream->num_bytes += nread;
- // Data was already written, so all we need is to update 'wpos' to reflect
- // the space actually used in the buffer.
- rbuffer_produced(stream->buffer, nread);
- invoke_read_cb(stream, nread, false);
+ stream->write_pos += cnt;
+ invoke_read_cb(stream, false);
+}
+
+static size_t rstream_space(RStream *stream)
+{
+ return (size_t)((stream->buffer + ARENA_BLOCK_SIZE) - stream->write_pos);
}
/// Called by the by the 'idle' handle to emulate a reading event
@@ -146,52 +151,37 @@ static void fread_idle_cb(uv_idle_t *handle)
uv_fs_t req;
RStream *stream = handle->data;
+ stream->uvbuf.base = stream->write_pos;
// `uv_buf_t.len` happens to have different size on Windows.
- size_t write_count;
- stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &write_count);
- stream->uvbuf.len = UV_BUF_LEN(write_count);
-
- // the offset argument to uv_fs_read is int64_t, could someone really try
- // to read more than 9 quintillion (9e18) bytes?
- // upcast is meant to avoid tautological condition warning on 32 bits
- uintmax_t fpos_intmax = stream->fpos;
- if (fpos_intmax > INT64_MAX) {
- ELOG("stream offset overflow");
- preserve_exit("stream offset overflow");
- }
+ stream->uvbuf.len = UV_BUF_LEN(rstream_space(stream));
// Synchronous read
- uv_fs_read(handle->loop,
- &req,
- stream->s.fd,
- &stream->uvbuf,
- 1,
- (int64_t)stream->fpos,
- NULL);
+ uv_fs_read(handle->loop, &req, stream->s.fd, &stream->uvbuf, 1, stream->fpos, NULL);
uv_fs_req_cleanup(&req);
if (req.result <= 0) {
uv_idle_stop(&stream->s.uv.idle);
- invoke_read_cb(stream, 0, true);
+ invoke_read_cb(stream, true);
return;
}
- // no errors (req.result (ssize_t) is positive), it's safe to cast.
- size_t nread = (size_t)req.result;
- rbuffer_produced(stream->buffer, nread);
- stream->fpos += nread;
- invoke_read_cb(stream, nread, false);
+ // no errors (req.result (ssize_t) is positive), it's safe to use.
+ stream->write_pos += req.result;
+ stream->fpos += req.result;
+ invoke_read_cb(stream, false);
}
static void read_event(void **argv)
{
RStream *stream = argv[0];
+ stream->pending_read = false;
if (stream->read_cb) {
- size_t count = (uintptr_t)argv[1];
- bool eof = (uintptr_t)argv[2];
- stream->did_eof = eof;
- stream->read_cb(stream, stream->buffer, count, stream->s.cb_data, eof);
+ size_t available = rstream_available(stream);
+ size_t consumed = stream->read_cb(stream, stream->read_pos, available, stream->s.cb_data,
+ stream->did_eof);
+ assert(consumed <= available);
+ rstream_consume(stream, consumed);
}
stream->s.pending_reqs--;
if (stream->s.closed && !stream->s.pending_reqs) {
@@ -199,13 +189,48 @@ static void read_event(void **argv)
}
}
-static void invoke_read_cb(RStream *stream, size_t count, bool eof)
+size_t rstream_available(RStream *stream)
{
+ return (size_t)(stream->write_pos - stream->read_pos);
+}
+
+void rstream_consume(RStream *stream, size_t consumed)
+{
+ stream->read_pos += consumed;
+ size_t remaining = (size_t)(stream->write_pos - stream->read_pos);
+ if (remaining > 0 && stream->read_pos > stream->buffer) {
+ memmove(stream->buffer, stream->read_pos, remaining);
+ stream->read_pos = stream->buffer;
+ stream->write_pos = stream->buffer + remaining;
+ } else if (remaining == 0) {
+ stream->read_pos = stream->write_pos = stream->buffer;
+ }
+
+ if (stream->want_read && stream->paused_full && rstream_space(stream)) {
+ assert(stream->read_cb);
+ stream->paused_full = false;
+ rstream_start_inner(stream);
+ }
+}
+
+static void invoke_read_cb(RStream *stream, bool eof)
+{
+ stream->did_eof |= eof;
+
+ if (!rstream_space(stream)) {
+ rstream_stop_inner(stream);
+ stream->paused_full = true;
+ }
+
+ // we cannot use pending_reqs as a socket can have both pending reads and writes
+ if (stream->pending_read) {
+ return;
+ }
+
// Don't let the stream be closed before the event is processed.
stream->s.pending_reqs++;
-
- CREATE_EVENT(stream->s.events, read_event,
- stream, (void *)(uintptr_t *)count, (void *)(uintptr_t)eof);
+ stream->pending_read = true;
+ CREATE_EVENT(stream->s.events, read_event, stream);
}
void rstream_may_close(RStream *stream)
diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c
index 3d26dd868f..bc1b503f4c 100644
--- a/src/nvim/event/stream.c
+++ b/src/nvim/event/stream.c
@@ -8,7 +8,6 @@
#include "nvim/event/loop.h"
#include "nvim/event/stream.h"
#include "nvim/log.h"
-#include "nvim/rbuffer.h"
#include "nvim/types_defs.h"
#ifdef MSWIN
# include "nvim/os/os_win_console.h"
@@ -149,7 +148,7 @@ static void rstream_close_cb(uv_handle_t *handle)
{
RStream *stream = handle->data;
if (stream->buffer) {
- rbuffer_free(stream->buffer);
+ free_block(stream->buffer);
}
close_cb(handle);
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 2f54aa511b..1e2c515195 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1729,12 +1729,6 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview)
}
const char *errormsg = NULL;
-#undef ERROR
-#define ERROR(msg) \
- do { \
- errormsg = msg; \
- goto end; \
- } while (0)
cmdmod_T save_cmdmod = cmdmod;
cmdmod = cmdinfo->cmdmod;
@@ -1745,16 +1739,19 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview)
if (!MODIFIABLE(curbuf) && (eap->argt & EX_MODIFY)
// allow :put in terminals
&& !(curbuf->terminal && eap->cmdidx == CMD_put)) {
- ERROR(_(e_modifiable));
+ errormsg = _(e_modifiable);
+ goto end;
}
if (!IS_USER_CMDIDX(eap->cmdidx)) {
if (cmdwin_type != 0 && !(eap->argt & EX_CMDWIN)) {
// Command not allowed in the command line window
- ERROR(_(e_cmdwin));
+ errormsg = _(e_cmdwin);
+ goto end;
}
if (text_locked() && !(eap->argt & EX_LOCK_OK)) {
// Command not allowed when text is locked
- ERROR(_(get_text_locked_msg()));
+ errormsg = _(get_text_locked_msg());
+ goto end;
}
}
// Disallow editing another buffer when "curbuf->b_ro_locked" is set.
@@ -1802,7 +1799,6 @@ end:
do_cmdline_end();
return retv;
-#undef ERROR
}
static void profile_cmd(const exarg_T *eap, cstack_T *cstack, LineGetter fgetline, void *cookie)
@@ -2696,7 +2692,7 @@ int parse_command_modifiers(exarg_T *eap, const char **errormsg, cmdmod_T *cmod,
/// Apply the command modifiers. Saves current state in "cmdmod", call
/// undo_cmdmod() later.
-static void apply_cmdmod(cmdmod_T *cmod)
+void apply_cmdmod(cmdmod_T *cmod)
{
if ((cmod->cmod_flags & CMOD_SANDBOX) && !cmod->cmod_did_sandbox) {
sandbox++;
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index 22ee0a1c98..ee0eabbebb 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -17,10 +17,13 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
+#include "nvim/eval/window.h"
+#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/fold.h"
#include "nvim/globals.h"
@@ -40,6 +43,7 @@
#include "nvim/runtime.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
+#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/stdlib.c.generated.h"
@@ -568,6 +572,99 @@ static int nlua_foldupdate(lua_State *lstate)
return 0;
}
+static int nlua_with(lua_State *L)
+{
+ int flags = 0;
+ buf_T *buf = NULL;
+ win_T *win = NULL;
+
+#define APPLY_FLAG(key, flag) \
+ if (strequal((key), k) && (v)) { \
+ flags |= (flag); \
+ }
+
+ luaL_argcheck(L, lua_istable(L, 1), 1, "table expected");
+ lua_pushnil(L); // [dict, ..., nil]
+ while (lua_next(L, 1)) {
+ // [dict, ..., key, value]
+ if (lua_type(L, -2) == LUA_TSTRING) {
+ const char *k = lua_tostring(L, -2);
+ bool v = lua_toboolean(L, -1);
+ if (strequal("buf", k)) { \
+ buf = handle_get_buffer((int)luaL_checkinteger(L, -1));
+ } else if (strequal("win", k)) { \
+ win = handle_get_window((int)luaL_checkinteger(L, -1));
+ } else {
+ APPLY_FLAG("sandbox", CMOD_SANDBOX);
+ APPLY_FLAG("silent", CMOD_SILENT);
+ APPLY_FLAG("emsg_silent", CMOD_ERRSILENT);
+ APPLY_FLAG("unsilent", CMOD_UNSILENT);
+ APPLY_FLAG("noautocmd", CMOD_NOAUTOCMD);
+ APPLY_FLAG("hide", CMOD_HIDE);
+ APPLY_FLAG("keepalt", CMOD_KEEPALT);
+ APPLY_FLAG("keepmarks", CMOD_KEEPMARKS);
+ APPLY_FLAG("keepjumps", CMOD_KEEPJUMPS);
+ APPLY_FLAG("lockmarks", CMOD_LOCKMARKS);
+ APPLY_FLAG("keeppatterns", CMOD_KEEPPATTERNS);
+ }
+ }
+ // pop the value; lua_next will pop the key.
+ lua_pop(L, 1); // [dict, ..., key]
+ }
+ int status = 0;
+ int rets = 0;
+
+ cmdmod_T save_cmdmod = cmdmod;
+ cmdmod.cmod_flags = flags;
+ apply_cmdmod(&cmdmod);
+
+ if (buf || win) {
+ try_start();
+ }
+
+ aco_save_T aco;
+ win_execute_T win_execute_args;
+ Error err = ERROR_INIT;
+
+ if (win) {
+ tabpage_T *tabpage = win_find_tabpage(win);
+ if (!win_execute_before(&win_execute_args, win, tabpage)) {
+ goto end;
+ }
+ } else if (buf) {
+ aucmd_prepbuf(&aco, buf);
+ }
+
+ int s = lua_gettop(L);
+ lua_pushvalue(L, 2);
+ status = lua_pcall(L, 0, LUA_MULTRET, 0);
+ rets = lua_gettop(L) - s;
+
+ if (win) {
+ win_execute_after(&win_execute_args);
+ } else if (buf) {
+ aucmd_restbuf(&aco);
+ }
+
+end:
+ if (buf || win) {
+ try_end(&err);
+ }
+
+ undo_cmdmod(&cmdmod);
+ cmdmod = save_cmdmod;
+
+ if (status) {
+ return lua_error(L);
+ } else if (ERROR_SET(&err)) {
+ nlua_push_errstr(L, "%s", err.msg);
+ api_clear_error(&err);
+ return lua_error(L);
+ }
+
+ return rets;
+}
+
// Access to internal functions. For use in runtime/
static void nlua_state_add_internal(lua_State *const lstate)
{
@@ -582,6 +679,9 @@ static void nlua_state_add_internal(lua_State *const lstate)
// _updatefolds
lua_pushcfunction(lstate, &nlua_foldupdate);
lua_setfield(lstate, -2, "_foldupdate");
+
+ lua_pushcfunction(lstate, &nlua_with);
+ lua_setfield(lstate, -2, "_with_c");
}
void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread)
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 98d5d8c6cb..6a0dc10214 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -31,8 +31,6 @@
#include "nvim/msgpack_rpc/packer.h"
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/os/input.h"
-#include "nvim/rbuffer.h"
-#include "nvim/rbuffer_defs.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/ui_client.h"
@@ -202,10 +200,25 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem
return frame.errored ? NIL : frame.result;
}
-static void receive_msgpack(RStream *stream, RBuffer *rbuf, size_t c, void *data, bool eof)
+static size_t receive_msgpack(RStream *stream, const char *rbuf, size_t c, void *data, bool eof)
{
Channel *channel = data;
channel_incref(channel);
+ size_t consumed = 0;
+
+ DLOG("ch %" PRIu64 ": parsing %zu bytes from msgpack Stream: %p",
+ channel->id, c, (void *)stream);
+
+ if (c > 0) {
+ Unpacker *p = channel->rpc.unpacker;
+ p->read_ptr = rbuf;
+ p->read_size = c;
+ parse_msgpack(channel);
+
+ if (!unpacker_closed(p)) {
+ consumed = c - p->read_size;
+ }
+ }
if (eof) {
channel_close(channel->id, kChannelPartRpc, NULL);
@@ -213,25 +226,10 @@ static void receive_msgpack(RStream *stream, RBuffer *rbuf, size_t c, void *data
snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the client",
channel->id);
chan_close_with_error(channel, buf, LOGLVL_INF);
- goto end;
- }
-
- DLOG("ch %" PRIu64 ": parsing %zu bytes from msgpack Stream: %p",
- channel->id, rbuffer_size(rbuf), (void *)stream);
-
- Unpacker *p = channel->rpc.unpacker;
- size_t size = 0;
- p->read_ptr = rbuffer_read_ptr(rbuf, &size);
- p->read_size = size;
- parse_msgpack(channel);
-
- if (!unpacker_closed(p)) {
- size_t consumed = size - p->read_size;
- rbuffer_consumed_compact(rbuf, consumed);
}
-end:
channel_decref(channel);
+ return consumed;
}
static ChannelCallFrame *find_call_frame(RpcState *rpc, uint32_t request_id)
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 41b9aaf9a7..301e6e9cea 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -6192,10 +6192,10 @@ bool can_bs(int what)
return vim_strchr(p_bs, what) != NULL;
}
-/// Get the local or global value of 'backupcopy'.
+/// Get the local or global value of 'backupcopy' flags.
///
/// @param buf The buffer.
-unsigned get_bkc_value(buf_T *buf)
+unsigned get_bkc_flags(buf_T *buf)
{
return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags;
}
@@ -6211,7 +6211,7 @@ char *get_flp_value(buf_T *buf)
return buf->b_p_flp;
}
-/// Get the local or global value of the 'virtualedit' flags.
+/// Get the local or global value of 'virtualedit' flags.
unsigned get_ve_flags(win_T *wp)
{
return (wp->w_ve_flags ? wp->w_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU);
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index 5f372b2376..585c4964e2 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -21,8 +21,6 @@
#include "nvim/os/fileio.h"
#include "nvim/os/fs.h"
#include "nvim/os/os_defs.h"
-#include "nvim/rbuffer.h"
-#include "nvim/rbuffer_defs.h"
#include "nvim/types_defs.h"
#ifdef HAVE_SYS_UIO_H
diff --git a/src/nvim/os/fileio_defs.h b/src/nvim/os/fileio_defs.h
index 63b6f51c17..0f76fdb2aa 100644
--- a/src/nvim/os/fileio_defs.h
+++ b/src/nvim/os/fileio_defs.h
@@ -4,7 +4,6 @@
#include <stdint.h>
#include "nvim/func_attr.h"
-#include "nvim/rbuffer_defs.h"
/// Structure used to read from/write to file
typedef struct {
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 63eca0b6da..ea21a32230 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -27,8 +27,6 @@
#include "nvim/os/os_defs.h"
#include "nvim/os/time.h"
#include "nvim/profile.h"
-#include "nvim/rbuffer.h"
-#include "nvim/rbuffer_defs.h"
#include "nvim/state.h"
#include "nvim/state_defs.h"
@@ -62,7 +60,7 @@ void input_start(void)
}
used_stdin = true;
- rstream_init_fd(&main_loop, &read_stream, STDIN_FILENO, READ_BUFFER_SIZE);
+ rstream_init_fd(&main_loop, &read_stream, STDIN_FILENO);
rstream_start(&read_stream, input_read_cb, NULL);
}
@@ -157,7 +155,7 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
if (maxlen && input_available()) {
restart_cursorhold_wait(tb_change_cnt);
- // Safe to convert rbuffer_read to int, it will never overflow since
+ // Safe to convert `to_read` to int, it will never overflow since
// INPUT_BUFFER_SIZE fits in an int
size_t to_read = MIN((size_t)maxlen, input_available());
memcpy(buf, input_read_pos, to_read);
@@ -497,17 +495,15 @@ static InbufPollResult inbuf_poll(int ms, MultiQueue *events)
return input_eof ? kInputEof : kInputNone;
}
-static void input_read_cb(RStream *stream, RBuffer *buf, size_t c, void *data, bool at_eof)
+static size_t input_read_cb(RStream *stream, const char *buf, size_t c, void *data, bool at_eof)
{
if (at_eof) {
input_eof = true;
}
- assert(input_space() >= rbuffer_size(buf));
- RBUFFER_UNTIL_EMPTY(buf, ptr, len) {
- input_enqueue_raw(ptr, len);
- rbuffer_consumed(buf, len);
- }
+ assert(input_space() >= c);
+ input_enqueue_raw(buf, c);
+ return c;
}
static void process_ctrl_c(void)
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 026f14ebc8..b66faa2285 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -40,8 +40,6 @@
#include "nvim/path.h"
#include "nvim/pos_defs.h"
#include "nvim/profile.h"
-#include "nvim/rbuffer.h"
-#include "nvim/rbuffer_defs.h"
#include "nvim/state_defs.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
@@ -49,17 +47,11 @@
#include "nvim/ui.h"
#include "nvim/vim_defs.h"
-#define DYNAMIC_BUFFER_INIT { NULL, 0, 0 }
#define NS_1_SECOND 1000000000U // 1 second, in nanoseconds
#define OUT_DATA_THRESHOLD 1024 * 10U // 10KB, "a few screenfuls" of data.
#define SHELL_SPECIAL "\t \"&'$;<>()\\|"
-typedef struct {
- char *data;
- size_t cap, len;
-} DynamicBuffer;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/shell.c.generated.h"
#endif
@@ -669,7 +661,7 @@ char *shell_argv_to_str(char **const argv)
/// @return shell command exit code
int os_call_shell(char *cmd, ShellOpts opts, char *extra_args)
{
- DynamicBuffer input = DYNAMIC_BUFFER_INIT;
+ StringBuilder input = KV_INITIAL_VALUE;
char *output = NULL;
char **output_ptr = NULL;
int current_state = State;
@@ -698,9 +690,9 @@ int os_call_shell(char *cmd, ShellOpts opts, char *extra_args)
size_t nread;
int exitcode = do_os_system(shell_build_argv(cmd, extra_args),
- input.data, input.len, output_ptr, &nread,
+ input.items, input.size, output_ptr, &nread,
emsg_silent, forward_output);
- xfree(input.data);
+ kv_destroy(input);
if (output) {
write_output(output, nread, true);
@@ -864,7 +856,7 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu
bool has_input = (input != NULL && input[0] != NUL);
// the output buffer
- DynamicBuffer buf = DYNAMIC_BUFFER_INIT;
+ StringBuilder buf = KV_INITIAL_VALUE;
stream_read_cb data_cb = system_data_cb;
if (nread) {
*nread = 0;
@@ -907,9 +899,9 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu
if (has_input) {
wstream_init(&proc->in, 0);
}
- rstream_init(&proc->out, 0);
+ rstream_init(&proc->out);
rstream_start(&proc->out, data_cb, &buf);
- rstream_init(&proc->err, 0);
+ rstream_init(&proc->err);
rstream_start(&proc->err, data_cb, &buf);
// write the input, if any
@@ -952,18 +944,17 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu
// prepare the out parameters if requested
if (output) {
- if (buf.len == 0) {
+ assert(nread);
+ if (buf.size == 0) {
// no data received from the process, return NULL
*output = NULL;
- xfree(buf.data);
+ *nread = 0;
+ kv_destroy(buf);
} else {
+ *nread = buf.size;
// NUL-terminate to make the output directly usable as a C string
- buf.data[buf.len] = NUL;
- *output = buf.data;
- }
-
- if (nread) {
- *nread = buf.len;
+ kv_push(buf, NUL);
+ *output = buf.items;
}
}
@@ -973,29 +964,11 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu
return exitcode;
}
-/// - ensures at least `desired` bytes in buffer
-///
-/// TODO(aktau): fold with kvec/garray
-static void dynamic_buffer_ensure(DynamicBuffer *buf, size_t desired)
-{
- if (buf->cap >= desired) {
- assert(buf->data);
- return;
- }
-
- buf->cap = desired;
- kv_roundup32(buf->cap);
- buf->data = xrealloc(buf->data, buf->cap);
-}
-
-static void system_data_cb(RStream *stream, RBuffer *buf, size_t count, void *data, bool eof)
+static size_t system_data_cb(RStream *stream, const char *buf, size_t count, void *data, bool eof)
{
- DynamicBuffer *dbuf = data;
-
- size_t nread = buf->size;
- dynamic_buffer_ensure(dbuf, dbuf->len + nread + 1);
- rbuffer_read(buf, dbuf->data + dbuf->len, nread);
- dbuf->len += nread;
+ StringBuilder *dbuf = data;
+ kv_concat_len(*dbuf, buf, count);
+ return count;
}
/// Tracks output received for the current executing shell command, and displays
@@ -1078,7 +1051,7 @@ static bool out_data_decide_throttle(size_t size)
///
/// @param output Data to save, or NULL to invoke a special mode.
/// @param size Length of `output`.
-static void out_data_ring(char *output, size_t size)
+static void out_data_ring(const char *output, size_t size)
{
#define MAX_CHUNK_SIZE (OUT_DATA_THRESHOLD / 2)
static char last_skipped[MAX_CHUNK_SIZE]; // Saved output.
@@ -1120,11 +1093,11 @@ static void out_data_ring(char *output, size_t size)
/// @param output Data to append to screen lines.
/// @param count Size of data.
/// @param eof If true, there will be no more data output.
-static void out_data_append_to_screen(char *output, size_t *count, bool eof)
+static void out_data_append_to_screen(const char *output, size_t *count, bool eof)
FUNC_ATTR_NONNULL_ALL
{
- char *p = output;
- char *end = output + *count;
+ const char *p = output;
+ const char *end = output + *count;
while (p < end) {
if (*p == '\n' || *p == '\r' || *p == TAB || *p == BELL) {
msg_putchar_attr((uint8_t)(*p), 0);
@@ -1152,25 +1125,16 @@ end:
ui_flush();
}
-static void out_data_cb(RStream *stream, RBuffer *buf, size_t count, void *data, bool eof)
+static size_t out_data_cb(RStream *stream, const char *ptr, size_t count, void *data, bool eof)
{
- size_t cnt;
- char *ptr = rbuffer_read_ptr(buf, &cnt);
-
- if (ptr != NULL && cnt > 0
- && out_data_decide_throttle(cnt)) { // Skip output above a threshold.
+ if (count > 0 && out_data_decide_throttle(count)) { // Skip output above a threshold.
// Save the skipped output. If it is the final chunk, we display it later.
- out_data_ring(ptr, cnt);
- } else if (ptr != NULL) {
- out_data_append_to_screen(ptr, &cnt, eof);
- }
-
- if (cnt) {
- rbuffer_consumed(buf, cnt);
+ out_data_ring(ptr, count);
+ } else if (count > 0) {
+ out_data_append_to_screen(ptr, &count, eof);
}
- // Move remaining data to start of buffer, so the buffer can never wrap around.
- rbuffer_reset(buf);
+ return count;
}
/// Parses a command string into a sequence of words, taking quotes into
@@ -1234,7 +1198,7 @@ static size_t word_length(const char *str)
/// event loop starts. If we don't (by writing in chunks returned by `ml_get`)
/// the buffer being modified might get modified by reading from the process
/// before we finish writing.
-static void read_input(DynamicBuffer *buf)
+static void read_input(StringBuilder *buf)
{
size_t written = 0;
size_t len = 0;
@@ -1248,14 +1212,11 @@ static void read_input(DynamicBuffer *buf)
} else if (lp[written] == NL) {
// NL -> NUL translation
len = 1;
- dynamic_buffer_ensure(buf, buf->len + len);
- buf->data[buf->len++] = NUL;
+ kv_push(*buf, NUL);
} else {
char *s = vim_strchr(lp + written, NL);
len = s == NULL ? l : (size_t)(s - (lp + written));
- dynamic_buffer_ensure(buf, buf->len + len);
- memcpy(buf->data + buf->len, lp + written, len);
- buf->len += len;
+ kv_concat_len(*buf, lp + written, len);
}
if (len == l) {
@@ -1264,8 +1225,7 @@ static void read_input(DynamicBuffer *buf)
|| (!curbuf->b_p_bin && curbuf->b_p_fixeol)
|| (lnum != curbuf->b_no_eol_lnum
&& (lnum != curbuf->b_ml.ml_line_count || curbuf->b_p_eol))) {
- dynamic_buffer_ensure(buf, buf->len + 1);
- buf->data[buf->len++] = NL;
+ kv_push(*buf, NL);
}
lnum++;
if (lnum > curbuf->b_op_end.lnum) {
diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c
deleted file mode 100644
index 493c079d4c..0000000000
--- a/src/nvim/rbuffer.c
+++ /dev/null
@@ -1,230 +0,0 @@
-#include <assert.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <string.h>
-
-#include "nvim/macros_defs.h"
-#include "nvim/memory.h"
-#include "nvim/rbuffer.h"
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "rbuffer.c.generated.h"
-#endif
-
-/// Creates a new `RBuffer` instance.
-RBuffer *rbuffer_new(size_t capacity)
- FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
-{
- if (!capacity) {
- capacity = 0x10000;
- }
-
- RBuffer *rv = xcalloc(1, sizeof(RBuffer) + capacity);
- rv->full_cb = rv->nonfull_cb = NULL;
- rv->data = NULL;
- rv->size = 0;
- rv->write_ptr = rv->read_ptr = rv->start_ptr;
- rv->end_ptr = rv->start_ptr + capacity;
- rv->temp = NULL;
- return rv;
-}
-
-void rbuffer_free(RBuffer *buf) FUNC_ATTR_NONNULL_ALL
-{
- xfree(buf->temp);
- xfree(buf);
-}
-
-/// Return a pointer to a raw buffer containing the first empty slot available
-/// for writing. The second argument is a pointer to the maximum number of
-/// bytes that could be written.
-///
-/// It is necessary to call this function twice to ensure all empty space was
-/// used. See RBUFFER_UNTIL_FULL for a macro that simplifies this task.
-char *rbuffer_write_ptr(RBuffer *buf, size_t *write_count) FUNC_ATTR_NONNULL_ALL
-{
- if (buf->size == rbuffer_capacity(buf)) {
- *write_count = 0;
- return NULL;
- }
-
- if (buf->write_ptr >= buf->read_ptr) {
- *write_count = (size_t)(buf->end_ptr - buf->write_ptr);
- } else {
- *write_count = (size_t)(buf->read_ptr - buf->write_ptr);
- }
-
- return buf->write_ptr;
-}
-
-// Reset an RBuffer so read_ptr is at the beginning of the memory. If
-// necessary, this moves existing data by allocating temporary memory.
-void rbuffer_reset(RBuffer *buf) FUNC_ATTR_NONNULL_ALL
-{
- size_t temp_size;
- if ((temp_size = rbuffer_size(buf))) {
- if (buf->temp == NULL) {
- buf->temp = xcalloc(1, rbuffer_capacity(buf));
- }
- rbuffer_read(buf, buf->temp, buf->size);
- }
- buf->read_ptr = buf->write_ptr = buf->start_ptr;
- if (temp_size) {
- rbuffer_write(buf, buf->temp, temp_size);
- }
-}
-
-/// Adjust `rbuffer` write pointer to reflect produced data. This is called
-/// automatically by `rbuffer_write`, but when using `rbuffer_write_ptr`
-/// directly, this needs to called after the data was copied to the internal
-/// buffer. The write pointer will be wrapped if required.
-void rbuffer_produced(RBuffer *buf, size_t count) FUNC_ATTR_NONNULL_ALL
-{
- assert(count && count <= rbuffer_space(buf));
-
- buf->write_ptr += count;
- if (buf->write_ptr >= buf->end_ptr) {
- // wrap around
- buf->write_ptr -= rbuffer_capacity(buf);
- }
-
- buf->size += count;
- if (buf->full_cb && !rbuffer_space(buf)) {
- buf->full_cb(buf, buf->data);
- }
-}
-
-/// Return a pointer to a raw buffer containing the first byte available
-/// for reading. The second argument is a pointer to the maximum number of
-/// bytes that could be read.
-///
-/// It is necessary to call this function twice to ensure all available bytes
-/// were read. See RBUFFER_UNTIL_EMPTY for a macro that simplifies this task.
-char *rbuffer_read_ptr(RBuffer *buf, size_t *read_count) FUNC_ATTR_NONNULL_ALL
-{
- if (!buf->size) {
- *read_count = 0;
- return buf->read_ptr;
- }
-
- if (buf->read_ptr < buf->write_ptr) {
- *read_count = (size_t)(buf->write_ptr - buf->read_ptr);
- } else {
- *read_count = (size_t)(buf->end_ptr - buf->read_ptr);
- }
-
- return buf->read_ptr;
-}
-
-/// Adjust `rbuffer` read pointer to reflect consumed data. This is called
-/// automatically by `rbuffer_read`, but when using `rbuffer_read_ptr`
-/// directly, this needs to called after the data was copied from the internal
-/// buffer. The read pointer will be wrapped if required.
-void rbuffer_consumed(RBuffer *buf, size_t count)
- FUNC_ATTR_NONNULL_ALL
-{
- if (count == 0) {
- return;
- }
- assert(count <= buf->size);
-
- buf->read_ptr += count;
- if (buf->read_ptr >= buf->end_ptr) {
- buf->read_ptr -= rbuffer_capacity(buf);
- }
-
- bool was_full = buf->size == rbuffer_capacity(buf);
- buf->size -= count;
- if (buf->nonfull_cb && was_full) {
- buf->nonfull_cb(buf, buf->data);
- }
-}
-
-/// Use instead of rbuffer_consumed to use rbuffer in a linear, non-cyclic fashion.
-///
-/// This is generally useful if we can guarantee to parse all input
-/// except some small incomplete token, like when parsing msgpack.
-void rbuffer_consumed_compact(RBuffer *buf, size_t count)
- FUNC_ATTR_NONNULL_ALL
-{
- assert(buf->read_ptr <= buf->write_ptr);
- rbuffer_consumed(buf, count);
- if (buf->read_ptr > buf->start_ptr) {
- assert((size_t)(buf->write_ptr - buf->read_ptr) == buf->size
- || buf->write_ptr == buf->start_ptr);
- memmove(buf->start_ptr, buf->read_ptr, buf->size);
- buf->read_ptr = buf->start_ptr;
- buf->write_ptr = buf->read_ptr + buf->size;
- }
-}
-
-// Higher level functions for copying from/to RBuffer instances and data
-// pointers
-size_t rbuffer_write(RBuffer *buf, const char *src, size_t src_size)
- FUNC_ATTR_NONNULL_ALL
-{
- size_t size = src_size;
-
- RBUFFER_UNTIL_FULL(buf, wptr, wcnt) {
- size_t copy_count = MIN(src_size, wcnt);
- memcpy(wptr, src, copy_count);
- rbuffer_produced(buf, copy_count);
-
- if (!(src_size -= copy_count)) {
- return size;
- }
-
- src += copy_count;
- }
-
- return size - src_size;
-}
-
-size_t rbuffer_read(RBuffer *buf, char *dst, size_t dst_size)
- FUNC_ATTR_NONNULL_ALL
-{
- size_t size = dst_size;
-
- RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) {
- size_t copy_count = MIN(dst_size, rcnt);
- memcpy(dst, rptr, copy_count);
- rbuffer_consumed(buf, copy_count);
-
- if (!(dst_size -= copy_count)) {
- return size;
- }
-
- dst += copy_count;
- }
-
- return size - dst_size;
-}
-
-char *rbuffer_get(RBuffer *buf, size_t index)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
-{
- assert(index < buf->size);
- char *rptr = buf->read_ptr + index;
- if (rptr >= buf->end_ptr) {
- rptr -= rbuffer_capacity(buf);
- }
- return rptr;
-}
-
-int rbuffer_cmp(RBuffer *buf, const char *str, size_t count)
- FUNC_ATTR_NONNULL_ALL
-{
- assert(count <= buf->size);
- size_t rcnt;
- rbuffer_read_ptr(buf, &rcnt);
- size_t n = MIN(count, rcnt);
- int rv = memcmp(str, buf->read_ptr, n);
- count -= n;
- size_t remaining = buf->size - rcnt;
-
- if (rv || !count || !remaining) {
- return rv;
- }
-
- return memcmp(str + n, buf->start_ptr, count);
-}
diff --git a/src/nvim/rbuffer.h b/src/nvim/rbuffer.h
deleted file mode 100644
index 942e1f2365..0000000000
--- a/src/nvim/rbuffer.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Specialized ring buffer. This is basically an array that wraps read/write
-// pointers around the memory region. It should be more efficient than the old
-// RBuffer which required memmove() calls to relocate read/write positions.
-//
-// The main purpose of RBuffer is simplify memory management when reading from
-// uv_stream_t instances:
-//
-// - The event loop writes data to a RBuffer, advancing the write pointer
-// - The main loop reads data, advancing the read pointer
-// - If the buffer becomes full(size == capacity) the rstream is temporarily
-// stopped(automatic backpressure handling)
-//
-// Reference: http://en.wikipedia.org/wiki/Circular_buffer
-#pragma once
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "nvim/rbuffer_defs.h" // IWYU pragma: keep
-
-// Macros that simplify working with the read/write pointers directly by hiding
-// ring buffer wrap logic. Some examples:
-//
-// - Pass the write pointer to a function(write_data) that incrementally
-// produces data, returning the number of bytes actually written to the
-// ring buffer:
-//
-// RBUFFER_UNTIL_FULL(rbuf, ptr, cnt)
-// rbuffer_produced(rbuf, write_data(state, ptr, cnt));
-//
-// - Pass the read pointer to a function(read_data) that incrementally
-// consumes data, returning the number of bytes actually read from the
-// ring buffer:
-//
-// RBUFFER_UNTIL_EMPTY(rbuf, ptr, cnt)
-// rbuffer_consumed(rbuf, read_data(state, ptr, cnt));
-//
-// Note that the rbuffer_{produced,consumed} calls are necessary or these macros
-// create infinite loops
-#define RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) \
- for (size_t rcnt = 0, _r = 1; _r; _r = 0) \
- for (char *rptr = rbuffer_read_ptr(buf, &rcnt); \
- buf->size; \
- rptr = rbuffer_read_ptr(buf, &rcnt))
-
-#define RBUFFER_UNTIL_FULL(buf, wptr, wcnt) \
- for (size_t wcnt = 0, _r = 1; _r; _r = 0) \
- for (char *wptr = rbuffer_write_ptr(buf, &wcnt); \
- rbuffer_space(buf); \
- wptr = rbuffer_write_ptr(buf, &wcnt))
-
-// Iteration
-#define RBUFFER_EACH(buf, c, i) \
- for (size_t i = 0; \
- i < buf->size; \
- i = buf->size) \
- for (char c = 0; \
- i < buf->size ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \
- i++)
-
-#define RBUFFER_EACH_REVERSE(buf, c, i) \
- for (size_t i = buf->size; \
- i != SIZE_MAX; \
- i = SIZE_MAX) \
- for (char c = 0; \
- i-- > 0 ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \
- )
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "rbuffer.h.generated.h"
-#endif
diff --git a/src/nvim/rbuffer_defs.h b/src/nvim/rbuffer_defs.h
deleted file mode 100644
index 51dc349846..0000000000
--- a/src/nvim/rbuffer_defs.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#pragma once
-
-#include <stddef.h>
-
-#include "nvim/func_attr.h"
-
-typedef struct rbuffer RBuffer;
-/// Type of function invoked during certain events:
-/// - When the RBuffer switches to the full state
-/// - When the RBuffer switches to the non-full state
-typedef void (*rbuffer_callback)(RBuffer *buf, void *data);
-
-struct rbuffer {
- rbuffer_callback full_cb, nonfull_cb;
- void *data;
- size_t size;
- // helper memory used to by rbuffer_reset if required
- char *temp;
- char *end_ptr, *read_ptr, *write_ptr;
- char start_ptr[];
-};
-
-static inline size_t rbuffer_size(RBuffer *buf)
- REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
-
-static inline size_t rbuffer_size(RBuffer *buf)
-{
- return buf->size;
-}
-
-static inline size_t rbuffer_capacity(RBuffer *buf)
- REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
-
-static inline size_t rbuffer_capacity(RBuffer *buf)
-{
- return (size_t)(buf->end_ptr - buf->start_ptr);
-}
-
-static inline size_t rbuffer_space(RBuffer *buf)
- REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
-
-static inline size_t rbuffer_space(RBuffer *buf)
-{
- return rbuffer_capacity(buf) - buf->size;
-}
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 5130678a81..a5768cfc06 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -15,7 +15,6 @@
#include "nvim/option_vars.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
-#include "nvim/rbuffer.h"
#include "nvim/strings.h"
#include "nvim/tui/input.h"
#include "nvim/tui/input_defs.h"
@@ -153,7 +152,7 @@ void tinput_init(TermInput *input, Loop *loop)
termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS);
// setup input handle
- rstream_init_fd(loop, &input->read_stream, input->in_fd, READ_STREAM_SIZE);
+ rstream_init_fd(loop, &input->read_stream, input->in_fd);
// initialize a timer handle for handling ESC with libtermkey
uv_timer_init(&loop->uv, &input->timer_handle);
@@ -211,9 +210,9 @@ static void tinput_flush(TermInput *input)
input->key_buffer_len = 0;
}
-static void tinput_enqueue(TermInput *input, char *buf, size_t size)
+static void tinput_enqueue(TermInput *input, const char *buf, size_t size)
{
- if (input->key_buffer_len > KEY_BUFFER_SIZE - 0xff) {
+ if (input->key_buffer_len > KEY_BUFFER_SIZE - size) {
// don't ever let the buffer get too full or we risk putting incomplete keys into it
tinput_flush(input);
}
@@ -463,8 +462,10 @@ static void tinput_timer_cb(uv_timer_t *handle)
TermInput *input = handle->data;
// If the raw buffer is not empty, process the raw buffer first because it is
// processing an incomplete bracketed paster sequence.
- if (rbuffer_size(input->read_stream.buffer)) {
- handle_raw_buffer(input, true);
+ size_t size = rstream_available(&input->read_stream);
+ if (size) {
+ size_t consumed = handle_raw_buffer(input, true, input->read_stream.read_pos, size);
+ rstream_consume(&input->read_stream, consumed);
}
tk_getkeys(input, true);
tinput_flush(input);
@@ -478,39 +479,37 @@ static void tinput_timer_cb(uv_timer_t *handle)
///
/// @param input the input stream
/// @return true iff handle_focus_event consumed some input
-static bool handle_focus_event(TermInput *input)
+static size_t handle_focus_event(TermInput *input, const char *ptr, size_t size)
{
- if (rbuffer_size(input->read_stream.buffer) > 2
- && (!rbuffer_cmp(input->read_stream.buffer, "\x1b[I", 3)
- || !rbuffer_cmp(input->read_stream.buffer, "\x1b[O", 3))) {
- bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I';
- // Advance past the sequence
- rbuffer_consumed(input->read_stream.buffer, 3);
+ if (size >= 3
+ && (!memcmp(ptr, "\x1b[I", 3)
+ || !memcmp(ptr, "\x1b[O", 3))) {
+ bool focus_gained = ptr[2] == 'I';
MAXSIZE_TEMP_ARRAY(args, 1);
ADD_C(args, BOOLEAN_OBJ(focus_gained));
rpc_send_event(ui_client_channel_id, "nvim_ui_set_focus", args);
- return true;
+ return 3; // Advance past the sequence
}
- return false;
+ return 0;
}
#define START_PASTE "\x1b[200~"
#define END_PASTE "\x1b[201~"
-static HandleState handle_bracketed_paste(TermInput *input)
+static size_t handle_bracketed_paste(TermInput *input, const char *ptr, size_t size,
+ bool *incomplete)
{
- size_t buf_size = rbuffer_size(input->read_stream.buffer);
- if (buf_size > 5
- && (!rbuffer_cmp(input->read_stream.buffer, START_PASTE, 6)
- || !rbuffer_cmp(input->read_stream.buffer, END_PASTE, 6))) {
- bool enable = *rbuffer_get(input->read_stream.buffer, 4) == '0';
+ if (size >= 6
+ && (!memcmp(ptr, START_PASTE, 6)
+ || !memcmp(ptr, END_PASTE, 6))) {
+ bool enable = ptr[4] == '0';
if (input->paste && enable) {
- return kNotApplicable; // Pasting "start paste" code literally.
+ return 0; // Pasting "start paste" code literally.
}
+
// Advance past the sequence
- rbuffer_consumed(input->read_stream.buffer, 6);
if (!!input->paste == enable) {
- return kComplete; // Spurious "disable paste" code.
+ return 6; // Spurious "disable paste" code.
}
if (enable) {
@@ -525,15 +524,15 @@ static HandleState handle_bracketed_paste(TermInput *input)
// Paste phase: "disabled".
input->paste = 0;
}
- return kComplete;
- } else if (buf_size < 6
- && (!rbuffer_cmp(input->read_stream.buffer, START_PASTE, buf_size)
- || !rbuffer_cmp(input->read_stream.buffer,
- END_PASTE, buf_size))) {
+ return 6;
+ } else if (size < 6
+ && (!memcmp(ptr, START_PASTE, size)
+ || !memcmp(ptr, END_PASTE, size))) {
// Wait for further input, as the sequence may be split.
- return kIncomplete;
+ *incomplete = true;
+ return 0;
}
- return kNotApplicable;
+ return 0;
}
/// Handle an OSC or DCS response sequence from the terminal.
@@ -644,20 +643,31 @@ static void handle_unknown_csi(TermInput *input, const TermKeyKey *key)
}
}
-static void handle_raw_buffer(TermInput *input, bool force)
+static size_t handle_raw_buffer(TermInput *input, bool force, const char *data, size_t size)
{
- HandleState is_paste = kNotApplicable;
+ const char *ptr = data;
do {
- if (!force
- && (handle_focus_event(input)
- || (is_paste = handle_bracketed_paste(input)) != kNotApplicable)) {
- if (is_paste == kIncomplete) {
+ if (!force) {
+ size_t consumed = handle_focus_event(input, ptr, size);
+ if (consumed) {
+ ptr += consumed;
+ size -= consumed;
+ continue;
+ }
+
+ bool incomplete = false;
+ consumed = handle_bracketed_paste(input, ptr, size, &incomplete);
+ if (incomplete) {
+ assert(consumed == 0);
// Wait for the next input, leaving it in the raw buffer due to an
// incomplete sequence.
- return;
+ return (size_t)(ptr - data);
+ } else if (consumed) {
+ ptr += consumed;
+ size -= consumed;
+ continue;
}
- continue;
}
//
@@ -666,55 +676,47 @@ static void handle_raw_buffer(TermInput *input, bool force)
// calls (above) depend on this.
//
size_t count = 0;
- RBUFFER_EACH(input->read_stream.buffer, c, i) {
+ for (size_t i = 0; i < size; i++) {
count = i + 1;
- if (c == '\x1b' && count > 1) {
+ if (ptr[i] == '\x1b' && count > 1) {
count--;
break;
}
}
// Push bytes directly (paste).
if (input->paste) {
- RBUFFER_UNTIL_EMPTY(input->read_stream.buffer, ptr, len) {
- size_t consumed = MIN(count, len);
- assert(consumed <= input->read_stream.buffer->size);
- tinput_enqueue(input, ptr, consumed);
- rbuffer_consumed(input->read_stream.buffer, consumed);
- if (!(count -= consumed)) {
- break;
- }
- }
+ tinput_enqueue(input, ptr, count);
+ ptr += count;
+ size -= count;
continue;
}
+
// Push through libtermkey (translates to "<keycode>" strings, etc.).
- RBUFFER_UNTIL_EMPTY(input->read_stream.buffer, ptr, len) {
- const size_t size = MIN(count, len);
- if (size > termkey_get_buffer_remaining(input->tk)) {
+ {
+ const size_t to_use = MIN(count, size);
+ if (to_use > termkey_get_buffer_remaining(input->tk)) {
// We are processing a very long escape sequence. Increase termkey's
// internal buffer size. We don't handle out of memory situations so
// abort if it fails
- const size_t delta = size - termkey_get_buffer_remaining(input->tk);
+ const size_t delta = to_use - termkey_get_buffer_remaining(input->tk);
const size_t bufsize = termkey_get_buffer_size(input->tk);
if (!termkey_set_buffer_size(input->tk, MAX(bufsize + delta, bufsize * 2))) {
abort();
}
}
- size_t consumed = termkey_push_bytes(input->tk, ptr, size);
+ size_t consumed = termkey_push_bytes(input->tk, ptr, to_use);
// We resize termkey's buffer when it runs out of space, so this should
// never happen
- assert(consumed <= rbuffer_size(input->read_stream.buffer));
- rbuffer_consumed(input->read_stream.buffer, consumed);
+ assert(consumed <= to_use);
+ ptr += consumed;
+ size -= consumed;
// Process the input buffer now for any keys
tk_getkeys(input, false);
-
- if (!(count -= consumed)) {
- break;
- }
}
- } while (rbuffer_size(input->read_stream.buffer));
+ } while (size);
const size_t tk_size = termkey_get_buffer_size(input->tk);
const size_t tk_remaining = termkey_get_buffer_remaining(input->tk);
@@ -726,23 +728,25 @@ static void handle_raw_buffer(TermInput *input, bool force)
abort();
}
}
+
+ return (size_t)(ptr - data);
}
-static void tinput_read_cb(RStream *stream, RBuffer *buf, size_t count_, void *data, bool eof)
+static size_t tinput_read_cb(RStream *stream, const char *buf, size_t count_, void *data, bool eof)
{
TermInput *input = data;
+ size_t consumed = handle_raw_buffer(input, false, buf, count_);
+ tinput_flush(input);
+
if (eof) {
loop_schedule_fast(&main_loop, event_create(tinput_done_event, NULL));
- return;
+ return consumed;
}
- handle_raw_buffer(input, false);
- tinput_flush(input);
-
// An incomplete sequence was found. Leave it in the raw buffer and wait for
// the next input.
- if (rbuffer_size(input->read_stream.buffer)) {
+ if (consumed < count_) {
// If 'ttimeout' is not set, start the timer with a timeout of 0 to process
// the next input.
int64_t ms = input->ttimeout
@@ -750,11 +754,7 @@ static void tinput_read_cb(RStream *stream, RBuffer *buf, size_t count_, void *d
// Stop the current timer if already running
uv_timer_stop(&input->timer_handle);
uv_timer_start(&input->timer_handle, tinput_timer_cb, (uint32_t)ms, 0);
- return;
}
- // Make sure the next input escape sequence fits into the ring buffer without
- // wraparound, else it could be misinterpreted (because rbuffer_read_ptr()
- // exposes the underlying buffer to callers unaware of the wraparound).
- rbuffer_reset(input->read_stream.buffer);
+ return consumed;
}
diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h
index c594228c07..8d0c0c20e9 100644
--- a/src/nvim/tui/input.h
+++ b/src/nvim/tui/input.h
@@ -5,7 +5,6 @@
#include <uv.h>
#include "nvim/event/defs.h"
-#include "nvim/rbuffer_defs.h"
#include "nvim/tui/input_defs.h" // IWYU pragma: keep
#include "nvim/tui/tui_defs.h"
#include "nvim/types_defs.h"
@@ -17,7 +16,7 @@ typedef enum {
kKeyEncodingXterm, ///< Xterm's modifyOtherKeys encoding (XTMODKEYS)
} KeyEncoding;
-#define KEY_BUFFER_SIZE 0xfff
+#define KEY_BUFFER_SIZE 0x1000
typedef struct {
int in_fd;
// Phases: -1=all 0=disabled 1=first-chunk 2=continue 3=last-chunk
@@ -40,12 +39,6 @@ typedef struct {
size_t key_buffer_len;
} TermInput;
-typedef enum {
- kIncomplete = -1,
- kNotApplicable = 0,
- kComplete = 1,
-} HandleState;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/input.h.generated.h"
#endif
diff --git a/test/functional/lua/glob_spec.lua b/test/functional/lua/glob_spec.lua
index 56cd4c9bb5..b3e1b79ee7 100644
--- a/test/functional/lua/glob_spec.lua
+++ b/test/functional/lua/glob_spec.lua
@@ -161,7 +161,7 @@ describe('glob', function()
eq(false, match('{ab,cd}', 'a'))
eq(true, match('{ab,cd}', 'cd'))
eq(true, match('{a,b,c}', 'c'))
- eq(true, match('{a,{b,c}}', 'c'))
+ eq(false, match('{a,{b,c}}', 'c')) -- {} cannot nest
end)
it('should match [] groups', function()
@@ -223,6 +223,17 @@ describe('glob', function()
eq(true, match('{[0-9],[a-z]}', '0'))
eq(true, match('{[0-9],[a-z]}', 'a'))
eq(false, match('{[0-9],[a-z]}', 'A'))
+
+ -- glob is from willRename filter in typescript-language-server
+ -- https://github.com/typescript-language-server/typescript-language-server/blob/b224b878652438bcdd639137a6b1d1a6630129e4/src/lsp-server.ts#L266
+ eq(true, match('**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}', 'test.js'))
+ eq(true, match('**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}', 'test.ts'))
+ eq(true, match('**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}', 'test.mts'))
+ eq(true, match('**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}', 'test.mjs'))
+ eq(true, match('**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}', 'test.cjs'))
+ eq(true, match('**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}', 'test.cts'))
+ eq(true, match('**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}', 'test.jsx'))
+ eq(true, match('**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}', 'test.tsx'))
end)
end)
end)
diff --git a/test/functional/lua/with_spec.lua b/test/functional/lua/with_spec.lua
new file mode 100644
index 0000000000..36dee9630a
--- /dev/null
+++ b/test/functional/lua/with_spec.lua
@@ -0,0 +1,292 @@
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+local Screen = require('test.functional.ui.screen')
+
+local fn = n.fn
+local api = n.api
+local command = n.command
+local eq = t.eq
+local exec_lua = n.exec_lua
+local matches = t.matches
+local pcall_err = t.pcall_err
+
+before_each(function()
+ n.clear()
+end)
+
+describe('vim._with {buf = }', function()
+ it('does not trigger autocmd', function()
+ exec_lua [[
+ local new = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_create_autocmd( { 'BufEnter', 'BufLeave', 'BufWinEnter', 'BufWinLeave' }, {
+ callback = function() _G.n = (_G.n or 0) + 1 end
+ })
+ vim._with({buf = new}, function()
+ end)
+ assert(_G.n == nil)
+ ]]
+ end)
+
+ it('trigger autocmd if changed within context', function()
+ exec_lua [[
+ local new = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_create_autocmd( { 'BufEnter', 'BufLeave', 'BufWinEnter', 'BufWinLeave' }, {
+ callback = function() _G.n = (_G.n or 0) + 1 end
+ })
+ vim._with({}, function()
+ vim.api.nvim_set_current_buf(new)
+ assert(_G.n ~= nil)
+ end)
+ ]]
+ end)
+
+ it('can access buf options', function()
+ local buf1 = api.nvim_get_current_buf()
+ local buf2 = exec_lua [[
+ buf2 = vim.api.nvim_create_buf(false, true)
+ return buf2
+ ]]
+
+ eq(false, api.nvim_get_option_value('autoindent', { buf = buf1 }))
+ eq(false, api.nvim_get_option_value('autoindent', { buf = buf2 }))
+
+ local val = exec_lua [[
+ return vim._with({buf = buf2}, function()
+ vim.cmd "set autoindent"
+ return vim.api.nvim_get_current_buf()
+ end)
+ ]]
+
+ eq(false, api.nvim_get_option_value('autoindent', { buf = buf1 }))
+ eq(true, api.nvim_get_option_value('autoindent', { buf = buf2 }))
+ eq(buf1, api.nvim_get_current_buf())
+ eq(buf2, val)
+ end)
+
+ it('does not cause ml_get errors with invalid visual selection', function()
+ exec_lua [[
+ local api = vim.api
+ local t = function(s) return api.nvim_replace_termcodes(s, true, true, true) end
+ api.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"})
+ api.nvim_feedkeys(t "G<C-V>", "txn", false)
+ vim._with({buf = api.nvim_create_buf(false, true)}, function() vim.cmd "redraw" end)
+ ]]
+ end)
+
+ it('can be nested crazily with hidden buffers', function()
+ eq(
+ true,
+ exec_lua([[
+ local function scratch_buf_call(fn)
+ local buf = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_set_option_value('cindent', true, {buf = buf})
+ return vim._with({buf = buf}, function()
+ return vim.api.nvim_get_current_buf() == buf
+ and vim.api.nvim_get_option_value('cindent', {buf = buf})
+ and fn()
+ end) and vim.api.nvim_buf_delete(buf, {}) == nil
+ end
+
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return scratch_buf_call(function()
+ return true
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ end)
+ ]])
+ )
+ end)
+
+ it('can return values by reference', function()
+ eq(
+ { 4, 7 },
+ exec_lua [[
+ local val = {4, 10}
+ local ref = vim._with({ buf = 0}, function() return val end)
+ ref[2] = 7
+ return val
+ ]]
+ )
+ end)
+end)
+
+describe('vim._with {win = }', function()
+ it('does not trigger autocmd', function()
+ exec_lua [[
+ local old = vim.api.nvim_get_current_win()
+ vim.cmd("new")
+ local new = vim.api.nvim_get_current_win()
+ vim.api.nvim_create_autocmd( { 'WinEnter', 'WinLeave' }, {
+ callback = function() _G.n = (_G.n or 0) + 1 end
+ })
+ vim._with({win = old}, function()
+ end)
+ assert(_G.n == nil)
+ ]]
+ end)
+
+ it('trigger autocmd if changed within context', function()
+ exec_lua [[
+ local old = vim.api.nvim_get_current_win()
+ vim.cmd("new")
+ local new = vim.api.nvim_get_current_win()
+ vim.api.nvim_create_autocmd( { 'WinEnter', 'WinLeave' }, {
+ callback = function() _G.n = (_G.n or 0) + 1 end
+ })
+ vim._with({}, function()
+ vim.api.nvim_set_current_win(old)
+ assert(_G.n ~= nil)
+ end)
+ ]]
+ end)
+
+ it('can access window options', function()
+ command('vsplit')
+ local win1 = api.nvim_get_current_win()
+ command('wincmd w')
+ local win2 = exec_lua [[
+ win2 = vim.api.nvim_get_current_win()
+ return win2
+ ]]
+ command('wincmd p')
+
+ eq('', api.nvim_get_option_value('winhighlight', { win = win1 }))
+ eq('', api.nvim_get_option_value('winhighlight', { win = win2 }))
+
+ local val = exec_lua [[
+ return vim._with({win = win2}, function()
+ vim.cmd "setlocal winhighlight=Normal:Normal"
+ return vim.api.nvim_get_current_win()
+ end)
+ ]]
+
+ eq('', api.nvim_get_option_value('winhighlight', { win = win1 }))
+ eq('Normal:Normal', api.nvim_get_option_value('winhighlight', { win = win2 }))
+ eq(win1, api.nvim_get_current_win())
+ eq(win2, val)
+ end)
+
+ it('does not cause ml_get errors with invalid visual selection', function()
+ -- Add lines to the current buffer and make another window looking into an empty buffer.
+ exec_lua [[
+ _G.api = vim.api
+ _G.t = function(s) return api.nvim_replace_termcodes(s, true, true, true) end
+ _G.win_lines = api.nvim_get_current_win()
+ vim.cmd "new"
+ _G.win_empty = api.nvim_get_current_win()
+ api.nvim_set_current_win(win_lines)
+ api.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"})
+ ]]
+
+ -- Start Visual in current window, redraw in other window with fewer lines.
+ exec_lua [[
+ api.nvim_feedkeys(t "G<C-V>", "txn", false)
+ vim._with({win = win_empty}, function() vim.cmd "redraw" end)
+ ]]
+
+ -- Start Visual in current window, extend it in other window with more lines.
+ exec_lua [[
+ api.nvim_feedkeys(t "<Esc>gg", "txn", false)
+ api.nvim_set_current_win(win_empty)
+ api.nvim_feedkeys(t "gg<C-V>", "txn", false)
+ vim._with({win = win_lines}, function() api.nvim_feedkeys(t "G<C-V>", "txn", false) end)
+ vim.cmd "redraw"
+ ]]
+ end)
+
+ it('updates ruler if cursor moved', function()
+ local screen = Screen.new(30, 5)
+ screen:set_default_attr_ids {
+ [1] = { reverse = true },
+ [2] = { bold = true, reverse = true },
+ }
+ screen:attach()
+ exec_lua [[
+ _G.api = vim.api
+ vim.opt.ruler = true
+ local lines = {}
+ for i = 0, 499 do lines[#lines + 1] = tostring(i) end
+ api.nvim_buf_set_lines(0, 0, -1, true, lines)
+ api.nvim_win_set_cursor(0, {20, 0})
+ vim.cmd "split"
+ _G.win = api.nvim_get_current_win()
+ vim.cmd "wincmd w | redraw"
+ ]]
+ screen:expect [[
+ 19 |
+ {1:[No Name] [+] 20,1 3%}|
+ ^19 |
+ {2:[No Name] [+] 20,1 3%}|
+ |
+ ]]
+ exec_lua [[
+ vim._with({win = win}, function() api.nvim_win_set_cursor(0, {100, 0}) end)
+ vim.cmd "redraw"
+ ]]
+ screen:expect [[
+ 99 |
+ {1:[No Name] [+] 100,1 19%}|
+ ^19 |
+ {2:[No Name] [+] 20,1 3%}|
+ |
+ ]]
+ end)
+
+ it('can return values by reference', function()
+ eq(
+ { 7, 10 },
+ exec_lua [[
+ local val = {4, 10}
+ local ref = vim._with({win = 0}, function() return val end)
+ ref[1] = 7
+ return val
+ ]]
+ )
+ end)
+
+ it('layout in current tabpage does not affect windows in others', function()
+ command('tab split')
+ local t2_move_win = api.nvim_get_current_win()
+ command('vsplit')
+ local t2_other_win = api.nvim_get_current_win()
+ command('tabprevious')
+ matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)'))
+ command('vsplit')
+
+ exec_lua('vim._with({win = ...}, function() vim.cmd.wincmd "J" end)', t2_move_win)
+ eq({ 'col', { { 'leaf', t2_other_win }, { 'leaf', t2_move_win } } }, fn.winlayout(2))
+ end)
+end)
+
+describe('vim._with {lockmarks = true}', function()
+ it('is reset', function()
+ local mark = exec_lua [[
+ vim.api.nvim_buf_set_lines(0, 0, 0, false, {"marky", "snarky", "malarkey"})
+ vim.api.nvim_buf_set_mark(0,"m",1,0, {})
+ vim._with({lockmarks = true}, function()
+ vim.api.nvim_buf_set_lines(0, 0, 2, false, {"mass", "mess", "moss"})
+ end)
+ return vim.api.nvim_buf_get_mark(0,"m")
+ ]]
+ t.eq(mark, { 1, 0 })
+ end)
+end)
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 6d28b83be8..50e9c0cc0f 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -2690,13 +2690,15 @@ describe('LSP', function()
{
filename = '/fake/uri',
lnum = 1,
+ end_lnum = 2,
col = 3,
+ end_col = 4,
text = 'testing',
user_data = {
uri = 'file:///fake/uri',
range = {
start = { line = 0, character = 2 },
- ['end'] = { line = 0, character = 3 },
+ ['end'] = { line = 1, character = 3 },
},
},
},
@@ -2710,7 +2712,7 @@ describe('LSP', function()
uri = 'file:///fake/uri',
range = {
start = { line = 0, character = 2 },
- ['end'] = { line = 0, character = 3 },
+ ['end'] = { line = 1, character = 3 },
}
},
}
@@ -2723,7 +2725,9 @@ describe('LSP', function()
{
filename = '/fake/uri',
lnum = 1,
+ end_lnum = 1,
col = 3,
+ end_col = 4,
text = 'testing',
user_data = {
targetUri = 'file:///fake/uri',
diff --git a/test/unit/fixtures/rbuffer.c b/test/unit/fixtures/rbuffer.c
deleted file mode 100644
index d587d6b054..0000000000
--- a/test/unit/fixtures/rbuffer.c
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "nvim/rbuffer.h"
-#include "rbuffer.h"
-
-
-void ut_rbuffer_each_read_chunk(RBuffer *buf, each_ptr_cb cb)
-{
- RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) {
- cb(rptr, rcnt);
- rbuffer_consumed(buf, rcnt);
- }
-}
-
-void ut_rbuffer_each_write_chunk(RBuffer *buf, each_ptr_cb cb)
-{
- RBUFFER_UNTIL_FULL(buf, wptr, wcnt) {
- cb(wptr, wcnt);
- rbuffer_produced(buf, wcnt);
- }
-}
-void ut_rbuffer_each(RBuffer *buf, each_cb cb)
-{
- RBUFFER_EACH(buf, c, i) cb(c, i);
-}
-
-void ut_rbuffer_each_reverse(RBuffer *buf, each_cb cb)
-{
- RBUFFER_EACH_REVERSE(buf, c, i) cb(c, i);
-}
diff --git a/test/unit/fixtures/rbuffer.h b/test/unit/fixtures/rbuffer.h
deleted file mode 100644
index 640092c627..0000000000
--- a/test/unit/fixtures/rbuffer.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "nvim/rbuffer.h"
-
-typedef void(*each_ptr_cb)(char *ptr, size_t cnt);
-typedef void(*each_cb)(char c, size_t i);
-
-void ut_rbuffer_each_read_chunk(RBuffer *buf, each_ptr_cb cb);
-void ut_rbuffer_each_write_chunk(RBuffer *buf, each_ptr_cb cb);
-void ut_rbuffer_each(RBuffer *buf, each_cb cb);
-void ut_rbuffer_each_reverse(RBuffer *buf, each_cb cb);
diff --git a/test/unit/rbuffer_spec.lua b/test/unit/rbuffer_spec.lua
deleted file mode 100644
index ad18ea2ddc..0000000000
--- a/test/unit/rbuffer_spec.lua
+++ /dev/null
@@ -1,340 +0,0 @@
-local t = require('test.unit.testutil')
-local itp = t.gen_itp(it)
-
-local eq = t.eq
-local ffi = t.ffi
-local cstr = t.cstr
-local to_cstr = t.to_cstr
-local child_call_once = t.child_call_once
-
-local rbuffer = t.cimport('./test/unit/fixtures/rbuffer.h')
-
-describe('rbuffer functions', function()
- local capacity = 16
- local rbuf
-
- local function inspect()
- return ffi.string(rbuf.start_ptr, capacity)
- end
-
- local function write(str)
- local buf = to_cstr(str)
- return rbuffer.rbuffer_write(rbuf, buf, #str)
- end
-
- local function read(len)
- local buf = cstr(len)
- len = rbuffer.rbuffer_read(rbuf, buf, len)
- return ffi.string(buf, len)
- end
-
- local function get(idx)
- return ffi.string(rbuffer.rbuffer_get(rbuf, idx), 1)
- end
-
- before_each(function()
- child_call_once(function()
- rbuf = ffi.gc(rbuffer.rbuffer_new(capacity), rbuffer.rbuffer_free)
- -- fill the internal buffer with the character '0' to simplify inspecting
- ffi.C.memset(rbuf.start_ptr, string.byte('0'), capacity)
- end)
- end)
-
- describe('RBUFFER_UNTIL_FULL', function()
- local chunks
-
- local function collect_write_chunks()
- rbuffer.ut_rbuffer_each_write_chunk(rbuf, function(wptr, wcnt)
- table.insert(chunks, ffi.string(wptr, wcnt))
- end)
- end
-
- before_each(function()
- chunks = {}
- end)
-
- describe('with empty buffer in one contiguous chunk', function()
- itp('is called once with the empty chunk', function()
- collect_write_chunks()
- eq({ '0000000000000000' }, chunks)
- end)
- end)
-
- describe('with partially empty buffer in one contiguous chunk', function()
- itp('is called once with the empty chunk', function()
- write('string')
- collect_write_chunks()
- eq({ '0000000000' }, chunks)
- end)
- end)
-
- describe('with filled buffer in one contiguous chunk', function()
- itp('is not called', function()
- write('abcdefghijklmnopq')
- collect_write_chunks()
- eq({}, chunks)
- end)
- end)
-
- describe('with buffer partially empty in two contiguous chunks', function()
- itp('is called twice with each filled chunk', function()
- write('1234567890')
- read(8)
- collect_write_chunks()
- eq({ '000000', '12345678' }, chunks)
- end)
- end)
-
- describe('with buffer empty in two contiguous chunks', function()
- itp('is called twice with each filled chunk', function()
- write('12345678')
- read(8)
- collect_write_chunks()
- eq({ '00000000', '12345678' }, chunks)
- end)
- end)
-
- describe('with buffer filled in two contiguous chunks', function()
- itp('is not called', function()
- write('12345678')
- read(8)
- write('abcdefghijklmnopq')
- collect_write_chunks()
- eq({}, chunks)
- end)
- end)
- end)
-
- describe('RBUFFER_UNTIL_EMPTY', function()
- local chunks
-
- local function collect_read_chunks()
- rbuffer.ut_rbuffer_each_read_chunk(rbuf, function(rptr, rcnt)
- table.insert(chunks, ffi.string(rptr, rcnt))
- end)
- end
-
- before_each(function()
- chunks = {}
- end)
-
- describe('with empty buffer', function()
- itp('is not called', function()
- collect_read_chunks()
- eq({}, chunks)
- end)
- end)
-
- describe('with partially filled buffer in one contiguous chunk', function()
- itp('is called once with the filled chunk', function()
- write('string')
- collect_read_chunks()
- eq({ 'string' }, chunks)
- end)
- end)
-
- describe('with filled buffer in one contiguous chunk', function()
- itp('is called once with the filled chunk', function()
- write('abcdefghijklmnopq')
- collect_read_chunks()
- eq({ 'abcdefghijklmnop' }, chunks)
- end)
- end)
-
- describe('with buffer partially filled in two contiguous chunks', function()
- itp('is called twice with each filled chunk', function()
- write('1234567890')
- read(10)
- write('long string')
- collect_read_chunks()
- eq({ 'long s', 'tring' }, chunks)
- end)
- end)
-
- describe('with buffer filled in two contiguous chunks', function()
- itp('is called twice with each filled chunk', function()
- write('12345678')
- read(8)
- write('abcdefghijklmnopq')
- collect_read_chunks()
- eq({ 'abcdefgh', 'ijklmnop' }, chunks)
- end)
- end)
- end)
-
- describe('RBUFFER_EACH', function()
- local chars
-
- local function collect_chars()
- rbuffer.ut_rbuffer_each(rbuf, function(c, i)
- table.insert(chars, { string.char(c), tonumber(i) })
- end)
- end
- before_each(function()
- chars = {}
- end)
-
- describe('with empty buffer', function()
- itp('is not called', function()
- collect_chars()
- eq({}, chars)
- end)
- end)
-
- describe('with buffer filled in two contiguous chunks', function()
- itp('collects each character and index', function()
- write('1234567890')
- read(10)
- write('long string')
- collect_chars()
- eq({
- { 'l', 0 },
- { 'o', 1 },
- { 'n', 2 },
- { 'g', 3 },
- { ' ', 4 },
- { 's', 5 },
- { 't', 6 },
- { 'r', 7 },
- { 'i', 8 },
- { 'n', 9 },
- { 'g', 10 },
- }, chars)
- end)
- end)
- end)
-
- describe('RBUFFER_EACH_REVERSE', function()
- local chars
-
- local function collect_chars()
- rbuffer.ut_rbuffer_each_reverse(rbuf, function(c, i)
- table.insert(chars, { string.char(c), tonumber(i) })
- end)
- end
- before_each(function()
- chars = {}
- end)
-
- describe('with empty buffer', function()
- itp('is not called', function()
- collect_chars()
- eq({}, chars)
- end)
- end)
-
- describe('with buffer filled in two contiguous chunks', function()
- itp('collects each character and index', function()
- write('1234567890')
- read(10)
- write('long string')
- collect_chars()
- eq({
- { 'g', 10 },
- { 'n', 9 },
- { 'i', 8 },
- { 'r', 7 },
- { 't', 6 },
- { 's', 5 },
- { ' ', 4 },
- { 'g', 3 },
- { 'n', 2 },
- { 'o', 1 },
- { 'l', 0 },
- }, chars)
- end)
- end)
- end)
-
- describe('rbuffer_cmp', function()
- local function cmp(str)
- local rv = rbuffer.rbuffer_cmp(rbuf, to_cstr(str), #str)
- if rv == 0 then
- return 0
- else
- return rv / math.abs(rv)
- end
- end
-
- describe('with buffer filled in two contiguous chunks', function()
- itp('compares the common longest sequence', function()
- write('1234567890')
- read(10)
- write('long string')
- eq(0, cmp('long string'))
- eq(0, cmp('long strin'))
- eq(-1, cmp('long striM'))
- eq(1, cmp('long strio'))
- eq(0, cmp('long'))
- eq(-1, cmp('lonG'))
- eq(1, cmp('lonh'))
- end)
- end)
-
- describe('with empty buffer', function()
- itp('returns 0 since no characters are compared', function()
- eq(0, cmp(''))
- end)
- end)
- end)
-
- describe('rbuffer_write', function()
- itp('fills the internal buffer and returns the write count', function()
- eq(12, write('short string'))
- eq('short string0000', inspect())
- end)
-
- itp('wont write beyond capacity', function()
- eq(16, write('very very long string'))
- eq('very very long s', inspect())
- end)
- end)
-
- describe('rbuffer_read', function()
- itp('reads what was previously written', function()
- write('to read')
- eq('to read', read(20))
- end)
-
- itp('reads nothing if the buffer is empty', function()
- eq('', read(20))
- write('empty')
- eq('empty', read(20))
- eq('', read(20))
- end)
- end)
-
- describe('rbuffer_get', function()
- itp('fetch the pointer at offset, wrapping if required', function()
- write('1234567890')
- read(10)
- write('long string')
- eq('l', get(0))
- eq('o', get(1))
- eq('n', get(2))
- eq('g', get(3))
- eq(' ', get(4))
- eq('s', get(5))
- eq('t', get(6))
- eq('r', get(7))
- eq('i', get(8))
- eq('n', get(9))
- eq('g', get(10))
- end)
- end)
-
- describe('wrapping behavior', function()
- itp('writing/reading wraps across the end of the internal buffer', function()
- write('1234567890')
- eq('1234', read(4))
- eq('5678', read(4))
- write('987654321')
- eq('3214567890987654', inspect())
- eq('90987654321', read(20))
- eq('', read(4))
- write('abcdefghijklmnopqrs')
- eq('nopabcdefghijklm', inspect())
- eq('abcdefghijklmnop', read(20))
- end)
- end)
-end)