cat / 012
clone

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

cat/module-path
012 / internal / ui / suggest / tables.go
tables.go 3.9K
1package suggest23import (4 "strings"56 "github.com/charmbracelet/x/ansi"78 "src.rest/cat/012/internal/sheet"9 "src.rest/cat/012/internal/ui/formula"10 "src.rest/cat/012/internal/ui/theme"11)1213// Tables in formula suggestions: typing =Sa offers the table Sales with14// the named ranges, and once its "[" is typed, Sales[A offers its15// columns (Amount) and, after a #, the items (#All, #Headers ...). The16// context line shows the table's columns with the one being typed17// marked, as it shows a function's arguments.1819// items are the items a structured reference names rows by.20var items = []struct{ name, desc string }{21 {"#All", "The whole table, header row included"},22 {"#Data", "The data rows, as the table's name alone"},23 {"#Headers", "The header row"},24 {"#This Row", "The formula's own row, as @ before a column"},25}2627// tableSuggestion is the suggestion for a table formulas can read.28func tableSuggestion(sh *sheet.Sheet, t sheet.TableInfo) Suggestion {29 where := "table, no rows yet"30 if t.Shown() {31 where = "table, " + t.Range.String()32 if t.Sheet != sh {33 where = "table, " + sheet.Qualified(t.Sheet.Name(), t.Range)34 }35 }36 return Suggestion{Name: t.Name, Detail: where, Desc: "Table " + t.Name + ": " + strings.Join(t.Cols, ", ") + "; type [ for its columns"}37}3839// columnSuggestions lists what may go in the brackets of the table40// named table, for word typed there: its columns starting with it,41// then from two letters on those containing it, and the items after a42// #. A column is offered as a structured reference writes it, its [ ] #43// and ' escaped.44func columnSuggestions(sh *sheet.Sheet, table, word string) []Suggestion {45 t, ok := sh.Book().LookupTable(table)46 if !ok {47 return nil48 }49 w := strings.ToUpper(word)50 var prefix, inner []Suggestion51 if strings.HasPrefix(w, "#") {52 for _, it := range items {53 if strings.HasPrefix(strings.ToUpper(it.name), w) {54 prefix = append(prefix, Suggestion{Name: it.name, Detail: "item", Desc: it.desc, Column: true})55 }56 }57 return prefix58 }59 for i, col := range t.Cols {60 s := Suggestion{Name: sheet.EscapeColumn(col), Detail: "column " + sheet.ColName(t.Range.From.Col+i),61 Desc: "The " + col + " column of " + t.Name, Column: true}62 switch name := strings.ToUpper(col); {63 case strings.HasPrefix(name, w):64 prefix = append(prefix, s)65 case len(w) >= 2 && strings.Contains(name, w):66 inner = append(inner, s)67 }68 }69 return append(prefix, inner...)70}7172// InTable reports the table whose brackets the caret is in, if any: buf73// is the formula as it's parsed (see Host.Stored).74func InTable(buf []rune, pos int) (string, bool) {75 c := formula.ScanCaret(buf, pos)76 return c.Table, c.Table != ""77}7879// TableLine puts the columns of the table whose brackets the caret is80// in on the context line, Sales[Region, Units, Amount], with the column81// being typed marked and the items after, and the keys that apply82// (hints) as room allows. It reports false outside a table's brackets83// or for a table formulas can't find.84func TableLine(th *theme.Theme, width int, sh *sheet.Sheet, buf []rune, pos int, hints string) (left, right string, ok bool) {85 c := formula.ScanCaret(buf, pos)86 t, found := sh.Book().LookupTable(c.Table)87 if c.Table == "" || !found {88 return "", "", false89 }90 typed := strings.TrimPrefix(c.Word, "@")91 var b strings.Builder92 b.WriteString(th.Key.Render(t.Name) + "[")93 marked := false94 for i, col := range t.Cols {95 if i > 0 {96 b.WriteString(", ")97 }98 if !marked && typed != "" && strings.HasPrefix(strings.ToUpper(col), strings.ToUpper(typed)) {99 b.WriteString(th.Argument.Render(col))100 marked = true101 continue102 }103 b.WriteString(col)104 }105 b.WriteString("]")106 full := b.String() + " " + th.Muted.Render("#All #Data #Headers, @ for this row")107 for _, try := range [][2]string{{full, hints}, {b.String(), hints}, {full, ""}} {108 if ansi.StringWidth(try[0])+3+ansi.StringWidth(try[1]) <= width {109 return try[0], try[1], true110 }111 }112 return ansi.Truncate(b.String(), width, "…"), "", true113}