forked from neovim/neovim
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstringbuffer.lua
More file actions
134 lines (111 loc) · 2.92 KB
/
stringbuffer.lua
File metadata and controls
134 lines (111 loc) · 2.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
-- 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._core.stringbuffer
--- @return integer
function M.len(buf)
return #buf
end
return M
end
--- @class vim._core.stringbuffer.ptr
--- @field [integer] integer
--- @class vim._core.stringbuffer
--- @field private ptr vim._core.stringbuffer.ptr
--- @field private buf string[]
--- @field package len integer absolute length of the `buf`
--- @field package skip_ptr integer
local StrBuffer = {}
StrBuffer.__index = StrBuffer
function StrBuffer:_normalize()
if #self.buf > 1 then
self.buf = { table.concat(self.buf) }
end
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
return self
end
--- @return string
function StrBuffer:tostring()
return self:_normalize().buf[1] or ''
end
StrBuffer.__tostring = StrBuffer.tostring
--- @private
--- Efficiently peek at the first `n` characters of the buffer.
--- @param n integer
--- @return string
function StrBuffer:_peek(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)
n = n or self.len
local r = self:_peek(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
--- Efficiently read the character at the 0-based index `k` in the buffer.
--- @param k integer
--- @return integer
function StrBuffer:_index(k)
return self:_normalize().buf[1]:byte(k + 1)
end
--- @return vim._core.stringbuffer.ptr, integer
function StrBuffer:ref()
return self.ptr, self.len - self.skip_ptr
end
function M.new()
local self = setmetatable({}, StrBuffer):reset()
---@diagnostic disable-next-line: invisible
self.ptr = setmetatable({}, {
__index = function(_, k)
return StrBuffer._index(self, k)
end,
})
return self
end
--- @param buf vim._core.stringbuffer
function M.len(buf)
return buf.len - buf.skip_ptr
end
return M