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 / rows.go
rows.go 3.6K
1package ui23import (4 tea "charm.land/bubbletea/v2"56 "src.rest/cat/012/internal/sheet"7)89// Whole-row commands, as vim's line operators: copy rows (yy), cut them10// (dd), paste them back as new rows (p, P), and open a new row to type in11// (o, O). They work without vim keys too, from the palette.1213func init() {14 register(15 &command{id: "row.yank", title: "Copy rows", desc: "Copy the selected rows whole, to paste as new rows", run: func(m *Model) tea.Cmd {16 r := m.selectedRows()17 cmd := m.copyRows(r)18 m.note = "Copied " + rowCount(r.To.Row-r.From.Row+1)19 return cmd20 }},21 &command{id: "row.cut", title: "Cut rows", desc: "Delete the selected rows, keeping them to paste as new rows", run: func(m *Model) tea.Cmd {22 r := m.selectedRows()23 cmd := m.copyRows(r)24 n := r.To.Row - r.From.Row + 125 m.sheet.DeleteRows(r.From.Row, n)26 m.note = "Deleted " + rowCount(n) + "; p pastes them back"27 return cmd28 }},29 &command{id: "row.paste_below", title: "Paste rows below", desc: "Insert the copied rows below the active cell's row",30 run: func(m *Model) tea.Cmd { return m.pasteRows(m.cur.Row + 1) }, enabled: (*Model).rowsCopied},31 &command{id: "row.paste_above", title: "Paste rows above", desc: "Insert the copied rows above the active cell's row",32 run: func(m *Model) tea.Cmd { return m.pasteRows(m.cur.Row) }, enabled: (*Model).rowsCopied},33 &command{id: "row.open_below", title: "Open a row below", desc: "Insert a row below the active cell and start typing in it",34 run: func(m *Model) tea.Cmd { return m.openRow(m.cur.Row + 1) }},35 &command{id: "row.open_above", title: "Open a row above", desc: "Insert a row above the active cell and start typing in it",36 run: func(m *Model) tea.Cmd { return m.openRow(m.cur.Row) }},37 )38}3940// selectedRows is the rows of the selection, whole, so a copy takes their41// row formats along as a whole-row copy does.42func (m *Model) selectedRows() sheet.Rect {43 r := m.selection()44 return sheet.Rect{From: sheet.Addr{Row: r.From.Row}, To: sheet.Addr{Col: sheet.MaxCols - 1, Row: r.To.Row}}45}4647// copyRows puts rows r on the clipboard, marked as whole rows so pasting48// inserts them, and on the system clipboard.49func (m *Model) copyRows(r sheet.Rect) tea.Cmd {50 clip := m.sheet.Copy(r)51 m.copied = clipboard{clip: clip, sheet: m.sheet}52 m.vim.rows = clip53 return tea.SetClipboard(formatTSV(clip.TextIn(m.locale())))54}5556// rowsCopied reports whether the clipboard holds whole rows.57func (m *Model) rowsCopied() bool {58 return m.vim.rows != nil && m.copied.clip == m.vim.rows59}6061// pasteRows inserts the copied rows at row at, as many times as the vim62// count says, as one undo step, and moves to the first of them.63func (m *Model) pasteRows(at int) tea.Cmd {64 clip := m.copied.clip65 cols, rows := clip.Size()66 n := rows * m.vim.n()67 dst := sheet.Rect{From: sheet.Addr{Row: at}, To: sheet.Addr{Col: cols - 1, Row: at + n - 1}}68 if !dst.To.Valid() {69 return m.structural(sheet.ErrPushedOff)70 }71 err := m.sheet.Batch(sheet.Change{Label: "paste " + rowCount(n), Focus: dst, Sheet: m.sheet}, func() error {72 if err := m.sheet.InsertRows(at, n); err != nil {73 return err74 }75 _, err := m.sheet.Paste(clip, dst, false)76 return err77 })78 if err != nil {79 return m.structural(err)80 }81 m.clearSelection()82 m.cur.Row = at83 m.note = "Pasted " + rowCount(n)84 return nil85}8687// openRow inserts a blank row at row at and starts an entry in it, in the88// active cell's column.89func (m *Model) openRow(at int) tea.Cmd {90 if at >= sheet.MaxRows {91 return m.structural(sheet.ErrPushedOff)92 }93 if err := m.sheet.InsertRows(at, 1); err != nil {94 return m.structural(err)95 }96 m.clearSelection()97 m.cur.Row = at98 m.startEntry(modeEnter, "")99 return nil100}