mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	feat(lua): add ringbuffer (#22894)
https://en.wikipedia.org/wiki/Circular_buffer
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							38b0bb3c93
						
					
				
				
					commit
					7c661207cc
				
			@@ -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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user