cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

main
012 / internal / ui / nbview / layout.go
layout.go 3.4K
1package nbview23import (4 "strings"56 "src.rest/cat/012/internal/notebook"7 "src.rest/cat/012/internal/ui/lineedit"8)910// Where things go across the body, as in Jupyter: a bar marking the11// active cell, the prompts ([1]: and Out[1]:) right-aligned in a column12// of their own, then a code cell's box with its text two columns in,13// and a ▶ after the box. Outputs and notes' text line up with the code.14//15// ▌ [1]: ╭─ files ──────────────── ✓ <1s ─╮16// │ ls │ ▶17// ╰─────────────────────────────────╯18// Out[1]: name type size19const (20 promptW = 8 // [12]: and Out[12]:, right-aligned21 gutter = 1 + promptW + 1 // the bar, the prompt, a space: where a box starts22 textX = gutter + 2 // where code, notes and outputs start23 runW = 2 // " ▶" after a box24 boxW = 4 // a box's sides and the spaces inside them25)2627// content is how wide a cell's code is drawn: inside its box.28func (v *View) content() int { return max(v.width-gutter-boxW-runW, 8) }2930// outWidth is how wide outputs and notes are drawn.31func (v *View) outWidth() int { return max(v.width-textX-1, 8) }3233// block is what a cell takes on the screen, in lines: a box's borders34// (box is 1 for a code cell, or a note being edited), its source, its35// output, and a blank line after.36type block struct {37 box, src, out int38}3940func (b block) lines() int { return 2*b.box + b.src + b.out + 1 }4142// rowKind is what a line of a block is.43type rowKind int4445const (46 rowTop rowKind = iota // the box's top border47 rowSrc // a line of source, or of a note48 rowBottom // the box's bottom border49 rowOut // a line of output50 rowGap // the blank line after the cell51)5253// row is what line k of the block is, and which of its kind.54func (b block) row(k int) (rowKind, int) {55 switch {56 case k < b.box:57 return rowTop, 058 case k < b.box+b.src:59 return rowSrc, k - b.box60 case k < 2*b.box+b.src:61 return rowBottom, 062 case k < 2*b.box+b.src+b.out:63 return rowOut, k - 2*b.box - b.src64 }65 return rowGap, 066}6768// boxed reports whether cell i is drawn in a box: code, or a note being69// edited.70func (v *View) boxed(i int, c notebook.Cell) bool {71 return c.Kind == notebook.Code || v.edit.on && i == v.sel72}7374// blockOf lays out cell i.75func (v *View) blockOf(i int, c notebook.Cell) block {76 var b block77 if v.boxed(i, c) {78 b.box = 179 }80 switch {81 case v.edit.on && i == v.sel:82 b.src = len(v.edit.rows(v.content()))83 case c.Kind == notebook.Note:84 b.src = len(v.noteLines(c))85 default:86 b.src = len(lineedit.Wrap([]rune(c.Source), v.content()))87 }88 b.out = v.outHeight(c)89 return b90}9192// outHeight is how many lines cell c's output takes: none, one while93// it's folded, or its window.94func (v *View) outHeight(c notebook.Cell) int {95 sh := v.shown(c)96 switch f := v.foldOf(c.ID); {97 case sh.kind == outNone:98 return 099 case f.hidden:100 return 1101 default:102 return sh.height(f.whole, v.outWidth())103 }104}105106// noteLines are a note cell's lines as drawn.107func (v *View) noteLines(c notebook.Cell) []string {108 if strings.TrimSpace(c.Source) == "" {109 return []string{v.h.Theme().Muted.Render("An empty note: Enter writes it, in Markdown")}110 }111 return markdown(v.h.Theme(), c.Source, v.outWidth())112}