cat / 012
clone

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

main
012 / internal / sheet / notes.go
notes.go 2.9K
1package sheet23import (4 "strings"5 "unicode"6)78// Notes are Sheets' notes (Insert > Note): plain text kept on a cell,9// shown when the cell is active or hovered. A note is part of its cell10// (Cell.Note), so it moves with the cell when rows are inserted, deleted11// or sorted, copies with it, stays when the contents are cleared, and12// every change to one is an undo step like any other change to a cell.1314// MaxNote caps a note's length in bytes, so a file or a paste can't15// make one without bound.16const MaxNote = 40961718// Note returns the note on the cell at a, or "".19func (s *Sheet) Note(a Addr) string {20 if c := s.cells.richAt(a); c != nil { // plain cells have no note21 return c.Note22 }23 return ""24}2526// CleanNote tidies a note as SetNote stores it: control characters27// other than line breaks dropped, trailing space trimmed, at most28// MaxNote bytes.29func CleanNote(text string) string {30 text = strings.ReplaceAll(text, "\r\n", "\n")31 text = strings.Map(func(r rune) rune {32 if r == '\n' {33 return r34 }35 if unicode.IsControl(r) {36 return -137 }38 return r39 }, text)40 text = strings.TrimRightFunc(text, unicode.IsSpace)41 if len(text) > MaxNote {42 text = strings.ToValidUTF8(text[:MaxNote], "")43 }44 return text45}4647// SetNote sets the note on the cell at a as one undo step; an empty note48// deletes it. A pivot table's results can't take notes: ErrPivotEdit;49// nor can the cells an array spills into: ErrSpillEdit.50func (s *Sheet) SetNote(a Addr, text string) error {51 if s.InPivot(Rect{From: a, To: a}) {52 return ErrPivotEdit53 }54 if _, ok := s.SpillAnchor(a); ok {55 return ErrSpillEdit56 }57 text = CleanNote(text)58 if s.Note(a) == text {59 return nil60 }61 label := "note on " + a.String()62 if text == "" {63 label = "delete note on " + a.String()64 }65 s.change(label, Rect{From: a, To: a}, func() { s.putNote(a, text) })66 return nil67}6869// putNote stores a note without recording a step of its own.70func (s *Sheet) putNote(a Addr, text string) {71 old := s.cells.get(a)72 c := old.clone()73 if c == nil {74 c = &Cell{}75 }76 c.Note = text77 if c.Blank() && c.Format.IsZero() && c.Style.IsZero() && text == "" {78 c = nil79 }80 s.place(a, c)81}8283// LoadNote sets a note as a loader does: without recording undo.84func (s *Sheet) LoadNote(a Addr, text string) {85 if text = CleanNote(text); a.Valid() && text != "" {86 s.putNote(a, text)87 }88}8990// NotesIn returns the cells in r that have notes, in row-major order.91// It walks the cells the sheet holds, not r's addresses.92func (s *Sheet) NotesIn(r Rect) []Addr {93 var out []Addr94 for a := range s.cells.anyKeysIn(r) {95 if s.Note(a) != "" {96 out = append(out, a)97 }98 }99 sortAddrs(out)100 return out101}102103// ClearNotes deletes the notes in r as one undo step and returns how many104// there were.105func (s *Sheet) ClearNotes(r Rect) int {106 notes := s.NotesIn(r)107 if len(notes) == 0 {108 return 0109 }110 s.change("delete notes in "+r.String(), r, func() {111 for _, a := range notes {112 s.putNote(a, "")113 }114 })115 return len(notes)116}