summaryrefslogtreecommitdiff
path: root/Data/BuiltIn/Libraries/inspect
diff options
context:
space:
mode:
Diffstat (limited to 'Data/BuiltIn/Libraries/inspect')
-rw-r--r--Data/BuiltIn/Libraries/inspect/.travis.yml36
-rw-r--r--Data/BuiltIn/Libraries/inspect/CHANGELOG.md39
-rw-r--r--Data/BuiltIn/Libraries/inspect/MIT-LICENSE.txt20
-rw-r--r--Data/BuiltIn/Libraries/inspect/README.md253
-rw-r--r--Data/BuiltIn/Libraries/inspect/inspect.lua334
-rw-r--r--Data/BuiltIn/Libraries/inspect/rockspecs/inspect-1.2-2.rockspec23
-rw-r--r--Data/BuiltIn/Libraries/inspect/rockspecs/inspect-2.0-1.rockspec23
-rw-r--r--Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-1.rockspec23
-rw-r--r--Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-2.rockspec23
-rw-r--r--Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-3.rockspec23
-rw-r--r--Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-4.rockspec23
-rw-r--r--Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.1.1-0.rockspec23
-rw-r--r--Data/BuiltIn/Libraries/inspect/spec/inspect_spec.lua460
-rw-r--r--Data/BuiltIn/Libraries/inspect/spec/unindent.lua39
14 files changed, 1342 insertions, 0 deletions
diff --git a/Data/BuiltIn/Libraries/inspect/.travis.yml b/Data/BuiltIn/Libraries/inspect/.travis.yml
new file mode 100644
index 0000000..91f7f93
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/.travis.yml
@@ -0,0 +1,36 @@
+language: python
+sudo: false
+
+env:
+ - LUA="lua=5.1"
+ - LUA="lua=5.2"
+ - LUA="lua=5.3"
+ - LUA="luajit=2.0"
+ - LUA="luajit=2.1"
+
+before_install:
+ - pip install hererocks
+ - hererocks lua_install -r^ --$LUA
+ - export PATH=$PATH:$PWD/lua_install/bin # Add directory with all installed binaries to PATH
+
+install:
+ - luarocks install luacheck
+ - luarocks install busted
+ - luarocks install luacov
+ - luarocks install luacov-coveralls
+
+script:
+ - luacheck --std max+busted *.lua spec
+ - busted --verbose --coverage
+
+after_success:
+ - luacov-coveralls --exclude $TRAVIS_BUILD_DIR/lua_install
+
+branches:
+ except:
+ - gh-pages
+
+notifications:
+ email:
+ on_success: change
+ on_failure: always
diff --git a/Data/BuiltIn/Libraries/inspect/CHANGELOG.md b/Data/BuiltIn/Libraries/inspect/CHANGELOG.md
new file mode 100644
index 0000000..bc2311b
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/CHANGELOG.md
@@ -0,0 +1,39 @@
+## v3.1.1
+
+* Better handling of LuaJIT's `ctype` and `cdata` values (#34, thanks @akopytov)
+
+## v3.1.0
+
+* Fixes bug: all control codes are escaped correctly (instead of only the named ones such as \n).
+ Example: \1 becomes \\1 (or \\001 when followed by a digit)
+* Fixes bug when using the `process` option in recursive tables
+* Overriding global `tostring` with inspect no longer results in an error.
+* Simplifies id generation, using less tables and metatables.
+
+## v3.0.3
+* Fixes a bug which sometimes displayed struct-like parts of tables as sequence-like due
+ to the way rawlen/the # operator are implemented.
+
+## v3.0.2
+* Fixes a bug when a table was garbage-collected while inspect was trying to render it
+
+## v3.0.1
+* Fixes a bug when dealing with tables which have a __len metamethod in Lua >= 5.2
+
+## v3.0.0
+
+The basic functionality remains as before, but there's one backwards-incompatible change if you used `options.filter`.
+
+* **Removed** `options.filter`
+* **Added** `options.process`, which can be used to do the same as `options.filter`, and more.
+* **Added** two new constants, `inspect.METATABLE` and `inspect.KEY`
+* **Added** `options.indent` & `options.newline`.
+
+
+## v2.0.0
+
+* Ability to deal with LuaJit's custom types
+* License change from BSD to MIT
+* Moved second parameter (depth) to options (options.depth)
+* Added a new parameter, options.filter.
+* Reimplemented some parts of the system without object orientation
diff --git a/Data/BuiltIn/Libraries/inspect/MIT-LICENSE.txt b/Data/BuiltIn/Libraries/inspect/MIT-LICENSE.txt
new file mode 100644
index 0000000..555835c
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/MIT-LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2013 Enrique García Cota
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+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.
diff --git a/Data/BuiltIn/Libraries/inspect/README.md b/Data/BuiltIn/Libraries/inspect/README.md
new file mode 100644
index 0000000..e9c6f86
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/README.md
@@ -0,0 +1,253 @@
+inspect.lua
+===========
+
+[![Build Status](https://travis-ci.org/kikito/inspect.lua.png?branch=master)](https://travis-ci.org/kikito/inspect.lua)
+[![Coverage Status](https://coveralls.io/repos/github/kikito/inspect.lua/badge.svg?branch=master)](https://coveralls.io/github/kikito/inspect.lua?branch=master)
+
+
+This library transforms any Lua value into a human-readable representation. It is especially useful for debugging errors in tables.
+
+The objective here is human understanding (i.e. for debugging), not serialization or compactness.
+
+Examples of use
+===============
+
+`inspect` has the following declaration: `local str = inspect(value, <options>)`.
+
+`value` can be any Lua value.
+
+`inspect` transforms simple types (like strings or numbers) into strings.
+
+```lua
+assert(inspect(1) == "1")
+assert(inspect("Hello") == '"Hello"')
+```
+
+Tables, on the other hand, are rendered in a way a human can read easily.
+
+"Array-like" tables are rendered horizontally:
+
+```lua
+assert(inspect({1,2,3,4}) == "{ 1, 2, 3, 4 }")
+```
+
+"Dictionary-like" tables are rendered with one element per line:
+
+```lua
+assert(inspect({a=1,b=2}) == [[{
+ a = 1,
+ b = 2
+}]])
+```
+
+The keys will be sorted alphanumerically when possible.
+
+"Hybrid" tables will have the array part on the first line, and the dictionary part just below them:
+
+```lua
+assert(inspect({1,2,3,b=2,a=1}) == [[{ 1, 2, 3,
+ a = 1,
+ b = 2
+}]])
+```
+
+Subtables are indented with two spaces per level.
+
+```lua
+assert(inspect({a={b=2}}) == [[{
+ a = {
+ b = 2
+ }
+}]])
+```
+
+Functions, userdata and any other custom types from Luajit are simply as `<function x>`, `<userdata x>`, etc.:
+
+```lua
+assert(inspect({ f = print, ud = some_user_data, thread = a_thread} ) == [[{
+ f = <function 1>,
+ u = <userdata 1>,
+ thread = <thread 1>
+}]])
+```
+
+If the table has a metatable, inspect will include it at the end, in a special field called `<metatable>`:
+
+```lua
+assert(inspect(setmetatable({a=1}, {b=2}) == [[{
+ a = 1
+ <metatable> = {
+ b = 2
+ }
+}]]))
+```
+
+`inspect` can handle tables with loops inside them. It will print `<id>` right before the table is printed out the first time, and replace the whole table with `<table id>` from then on, preventing infinite loops.
+
+```lua
+local a = {1, 2}
+local b = {3, 4, a}
+a[3] = b -- a references b, and b references a
+assert(inspect(a) == "<1>{ 1, 2, { 3, 4, <table 1> } }")
+```
+
+Notice that since both `a` appears more than once in the expression, it is prefixed by `<1>` and replaced by `<table 1>` every time it appears later on.
+
+### options
+
+`inspect` has a second parameter, called `options`. It is not mandatory, but when it is provided, it must be a table.
+
+#### options.depth
+
+`options.depth` sets the maximum depth that will be printed out.
+When the max depth is reached, `inspect` will stop parsing tables and just return `{...}`:
+
+```lua
+
+local t5 = {a = {b = {c = {d = {e = 5}}}}}
+
+assert(inspect(t5, {depth = 4}) == [[{
+ a = {
+ b = {
+ c = {
+ d = {...}
+ }
+ }
+ }
+}]])
+
+assert(inspect(t5, {depth = 2}) == [[{
+ a = {
+ b = {...}
+ }
+}]])
+
+```
+
+`options.depth` defaults to infinite (`math.huge`).
+
+#### options.newline & options.indent
+
+These are the strings used by `inspect` to respectively add a newline and indent each level of a table.
+
+By default, `options.newline` is `"\n"` and `options.indent` is `" "` (two spaces).
+
+``` lua
+local t = {a={b=1}}
+
+assert(inspect(t) == [[{
+ a = {
+ b = 1
+ }
+}]])
+
+assert(inspect(t, {newline='@', indent="++"}), "{@++a = {@++++b = 1@++}@}"
+```
+
+#### options.process
+
+`options.process` is a function which allow altering the passed object before transforming it into a string.
+A typical way to use it would be to remove certain values so that they don't appear at all.
+
+`options.process` has the following signature:
+
+``` lua
+local processed_item = function(item, path)
+```
+
+* `item` is either a key or a value on the table, or any of its subtables
+* `path` is an array-like table built with all the keys that have been used to reach `item`, from the root.
+ * For values, it is just a regular list of keys. For example, to reach the 1 in `{a = {b = 1}}`, the `path`
+ will be `{'a', 'b'}`
+ * For keys, the special value `inspect.KEY` is inserted. For example, to reach the `c` in `{a = {b = {c = 1}}}`,
+ the path will be `{'a', 'b', 'c', inspect.KEY }`
+ * For metatables, the special value `inspect.METATABLE` is inserted. For `{a = {b = 1}}}`, the path
+ `{'a', {b = 1}, inspect.METATABLE}` means "the metatable of the table `{b = 1}`".
+* `processed_item` is the value returned by `options.process`. If it is equal to `item`, then the inspected
+ table will look unchanged. If it is different, then the table will look different; most notably, if it's `nil`,
+ the item will dissapear on the inspected table.
+
+#### Examples
+
+Remove a particular metatable from the result:
+
+``` lua
+local t = {1,2,3}
+local mt = {b = 2}
+setmetatable(t, mt)
+
+local remove_mt = function(item)
+ if item ~= mt then return item end
+end
+
+-- mt does not appear
+assert(inspect(t, {process = remove_mt}) == "{ 1, 2, 3 }")
+```
+
+The previous exaple only works for a particular metatable. If you want to make *all* metatables, you can use the `path` parameter to check
+wether the last element is `inspect.METATABLE`, and return `nil` instead of the item:
+
+``` lua
+local t, mt = ... -- (defined as before)
+
+local remove_all_metatables = function(item, path)
+ if path[#path] ~= inspect.METATABLE then return item end
+end
+
+assert(inspect(t, {process = remove_all_metatables}) == "{ 1, 2, 3 }")
+```
+
+Filter a value:
+
+```lua
+local anonymize_password = function(item, path)
+ if path[#path] == 'password' then return "XXXX" end
+ return item
+end
+
+local info = {user = 'peter', password = 'secret'}
+
+assert(inspect(info, {process = anonymize_password}) == [[{
+ password = "XXXX",
+ user = "peter"
+}]])
+```
+
+Gotchas / Warnings
+==================
+
+This method is *not* appropriate for saving/restoring tables. It is meant to be used by the programmer mainly while debugging a program.
+
+Installation
+============
+
+If you are using luarocks, just run
+
+ luarocks install inspect
+
+Otherwise, you can just copy the inspect.lua file somewhere in your projects (maybe inside a /lib/ folder) and require it accordingly.
+
+Remember to store the value returned by require somewhere! (I suggest a local variable named inspect, although others might like table.inspect)
+
+ local inspect = require 'inspect'
+ -- or --
+ local inspect = require 'lib.inspect'
+
+Also, make sure to read the license; the text of that license file must appear somewhere in your projects' files. For your convenience, it's included at the begining of inspect.lua.
+
+Specs
+=====
+
+This project uses [busted](http://olivinelabs.com/busted/) for its specs. If you want to run the specs, you will have to install busted first. Then just execute the following from the root inspect folder:
+
+ busted
+
+Change log
+==========
+
+Read it on the CHANGELOG.md file
+
+
+
+
+
diff --git a/Data/BuiltIn/Libraries/inspect/inspect.lua b/Data/BuiltIn/Libraries/inspect/inspect.lua
new file mode 100644
index 0000000..e2e3806
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/inspect.lua
@@ -0,0 +1,334 @@
+local inspect ={
+ _VERSION = 'inspect.lua 3.1.0',
+ _URL = 'http://github.com/kikito/inspect.lua',
+ _DESCRIPTION = 'human-readable representations of tables',
+ _LICENSE = [[
+ MIT LICENSE
+
+ Copyright (c) 2013 Enrique García Cota
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ 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})
+
+local function rawpairs(t)
+ return next, t, nil
+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
+ return "'" .. str .. "'"
+ end
+ return '"' .. str:gsub('"', '\\"') .. '"'
+end
+
+-- \a => '\\a', \0 => '\\0', 31 => '\31'
+local shortControlCharEscapes = {
+ ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
+ ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v"
+}
+local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031
+for i=0, 31 do
+ local ch = string.char(i)
+ if not shortControlCharEscapes[ch] then
+ shortControlCharEscapes[ch] = "\\"..i
+ longControlCharEscapes[ch] = string.format("\\%03d", i)
+ end
+end
+
+local function escape(str)
+ return (str:gsub("\\", "\\\\")
+ :gsub("(%c)%f[0-9]", longControlCharEscapes)
+ :gsub("%c", shortControlCharEscapes))
+end
+
+local function isIdentifier(str)
+ return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
+end
+
+local function isSequenceKey(k, sequenceLength)
+ return type(k) == 'number'
+ and 1 <= k
+ and k <= sequenceLength
+ and math.floor(k) == k
+end
+
+local defaultTypeOrders = {
+ ['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
+
+ local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
+ -- 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
+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)
+ end
+ return len - 1
+end
+
+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
+ end
+ end
+ table.sort(keys, sortKeys)
+ return keys, keysLength, sequenceLength
+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)
+ else
+ tableAppearances[t] = tableAppearances[t] + 1
+ 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]
+ end
+ 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
+
+ local processed = process(item, path)
+ if type(processed) == 'table' then
+ local processedCopy = {}
+ visited[item] = processedCopy
+ local processedKey
+
+ 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)
+ 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
+ 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
+end
+
+function Inspector:down(f)
+ self.level = self.level + 1
+ f()
+ self.level = self.level - 1
+end
+
+function Inspector:tabify()
+ self:puts(self.newline, string.rep(self.indent, self.level))
+end
+
+function Inspector:alreadyVisited(v)
+ return self.ids[v] ~= nil
+end
+
+function Inspector:getId(v)
+ local id = self.ids[v]
+ if not id then
+ local tv = type(v)
+ id = (self.maxIds[tv] or 0) + 1
+ self.maxIds[tv] = id
+ self.ids[v] = 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: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)
+
+ 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
+ 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
+ end
+
+ if type(mt) == 'table' then
+ if count > 0 then self:puts(',') end
+ self:tabify()
+ self:puts('<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:puts('}')
+ end
+end
+
+function Inspector:putValue(v)
+ local tv = type(v)
+
+ if tv == 'string' then
+ self:puts(smartQuote(escape(v)))
+ elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or
+ tv == 'cdata' or tv == 'ctype' then
+ self:puts(tostring(v))
+ elseif tv == 'table' then
+ self:putTable(v)
+ else
+ self:puts('<', tv, ' ', self:getId(v), '>')
+ end
+end
+
+-------------------------------------------------------------------
+
+function inspect.inspect(root, options)
+ options = options or {}
+
+ local depth = options.depth or math.huge
+ local newline = options.newline or '\n'
+ local indent = options.indent or ' '
+ local process = options.process
+
+ if process then
+ root = processRecursive(process, root, {}, {})
+ end
+
+ local inspector = setmetatable({
+ depth = depth,
+ level = 0,
+ buffer = {},
+ ids = {},
+ maxIds = {},
+ newline = newline,
+ indent = indent,
+ tableAppearances = countTableAppearances(root)
+ }, Inspector_mt)
+
+ inspector:putValue(root)
+
+ return table.concat(inspector.buffer)
+end
+
+setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end })
+
+return inspect
+
diff --git a/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-1.2-2.rockspec b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-1.2-2.rockspec
new file mode 100644
index 0000000..b683143
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-1.2-2.rockspec
@@ -0,0 +1,23 @@
+package = "inspect"
+version = "1.2-2"
+source = {
+ url = "https://github.com/kikito/inspect.lua/archive/v1.2.0.tar.gz",
+ dir = "inspect.lua-1.2.0"
+}
+description = {
+ summary = "Lua table visualizer, ideal for debugging",
+ detailed = [[
+ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name, handles data recursion
+ ]],
+ homepage = "https://github.com/kikito/inspect.lua",
+ license = "MIT <http://opensource.org/licenses/MIT>"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "builtin",
+ modules = {
+ inspect = "inspect.lua"
+ }
+}
diff --git a/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-2.0-1.rockspec b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-2.0-1.rockspec
new file mode 100644
index 0000000..286bbd5
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-2.0-1.rockspec
@@ -0,0 +1,23 @@
+package = "inspect"
+version = "2.0-1"
+source = {
+ url = "https://github.com/kikito/inspect.lua/archive/v2.0.0.tar.gz",
+ dir = "inspect.lua-2.0.0"
+}
+description = {
+ summary = "Lua table visualizer, ideal for debugging",
+ detailed = [[
+ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name, handles data recursion
+ ]],
+ homepage = "https://github.com/kikito/inspect.lua",
+ license = "MIT <http://opensource.org/licenses/MIT>"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "builtin",
+ modules = {
+ inspect = "inspect.lua"
+ }
+}
diff --git a/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-1.rockspec b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-1.rockspec
new file mode 100644
index 0000000..387d450
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-1.rockspec
@@ -0,0 +1,23 @@
+package = "inspect"
+version = "3.0-1"
+source = {
+ url = "https://github.com/kikito/inspect.lua/archive/v3.0.0.tar.gz",
+ dir = "inspect.lua-3.0.0"
+}
+description = {
+ summary = "Lua table visualizer, ideal for debugging",
+ detailed = [[
+ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name and handles recursive tables properly.
+ ]],
+ homepage = "https://github.com/kikito/inspect.lua",
+ license = "MIT <http://opensource.org/licenses/MIT>"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "builtin",
+ modules = {
+ inspect = "inspect.lua"
+ }
+}
diff --git a/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-2.rockspec b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-2.rockspec
new file mode 100644
index 0000000..b22ec9c
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-2.rockspec
@@ -0,0 +1,23 @@
+package = "inspect"
+version = "3.0-2"
+source = {
+ url = "https://github.com/kikito/inspect.lua/archive/v3.0.1.tar.gz",
+ dir = "inspect.lua-3.0.1"
+}
+description = {
+ summary = "Lua table visualizer, ideal for debugging",
+ detailed = [[
+ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name and handles recursive tables properly.
+ ]],
+ homepage = "https://github.com/kikito/inspect.lua",
+ license = "MIT <http://opensource.org/licenses/MIT>"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "builtin",
+ modules = {
+ inspect = "inspect.lua"
+ }
+}
diff --git a/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-3.rockspec b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-3.rockspec
new file mode 100644
index 0000000..9e07b01
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-3.rockspec
@@ -0,0 +1,23 @@
+package = "inspect"
+version = "3.0-3"
+source = {
+ url = "https://github.com/kikito/inspect.lua/archive/v3.0.2.tar.gz",
+ dir = "inspect.lua-3.0.2"
+}
+description = {
+ summary = "Lua table visualizer, ideal for debugging",
+ detailed = [[
+ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name and handles recursive tables properly.
+ ]],
+ homepage = "https://github.com/kikito/inspect.lua",
+ license = "MIT <http://opensource.org/licenses/MIT>"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "builtin",
+ modules = {
+ inspect = "inspect.lua"
+ }
+}
diff --git a/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-4.rockspec b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-4.rockspec
new file mode 100644
index 0000000..56f3d48
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.0-4.rockspec
@@ -0,0 +1,23 @@
+package = "inspect"
+version = "3.0-4"
+source = {
+ url = "https://github.com/kikito/inspect.lua/archive/v3.0.3.tar.gz",
+ dir = "inspect.lua-3.0.3"
+}
+description = {
+ summary = "Lua table visualizer, ideal for debugging",
+ detailed = [[
+ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name and handles recursive tables properly.
+ ]],
+ homepage = "https://github.com/kikito/inspect.lua",
+ license = "MIT <http://opensource.org/licenses/MIT>"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "builtin",
+ modules = {
+ inspect = "inspect.lua"
+ }
+}
diff --git a/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.1.1-0.rockspec b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.1.1-0.rockspec
new file mode 100644
index 0000000..740b644
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/rockspecs/inspect-3.1.1-0.rockspec
@@ -0,0 +1,23 @@
+package = "inspect"
+version = "3.1.1-0"
+source = {
+ url = "https://github.com/kikito/inspect.lua/archive/v3.1.1.tar.gz",
+ dir = "inspect.lua-3.1.1"
+}
+description = {
+ summary = "Lua table visualizer, ideal for debugging",
+ detailed = [[
+ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name and handles recursive tables properly.
+ ]],
+ homepage = "https://github.com/kikito/inspect.lua",
+ license = "MIT <http://opensource.org/licenses/MIT>"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "builtin",
+ modules = {
+ inspect = "inspect.lua"
+ }
+}
diff --git a/Data/BuiltIn/Libraries/inspect/spec/inspect_spec.lua b/Data/BuiltIn/Libraries/inspect/spec/inspect_spec.lua
new file mode 100644
index 0000000..edea720
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/spec/inspect_spec.lua
@@ -0,0 +1,460 @@
+local inspect = require 'inspect'
+local unindent = require 'spec.unindent'
+local is_luajit, ffi = pcall(require, 'ffi')
+local has_rawlen = type(_G.rawlen) == 'function'
+
+describe( 'inspect', function()
+
+ describe('numbers', function()
+ it('works', function()
+ assert.equals("1", inspect(1))
+ assert.equals("1.5", inspect(1.5))
+ assert.equals("-3.14", inspect(-3.14))
+ end)
+ end)
+
+ describe('strings', function()
+ it('puts quotes around regular strings', function()
+ assert.equals('"hello"', inspect("hello"))
+ end)
+
+ it('puts apostrophes around strings with quotes', function()
+ assert.equals("'I have \"quotes\"'", inspect('I have "quotes"'))
+ end)
+
+ it('uses regular quotes if the string has both quotes and apostrophes', function()
+ assert.equals('"I have \\"quotes\\" and \'apostrophes\'"', inspect("I have \"quotes\" and 'apostrophes'"))
+ end)
+
+ it('escapes newlines properly', function()
+ assert.equals('"I have \\n new \\n lines"', inspect('I have \n new \n lines'))
+ end)
+
+ it('escapes tabs properly', function()
+ assert.equals('"I have \\t a tab character"', inspect('I have \t a tab character'))
+ end)
+
+ it('escapes backspaces properly', function()
+ assert.equals('"I have \\b a back space"', inspect('I have \b a back space'))
+ end)
+
+ it('escapes unnamed control characters with 1 or 2 digits', function()
+ assert.equals('"Here are some control characters: \\0 \\1 \\6 \\17 \\27 \\31"',
+ inspect('Here are some control characters: \0 \1 \6 \17 \27 \31'))
+ end)
+
+ it('escapes unnamed control characters with 3 digits when they are followed by numbers', function()
+ assert.equals('"Control chars followed by digits \\0001 \\0011 \\0061 \\0171 \\0271 \\0311"',
+ inspect('Control chars followed by digits \0001 \0011 \0061 \0171 \0271 \0311'))
+ end)
+
+ it('backslashes its backslashes', function()
+ assert.equals('"I have \\\\ a backslash"', inspect('I have \\ a backslash'))
+ assert.equals('"I have \\\\\\\\ two backslashes"', inspect('I have \\\\ two backslashes'))
+ assert.equals('"I have \\\\\\n a backslash followed by a newline"',
+ inspect('I have \\\n a backslash followed by a newline'))
+ end)
+
+ end)
+
+ it('works with nil', function()
+ assert.equals('nil', inspect(nil))
+ end)
+
+ it('works with functions', function()
+ assert.equals('{ <function 1>, <function 2>, <function 1> }', inspect({ print, type, print }))
+ end)
+
+ it('works with booleans', function()
+ assert.equals('true', inspect(true))
+ assert.equals('false', inspect(false))
+ end)
+
+ if is_luajit then
+ it('works with luajit cdata', function()
+ assert.equals('{ cdata<int>: PTR, ctype<int>, cdata<int>: PTR }',
+ inspect({ ffi.new("int", 1), ffi.typeof("int"), ffi.typeof("int")(1) }):gsub('(0x%x+)','PTR'))
+ end)
+ end
+
+ describe('tables', function()
+
+ it('works with simple array-like tables', function()
+ assert.equals("{ 1, 2, 3 }", inspect({1,2,3}))
+ end)
+
+ it('works with nested arrays', function()
+ assert.equals('{ "a", "b", "c", { "d", "e" }, "f" }', inspect({'a','b','c', {'d','e'}, 'f'}))
+ end)
+
+ if has_rawlen then
+ it('handles arrays with a __len metatable correctly (ignoring the __len metatable and using rawlen)', function()
+ local arr = setmetatable({1,2,3}, {__len = function() return nil end})
+ assert.equals("{ 1, 2, 3,\n <metatable> = {\n __len = <function 1>\n }\n}", inspect(arr))
+ end)
+
+ it('handles tables with a __pairs metamethod (ignoring the __pairs metamethod and using next)', function()
+ local t = setmetatable({ {}, name = "yeah" }, { __pairs = function() end })
+ assert.equals(
+ unindent([[{ {},
+ name = "yeah",
+ <metatable> = {
+ __pairs = <function 1>
+ }
+ }]]),
+ inspect(t))
+ end)
+ end
+
+ it('works with simple dictionary tables', function()
+ assert.equals("{\n a = 1,\n b = 2\n}", inspect({a = 1, b = 2}))
+ end)
+
+ it('identifies tables with no number 1 as struct-like', function()
+ assert.equals(unindent([[{
+ [2] = 1,
+ [25] = 1,
+ id = 1
+ }
+ ]]), inspect({[2]=1,[25]=1,id=1}))
+ end)
+
+ it('identifies numeric non-array keys as dictionary keys', function()
+ assert.equals("{ 1, 2,\n [-1] = true\n}", inspect({1, 2, [-1] = true}))
+ assert.equals("{ 1, 2,\n [1.5] = true\n}", inspect({1, 2, [1.5] = true}))
+ end)
+
+ it('sorts keys in dictionary tables', function()
+ local t = { 1,2,3,
+ [print] = 1, ["buy more"] = 1, a = 1,
+ [coroutine.create(function() end)] = 1,
+ [14] = 1, [{c=2}] = 1, [true]= 1
+ }
+ assert.equals(unindent([[
+ { 1, 2, 3,
+ [14] = 1,
+ [true] = 1,
+ a = 1,
+ ["buy more"] = 1,
+ [{
+ c = 2
+ }] = 1,
+ [<function 1>] = 1,
+ [<thread 1>] = 1
+ }
+ ]]), inspect(t))
+ end)
+
+ it('works with nested dictionary tables', function()
+ assert.equals(unindent([[{
+ a = 1,
+ b = {
+ c = 2
+ },
+ d = 3
+ }]]), inspect( {d=3, b={c=2}, a=1} ))
+ end)
+
+ it('works with hybrid tables', function()
+ assert.equals(unindent([[
+ { "a", {
+ b = 1
+ }, 2,
+ ["ahoy you"] = 4,
+ c = 3
+ }
+ ]]), inspect({ 'a', {b = 1}, 2, c = 3, ['ahoy you'] = 4 }))
+
+
+ end)
+
+ it('displays <table x> instead of repeating an already existing table', function()
+ local a = { 1, 2, 3 }
+ local b = { 'a', 'b', 'c', a }
+ a[4] = b
+ a[5] = a
+ a[6] = b
+ assert.equals('<1>{ 1, 2, 3, <2>{ "a", "b", "c", <table 1> }, <table 1>, <table 2> }', inspect(a))
+ end)
+
+ describe('The depth parameter', function()
+ local level5 = { 1,2,3, a = { b = { c = { d = { e = 5 } } } } }
+ local keys = { [level5] = true }
+
+ it('has infinite depth by default', function()
+ assert.equals(unindent([[
+ { 1, 2, 3,
+ a = {
+ b = {
+ c = {
+ d = {
+ e = 5
+ }
+ }
+ }
+ }
+ }
+ ]]), inspect(level5))
+ end)
+ it('is modifiable by the user', function()
+ assert.equals(unindent([[
+ { 1, 2, 3,
+ a = {
+ b = {...}
+ }
+ }
+ ]]), inspect(level5, {depth = 2}))
+
+ assert.equals(unindent([[
+ { 1, 2, 3,
+ a = {...}
+ }
+ ]]), inspect(level5, {depth = 1}))
+
+ assert.equals(unindent([[
+ { 1, 2, 3,
+ a = {
+ b = {
+ c = {
+ d = {...}
+ }
+ }
+ }
+ }
+ ]]), inspect(level5, {depth = 4}))
+
+ assert.equals("{...}", inspect(level5, {depth = 0}))
+ end)
+
+ it('respects depth on keys', function()
+ assert.equals(unindent([[
+ {
+ [{ 1, 2, 3,
+ a = {
+ b = {
+ c = {...}
+ }
+ }
+ }] = true
+ }
+ ]]), inspect(keys, {depth = 4}))
+ end)
+ end)
+
+ describe('the newline option', function()
+ it('changes the substring used for newlines', function()
+ local t = {a={b=1}}
+
+ assert.equal("{@ a = {@ b = 1@ }@}", inspect(t, {newline='@'}))
+ end)
+ end)
+
+ describe('the indent option', function()
+ it('changes the substring used for indenting', function()
+ local t = {a={b=1}}
+
+ assert.equal("{\n>>>a = {\n>>>>>>b = 1\n>>>}\n}", inspect(t, {indent='>>>'}))
+ end)
+ end)
+
+ describe('the process option', function()
+
+ it('removes one element', function()
+ local names = {'Andrew', 'Peter', 'Ann' }
+ local removeAnn = function(item) if item ~= 'Ann' then return item end end
+ assert.equals('{ "Andrew", "Peter" }', inspect(names, {process = removeAnn}))
+ end)
+
+ it('uses the path', function()
+ local names = {'Andrew', 'Peter', 'Ann' }
+ local removeThird = function(item, path) if path[1] ~= 3 then return item end end
+ assert.equals('{ "Andrew", "Peter" }', inspect(names, {process = removeThird}))
+ end)
+
+ it('replaces items', function()
+ local names = {'Andrew', 'Peter', 'Ann' }
+ local filterAnn = function(item) return item == 'Ann' and '<filtered>' or item end
+ assert.equals('{ "Andrew", "Peter", "<filtered>" }', inspect(names, {process = filterAnn}))
+ end)
+
+ it('nullifies metatables', function()
+ local mt = {'world'}
+ local t = setmetatable({'hello'}, mt)
+ local removeMt = function(item) if item ~= mt then return item end end
+ assert.equals('{ "hello" }', inspect(t, {process = removeMt}))
+ end)
+
+ it('nullifies metatables using their paths', function()
+ local mt = {'world'}
+ local t = setmetatable({'hello'}, mt)
+ local removeMt = function(item, path) if path[#path] ~= inspect.METATABLE then return item end end
+ assert.equals('{ "hello" }', inspect(t, {process = removeMt}))
+ end)
+
+ it('nullifies the root object', function()
+ local names = {'Andrew', 'Peter', 'Ann' }
+ local removeNames = function(item) if item ~= names then return item end end
+ assert.equals('nil', inspect(names, {process = removeNames}))
+ end)
+
+ it('changes keys', function()
+ local dict = {a = 1}
+ local changeKey = function(item) return item == 'a' and 'x' or item end
+ assert.equals('{\n x = 1\n}', inspect(dict, {process = changeKey}))
+ end)
+
+ it('nullifies keys', function()
+ local dict = {a = 1, b = 2}
+ local removeA = function(item) return item ~= 'a' and item or nil end
+ assert.equals('{\n b = 2\n}', inspect(dict, {process = removeA}))
+ end)
+
+ it('prints inspect.KEY & inspect.METATABLE', function()
+ local t = {inspect.KEY, inspect.METATABLE}
+ assert.equals("{ inspect.KEY, inspect.METATABLE }", inspect(t))
+ end)
+
+ it('marks key paths with inspect.KEY and metatables with inspect.METATABLE', function()
+ local t = { [{a=1}] = setmetatable({b=2}, {c=3}) }
+
+ local items = {}
+ local addItem = function(item, path)
+ items[#items + 1] = {item = item, path = path}
+ return item
+ end
+
+ inspect(t, {process = addItem})
+
+ assert.same({
+ {item = t, path = {}},
+ {item = {a=1}, path = {{a=1}, inspect.KEY}},
+ {item = 'a', path = {{a=1}, inspect.KEY, 'a', inspect.KEY}},
+ {item = 1, path = {{a=1}, inspect.KEY, 'a'}},
+ {item = setmetatable({b=2}, {c=3}), path = {{a=1}}},
+ {item = 'b', path = {{a=1}, 'b', inspect.KEY}},
+ {item = 2, path = {{a=1}, 'b'}},
+ {item = {c=3}, path = {{a=1}, inspect.METATABLE}},
+ {item = 'c', path = {{a=1}, inspect.METATABLE, 'c', inspect.KEY}},
+ {item = 3, path = {{a=1}, inspect.METATABLE, 'c'}}
+ }, items)
+
+ end)
+
+ it('handles recursive tables correctly', function()
+ local tbl = { 1,2,3}
+ tbl.loop = tbl
+ inspect(tbl, { process=function(x) return x end})
+ end)
+ end)
+
+ describe('metatables', function()
+
+ it('includes the metatable as an extra hash attribute', function()
+ local foo = { foo = 1, __mode = 'v' }
+ local bar = setmetatable({a = 1}, foo)
+ assert.equals(unindent([[
+ {
+ a = 1,
+ <metatable> = {
+ __mode = "v",
+ foo = 1
+ }
+ }
+ ]]), inspect(bar))
+ end)
+
+ it('can be used on the __tostring metamethod of a table without errors', function()
+ local f = function(x) return inspect(x) end
+ local tbl = setmetatable({ x = 1 }, { __tostring = f })
+ assert.equals(unindent([[
+ {
+ x = 1,
+ <metatable> = {
+ __tostring = <function 1>
+ }
+ }
+ ]]), tostring(tbl))
+ end)
+
+ it('does not allow collecting weak tables while they are being inspected', function()
+ collectgarbage('stop')
+ finally(function() collectgarbage('restart') end)
+ local shimMetatable = {
+ __mode = 'v',
+ __index = function() return {} end,
+ }
+ local function shim() return setmetatable({}, shimMetatable) end
+ local t = shim()
+ t.key = shim()
+ assert.equals(unindent([[
+ {
+ key = {
+ <metatable> = <1>{
+ __index = <function 1>,
+ __mode = "v"
+ }
+ },
+ <metatable> = <table 1>
+ }
+ ]]), inspect(t))
+ end)
+
+ it('ignores metatables with __metatable field set to non-nil and non-table type', function()
+ local function process(item) return item end
+ local function inspector(data) return inspect(data, {process=process}) end
+
+ local foo = setmetatable({}, {__metatable=false})
+ local bar = setmetatable({}, {__metatable=true})
+ local baz = setmetatable({}, {__metatable=10})
+ local spam = setmetatable({}, {__metatable=nil})
+ local eggs = setmetatable({}, {__metatable={}})
+ assert.equals(unindent('{}'), inspector(foo))
+ assert.equals(unindent('{}'), inspector(bar))
+ assert.equals(unindent('{}'), inspector(baz))
+ assert.equals(unindent([[
+ {
+ <metatable> = {}
+ }
+ ]]), inspector(spam))
+ assert.equals(unindent([[
+ {
+ <metatable> = {}
+ }
+ ]]), inspector(eggs))
+ end)
+
+ describe('When a table is its own metatable', function()
+ it('accepts a table that is its own metatable without stack overflowing', function()
+ local x = {}
+ setmetatable(x,x)
+ assert.equals(unindent([[
+ <1>{
+ <metatable> = <table 1>
+ }
+ ]]), inspect(x))
+ end)
+
+ it('can invoke the __tostring method without stack overflowing', function()
+ local t = {}
+ t.__index = t
+ setmetatable(t,t)
+ assert.equals(unindent([[
+ <1>{
+ __index = <table 1>,
+ <metatable> = <table 1>
+ }
+ ]]), inspect(t))
+ end)
+ end)
+ end)
+ end)
+
+ it('allows changing the global tostring', function()
+ local save = _G.tostring
+ _G.tostring = inspect
+ local s = tostring({1, 2, 3})
+ _G.tostring = save
+ assert.equals("{ 1, 2, 3 }", s)
+ end)
+
+end)
diff --git a/Data/BuiltIn/Libraries/inspect/spec/unindent.lua b/Data/BuiltIn/Libraries/inspect/spec/unindent.lua
new file mode 100644
index 0000000..02324a1
--- /dev/null
+++ b/Data/BuiltIn/Libraries/inspect/spec/unindent.lua
@@ -0,0 +1,39 @@
+-- Unindenting transforms a string like this:
+-- [[
+-- {
+-- foo = 1,
+-- bar = 2
+-- }
+-- ]]
+--
+-- Into the same one without indentation, nor start/end newlines
+--
+-- [[{
+-- foo = 1,
+-- bar = 2
+-- }]]
+--
+-- This makes the strings look and read better in the tests
+--
+
+local getIndentPreffix = function(str)
+ local level = math.huge
+ local minPreffix = ""
+ local len
+ for preffix in str:gmatch("\n( +)") do
+ len = #preffix
+ if len < level then
+ level = len
+ minPreffix = preffix
+ end
+ end
+ return minPreffix
+end
+
+local unindent = function(str)
+ str = str:gsub(" +$", ""):gsub("^ +", "") -- remove spaces at start and end
+ local preffix = getIndentPreffix(str)
+ return (str:gsub("\n" .. preffix, "\n"):gsub("\n$", ""))
+end
+
+return unindent