aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Fußenegger <mfussenegger@users.noreply.github.com>2023-06-08 12:11:24 +0200
committerGitHub <noreply@github.com>2023-06-08 12:11:24 +0200
commit7c661207cc4357553ed2b057b6c8f28400024361 (patch)
treef84c1082b2bbe5fc967c49a631aae8bc30b8dcd9
parent38b0bb3c93022afcc54c0891d2eced02d2a9fa3a (diff)
downloadrneovim-7c661207cc4357553ed2b057b6c8f28400024361.tar.gz
rneovim-7c661207cc4357553ed2b057b6c8f28400024361.tar.bz2
rneovim-7c661207cc4357553ed2b057b6c8f28400024361.zip
feat(lua): add ringbuffer (#22894)
https://en.wikipedia.org/wiki/Circular_buffer
-rw-r--r--runtime/doc/lua.txt54
-rw-r--r--runtime/doc/news.txt2
-rw-r--r--runtime/lua/vim/shared.lua94
-rw-r--r--test/functional/lua/vim_spec.lua40
4 files changed, 190 insertions, 0 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 5e0a1edc11..babd9b801c 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -1847,6 +1847,60 @@ pesc({s}) *vim.pesc()*
See also: ~
• https://github.com/rxi/lume
+ringbuf({size}) *vim.ringbuf()*
+ Create a ring buffer limited to a maximal number of items. Once the buffer
+ is full, adding a new entry overrides the oldest entry.
+>
+
+ local ringbuf = vim.ringbuf(4)
+ ringbuf:push("a")
+ ringbuf:push("b")
+ ringbuf:push("c")
+ ringbuf:push("d")
+ ringbuf:push("e") -- overrides "a"
+ print(ringbuf:pop()) -- returns "b"
+ print(ringbuf:pop()) -- returns "c"
+
+ -- Can be used as iterator. Pops remaining items:
+ for val in ringbuf do
+ print(val)
+ end
+<
+
+ Returns a Ringbuf instance with the following methods:
+
+ • |Ringbuf:push()|
+ • |Ringbuf:pop()|
+ • |Ringbuf:peek()|
+ • |Ringbuf:clear()|
+
+ Parameters: ~
+ • {size} (integer)
+
+ Return: ~
+ (table)
+
+Ringbuf:clear({self}) *Ringbuf:clear()*
+ Clear all items.
+
+Ringbuf:peek({self}) *Ringbuf:peek()*
+ Returns the first unread item without removing it
+
+ Return: ~
+ any?|ni
+
+Ringbuf:pop({self}) *Ringbuf:pop()*
+ Removes and returns the first unread item
+
+ Return: ~
+ any?|ni
+
+Ringbuf:push({self}, {item}) *Ringbuf:push()*
+ Adds an item, overriding the oldest item if the buffer is full.
+
+ Parameters: ~
+ • {item} any
+
spairs({t}) *vim.spairs()*
Enumerate a table sorted by its keys.
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 4afb3429f4..dbf5b131eb 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -59,6 +59,8 @@ The following new APIs or features were added.
• |vim.iter()| provides a generic iterator interface for tables and Lua
iterators |luaref-in|.
+• Added |vim.ringbuf()| to create ring buffers.
+
• Added |vim.keycode()| for translating keycodes in a string.
• Added |vim.treesitter.query.omnifunc()| for treesitter query files (set by
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 4f230c4412..f899157ab7 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -881,4 +881,98 @@ function vim.defaulttable(create)
})
end
+do
+ ---@class vim.Ringbuf<T>
+ ---@field private _items table[]
+ ---@field private _idx_read integer
+ ---@field private _idx_write integer
+ ---@field private _size integer
+ local Ringbuf = {}
+
+ --- Clear all items
+ function Ringbuf.clear(self)
+ self._items = {}
+ self._idx_read = 0
+ self._idx_write = 0
+ end
+
+ --- Adds an item, overriding the oldest item if the buffer is full.
+ ---@generic T
+ ---@param item T
+ function Ringbuf.push(self, item)
+ self._items[self._idx_write] = item
+ self._idx_write = (self._idx_write + 1) % self._size
+ if self._idx_write == self._idx_read then
+ self._idx_read = (self._idx_read + 1) % self._size
+ end
+ end
+
+ --- Removes and returns the first unread item
+ ---@generic T
+ ---@return T?
+ function Ringbuf.pop(self)
+ local idx_read = self._idx_read
+ if idx_read == self._idx_write then
+ return nil
+ end
+ local item = self._items[idx_read]
+ self._items[idx_read] = nil
+ self._idx_read = (idx_read + 1) % self._size
+ return item
+ end
+
+ --- Returns the first unread item without removing it
+ ---@generic T
+ ---@return T?
+ function Ringbuf.peek(self)
+ if self._idx_read == self._idx_write then
+ return nil
+ end
+ return self._items[self._idx_read]
+ end
+
+ --- Create a ring buffer limited to a maximal number of items.
+ --- Once the buffer is full, adding a new entry overrides the oldest entry.
+ ---
+ --- <pre>
+ --- local ringbuf = vim.ringbuf(4)
+ --- ringbuf:push("a")
+ --- ringbuf:push("b")
+ --- ringbuf:push("c")
+ --- ringbuf:push("d")
+ --- ringbuf:push("e") -- overrides "a"
+ --- print(ringbuf:pop()) -- returns "b"
+ --- print(ringbuf:pop()) -- returns "c"
+ ---
+ --- -- Can be used as iterator. Pops remaining items:
+ --- for val in ringbuf do
+ --- print(val)
+ --- end
+ --- </pre>
+ ---
+ --- Returns a Ringbuf instance with the following methods:
+ ---
+ --- - |Ringbuf:push()|
+ --- - |Ringbuf:pop()|
+ --- - |Ringbuf:peek()|
+ --- - |Ringbuf:clear()|
+ ---
+ ---@param size integer
+ ---@return vim.Ringbuf ringbuf (table)
+ function vim.ringbuf(size)
+ local ringbuf = {
+ _items = {},
+ _size = size + 1,
+ _idx_read = 0,
+ _idx_write = 0,
+ }
+ return setmetatable(ringbuf, {
+ __index = Ringbuf,
+ __call = function(self)
+ return self:pop()
+ end,
+ })
+ end
+end
+
return vim
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 55c03e21b3..d5f550a5d1 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -3040,6 +3040,46 @@ describe('lua stdlib', function()
eq(4, exec_lua [[ return vim.re.match("abcde", '[a-c]+') ]])
end)
+
+ it("vim.ringbuf", function()
+ local results = exec_lua([[
+ local ringbuf = vim.ringbuf(3)
+ ringbuf:push("a") -- idx: 0
+ local peeka1 = ringbuf:peek()
+ local peeka2 = ringbuf:peek()
+ local popa = ringbuf:pop()
+ local popnil = ringbuf:pop()
+ ringbuf:push("a") -- idx: 1
+ ringbuf:push("b") -- idx: 2
+
+ -- doesn't read last added item, but uses separate read index
+ local pop_after_add_b = ringbuf:pop()
+
+ ringbuf:push("c") -- idx: 3 wraps around, overrides idx: 0 "a"
+ ringbuf:push("d") -- idx: 4 wraps around, overrides idx: 1 "a"
+ return {
+ peeka1 = peeka1,
+ peeka2 = peeka2,
+ pop1 = popa,
+ pop2 = popnil,
+ pop3 = ringbuf:pop(),
+ pop4 = ringbuf:pop(),
+ pop5 = ringbuf:pop(),
+ pop_after_add_b = pop_after_add_b,
+ }
+ ]])
+ local expected = {
+ peeka1 = "a",
+ peeka2 = "a",
+ pop1 = "a",
+ pop2 = nil,
+ pop3 = "b",
+ pop4 = "c",
+ pop5 = "d",
+ pop_after_add_b = "a",
+ }
+ eq(expected, results)
+ end)
end)
describe('lua: builtin modules', function()