aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/_stringbuffer.lua
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim/_stringbuffer.lua')
-rw-r--r--runtime/lua/vim/_stringbuffer.lua110
1 files changed, 110 insertions, 0 deletions
diff --git a/runtime/lua/vim/_stringbuffer.lua b/runtime/lua/vim/_stringbuffer.lua
new file mode 100644
index 0000000000..92965ee54d
--- /dev/null
+++ b/runtime/lua/vim/_stringbuffer.lua
@@ -0,0 +1,110 @@
+-- Basic shim for LuaJIT's stringbuffer.
+-- Note this does not implement the full API.
+-- This is intentionally internal-only. If we want to expose it, we should
+-- reimplement this a userdata and ship it as `string.buffer`
+-- (minus the FFI stuff) for Lua 5.1.
+local M = {}
+
+local has_strbuffer, strbuffer = pcall(require, 'string.buffer')
+
+if has_strbuffer then
+ M.new = strbuffer.new
+
+ -- Lua 5.1 does not have __len metamethod so we need to provide a len()
+ -- function to use instead.
+
+ --- @param buf vim._stringbuffer
+ --- @return integer
+ function M.len(buf)
+ return #buf
+ end
+
+ return M
+end
+
+--- @class vim._stringbuffer
+--- @field private buf string[]
+--- @field package len integer absolute length of the `buf`
+--- @field package skip_ptr integer
+local StrBuffer = {}
+StrBuffer.__index = StrBuffer
+
+--- @return string
+function StrBuffer:tostring()
+ if #self.buf > 1 then
+ self.buf = { table.concat(self.buf) }
+ end
+
+ -- assert(self.len == #(self.buf[1] or ''), 'len mismatch')
+
+ if self.skip_ptr > 0 then
+ if self.buf[1] then
+ self.buf[1] = self.buf[1]:sub(self.skip_ptr + 1)
+ self.len = self.len - self.skip_ptr
+ end
+ self.skip_ptr = 0
+ end
+
+ -- assert(self.len == #(self.buf[1] or ''), 'len mismatch')
+
+ return self.buf[1] or ''
+end
+
+StrBuffer.__tostring = StrBuffer.tostring
+
+--- @private
+--- Efficiently peak at the first `n` characters of the buffer.
+--- @param n integer
+--- @return string
+function StrBuffer:_peak(n)
+ local skip, buf1 = self.skip_ptr, self.buf[1]
+ if buf1 and (n + skip) < #buf1 then
+ return buf1:sub(skip + 1, skip + n)
+ end
+ return self:tostring():sub(1, n)
+end
+
+--- @param chunk string
+function StrBuffer:put(chunk)
+ local s = tostring(chunk)
+ self.buf[#self.buf + 1] = s
+ self.len = self.len + #s
+ return self
+end
+
+--- @param str string
+function StrBuffer:set(str)
+ return self:reset():put(str)
+end
+
+--- @param n integer
+--- @return string
+function StrBuffer:get(n)
+ local r = self:_peak(n)
+ self:skip(n)
+ return r
+end
+
+--- @param n integer
+function StrBuffer:skip(n)
+ self.skip_ptr = math.min(self.len, self.skip_ptr + n)
+ return self
+end
+
+function StrBuffer:reset()
+ self.buf = {}
+ self.skip_ptr = 0
+ self.len = 0
+ return self
+end
+
+function M.new()
+ return setmetatable({}, StrBuffer):reset()
+end
+
+--- @param buf vim._stringbuffer
+function M.len(buf)
+ return buf.len - buf.skip_ptr
+end
+
+return M