aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/inspect.lua
diff options
context:
space:
mode:
authorJosh Rahm <rahm@google.com>2022-07-18 19:37:18 +0000
committerJosh Rahm <rahm@google.com>2022-07-18 19:37:18 +0000
commit308e1940dcd64aa6c344c403d4f9e0dda58d9c5c (patch)
tree35fe43e01755e0f312650667004487a44d6b7941 /runtime/lua/vim/inspect.lua
parent96a00c7c588b2f38a2424aeeb4ea3581d370bf2d (diff)
parente8c94697bcbe23a5c7b07c292b90a6b70aadfa87 (diff)
downloadrneovim-308e1940dcd64aa6c344c403d4f9e0dda58d9c5c.tar.gz
rneovim-308e1940dcd64aa6c344c403d4f9e0dda58d9c5c.tar.bz2
rneovim-308e1940dcd64aa6c344c403d4f9e0dda58d9c5c.zip
Merge remote-tracking branch 'upstream/master' into rahm
Diffstat (limited to 'runtime/lua/vim/inspect.lua')
-rw-r--r--runtime/lua/vim/inspect.lua383
1 files changed, 197 insertions, 186 deletions
diff --git a/runtime/lua/vim/inspect.lua b/runtime/lua/vim/inspect.lua
index 0448ea487f..0a53fb203b 100644
--- a/runtime/lua/vim/inspect.lua
+++ b/runtime/lua/vim/inspect.lua
@@ -1,6 +1,6 @@
-local inspect ={
+local inspect = {
_VERSION = 'inspect.lua 3.1.0',
- _URL = 'http://github.com/kikito/inspect.lua',
+ _URL = 'http://github.com/kikito/inspect.lua',
_DESCRIPTION = 'human-readable representations of tables',
_LICENSE = [[
MIT LICENSE
@@ -25,13 +25,26 @@ local inspect ={
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- ]]
+ ]],
}
-local tostring = tostring
+inspect.KEY = setmetatable({}, {
+ __tostring = function()
+ return 'inspect.KEY'
+ end,
+})
+inspect.METATABLE = setmetatable({}, {
+ __tostring = function()
+ return 'inspect.METATABLE'
+ end,
+})
-inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end})
-inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end})
+local tostring = tostring
+local rep = string.rep
+local match = string.match
+local char = string.char
+local gsub = string.gsub
+local fmt = string.format
local function rawpairs(t)
return next, t, nil
@@ -40,127 +53,130 @@ end
-- Apostrophizes the string if it has quotes, but not aphostrophes
-- Otherwise, it returns a regular quoted string
local function smartQuote(str)
- if str:match('"') and not str:match("'") then
+ if match(str, '"') and not match(str, "'") then
return "'" .. str .. "'"
end
- return '"' .. str:gsub('"', '\\"') .. '"'
+ return '"' .. gsub(str, '"', '\\"') .. '"'
end
-- \a => '\\a', \0 => '\\0', 31 => '\31'
local shortControlCharEscapes = {
- ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
- ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v"
+ ['\a'] = '\\a',
+ ['\b'] = '\\b',
+ ['\f'] = '\\f',
+ ['\n'] = '\\n',
+ ['\r'] = '\\r',
+ ['\t'] = '\\t',
+ ['\v'] = '\\v',
+ ['\127'] = '\\127',
}
-local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031
-for i=0, 31 do
- local ch = string.char(i)
+local longControlCharEscapes = { ['\127'] = '\127' }
+for i = 0, 31 do
+ local ch = char(i)
if not shortControlCharEscapes[ch] then
- shortControlCharEscapes[ch] = "\\"..i
- longControlCharEscapes[ch] = string.format("\\%03d", i)
+ shortControlCharEscapes[ch] = '\\' .. i
+ longControlCharEscapes[ch] = fmt('\\%03d', i)
end
end
local function escape(str)
- return (str:gsub("\\", "\\\\")
- :gsub("(%c)%f[0-9]", longControlCharEscapes)
- :gsub("%c", shortControlCharEscapes))
+ return (
+ gsub(
+ gsub(gsub(str, '\\', '\\\\'), '(%c)%f[0-9]', longControlCharEscapes),
+ '%c',
+ shortControlCharEscapes
+ )
+ )
end
local function isIdentifier(str)
- return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
+ return type(str) == 'string' and not not str:match('^[_%a][_%a%d]*$')
end
+local flr = math.floor
local function isSequenceKey(k, sequenceLength)
- return type(k) == 'number'
- and 1 <= k
- and k <= sequenceLength
- and math.floor(k) == k
+ return type(k) == 'number' and flr(k) == k and 1 <= k and k <= sequenceLength
end
local defaultTypeOrders = {
- ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
- ['function'] = 5, ['userdata'] = 6, ['thread'] = 7
+ ['number'] = 1,
+ ['boolean'] = 2,
+ ['string'] = 3,
+ ['table'] = 4,
+ ['function'] = 5,
+ ['userdata'] = 6,
+ ['thread'] = 7,
}
local function sortKeys(a, b)
local ta, tb = type(a), type(b)
-- strings and numbers are sorted numerically/alphabetically
- if ta == tb and (ta == 'string' or ta == 'number') then return a < b end
+ if ta == tb and (ta == 'string' or ta == 'number') then
+ return a < b
+ end
- local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
+ local dta = defaultTypeOrders[ta] or 100
+ local dtb = defaultTypeOrders[tb] or 100
-- Two default types are compared according to the defaultTypeOrders table
- if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb]
- elseif dta then return true -- default types before custom ones
- elseif dtb then return false -- custom types after default ones
- end
-- custom types are sorted out alphabetically
- return ta < tb
+ return dta == dtb and ta < tb or dta < dtb
end
--- For implementation reasons, the behavior of rawlen & # is "undefined" when
--- tables aren't pure sequences. So we implement our own # operator.
-local function getSequenceLength(t)
- local len = 1
- local v = rawget(t,len)
- while v ~= nil do
- len = len + 1
- v = rawget(t,len)
+local function getKeys(t)
+ local seqLen = 1
+ while rawget(t, seqLen) ~= nil do
+ seqLen = seqLen + 1
end
- return len - 1
-end
+ seqLen = seqLen - 1
-local function getNonSequentialKeys(t)
- local keys, keysLength = {}, 0
- local sequenceLength = getSequenceLength(t)
- for k,_ in rawpairs(t) do
- if not isSequenceKey(k, sequenceLength) then
- keysLength = keysLength + 1
- keys[keysLength] = k
+ local keys, keysLen = {}, 0
+ for k in rawpairs(t) do
+ if not isSequenceKey(k, seqLen) then
+ keysLen = keysLen + 1
+ keys[keysLen] = k
end
end
table.sort(keys, sortKeys)
- return keys, keysLength, sequenceLength
+ return keys, keysLen, seqLen
end
-local function countTableAppearances(t, tableAppearances)
- tableAppearances = tableAppearances or {}
-
- if type(t) == 'table' then
- if not tableAppearances[t] then
- tableAppearances[t] = 1
- for k,v in rawpairs(t) do
- countTableAppearances(k, tableAppearances)
- countTableAppearances(v, tableAppearances)
- end
- countTableAppearances(getmetatable(t), tableAppearances)
+local function countCycles(x, cycles)
+ if type(x) == 'table' then
+ if cycles[x] then
+ cycles[x] = cycles[x] + 1
else
- tableAppearances[t] = tableAppearances[t] + 1
+ cycles[x] = 1
+ for k, v in rawpairs(x) do
+ countCycles(k, cycles)
+ countCycles(v, cycles)
+ end
+ countCycles(getmetatable(x), cycles)
end
end
-
- return tableAppearances
-end
-
-local copySequence = function(s)
- local copy, len = {}, #s
- for i=1, len do copy[i] = s[i] end
- return copy, len
end
-local function makePath(path, ...)
- local keys = {...}
- local newPath, len = copySequence(path)
- for i=1, #keys do
- newPath[len + i] = keys[i]
+local function makePath(path, a, b)
+ local newPath = {}
+ local len = #path
+ for i = 1, len do
+ newPath[i] = path[i]
end
+
+ newPath[len + 1] = a
+ newPath[len + 2] = b
+
return newPath
end
local function processRecursive(process, item, path, visited)
- if item == nil then return nil end
- if visited[item] then return visited[item] end
+ if item == nil then
+ return nil
+ end
+ if visited[item] then
+ return visited[item]
+ end
local processed = process(item, path)
if type(processed) == 'table' then
@@ -168,171 +184,166 @@ local function processRecursive(process, item, path, visited)
visited[item] = processedCopy
local processedKey
- for k,v in rawpairs(processed) do
+ for k, v in rawpairs(processed) do
processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
if processedKey ~= nil then
- processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
+ processedCopy[processedKey] =
+ processRecursive(process, v, makePath(path, processedKey), visited)
end
end
- local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
- if type(mt) ~= 'table' then mt = nil end -- ignore not nil/table __metatable field
+ local mt =
+ processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
+ if type(mt) ~= 'table' then
+ mt = nil
+ end
setmetatable(processedCopy, mt)
processed = processedCopy
end
return processed
end
-
-
--------------------------------------------------------------------
-
-local Inspector = {}
-local Inspector_mt = {__index = Inspector}
-
-function Inspector:puts(...)
- local args = {...}
- local buffer = self.buffer
- local len = #buffer
- for i=1, #args do
- len = len + 1
- buffer[len] = args[i]
- end
+local function puts(buf, str)
+ buf.n = buf.n + 1
+ buf[buf.n] = str
end
-function Inspector:down(f)
- self.level = self.level + 1
- f()
- self.level = self.level - 1
-end
+local Inspector = {}
-function Inspector:tabify()
- self:puts(self.newline, string.rep(self.indent, self.level))
-end
+local Inspector_mt = { __index = Inspector }
-function Inspector:alreadyVisited(v)
- return self.ids[v] ~= nil
+local function tabify(inspector)
+ puts(inspector.buf, inspector.newline .. rep(inspector.indent, inspector.level))
end
function Inspector:getId(v)
local id = self.ids[v]
+ local ids = self.ids
if not id then
local tv = type(v)
- id = (self.maxIds[tv] or 0) + 1
- self.maxIds[tv] = id
- self.ids[v] = id
+ id = (ids[tv] or 0) + 1
+ ids[v], ids[tv] = id, id
end
return tostring(id)
end
-function Inspector:putKey(k)
- if isIdentifier(k) then return self:puts(k) end
- self:puts("[")
- self:putValue(k)
- self:puts("]")
-end
+function Inspector:putValue(v)
+ local buf = self.buf
+ local tv = type(v)
+ if tv == 'string' then
+ puts(buf, smartQuote(escape(v)))
+ elseif
+ tv == 'number'
+ or tv == 'boolean'
+ or tv == 'nil'
+ or tv == 'cdata'
+ or tv == 'ctype'
+ or (vim and v == vim.NIL)
+ then
+ puts(buf, tostring(v))
+ elseif tv == 'table' and not self.ids[v] then
+ local t = v
+
+ if t == inspect.KEY or t == inspect.METATABLE then
+ puts(buf, tostring(t))
+ elseif self.level >= self.depth then
+ puts(buf, '{...}')
+ else
+ if self.cycles[t] > 1 then
+ puts(buf, fmt('<%d>', self:getId(t)))
+ end
-function Inspector:putTable(t)
- if t == inspect.KEY or t == inspect.METATABLE then
- self:puts(tostring(t))
- elseif self:alreadyVisited(t) then
- self:puts('<table ', self:getId(t), '>')
- elseif self.level >= self.depth then
- self:puts('{...}')
- else
- if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end
-
- local nonSequentialKeys, nonSequentialKeysLength, sequenceLength = getNonSequentialKeys(t)
- local mt = getmetatable(t)
- if (vim and sequenceLength == 0 and nonSequentialKeysLength == 0
- and mt == vim._empty_dict_mt) then
- self:puts(tostring(t))
- return
- end
+ local keys, keysLen, seqLen = getKeys(t)
+ local mt = getmetatable(t)
- self:puts('{')
- self:down(function()
- local count = 0
- for i=1, sequenceLength do
- if count > 0 then self:puts(',') end
- self:puts(' ')
- self:putValue(t[i])
- count = count + 1
+ if vim and seqLen == 0 and keysLen == 0 and mt == vim._empty_dict_mt then
+ puts(buf, tostring(t))
+ return
end
- for i=1, nonSequentialKeysLength do
- local k = nonSequentialKeys[i]
- if count > 0 then self:puts(',') end
- self:tabify()
- self:putKey(k)
- self:puts(' = ')
- self:putValue(t[k])
- count = count + 1
+ puts(buf, '{')
+ self.level = self.level + 1
+
+ for i = 1, seqLen + keysLen do
+ if i > 1 then
+ puts(buf, ',')
+ end
+ if i <= seqLen then
+ puts(buf, ' ')
+ self:putValue(t[i])
+ else
+ local k = keys[i - seqLen]
+ tabify(self)
+ if isIdentifier(k) then
+ puts(buf, k)
+ else
+ puts(buf, '[')
+ self:putValue(k)
+ puts(buf, ']')
+ end
+ puts(buf, ' = ')
+ self:putValue(t[k])
+ end
end
if type(mt) == 'table' then
- if count > 0 then self:puts(',') end
- self:tabify()
- self:puts('<metatable> = ')
+ if seqLen + keysLen > 0 then
+ puts(buf, ',')
+ end
+ tabify(self)
+ puts(buf, '<metatable> = ')
self:putValue(mt)
end
- end)
- if nonSequentialKeysLength > 0 or type(mt) == 'table' then -- result is multi-lined. Justify closing }
- self:tabify()
- elseif sequenceLength > 0 then -- array tables have one extra space before closing }
- self:puts(' ')
- end
+ self.level = self.level - 1
- self:puts('}')
- end
-end
-
-function Inspector:putValue(v)
- local tv = type(v)
+ if keysLen > 0 or type(mt) == 'table' then
+ tabify(self)
+ elseif seqLen > 0 then
+ puts(buf, ' ')
+ end
- if tv == 'string' then
- self:puts(smartQuote(escape(v)))
- elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or
- tv == 'cdata' or tv == 'ctype' or (vim and v == vim.NIL) then
- self:puts(tostring(v))
- elseif tv == 'table' then
- self:putTable(v)
+ puts(buf, '}')
+ end
else
- self:puts('<', tv, ' ', self:getId(v), '>')
+ puts(buf, fmt('<%s %d>', tv, self:getId(v)))
end
end
--------------------------------------------------------------------
-
function inspect.inspect(root, options)
- options = options or {}
+ options = options or {}
- local depth = options.depth or math.huge
+ local depth = options.depth or math.huge
local newline = options.newline or '\n'
- local indent = options.indent or ' '
+ local indent = options.indent or ' '
local process = options.process
if process then
root = processRecursive(process, root, {}, {})
end
+ local cycles = {}
+ countCycles(root, cycles)
+
local inspector = setmetatable({
- depth = depth,
- level = 0,
- buffer = {},
- ids = {},
- maxIds = {},
- newline = newline,
- indent = indent,
- tableAppearances = countTableAppearances(root)
+ buf = { n = 0 },
+ ids = {},
+ cycles = cycles,
+ depth = depth,
+ level = 0,
+ newline = newline,
+ indent = indent,
}, Inspector_mt)
inspector:putValue(root)
- return table.concat(inspector.buffer)
+ return table.concat(inspector.buf)
end
-setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end })
+setmetatable(inspect, {
+ __call = function(_, root, options)
+ return inspect.inspect(root, options)
+ end,
+})
return inspect