/- *plum :: :: This library includes `plume`, the actual pretty printing logic, :: and a handful of utilities for constructing plums. :: :: Generally, you'll just use `plume` like this: :: :: ~(tall plume plum) :: Pretty print `plum` in tall mode. :: ~(flat plume plum) :: Pretty print `plum` in wide mode. :: :: There is probably no reason to look at the utility routines unless :: you are writing something to generate `plum`s. :: :: This is the pretty-printer. Use the `flat` arm to render a plum :: into a single line and use the `tall` arm to get a nice multi-line :: rendering that switches to wide mode if there's enough space. :: :: For details about how this works and what exactly it does in various :: cases, take a look at the docs for `plum`, `plumfmt`, and at the :: docs on the arms of this door. :: ^? |% ++ plume |_ =plum :: :: An line, indented by `indent` spaces. :: +$ line [indent=@ud text=tape] :: :: An sequence of indented lines. :: +$ block (list line) :: :: +flat: print as a single line :: ++ flat ^- wain text:linear :: :: +tall: print as multiple lines :: ++ tall ^- wain %+ turn window |= line (crip (runt [indent ' '] text)) :: :: +adjust: adjust lines to right :: ++ adjust |= [tab=@ud =block] ^- ^block (turn block |=([@ud tape] [(add tab +<-) +<+])) :: :: Prepend `n` spaces to a tape. :: ++ prepend-spaces |= [n=@ t=tape] ^- tape (runt [n ' '] t) :: :: +window: print as list of tabbed lines :: ++ window ^- block ~+ :: for random access ?@ plum [0 (trip plum)]~ :: trivial text ?- -.plum :: :: %para: Line-wrappable paragraph. This is a stub; it should :: wrap text to 40 characters. :: %para [0 +:linear]~ :: :: %sbrk: nested subexpression :: :: This is an opportunity to switch to wide mode. First, try :: rendered in wide mode. If that's possible and the result :: isn't too big, use that. Otherwise recurse into the subplum :: without switching to wide mode. :: %sbrk =/ sub kid.plum ?+ sub window(plum sub) [%tree *] =/ wideresult ?~(wide.fmt.sub ~ [~ u=linear]) ?: ?&(?=(^ wideresult) (lte length.u.wideresult 40)) [0 text.u.wideresult]~ window(plum sub) == :: :: %tree: Try to render a text tree in tall mode. :: :: We want to render this in tall mode. First, verify that there :: the plum has a tall render (if not, fall back to `linear` :: formatting), then render all the subplums, and then render :: them in one of three ways: :: :: - If the `plumfmt` contains an `indef` and that indef has :: no prefix, then this is a variable-arity rune with a :: terminator: Use vertical formatting. :: :: - If the `plumfmt` contains an `indef` and that indef DOES have :: a prefix, then this is something that looks like a core: Use :: `core-like` formatting. :: :: - Otherwise, this is a rune with a fixed number of arguments :: Render the subplums using backstep indentation. :: :: There's also a special case where something has exactly one sub-plum. :: where something has exactly one sub-block. For example, we :: want this output: :: :: |- :: foo :: %tree ?~ tall.fmt.plum [0 text:linear]~ =/ prelude (trip intro.u.tall.fmt.plum) |^ =/ blocks (turn kids.plum |=(=^plum window(plum plum))) =/ prelude (trip intro.u.tall.fmt.plum) ?~ indef.u.tall.fmt.plum ?: =(1 (lent blocks)) [[0 prelude] (zing blocks)] (backstep prelude blocks) =/ prefix (trip sigil.u.indef.u.tall.fmt.plum) =/ finale (trip final.u.indef.u.tall.fmt.plum) ?~ blocks %+ weld ?~(prelude ~ [0 prelude]~) ?~(finale ~ [0 finale]~) ?~ prefix (running prelude blocks finale) (core-like prelude prefix blocks finale) -- == :: :: Render a plum in tall-mode using backstep indentation. Here, :: we are rendering things that look something like this: :: :: :+ foo :: bar :: baz :: ++ backstep |= [prelude=tape blocks=(list block)] ^- block %- zing =/ nkids (lent blocks) =/ idx 1 |- ^- (list block) ?~ blocks ~ :_ $(blocks t.blocks, idx +(idx)) ^- block =/ indent (mul 2 (sub nkids idx)) ?. =(1 idx) (adjust indent i.blocks) (rune-inline-with-block prelude indent i.blocks) :: :: To make things look a bit nicer, we want to put the first :: sub-block on the same line as the rune. We want this: :: :: :- foo :: baz :: :: Instead of this: :: :: :- :: foo :: baz :: :: This handles the "foo" case. :: ++ rune-inline-with-block |= [rune=tape indent=@ blk=block] ^- block =. indent (max indent (add 2 (lent rune))) =. blk (adjust indent blk) ?~ rune blk ?~ blk [0 rune]~ :_ t.blk :- 0 %+ weld rune =/ spaces-btwn (sub indent.i.blk (lent rune)) (prepend-spaces spaces-btwn text.i.blk) :: :: Render a tall hoon with running indentation. Here, we are :: rendering things that look sopmething like: :: :: :~ foo :: bar :: baz :: == :: :: So, there's basically three cases here: Either the prelude :: is a rune, the prelude is empty, or prelude is some other :: random-ass thing. :: :: - If there is no prelude, then just combine all of the :: sub-blocks together unaltered. :: - If it's a rune (two-characters wide), then combine the :: rune and the first line into one line (separated by two :: spaces) and indent the rest of the lines by four spaces. :: - If the rune is some other random-ass thing (has a length :: that isn't 0 or 2), then render the prelude alone on the :: first line and then combine the sub-blocks together, :: all indented by another two spaces. :: :: Regardless, if there's a finale, stick it on the end without :: any indentation. :: ++ running |= [prelude=tape blocks=(list block) finale=tape] ^- block =/ result=block (zing blocks) =. result ?+ (lent prelude) [[0 prelude] (adjust 2 result)] :: unusual prelude %0 :: empty prelude result %2 :: rune prelude (rune-inline-with-block prelude 4 result) == ?~ finale result (snoc result [0 finale]) :: :: This renders sub-blocks where each sub-block needs to be :: prefixed by some tape. For example: :: :: |% :: ++ foo :: bar :: ++ baz :: qux :: -- :: ++ core-like |= [prelude=tape prefix=tape blocks=(list block) finale=tape] ^- block =/ clear (add 2 (lent prefix)) =/ result ^- block %- zing ^- (list block) %+ turn blocks |= blk=block ^- block ^+ +< =* tab ?~(blk 0 (sub clear (min clear indent.i.blk))) =. blk (adjust tab blk) ?~ blk ~ :_ t.blk :- 0 %+ weld prefix (runt [(sub indent.i.blk (lent prefix)) ' '] text.i.blk) =. result ?~ finale result (snoc result [0 finale]) ?~ prelude result [[0 prelude] result] :: :: +linear: Render a plum onto a single line, even if it only has a :: wide form. :: ++ linear ^- [length=@ud text=tape] ~+ :: ~+ for random access ?@ plum [(met 3 plum) (trip plum)] :: Just a cord. ?- -.plum :: :: This is already in wide mode, so %sbrk nodes don't matter here. :: %sbrk linear(plum kid.plum) :: :: %para: To write a wrappable text paragraph to a single line, :: we just combine all the lines into one, interspersing single :: spaces chars. :: %para |- ^- [length=@ud text=tape] ?~ lines.plum [0 ~] =/ next $(lines.plum t.lines.plum) =/ this [length=(met 3 i.lines.plum) text=(trip i.lines.plum)] :- (add +(length.this) length.next) (weld text.this `tape`[' ' text.next]) :: :: Render a text tree to a single line. :: %tree |^ ^- [length=@ud text=tape] ?~ wide.fmt.plum (force-wide window) =/ body (render-body delimit.u.wide.fmt.plum kids.plum) ?~ enclose.u.wide.fmt.plum body (wrap-with-enclose u.enclose.u.wide.fmt.plum body) :: :: Given a list of subplums and a delimiter, render all the :: subplums onto a single line, and combine them into a single :: string by interspersing the delimiter. :: ++ render-body |= [delimit=cord kids=(list ^plum)] =/ stop (trip delimit) |- ^- [length=@ud text=tape] ?~ kids [0 ~] =/ next $(kids t.kids) =/ this linear(plum i.kids) ?~ text.next this :- :(add length.this (lent stop) length.next) :(weld text.this stop text.next) :: :: Wrap a wide-form-rendered result with the `enclose` cords :: from its `plumefmt`. :: ++ wrap-with-enclose |= [clamps=(pair cord cord) body=[length=@ text=tape]] ^- [length=@ud text=tape] :: =/ close [(trip -.clamps) (trip +.clamps)] :- :(add length.body (lent -.close) (lent +.close)) :(weld -.close text.body +.close) :: :: Given the result of rendering a plum in tall form, combine :: all the lines into one by separating each by two spaces. :: ++ force-wide |= render=(list [@ud text=tape]) ^- [length=@ud text=tape] :: ?~ render [0 ~] =/ next (force-wide t.render) :- :(add (lent text.i.render) 2 length.next) ?~(text.next text.i.render :(weld text.i.render " " text.next)) -- == -- :: :: Convenience function to build a `plumfmt` for a rune with a fixed :: number of parameters. :: ++ fixed |= rune=@ta ^- plumfmt [wide=`[' ' `[(cat 3 +< '(') ')']] tall=`[+< ~]] :: :: Same as `fixed` but only outputs in `tall` mode. :: ++ tall-fixed |= rune=cord ^- (unit [cord (unit [cord cord])]) `[rune ~] :: :: Convenience function to build a the `tall` part of a `plumfmt` for :: a running-style rune (one that takes a variable number of parameters :: and has a terminator). :: ++ tall-running |= [rune=cord sigil=cord term=cord] ^- (unit [cord (unit [cord cord])]) `[rune `[sigil term]] :: :: Convenience function for rendering a rune into a plum. This takes :: a rune, an optional tall-form terminator, optionally a short-form (if :: you don't supply a short-form, it'll just construct the standard :: wide-form (e.g. "?~(x x ~)") for you, and a list of sub-plums. :: ++ rune |= $: rune=cord term=(unit cord) short=(unit [cord cord cord]) kids=(list plum) == ^. plum |^ :- %sbrk :+ %tree :- (rune-wide-form rune short) ?~ term (tall-fixed rune) (tall-running rune '' u.term) kids :: :: If you just give this a rune, it'll build the standard wide-form. :: Otherwise, it'll just use the one that you gave it. :: ++ rune-wide-form |= [rune=cord short=(unit [fst=cord mid=cord lst=cord])] ^- (unit (pair cord (unit [cord cord]))) =* fst (cat 3 rune '(') =* std `[' ' `[fst ')']] ?~ short std `[mid.u.short `[fst.u.short lst.u.short]] -- :: :: Just a helper function for constructing a wide-form %tree plum. :: ++ simple-wide |= [init=cord sep=cord end=cord kids=(list plum)] ^- plum =/ fmt=plumfmt [wide=[~ sep [~ init end]] tall=~] [%tree fmt kids] :: :: Convenience function that builds a plum for a subexpression. The :: `%sbrk` tells the pretty-printer that this is a valid place to :: switch from tall mode to wide mode. :: ++ subtree |= [p=plumfmt q=(list plum)] ^- plum [%sbrk [%tree p q]] :: :: Convenience for generating plums that look like s-expressions. Useful :: for quickly getting decent-looking debug output. :: ++ sexp |= [sym=cord kids=(list plum)] ^- plum =/ head=cord (cat 3 '(' sym) =/ headspc=cord (cat 3 head ' ') =/ symcol=cord (cat 3 sym ':') =/ fmt=plumfmt [[~ ' ' [~ headspc ')']] [~ symcol [~ '' '']]] ?~ kids (cat 3 '(' (cat 3 sym ')')) [%sbrk [%tree fmt kids]] --