From b00eb9ae584252ed56308ce60b1f332a092f5b3e Mon Sep 17 00:00:00 2001 From: David Moon Date: Fri, 6 Sep 2024 17:53:59 -0500 Subject: [PATCH] full term decorations using generic child border dec (need to trim whitespace at the ends) --- src/core/layout/Dims.re | 150 ++++++++++++++ src/core/layout/Layout.re | 14 ++ src/core/layout/Loc.re | 10 - src/core/material/Mtrl.re | 4 + src/core/structure/Dims.re | 69 ------- src/web/view/dec/Meld.re | 407 ++++++++++++++++++++++++++++--------- src/web/view/dec/Token.re | 5 + 7 files changed, 484 insertions(+), 175 deletions(-) create mode 100644 src/core/layout/Dims.re delete mode 100644 src/core/structure/Dims.re diff --git a/src/core/layout/Dims.re b/src/core/layout/Dims.re new file mode 100644 index 00000000..6690d296 --- /dev/null +++ b/src/core/layout/Dims.re @@ -0,0 +1,150 @@ +open Sexplib.Std; +open Ppx_yojson_conv_lib.Yojson_conv.Primitives; +open Stds; + +// module Width = { +// [@deriving (show({with_path: false}), sexp, yojson)] +// type t = { +// body: int, // count of last-line chars up to trailing whitespace +// foot: int // count of last-line chars in trailing whitespace +// }; +// let mk = (~foot=0, body) => {body, foot}; +// let zero = mk(0); +// let total = w => w.body + w.foot; +// let add = (l, r) => +// l.body == 0 && r.body == 0 +// ? mk(0, ~foot=l.foot + r.foot) +// : {body: total(l) + r.body, foot: r.foot}; +// // last line indented only if nonempty body +// let indent = (w: t) => {...w, body: w.body + (w.body > 0 ? 2 : 0)}; +// }; + +// [@deriving (show({with_path: false}), sexp, yojson)] +// type t = { +// height: int, // number of newlines +// width: Width.t // number of characters in last line +// }; + +// let mk = (~height=0, width) => {height, width}; +// let zero = mk(Width.zero); + +// let indent = ({height, width}: t) => { +// height, +// width: (height > 0 ? Width.indent : Fun.id)(width), +// }; + +// let of_space = (spc: string) => { +// let lines = String.split_on_char('\n', spc); +// let width = Width.mk(0, ~foot=Utf8.length(Lists.ft_exn(lines))); +// mk(~height=List.length(lines) - 1, width); +// }; + +// let of_tok = (tok: Token.t) => +// switch (tok.mtrl) { +// | Space(_) => of_space(tok.text) +// | Grout(_) => mk(Width.mk(1)) +// | Tile(_) => mk(Width.mk(Token.length(tok))) +// }; + +// let rec of_cell = (c: Cell.t): t => +// switch (Cell.get(c)) { +// | None => zero +// | Some(m) => of_meld(m) +// } +// and of_meld = (m: Meld.t) => +// m +// |> Meld.fold(of_cell, (dims, tok, cell) => +// sum([ +// dims, +// of_tok(tok), +// of_cell(cell) |> (Token.indent(tok) ? indent : Fun.id), +// ]) +// ); + +module Width = { + // metrics describing the first/last line of a block + [@deriving (show({with_path: false}), sexp, yojson)] + type t = { + // column count of leading/trailing whitespace + pad: int, + // column count of the rest of the line + rest: int, + }; + let mk = (~pad=0, rest) => {pad, rest}; + let zero = {pad: 0, rest: 0}; + let total = ({pad, rest}: t) => pad + rest; + let add_same = (outer: t, inner: t) => + outer.rest == 0 + ? {pad: outer.pad + inner.pad, rest: inner.rest} + : {pad: outer.pad, rest: outer.rest + total(inner)}; +}; + +[@deriving (show({with_path: false}), sexp, yojson)] +type t = { + height: int, + // if height == 0, then + // Width.total(fst(widths)) == Width.total(snd(widths)) + widths: (Width.t, Width.t), +}; + +let zero = {height: 0, widths: Width.(zero, zero)}; +let newline = {height: 1, widths: Width.(zero, zero)}; +let indent = (n: int, {height, widths: (top, bot)}: t) => { + height, + widths: ( + {...top, pad: n + top.pad}, + bot.rest == 0 + ? {...bot, pad: n + bot.pad} : {...bot, rest: n + bot.rest}, + ), +}; + +// // associative, not commutative +let add = (l: t, r: t) => { + height: l.height + r.height, + widths: ( + l.height == 0 + ? Width.add_same(fst(l.widths), fst(r.widths)) : fst(l.widths), + r.height == 0 + ? Width.add_same(snd(r.widths), snd(l.widths)) : snd(r.widths), + ), +}; +let sum = List.fold_left(add, zero); + +let tok = (~height=0, width: Width.t) => {height, widths: (width, width)}; + +let of_space = (spc: string) => { + let lines = String.split_on_char('\n', spc); + let width = Width.mk(0, ~pad=Utf8.length(Lists.ft_exn(lines))); + tok(~height=List.length(lines) - 1, width); +}; + +let of_tok = (t: Token.t) => { + switch (t.mtrl) { + | Space(_) => of_space(t.text) + | Grout(_) => tok(Width.mk(1)) + | Tile(_) => tok(Width.mk(Token.length(t))) + }; +}; + +let rec of_block = (B(b): Block.t) => + b + |> Chain.fold_left_map( + sec => ((), of_sec(sec)), + ((), ind, sec) => { + let sec = of_sec(sec) |> add(newline) |> indent(ind); + ((), (), sec); + }, + ) + |> snd + |> Chain.loops + |> sum +and of_sec = + fun + | Block.Section.Line(line) => sum(List.map(of_tok, line)) + | Block(b) => of_block(b); + +let skip = (loc: Loc.t, ~over: t, ~ind: Loc.Col.t) => + Loc.{ + row: loc.row + over.height, + col: (over.height > 0 ? ind : loc.col) + Width.total(snd(over.widths)), + }; diff --git a/src/core/layout/Layout.re b/src/core/layout/Layout.re index 76c9182d..8bdd1fb7 100644 --- a/src/core/layout/Layout.re +++ b/src/core/layout/Layout.re @@ -220,3 +220,17 @@ let states = (~init: State.t, m: Tree.meld) => (s_end, state, s_mid); }, ); + +let row_ends = (~tree: Tree.t, row: Loc.Row.t): (Loc.Col.t, Loc.Col.t) => { + let (l, _) = + Loc.{row, col: 0} + |> path_of_loc(~tree) + |> Stds.Result.get_fail("unexpected") + |> state_of_path(~tree); + let (r, _) = + Loc.{row, col: Int.max_int} + |> path_of_loc(~tree) + |> Stds.Result.value(~default=Fun.const(Tree.end_path(tree, ~side=R))) + |> state_of_path(~tree); + (l.loc.col, r.loc.col); +}; diff --git a/src/core/layout/Loc.re b/src/core/layout/Loc.re index 3a854834..a164b0e8 100644 --- a/src/core/layout/Loc.re +++ b/src/core/layout/Loc.re @@ -35,16 +35,6 @@ module Base = { let shift = (n, loc: t) => {...loc, col: loc.col + n}; let return = (loc: t, ~ind: Col.t) => {row: loc.row + 1, col: ind}; - // let skip = (pos: t, ~over: Dims.t, ~return: Col.t) => { - // { - // // let h = Dims.Height.total(over.height); - // // let w = Dims.Width.total(over.width); - // row: pos.row + over.height, - // col: - // (over.height > 0 ? return : pos.col) + Dims.Width.total(over.width), - // }; - // p; - // }; }; include Base; diff --git a/src/core/material/Mtrl.re b/src/core/material/Mtrl.re index 7bbf3678..1810caff 100644 --- a/src/core/material/Mtrl.re +++ b/src/core/material/Mtrl.re @@ -1,3 +1,6 @@ +open Sexplib.Std; +open Ppx_yojson_conv_lib.Yojson_conv.Primitives; + module Base = { [@deriving (show({with_path: false}), sexp, yojson, ord)] type t('s, 'g, 't) = @@ -34,6 +37,7 @@ let map = (~space, ~grout, ~tile) => | Tile(t) => Tile(tile(t)); module Sorted = { + [@deriving (show({with_path: false}), sexp, yojson)] type t = Base.t(unit, Sort.t, Sort.t); // currently used specifically for walk comparison based on // stance sorted mtrl at each prec level diff --git a/src/core/structure/Dims.re b/src/core/structure/Dims.re deleted file mode 100644 index 91f66cb4..00000000 --- a/src/core/structure/Dims.re +++ /dev/null @@ -1,69 +0,0 @@ -open Sexplib.Std; -open Ppx_yojson_conv_lib.Yojson_conv.Primitives; -open Stds; - -module Width = { - [@deriving (show({with_path: false}), sexp, yojson)] - type t = { - body: int, // count of last-line chars up to trailing whitespace - foot: int // count of last-line chars in trailing whitespace - }; - let mk = (~foot=0, body) => {body, foot}; - let zero = mk(0); - let total = w => w.body + w.foot; - let add = (l, r) => - l.body == 0 && r.body == 0 - ? mk(0, ~foot=l.foot + r.foot) - : {body: total(l) + r.body, foot: r.foot}; - // last line indented only if nonempty body - let indent = (w: t) => {...w, body: w.body + (w.body > 0 ? 2 : 0)}; -}; - -[@deriving (show({with_path: false}), sexp, yojson)] -type t = { - height: int, // number of newlines - width: Width.t // number of characters in last line -}; - -let mk = (~height=0, width) => {height, width}; -let zero = mk(Width.zero); - -let indent = ({height, width}: t) => { - height, - width: (height > 0 ? Width.indent : Fun.id)(width), -}; - -// associative, not commutative -let add = (l: t, r: t) => { - height: l.height + r.height, - width: (r.height == 0 ? Width.add(l.width) : Fun.id)(r.width), -}; -let sum = List.fold_left(add, zero); - -let of_space = (spc: string) => { - let lines = String.split_on_char('\n', spc); - let width = Width.mk(0, ~foot=Utf8.length(Lists.ft_exn(lines))); - mk(~height=List.length(lines) - 1, width); -}; - -let of_tok = (tok: Token.t) => - switch (tok.mtrl) { - | Space(_) => of_space(tok.text) - | Grout(_) => mk(Width.mk(1)) - | Tile(_) => mk(Width.mk(Token.length(tok))) - }; - -let rec of_cell = (c: Cell.t): t => - switch (Cell.get(c)) { - | None => zero - | Some(m) => of_meld(m) - } -and of_meld = (m: Meld.t) => - m - |> Meld.fold(of_cell, (dims, tok, cell) => - sum([ - dims, - of_tok(tok), - of_cell(cell) |> (Token.indent(tok) ? indent : Fun.id), - ]) - ); diff --git a/src/web/view/dec/Meld.re b/src/web/view/dec/Meld.re index 89176dc4..8357c61e 100644 --- a/src/web/view/dec/Meld.re +++ b/src/web/view/dec/Meld.re @@ -1,20 +1,103 @@ +open Sexplib.Std; +open Ppx_yojson_conv_lib.Yojson_conv.Primitives; + module T = Token; open Tylr_core; module L = Layout; -module Profile = { - type t = { - indent: Loc.Col.t, - range: (Loc.t, Loc.t), - chain: Chain.t(Mtrl.Sorted.t, T.Profile.t), +let sort_clss = (s: Mtrl.Sorted.t) => + switch (s) { + | Space(_) => ["Space"] + | Grout(s) => ["Grout", Sort.to_str(s)] + | Tile(s) => ["Tile", Sort.to_str(s)] }; - let cells = p => Chain.loops(p.chain); - let tokens = p => Chain.links(p.chain); +module Child = { + module Profile = { + [@deriving (show({with_path: false}), sexp, yojson)] + type t = { + // indentation of delimiting tokens + ind: Loc.Col.t, + loc: Loc.t, + dims: Dims.t, + sort: Mtrl.Sorted.t, + delim: (bool, bool), + }; + }; + + let h_trunc = 0.3; + let v_trunc = 0.15; + + let v_line_offset = 0.5; + + let mk = (~font, p: Profile.t) => { + let end_loc: Loc.t = Dims.skip(p.loc, ~over=p.dims, ~ind=p.ind); + let Dims.{height, widths: (hd, _)} = p.dims; + let hd_line = + hd.rest == 0 + ? [] + : Util.Svgs.Path.[ + m(~x=p.loc.col, ~y=p.loc.row + 1) + |> cmdfudge(~x=fst(p.delim) ? T.concave_adj +. h_trunc : 0.), + h(~x=p.loc.col + Dims.Width.total(hd)) + |> cmdfudge( + ~x= + height == 0 && snd(p.delim) + ? -. T.concave_adj -. h_trunc : 0., + ), + ]; + let body_line = + height <= 0 || snd(p.delim) && height <= 0 + ? [] + : Util.Svgs.Path.[ + m(~x=p.ind, ~y=p.loc.row + 1) + |> cmdfudge(~x=-. v_line_offset, ~y=v_trunc), + v(~y=end_loc.row) + |> cmdfudge( + ~y=snd(p.delim) && end_loc.col == p.ind ? -. v_trunc : 1., + ), + ]; + let ft_line = + height == 0 || end_loc.col == p.ind + ? [] + : Util.Svgs.Path.[ + h(~x=end_loc.col) + |> cmdfudge(~x=snd(p.delim) ? -. T.concave_adj -. h_trunc : 0.), + ]; + + hd_line + @ body_line + @ ft_line + |> Util.Svgs.Path.view + |> Util.Nodes.add_classes(["child-line", ...sort_clss(p.sort)]) + |> Stds.Lists.single + |> Box.mk(~font, ~loc={row: 0, col: 0}); + }; +}; + +module Profile = { + [@deriving (show({with_path: false}), sexp, yojson)] + type t = Chain.t(Child.Profile.t, T.Profile.t); + // indentation of the meld's tokens + // indent: Loc.Col.t, + // // endpoints of the full meld range sans padding + // range: (Loc.t, Loc.t), + // chain: Chain.t(Child.Profile.t, T.Profile.t), + // whether or not the range ends start/end their respective rows. + // left type is hack where Some(col) corresponds to false and passes + // the max col of the row containing the left end for decoration purposes. + // full_row: (option(Loc.Col.t), bool), + // chain: Chain.t(Mtrl.Sorted.t, T.Profile.t), + // }; + // at least one token expected in chain + exception No_tokens; + + let cells = p => Chain.loops(p); + let tokens = p => Chain.links(p); let sort = (p: t) => switch (tokens(p)) { - | [] => failwith("meld profile with no tokens") + | [] => raise(No_tokens) | [hd, ..._] => hd.style |> Option.map((style: T.Style.t) => style.sort) }; @@ -25,100 +108,232 @@ module Profile = { M(l, _, r) as m: Meld.t, ) => { let (null_l, null_r) = Cell.Space.(is_space(l), is_space(r)); - let (s_end, states) = Layout.states(~init=state, lyt); + let (_, states) = Layout.states(~init=state, lyt); let s_tok = L.State.jump_cell(state, ~over=t_l); - let s_l = null_l ? s_tok : state; - let s_r = null_r ? Chain.ft(states) : s_end; - let chain = - Chain.combine(states, Meld.to_chain(m)) - |> Chain.map_loop(((_, c)) => - switch (Cell.get(c)) { + let n = Meld.length(m); + Chain.combine(L.Tree.to_chain(lyt), Meld.to_chain(m)) + |> Chain.combine(states) + |> Chain.mapi_loop((step, (s: L.State.t, (t_cell, cell))) => { + let delim = (step > 0, step < n - 1); + let sort = + switch (Cell.get(cell)) { | None => Mtrl.Space() | Some(M(_, w, _)) => Wald.sort(w) - } - ) - |> Chain.mapi_link((step, (state: L.State.t, tok)) => { - let null = ( - step == 1 && null_l, - step == Meld.length(m) - 2 && null_r, - ); - T.Profile.mk(~loc=state.loc, ~null, tok); - }); - {indent: s_tok.ind, range: (s_l.loc, s_r.loc), chain}; + }; + let dims = Dims.of_block(Tree.flatten(t_cell)); + let loc = s.loc; + let ind = s_tok.ind; + let r = Child.Profile.{ind, loc, dims, sort, delim}; + Stds.P.show("r", Child.Profile.show(r)); + r; + }) + |> Chain.mapi_link((step, (state: L.State.t, (_, tok))) => { + let null = (step == 1 && null_l, step == n - 2 && null_r); + T.Profile.mk(~loc=state.loc, ~null, tok); + }); }; + // weird abstraction boundary here with range and full_row getting passed in + // let mk = + // ( + // ~range, + // ~full_row, + // ~state: L.State.t, + // M(t_l, _, _) as lyt: L.Tree.meld, + // M(l, _, r) as m: Meld.t, + // ) => { + // let (null_l, null_r) = Cell.Space.(is_space(l), is_space(r)); + // let (_, states) = Layout.states(~init=state, lyt); + // let s_tok = L.State.jump_cell(state, ~over=t_l); + // let chain = + // Chain.combine(states, Meld.to_chain(m)) + // |> Chain.map_loop(((_, c)) => + // switch (Cell.get(c)) { + // | None => Mtrl.Space() + // | Some(M(_, w, _)) => Wald.sort(w) + // } + // ) + // |> Chain.mapi_link((step, (state: L.State.t, tok)) => { + // let null = ( + // step == 1 && null_l, + // step == Meld.length(m) - 2 && null_r, + // ); + // T.Profile.mk(~loc=state.loc, ~null, tok); + // }); + // {indent: s_tok.ind, range, full_row, chain}; + // }; }; -let sort_clss = (s: Mtrl.Sorted.t) => - switch (s) { - | Space(_) => ["Space"] - | Grout(s) => ["Grout", Sort.to_str(s)] - | Tile(s) => ["Tile", Sort.to_str(s)] - }; +// [@warning "-27"] +// let uni_lines = (~font, p: Profile.t) => +// switch (Profile.sort(p)) { +// | None => [] +// | Some(_) => +// open Util.Svgs.Path; +// let (end_l, end_r) = p.range; +// let (fr_l, fr_r) = p.full_row; +// let line_l = { +// let (s_l, t_l, _) = +// Chain.unlink(p.chain) |> Stds.Result.get_exn(Profile.No_tokens); +// if (end_l.row == t_l.loc.row) { +// [ +// m(~x=t_l.loc.col, ~y=t_l.loc.row + 1) +// |> cmdfudge(~x=-. T.concave_adj -. Child.h_trunc), +// h(~x=end_l.col), +// ] +// // |> cmdfudge(~x=-. T.concave_adj -. h_trunc), +// |> Util.Svgs.Path.view +// |> Util.Nodes.add_classes(["child-line", ...sort_clss(s_l)]) +// |> Stds.Lists.single +// |> Box.mk(~font, ~loc=Loc.zero); +// } else { +// open Stds; +// P.log("drawing l arm"); +// P.show("p", Profile.show(p)); +// // m_first.origin.col == indent +// // ? [ +// // m(~x=m_last_of_first.last.col - m_first.origin.col, ~y=0), +// // // TODO(d) need to take max of all rows, not just top +// // h(~x=max_col - m_first.origin.col), +// // shadowfudge(v(~y=l.row - m_last_of_first.origin.row)), +// // ] +// // : [ +// // shadowfudge(m(~x=0, ~y=1)), +// // h(~x=indent - m_first.origin.col), +// // shadowfudge(v(~y=l.row + 1 - m_first.origin.row)), +// // h(~x=max_col - m_first.origin.col), +// // shadowfudge(v(~y=l.row - m_first.origin.row)), +// // ] +// ( +// t_l.loc.col == p.indent +// ? [m(~x=t_l.loc.col, ~y=t_l.loc.row)] +// : [ +// m(~x=t_l.loc.col, ~y=t_l.loc.row + 1) +// |> cmdfudge(~x=-. T.concave_adj -. h_trunc), +// h(~x=p.indent), +// ] +// ) +// @ ( +// switch (fr_l) { +// | None => [v(~y=end_l.row)] +// | Some(col) => [ +// v(~y=end_l.row + 1), +// m(~x=end_l.col, ~y=end_l.row + 1), +// h(~x=col), +// ] +// } +// ) +// // |> cmdfudge(~x=-. T.concave_adj -. h_trunc), +// |> Util.Svgs.Path.view +// |> Util.Nodes.add_classes(["child-line", ...sort_clss(s_l)]) +// |> Stds.Lists.single +// |> Box.mk(~font, ~loc=Loc.zero); +// }; +// }; +// [line_l]; +// // let line_r = { +// // let (s_r, t_r, _) = +// // Chain.unlink(Chain.rev(p.chain)) +// // |> Stds.Result.get_exn(Profile.No_tokens); +// // let start = +// // m(~x=t_r.loc.col + t_r.len, ~y=t_r.loc.row + 1) +// // |> cmdfudge(~x=T.concave_adj +. h_trunc); +// // if (end_r.row == t_r.loc.row) { +// // [start, h(~x=end_r.col)] +// // // |> cmdfudge(~x=-. T.concave_adj -. h_trunc), +// // |> Util.Svgs.Path.view +// // |> Util.Nodes.add_classes(["child-line", ...sort_clss(s_r)]) +// // |> Stds.Lists.single +// // |> Box.mk(~font, ~loc=Loc.zero); +// // } else { +// // [ +// // start, +// // h(~x=p.indent), +// // ...switch (fr_l) { +// // | None => [v(~y=end_l.row)] +// // | Some(col) => [ +// // v(~y=end_l.row + 1), +// // m(~x=end_l.col, ~y=end_l.row + 1), +// // h(~x=col), +// // ] +// // }, +// // ] +// // // |> cmdfudge(~x=-. T.concave_adj -. h_trunc), +// // |> Util.Svgs.Path.view +// // |> Util.Nodes.add_classes(["child-line", ...sort_clss(s_l)]) +// // |> Stds.Lists.single +// // |> Box.mk(~font, ~loc=Loc.zero); +// // }; +// // }; +// // [line_l, line_r]; +// }; -let mk_lines = (~font, p: Profile.t) => - switch (Profile.sort(p)) { - | None => [] - | Some(_) => - let (sorts, toks) = - Chain.mapi((i, c) => (i, c), (i, t) => (i, t), p.chain); - let tok_rows = - toks - |> Base.List.group(~break=((_, l: T.Profile.t), (_, r: T.Profile.t)) => - l.loc.row != r.loc.row - ); - let h_trunc = 0.3; - let h_lines = - tok_rows - |> List.map(Stds.Lists.neighbors) - |> List.concat_map( - List.map((((step, l: T.Profile.t), (_, r: T.Profile.t))) => - Util.Svgs.Path.[ - m(~x=0, ~y=1) |> cmdfudge(~x=T.concave_adj +. h_trunc), - h(~x=r.loc.col - (l.loc.col + l.len)) - |> cmdfudge(~x=-. T.concave_adj -. h_trunc), - ] - |> Util.Svgs.Path.view - |> Util.Nodes.add_classes([ - "child-line", - ...sort_clss(List.assoc(step + 1, sorts)), - ]) - |> Stds.Lists.single - |> Box.mk(~font, ~loc={...l.loc, col: l.loc.col + l.len}) - ), - ); - let v_trunc = 0.15; - let v_lines = - Stds.Lists.neighbors(tok_rows) - |> List.map( - ((l: list((_, T.Profile.t)), r: list((_, T.Profile.t)))) => { - assert(l != [] && r != []); - let l_start = snd(List.hd(l)).loc; - let r_start = snd(List.hd(r)).loc; - // should this line extend to top or bottom (respectively) of row r? - let v_delta = - r_start.col == p.indent ? -. (1. +. 2. *. v_trunc) : -. v_trunc; - // if the line extends to bottom, adjust to account for concave tip - let h_delta = r_start.col == p.indent ? 0. : -. T.concave_adj; - let dy = Float.of_int(r_start.row - l_start.row) +. v_delta; - let path = - dy < 0.5 - ? [] - : Util.Svgs.Path.[ - m(~x=0, ~y=1) |> cmdfudge(~x=-. T.concave_adj, ~y=v_trunc), - V_({dy: dy}), - H_({dx: Float.of_int(r_start.col - p.indent) +. h_delta}), - ]; - path - |> Util.Svgs.Path.view - |> Util.Nodes.add_classes([ - "child-line", - ...sort_clss(List.assoc(fst(List.hd(r)) - 1, sorts)), - ]) - |> Stds.Lists.single - |> Box.mk(~font, ~loc=l_start); - }); - h_lines @ v_lines; - }; +// let mk_lines = (~font, p: Profile.t) => +// switch (Profile.sort(p)) { +// | None => [] +// | Some(_) => +// // index elements for index-based lookup of sorts adjacent to tokens +// let (sorts, toks) = +// Chain.mapi((i, c) => (i, c), (i, t) => (i, t), p.chain); +// // group tokens by row +// let tok_rows = +// toks +// |> Base.List.group(~break=((_, l: T.Profile.t), (_, r: T.Profile.t)) => +// l.loc.row != r.loc.row +// ); +// // bi lines within each row +// let h_lines = +// tok_rows +// |> List.map(Stds.Lists.neighbors) +// |> List.concat_map( +// List.map((((step, l: T.Profile.t), (_, r: T.Profile.t))) => +// Util.Svgs.Path.[ +// m(~x=0, ~y=1) |> cmdfudge(~x=T.concave_adj +. Child.h_trunc), +// h(~x=r.loc.col - (l.loc.col + l.len)) +// |> cmdfudge(~x=-. T.concave_adj -. Child.h_trunc), +// ] +// |> Util.Svgs.Path.view +// |> Util.Nodes.add_classes([ +// "child-line", +// ...sort_clss(List.assoc(step + 1, sorts)), +// ]) +// |> Stds.Lists.single +// |> Box.mk(~font, ~loc={...l.loc, col: l.loc.col + l.len}) +// ), +// ); +// // bi lines between rows +// let v_lines = +// Stds.Lists.neighbors(tok_rows) +// |> List.map( +// ((l: list((_, T.Profile.t)), r: list((_, T.Profile.t)))) => { +// assert(l != [] && r != []); +// let l_start = snd(List.hd(l)).loc; +// let r_start = snd(List.hd(r)).loc; +// // should this line extend to top or bottom (respectively) of row r? +// let v_delta = +// r_start.col == p.indent ? -. (1. +. 2. *. Child.v_trunc) : -. Child.v_trunc; +// // if the line extends to bottom, adjust to account for concave tip +// let h_delta = r_start.col == p.indent ? 0. : -. T.concave_adj; +// let dy = Float.of_int(r_start.row - l_start.row) +. v_delta; +// let path = +// dy < 0.5 +// ? [] +// : Util.Svgs.Path.[ +// m(~x=0, ~y=1) |> cmdfudge(~x=-. T.concave_adj, ~y=Child.v_trunc), +// V_({dy: dy}), +// H_({dx: Float.of_int(r_star t.col - p.indent) +. h_delta}), +// ]; +// path +// |> Util.Svgs.Path.view +// |> Util.Nodes.add_classes([ +// "child-line", +// ...sort_clss(List.assoc(fst(List.hd(r)) - 1, sorts)), +// ]) +// |> Stds.Lists.single +// |> Box.mk(~font, ~loc=l_start); +// }); +// h_lines @ v_lines; +// }; let mk = (~font, p: Profile.t) => - List.map(T.mk(~font), Profile.tokens(p)) @ mk_lines(~font, p); + List.map(T.mk(~font), Profile.tokens(p)) + @ List.map(Child.mk(~font), Profile.cells(p)); diff --git a/src/web/view/dec/Token.re b/src/web/view/dec/Token.re index aa7c094b..02a6d2a3 100644 --- a/src/web/view/dec/Token.re +++ b/src/web/view/dec/Token.re @@ -1,8 +1,12 @@ +open Sexplib.Std; +open Ppx_yojson_conv_lib.Yojson_conv.Primitives; + open Virtual_dom.Vdom; open Tylr_core; open Util.Svgs; module Style = { + [@deriving (show({with_path: false}), sexp, yojson)] type t = { sort: Sort.t, shape: Tip.s, @@ -19,6 +23,7 @@ module Style = { }; module Profile = { + [@deriving (show({with_path: false}), sexp, yojson)] type t = { loc: Loc.t, len: int,