_stringbuffer.lua 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. -- Basic shim for LuaJIT's stringbuffer.
  2. -- Note this does not implement the full API.
  3. -- This is intentionally internal-only. If we want to expose it, we should
  4. -- reimplement this a userdata and ship it as `string.buffer`
  5. -- (minus the FFI stuff) for Lua 5.1.
  6. local M = {}
  7. local has_strbuffer, strbuffer = pcall(require, 'string.buffer')
  8. if has_strbuffer then
  9. M.new = strbuffer.new
  10. -- Lua 5.1 does not have __len metamethod so we need to provide a len()
  11. -- function to use instead.
  12. --- @param buf vim._stringbuffer
  13. --- @return integer
  14. function M.len(buf)
  15. return #buf
  16. end
  17. return M
  18. end
  19. --- @class vim._stringbuffer
  20. --- @field private buf string[]
  21. --- @field package len integer absolute length of the `buf`
  22. --- @field package skip_ptr integer
  23. local StrBuffer = {}
  24. StrBuffer.__index = StrBuffer
  25. --- @return string
  26. function StrBuffer:tostring()
  27. if #self.buf > 1 then
  28. self.buf = { table.concat(self.buf) }
  29. end
  30. -- assert(self.len == #(self.buf[1] or ''), 'len mismatch')
  31. if self.skip_ptr > 0 then
  32. if self.buf[1] then
  33. self.buf[1] = self.buf[1]:sub(self.skip_ptr + 1)
  34. self.len = self.len - self.skip_ptr
  35. end
  36. self.skip_ptr = 0
  37. end
  38. -- assert(self.len == #(self.buf[1] or ''), 'len mismatch')
  39. return self.buf[1] or ''
  40. end
  41. StrBuffer.__tostring = StrBuffer.tostring
  42. --- @private
  43. --- Efficiently peak at the first `n` characters of the buffer.
  44. --- @param n integer
  45. --- @return string
  46. function StrBuffer:_peak(n)
  47. local skip, buf1 = self.skip_ptr, self.buf[1]
  48. if buf1 and (n + skip) < #buf1 then
  49. return buf1:sub(skip + 1, skip + n)
  50. end
  51. return self:tostring():sub(1, n)
  52. end
  53. --- @param chunk string
  54. function StrBuffer:put(chunk)
  55. local s = tostring(chunk)
  56. self.buf[#self.buf + 1] = s
  57. self.len = self.len + #s
  58. return self
  59. end
  60. --- @param str string
  61. function StrBuffer:set(str)
  62. return self:reset():put(str)
  63. end
  64. --- @param n integer
  65. --- @return string
  66. function StrBuffer:get(n)
  67. local r = self:_peak(n)
  68. self:skip(n)
  69. return r
  70. end
  71. --- @param n integer
  72. function StrBuffer:skip(n)
  73. self.skip_ptr = math.min(self.len, self.skip_ptr + n)
  74. return self
  75. end
  76. function StrBuffer:reset()
  77. self.buf = {}
  78. self.skip_ptr = 0
  79. self.len = 0
  80. return self
  81. end
  82. function M.new()
  83. return setmetatable({}, StrBuffer):reset()
  84. end
  85. --- @param buf vim._stringbuffer
  86. function M.len(buf)
  87. return buf.len - buf.skip_ptr
  88. end
  89. return M