nixos/lua-lsp/script/linked-table.lua

228 lines
4.3 KiB
Lua

---@class linked-table
---@field _left table
---@field _right table
local mt = {}
mt.__index = mt
mt._size = 0
local HEAD = {'<HEAD>'}
local TAIL = {'<TAIL>'}
---@param node any
---@return boolean
function mt:has(node)
return self._left[node] ~= nil
end
---@param node any
---@return boolean
function mt:isValidNode(node)
if node == nil
or node == HEAD
or node == TAIL then
return false
end
return true
end
---@param node any
---@param afterWho any
---@return boolean
function mt:pushAfter(node, afterWho)
if not self:isValidNode(node) then
return false
end
if self:has(node) then
return false
end
local right = self._right[afterWho]
if not right then
return false
end
self._right[afterWho] = node
self._right[node] = right
self._left[right] = node
self._left[node] = afterWho
self._size = self._size + 1
return true
end
---@param node any
---@param beforeWho any
---@return boolean
function mt:pushBefore(node, beforeWho)
if node == nil then
return false
end
local left = self._left[beforeWho]
if not left then
return false
end
return self:pushAfter(node, left)
end
---@param node any
---@return boolean
function mt:pop(node)
if not self:isValidNode(node) then
return false
end
local left = self._left[node]
if not left then
return false
end
local right = self._right[node]
self._right[left] = right
self._left[right] = left
self._right[node] = nil
self._left[node] = nil
self._size = self._size - 1
return true
end
---@param node any
---@return boolean
function mt:pushHead(node)
return self:pushAfter(node, HEAD)
end
---@param node any
---@return boolean
function mt:pushTail(node)
return self:pushBefore(node, TAIL)
end
---@param node any
---@return any
function mt:getAfter(node)
if node == nil then
node = HEAD
end
local right = self._right[node]
if right == TAIL then
return nil
end
return right
end
---@return any
function mt:getHead()
return self:getAfter(HEAD)
end
---@return any
function mt:getBefore(node)
if node == nil then
node = TAIL
end
local left = self._left[node]
if left == HEAD then
return nil
end
return left
end
---@return any
function mt:getTail()
return self:getBefore(TAIL)
end
---@return boolean
function mt:popHead()
return self:pop(self:getHead())
end
---@return boolean
function mt:popTail()
return self:pop(self:getTail())
end
---@param old any
---@param new any
---@return boolean
function mt:replace(old, new)
if not self:isValidNode(old)
or not self:isValidNode(new) then
return false
end
local left = self._left[old]
if not left then
return false
end
local right = self._right[old]
self._right[left] = new
self._right[new] = right
self._left[right] = new
self._left[new] = left
self._right[old] = nil
self._left[old] = nil
return true
end
---@return integer
function mt:getSize()
return self._size
end
---@param start any
---@param revert? boolean
---@return fun():any
function mt:pairs(start, revert)
if revert then
if start == nil then
start = self._left[TAIL]
end
local next = start
return function ()
local current = next
if current == HEAD then
return nil
end
next = self._left[current]
return current
end
else
if start == nil then
start = self._right[HEAD]
end
local next = start
return function ()
local current = next
if current == TAIL then
return nil
end
next = self._right[current]
return current
end
end
end
---@param start any
---@param revert? boolean
---@return string
function mt:dump(start, revert)
local t = {}
for node in self:pairs(start, revert) do
t[#t+1] = tostring(node)
end
return table.concat(t, ' ')
end
function mt:reset()
self._left = { [TAIL] = HEAD }
self._right = { [HEAD] = TAIL }
self._size = 0
end
---@return linked-table
return function ()
local self = setmetatable({}, mt)
self:reset()
return self
end