~technomancy/fennel

9384d52c55b8b7076c067cf1b14bd025220e557d — Andrey Orst 4 months ago c2ca6b2
better multi-line printing based on resulting length

This patch adds new `line-length` option to fennelview options
table. This option triggers multi-line output for tables, which
single-line representation at given indentation level will result in a
greater number, than one specified with the option.
Tables are no longer printed multi-line only because they contain
nested tables.

It also inverts the behavior of `__fennelview` second return value,
now it will be used to force multi-line output instead of forcing
single-line, which plays much nicer with introduced `line-length`
option. The check is now automated, so `__fennelview` can return just
table of lines most of the time, and `fennelview` will produce
multi-line output when needed.
3 files changed, 168 insertions(+), 139 deletions(-)

M fennelview.fnl
M fennelview.lua
M test/core.fnl
M fennelview.fnl => fennelview.fnl +69 -62
@@ 90,24 90,22 @@

(fn concat-table-lines
  [elements options multiline? indent table-type prefix]
  (.. (or prefix "")
      (if (= :seq table-type) "[" "{")
      (table.concat
       elements
       (if (and (not options.one-line?)
                (or multiline?
                    (> (length elements) (if (= table-type :seq)
                                             options.sequential-length
                                             options.associative-length))
                    (> indent 40)))
           (.. "\n" (string.rep " " indent))
           " "))
      (if (= :seq table-type) "]" "}")))
  (let [indent-str (.. "\n" (string.rep " " indent))
        open (.. (or prefix "") (if (= :seq table-type) "[" "{"))
        close (if (= :seq table-type) "]" "}")
        oneline (.. open (table.concat elements " ") close)]
    (if (and (not options.one-line?)
             (or multiline?
                 (> (length elements) (if (= table-type :seq)
                                          options.sequential-length
                                          options.associative-length))
                 (> (+ indent (length oneline)) options.line-length)))
        (.. open (table.concat elements indent-str) close)
        oneline)))

(fn pp-associative [t kv options indent key?]
  (var multiline? false)
  (let [elements []
        id (. options.seen t)]
  (let [id (. options.seen t)]
    (if (>= options.level options.depth) "{...}"
        (and id options.detect-cycles?) (.. "@" id "{...}")
        (let [visible-cycle? (visible-cycle? t options)


@@ 115,52 113,53 @@
              indent (table-indent t indent id)
              slength (or (and options.utf8? (-?> (rawget _G :utf8) (. :len)))
                          #(length $))
              prefix (if visible-cycle? (.. "@" id) "")]
          (each [i [k v] (pairs kv)]
            (when (or (= (type k) :table) (= (type v) :table))
              (set multiline? true))
            (let [k (pp.pp k options (+ indent 1) true)
                  v (pp.pp v options (+ indent (slength k) 1))]
              (table.insert elements (.. k " " v))))
              prefix (if visible-cycle? (.. "@" id) "")
              elements (icollect [_ [k v] (pairs kv)]
                         (let [k (pp.pp k options (+ indent 1) true)
                               v (pp.pp v options (+ indent (slength k) 1))]
                           (set multiline? (or multiline? (k:find "\n") (v:find "\n")))
                           (.. k " " v)))]
          (concat-table-lines
           elements options multiline? indent :table prefix)))))

(fn pp-sequence [t kv options indent]
  (var multiline? false)
  (let [elements []
        id (. options.seen t)]
  (let [id (. options.seen t)]
    (if (>= options.level options.depth) "[...]"
        (and id options.detect-cycles?) (.. "@" id "[...]")
        (let [visible-cycle? (visible-cycle? t options)
              id (and visible-cycle? (. options.seen t))
              indent (table-indent t indent id)
              prefix (if visible-cycle? (.. "@" id) "")]
          (each [_ [_ v] (pairs kv)]
            (when (= (type v) :table)
              (set multiline? true))
            (table.insert elements (pp.pp v options indent)))
              prefix (if visible-cycle? (.. "@" id) "")
              elements (icollect [_ [_ v] (pairs kv)]
                         (let [v (pp.pp v options indent)]
                           (set multiline? (or multiline? (v:find "\n")))
                           v))]
          (concat-table-lines
           elements options multiline? indent :seq prefix)))))

(fn concat-lines [lines options indent one-line?]
(fn concat-lines [lines options indent force-multi-line?]
  (if (= (length lines) 0)
      (if options.empty-as-sequence? "[]" "{}")
      (if (and (not options.one-line?)
               (not one-line?))
          (table.concat lines (.. "\n" (string.rep " " indent)))
          (-> (icollect [_ line (ipairs lines)]
                (line:gsub "^%s+" " "))
              table.concat))))
      (let [oneline (-> (icollect [_ line (ipairs lines)]
                          (line:gsub "^%s+" ""))
                        (table.concat " "))]
        (if (and (not options.one-line?)
                 (or force-multi-line?
                     (oneline:find "\n")
                     (> (+ indent (length oneline)) options.line-length)))
            (table.concat lines (.. "\n" (string.rep " " indent)))
            oneline))))

(fn pp-metamethod [t metamethod options indent]
  (if (>= options.level options.depth)
      (if options.empty-as-sequence? "[...]" "{...}")
      (let [_ (set options.visible-cycle? #(visible-cycle? $ options))
            (lines force-one-line?) (metamethod t pp.pp options indent)]
            (lines force-multi-line?) (metamethod t pp.pp options indent)]
        (set options.visible-cycle? nil)
        (match (type lines)
          :string lines ;; assuming that it is already single line
          :table  (concat-lines lines options indent force-one-line?)
          :string lines ;; TODO: assuming that result is already a single line. Maybe warn?
          :table  (concat-lines lines options indent force-multi-line?)
          _ (error "Error: __fennelview metamethod must return a table of lines")))))

(fn pp-table [x options indent]


@@ 196,6 195,7 @@
  (let [;; defaults are used when options are not provided
        defaults {:sequential-length 10
                  :associative-length 4
                  :line-length 80
                  :one-line? false
                  :depth 128
                  :detect-cycles? true


@@ 241,15 241,17 @@ Can take an options table with these keys:
* :metamethod? (boolean: default: true) use the __fennelview metamethod if found
* :empty-as-sequence? (boolean, default: false) render empty tables as []
* :sequential-length (number, default: 10) amount of elements at which
  multi-line sequence ouptut is produced.
  multi-line sequence ouptut is produced
* :associative-length (number, default: 4) amount of elements at which
  multi-line table ouptut is produced.
  multi-line table ouptut is produced
* :line-length (number, default: 80) length of the line at which
  multi-line output for tables is forced
* :utf8? (boolean, default true) whether to use utf8 module to compute string
  lengths

The __fennelview metamethod should take the table being serialized as its first
argument, a function as its second argument, options table as third argument,
and current amount of indentation as its last argument:
The `__fennelview` metamethod should take the table being serialized as its
first argument, a function as its second argument, options table as third
argument, and current amount of indentation as its last argument:

(fn [t view inspector indent] ...)



@@ 260,15 262,21 @@ amount of addition indentation you've introduced.

`inspector` table contains options described above, and also `visible-cycle?`
function, that takes a table being serialized, detects and saves information
about possible reachable cycle.  Should be used in __fennelview to implement
about possible reachable cycle.  Should be used in `__fennelview` to implement
cycle detection.

`__fennelview` metamethod should always return a table of correctly indented
lines when producing multi-line output, or a string when returning single-line
item. If single-line representation is needed in some cases, there's no need to
concatenate table manually, instead `__fennelview` should return two values - a
table of lines, and a boolean indicating if one-line representation should be
forced.
lines when producing multi-line output, or a string when always returning
single-line item.  `fennelview` will transform your data structure to correct
multi-line representation when needed.  There's no need to concatenate table
manually ever - `fennelview` will apply general rules for your data structure,
depending on current options.  By default multiline output is produced only when
inner data structures contains newlines, or when returning table of lines as
single line results in width greater than `line-size` option.

Multi-line representation can be forced by returning two values from
`__fennelview` - a table of indented lines as first value, and `true` as second
value, indicating that multi-line representation should be forced.

There's no need to incorporate indentation beyond needed to correctly align
elements within the printed representation of your data structure.  For example,


@@ 278,19 286,19 @@ if you want to print a multi-line table, like this:
          2
          3]

__fennelview should return a sequence of lines:
`__fennelview` should return a sequence of lines:

[\"@my-table[1\"
 \"          2\"
 \"          3]\"]

Note, since we've introduced inner indent string of length 10, when calling
`view` function from within __fennelview metamethod, in order to keep inner
`view` function from within `__fennelview` metamethod, in order to keep inner
tables indented correctly, `indent` must be increased by this amount of extra
indentation.

`view` function also accepts additional boolean argument, which controls if
strings should be printed as a colon-strings when possible. Set it to `true`
strings should be printed as a colon-strings when possible.  Set it to `true`
when `view` is being called on the key of a table.

Here's an implementation of such pretty-printer for an arbitrary sequential


@@ 305,19 313,18 @@ table:
      (tset 1 (.. \"@my-table[\" (or (. lines 1) \"\")))
      (tset (length lines) (.. (. lines (length lines)) \"]\")))))

Setting table's __fennelview metamethod to this function will provide correct
Setting table's `__fennelview` metamethod to this function will provide correct
results regardless of nesting:

>> {:my-table (setmetatable [{:a {} :b [[1] [2]]} 3]
>> {:my-table (setmetatable [[1 2 3 4 5]
                             {:smalls [6 7 8 9 10 11 12]
                              :bigs [500 1000 2000 3000 4000]}]
                            {:__fennelview pp-doc-example})
    :normal-table [{:c [1 2 3] :d :some-data} 4]}
{:my-table @my-table[{:a {}
                      :b [[1]
                          [2]]}
                     3]
 :normal-table [{:c [1 2 3]
                 :d \"some-data\"}
                4]}
{:my-table @my-table[[1 2 3 4 5]
                     {:bigs [500 1000 2000 3000 4000]
                      :smalls [6 7 8 9 10 11 12]}]
 :normal-table [{:c [1 2 3] :d \"some-data\"} 4]}

Note that even though we've only indented inner elements of our table with 10
spaces, the result is correctly indented in terms of outer table, and inner

M fennelview.lua => fennelview.lua +66 -50
@@ 96,6 96,8 @@ local function table_indent(t, indent, id)
end
local pp = {}
local function concat_table_lines(elements, options, multiline_3f, indent, table_type, prefix)
  local indent_str = ("\n" .. string.rep(" ", indent))
  local open = nil
  local function _2_()
    if ("seq" == table_type) then
      return "["


@@ 103,31 105,28 @@ local function concat_table_lines(elements, options, multiline_3f, indent, table
      return "{"
    end
  end
  local function _3_()
    local _3_
    if (table_type == "seq") then
      _3_ = options["sequential-length"]
    else
      _3_ = options["associative-length"]
    end
    if (not options["one-line?"] and (multiline_3f or (#elements > _3_) or (indent > 40))) then
      return ("\n" .. string.rep(" ", indent))
    else
      return " "
    end
  open = ((prefix or "") .. _2_())
  local close = nil
  if ("seq" == table_type) then
    close = "]"
  else
    close = "}"
  end
  local function _4_()
    if ("seq" == table_type) then
      return "]"
    else
      return "}"
    end
  local oneline = (open .. table.concat(elements, " ") .. close)
  local _4_
  if (table_type == "seq") then
    _4_ = options["sequential-length"]
  else
    _4_ = options["associative-length"]
  end
  if (not options["one-line?"] and (multiline_3f or (#elements > _4_) or ((indent + #oneline) > options["line-length"]))) then
    return (open .. table.concat(elements, indent_str) .. close)
  else
    return oneline
  end
  return ((prefix or "") .. _2_() .. table.concat(elements, _3_()) .. _4_())
end
local function pp_associative(t, kv, options, indent, key_3f)
  local multiline_3f = false
  local elements = {}
  local id = options.seen[t]
  if (options.level >= options.depth) then
    return "{...}"


@@ 156,23 155,29 @@ local function pp_associative(t, kv, options, indent, key_3f)
    else
      prefix = ""
    end
    for i, _6_0 in pairs(kv) do
      local _7_ = _6_0
      local k = _7_[1]
      local v = _7_[2]
      if ((type(k) == "table") or (type(v) == "table")) then
        multiline_3f = true
    local elements = nil
    do
      local tbl_0_ = {}
      for _, _6_0 in pairs(kv) do
        local _7_ = _6_0
        local k = _7_[1]
        local v = _7_[2]
        local _8_
        do
          local k0 = pp.pp(k, options, (indent0 + 1), true)
          local v0 = pp.pp(v, options, (indent0 + slength(k0) + 1))
          multiline_3f = (multiline_3f or k0:find("\n") or v0:find("\n"))
          _8_ = (k0 .. " " .. v0)
        end
        tbl_0_[(#tbl_0_ + 1)] = _8_
      end
      local k0 = pp.pp(k, options, (indent0 + 1), true)
      local v0 = pp.pp(v, options, (indent0 + slength(k0) + 1))
      table.insert(elements, (k0 .. " " .. v0))
      elements = tbl_0_
    end
    return concat_table_lines(elements, options, multiline_3f, indent0, "table", prefix)
  end
end
local function pp_sequence(t, kv, options, indent)
  local multiline_3f = false
  local elements = {}
  local id = options.seen[t]
  if (options.level >= options.depth) then
    return "[...]"


@@ 188,19 193,27 @@ local function pp_sequence(t, kv, options, indent)
    else
      prefix = ""
    end
    for _, _3_0 in pairs(kv) do
      local _4_ = _3_0
      local _0 = _4_[1]
      local v = _4_[2]
      if (type(v) == "table") then
        multiline_3f = true
    local elements = nil
    do
      local tbl_0_ = {}
      for _, _3_0 in pairs(kv) do
        local _4_ = _3_0
        local _0 = _4_[1]
        local v = _4_[2]
        local _5_
        do
          local v0 = pp.pp(v, options, indent0)
          multiline_3f = (multiline_3f or v0:find("\n"))
          _5_ = v0
        end
        tbl_0_[(#tbl_0_ + 1)] = _5_
      end
      table.insert(elements, pp.pp(v, options, indent0))
      elements = tbl_0_
    end
    return concat_table_lines(elements, options, multiline_3f, indent0, "seq", prefix)
  end
end
local function concat_lines(lines, options, indent, one_line_3f)
local function concat_lines(lines, options, indent, force_multi_line_3f)
  if (#lines == 0) then
    if options["empty-as-sequence?"] then
      return "[]"


@@ 208,17 221,20 @@ local function concat_lines(lines, options, indent, one_line_3f)
      return "{}"
    end
  else
    if (not options["one-line?"] and not one_line_3f) then
    local oneline = nil
    local _2_
    do
      local tbl_0_ = {}
      for _, line in ipairs(lines) do
        tbl_0_[(#tbl_0_ + 1)] = line:gsub("^%s+", "")
      end
      _2_ = tbl_0_
    end
    oneline = table.concat(_2_, " ")
    if (not options["one-line?"] and (force_multi_line_3f or oneline:find("\n") or ((indent + #oneline) > options["line-length"]))) then
      return table.concat(lines, ("\n" .. string.rep(" ", indent)))
    else
      local function _2_()
        local tbl_0_ = {}
        for _, line in ipairs(lines) do
          tbl_0_[(#tbl_0_ + 1)] = line:gsub("^%s+", " ")
        end
        return tbl_0_
      end
      return table.concat(_2_())
      return oneline
    end
  end
end


@@ 236,13 252,13 @@ local function pp_metamethod(t, metamethod, options, indent)
    end
    options["visible-cycle?"] = _2_
    _ = nil
    local lines, force_one_line_3f = metamethod(t, pp.pp, options, indent)
    local lines, force_multi_line_3f = metamethod(t, pp.pp, options, indent)
    options["visible-cycle?"] = nil
    local _3_0 = type(lines)
    if (_3_0 == "string") then
      return lines
    elseif (_3_0 == "table") then
      return concat_lines(lines, options, indent, force_one_line_3f)
      return concat_lines(lines, options, indent, force_multi_line_3f)
    else
      local _0 = _3_0
      return error("Error: __fennelview metamethod must return a table of lines")


@@ 321,7 337,7 @@ local function colon_string_3f(s)
  return s:find("^[-%w?\\^_!$%&*+./@:|<=>]+$")
end
local function make_options(t, options)
  local defaults = {["associative-length"] = 4, ["detect-cycles?"] = true, ["empty-as-sequence?"] = false, ["metamethod?"] = true, ["one-line?"] = false, ["sequential-length"] = 10, ["utf8?"] = true, depth = 128}
  local defaults = {["associative-length"] = 4, ["detect-cycles?"] = true, ["empty-as-sequence?"] = false, ["line-length"] = 80, ["metamethod?"] = true, ["one-line?"] = false, ["sequential-length"] = 10, ["utf8?"] = true, depth = 128}
  local overrides = {appearances = count_table_appearances(t, {}), level = 0, seen = {len = 0}}
  for k, v in pairs((options or {})) do
    defaults[k] = v

M test/core.fnl => test/core.fnl +33 -27
@@ 349,32 349,38 @@
               "{:a 1 :b 5}"
               ;; nesting
               "((require :fennelview) (let [t {}] [t t]) {:detect-cycles? false})"
               "[{}\n {}]"
               "[{} {}]"
               "((require :fennelview) (let [t {}] [t t]))"
               "[{}\n {}]"
               "[{} {}]"
               "((require :fennelview) [{}])"
               "[{}]"
               "((require :fennelview) {[{}] []})"
               "{[{}] {}}"
               "((require :fennelview) {[[]] {[[]] [[[]]]}} {:empty-as-sequence? true})"
               "{[[]] {[[]] [[[]]]}}"
               "((require :fennelview) [1 2 [3 4]])"
               "((require :fennelview) [1 2 [3 4]] {:sequential-length 2})"
               "[1\n 2\n [3 4]]"
               "((require :fennelview) {[1] [2 [3]] :data {4 {:data 5} 6 [0 1 2 3]}} {:sequential-length 3})"
               "{:data [{:data 5}\n        [0\n         1\n         2\n         3]]\n [1] [2\n      [3]]}"
               "{:data [{:data 5}\n        [0\n         1\n         2\n         3]]\n [1] [2 [3]]}"
               "((require :fennelview) {{:b 2} {:c 3 :d 4} {:a 1} {:b 2 :c 3}})"
               "{{:a 1} {:b 2 :c 3}\n {:b 2} {:c 3 :d 4}}"
               "{{:a 1} {:b 2 :c 3} {:b 2} {:c 3 :d 4}}"
               "((require :fennelview) [{:aaa [1 2 3]}] {:sequential-length 2})"
               "[{:aaa [1\n        2\n        3]}]"
               "((require :fennelview) {:a [1 2 3 4 5 6 7] :b [1 2 3 4 5 6 7] :c [1 2 3 4 5 6 7] :d [1 2 3 4 5 6 7]})"
               "{:a [1 2 3 4 5 6 7] :b [1 2 3 4 5 6 7] :c [1 2 3 4 5 6 7] :d [1 2 3 4 5 6 7]}"
               "((require :fennelview) {:a [1 2] :b [1 2] :c [1 2] :d [1 2]} {:line-length 3})"
               "{:a [1\n     2]\n :b [1\n     2]\n :c [1\n     2]\n :d [1\n     2]}"
               "((require :fennelview)  {:a [1 2 3 4 5 6 7 8] :b [1 2 3 4 5 6 7 8] :c [1 2 3 4 5 6 7 8] :d [1 2 3 4 5 6 7 8]})"
               "{:a [1 2 3 4 5 6 7 8]\n :b [1 2 3 4 5 6 7 8]\n :c [1 2 3 4 5 6 7 8]\n :d [1 2 3 4 5 6 7 8]}"
               ;; Unicode
               "((require :fennelview) \"ваыв\")"
               "\"ваыв\""
               "((require :fennelview) {[1] [2 [3]] :ваыв {4 {:ваыв 5} 6 [0 1 2 3]}} {:sequential-length 3})"
               (if _G.utf8
                   "{\"ваыв\" [{\"ваыв\" 5}\n         [0\n          1\n          2\n          3]]\n [1] [2\n      [3]]}"
                   "{\"ваыв\" [{\"ваыв\" 5}\n             [0\n              1\n              2\n              3]]\n [1] [2\n      [3]]}")
                   "{\"ваыв\" [{\"ваыв\" 5}\n         [0\n          1\n          2\n          3]]\n [1] [2 [3]]}"
                   "{\"ваыв\" [{\"ваыв\" 5}\n             [0\n              1\n              2\n              3]]\n [1] [2 [3]]}")
               ;; the next one may look incorrect in some editors, but is actually correct
               "((require :fennelview) {:ǍǍǍ {} :ƁƁƁ {:ǍǍǍ {} :ƁƁƁ {}}})"
               "((require :fennelview) {:ǍǍǍ {} :ƁƁƁ {:ǍǍǍ {} :ƁƁƁ {}}} {:associative-length 1})"
               (if _G.utf8 ; older versions of Lua can't indent this correctly
                   "{\"ƁƁƁ\" {\"ƁƁƁ\" {}\n        \"ǍǍǍ\" {}}\n \"ǍǍǍ\" {}}"
                   "{\"ƁƁƁ\" {\"ƁƁƁ\" {}\n           \"ǍǍǍ\" {}}\n \"ǍǍǍ\" {}}")


@@ 387,9 393,9 @@
               "@1[@1[...]]"
               "(local t1 {}) (local t2 {:t1 t1}) (tset t1 :t2 t2) ((require :fennelview) t1)"
               "@1{:t2 {:t1 @1{...}}}"
               "(local t1 {:a 1 :c 2}) (local v1 [1 2 3]) (tset t1 :b v1) (table.insert v1 2 t1) ((require :fennelview) t1)"
               "(local t1 {:a 1 :c 2}) (local v1 [1 2 3]) (tset t1 :b v1) (table.insert v1 2 t1) ((require :fennelview) t1 {:sequential-length 1})"
               "@1{:a 1\n   :b [1\n       @1{...}\n       2\n       3]\n   :c 2}"
               "(local v1 [1 2 3]) (local v2 [1 2 v1]) (local v3 [1 2 v2]) (table.insert v1 v2) (table.insert v1 v3) ((require :fennelview) v1)"
               "(local v1 [1 2 3]) (local v2 [1 2 v1]) (local v3 [1 2 v2]) (table.insert v1 v2) (table.insert v1 v3) ((require :fennelview) v1 {:sequential-length 1})"
               "@1[1\n   2\n   3\n   @2[1\n      2\n      @1[...]]\n   [1\n    2\n    @2[...]]]"
               "(local v1 []) (table.insert v1 v1) ((require :fennelview) v1 {:detect-cycles? false :one-line? true :depth 10})"
               "[[[[[[[[[[...]]]]]]]]]]"


@@ 397,39 403,39 @@
               "{{{{...} {...}} {{...} {...}}} {{{...} {...}} {{...} {...}}}}"
               ;; sorry :)
               "(local v1 []) (local v2 [v1]) (local v3 [v1 v2]) (local v4 [v2 v3]) (local v5 [v3 v4]) (local v6 [v4 v5]) (local v7 [v5 v6]) (local v8 [v6 v7]) (local v9 [v7 v8]) (local v10 [v8 v9]) (local v11 [v9 v10]) (table.insert v1 v2) (table.insert v1 v3) (table.insert v1 v4) (table.insert v1 v5) (table.insert v1 v6) (table.insert v1 v7) (table.insert v1 v8) (table.insert v1 v9) (table.insert v1 v10) (table.insert v1 v11) ((require :fennelview) v1)"
               "@1[@2[@1[...]]\n   @3[@1[...]\n      @2[...]]\n   @4[@2[...]\n      @3[...]]\n   @5[@3[...]\n      @4[...]]\n   @6[@4[...]\n      @5[...]]\n   @7[@5[...]\n      @6[...]]\n   @8[@6[...]\n      @7[...]]\n   @9[@7[...]\n      @8[...]]\n   @10[@8[...]\n       @9[...]]\n   [@9[...]\n    @10[...]]]"
               "@1[@2[@1[...]]\n   @3[@1[...] @2[...]]\n   @4[@2[...] @3[...]]\n   @5[@3[...] @4[...]]\n   @6[@4[...] @5[...]]\n   @7[@5[...] @6[...]]\n   @8[@6[...] @7[...]]\n   @9[@7[...] @8[...]]\n   @10[@8[...] @9[...]]\n   [@9[...] @10[...]]]"
               "(local v1 []) (local v2 [v1]) (local v3 [v1 v2]) (local v4 [v2 v3]) (local v5 [v3 v4]) (local v6 [v4 v5]) (local v7 [v5 v6]) (local v8 [v6 v7]) (local v9 [v7 v8]) (local v10 [v8 v9]) (local v11 [v9 v10]) (table.insert v1 v2) (table.insert v1 v3) (table.insert v1 v4) (table.insert v1 v5) (table.insert v1 v6) (table.insert v1 v7) (table.insert v1 v8) (table.insert v1 v9) (table.insert v1 v10) (table.insert v1 v11) (table.insert v2 v11) ((require :fennelview) v1)"
               "@1[@2[@1[...]\n      @3[@4[@5[@6[@7[@1[...]\n                     @2[...]]\n                  @8[@2[...]\n                     @7[...]]]\n               @9[@8[...]\n                  @6[...]]]\n            @10[@9[...]\n                @5[...]]]\n         @11[@10[...]\n             @4[...]]]]\n   @7[...]\n   @8[...]\n   @6[...]\n   @9[...]\n   @5[...]\n   @10[...]\n   @4[...]\n   @11[...]\n   @3[...]]"
               "@1[@2[@1[...]\n      @3[@4[@5[@6[@7[@1[...] @2[...]] @8[@2[...] @7[...]]]\n               @9[@8[...] @6[...]]]\n            @10[@9[...] @5[...]]]\n         @11[@10[...] @4[...]]]]\n   @7[...]\n   @8[...]\n   @6[...]\n   @9[...]\n   @5[...]\n   @10[...]\n   @4[...]\n   @11[...]\n   @3[...]]"
               ;; __fennelview metamethod test
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) l1)"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) l1)"
               "(1\n 2\n 3)"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) [l1])"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) [l1])"
               "[(1\n  2\n  3)]"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) [1 l1 2])"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) [1 l1 2])"
               "[1\n (1\n  2\n  3)\n 2]"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) [[1 l1 2]])"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) [[1 l1 2]])"
               "[[1\n  (1\n   2\n   3)\n  2]]"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) {:abc [l1]})"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) {:abc [l1]})"
               "{:abc [(1\n        2\n        3)]}"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) l1 {:one-line? true})"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) l1 {:one-line? true})"
               "(1 2 3)"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) [l1] {:one-line? true})"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) [l1] {:one-line? true})"
               "[(1 2 3)]"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) {:abc [l1]} {:one-line? true})"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) ((require :fennelview) {:abc [l1]} {:one-line? true})"
               "{:abc [(1 2 3)]}"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) l2)"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) l2)"
               "(:a\n \"a b\"\n [1 2 3]\n {:a (1\n      2\n      3)\n  :b {}})"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) {:list l2})"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) {:list l2})"
               "{:list (:a\n        \"a b\"\n        [1 2 3]\n        {:a (1\n             2\n             3)\n         :b {}})}"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) [l2])"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) [l2])"
               "[(:a\n  \"a b\"\n  [1 2 3]\n  {:a (1\n       2\n       3)\n   :b {}})]"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) {:abc [l1]})"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) {:abc [l1]})"
               "{:abc [(1\n        2\n        3)]}"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) l1 {:one-line? true})"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) l1 {:one-line? true})"
               "(1 2 3)"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) [l1] {:one-line? true})"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) [l1] {:one-line? true})"
               "[(1 2 3)]"
               "(fn pp-list [x pp opts indent] (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) {:abc [l1]} {:one-line? true})"
               "(fn pp-list [x pp opts indent] (values (icollect [i v (ipairs x)] (let [v (pp v opts (+ 1 indent) true)] (values (if (= i 1) (.. \"(\" v) (= i (length x)) (.. \" \" v \")\") (.. \" \" v))))) true)) (local l1 (setmetatable [1 2 3] {:__fennelview pp-list})) (local l2 (setmetatable [\"a\" \"a b\" [1 2 3] {:a l1 :b []}] {:__fennelview pp-list})) ((require :fennelview) {:abc [l1]} {:one-line? true})"
               "{:abc [(1 2 3)]}"
               ;; ensure it works on lists/syms inside compiler
               "(eval-compiler