1package ui23import (4 "slices"5 "strconv"67 tea "charm.land/bubbletea/v2"89 "src.rest/cat/012/internal/notebook"10)1112// Changing a notebook's cells, as Jupyter does, on the cells selected:13// the active cell, or the run Shift+Up and Down selected with it. Each14// change is one undo step (setCells), so z brings back what d d deleted.1516// nbRange is the selected cells of the notebook shown, first and last,17// and the cells.18func (m *Model) nbRange() (from, to int, cells []notebook.Cell) {19 from, to = m.nbView().Range()20 return from, to, m.sheet.NotebookCells()21}2223// cellWords names cells from to to for a note: "cell 3", "cells 2 to 4".24func cellWords(from, to int) string {25 if from == to {26 return "cell " + strconv.Itoa(from+1)27 }28 return "cells " + strconv.Itoa(from+1) + " to " + strconv.Itoa(to+1)29}3031// addCell adds an empty code cell above the selected ones (at 0) or32// below them (at 1), editing it with edit.33func (m *Model) addCell(at int, edit bool) tea.Cmd {34 v := m.nbView()35 from, to, cells := m.nbRange()36 i := from37 if at > 0 && len(cells) > 0 {38 i = to + 139 }40 v.StopEdit()41 cells = slices.Insert(cells, i, notebook.Cell{})42 m.setCells("add cell", cells)43 v.Select(i, false)44 if edit {45 return v.StartEdit()46 }47 return nil48}4950// deleteCells deletes the selected cells.51func (m *Model) deleteCells() {52 from, to, cells := m.nbRange()53 if len(cells) == 0 {54 return55 }56 m.setCells("delete "+cellWords(from, to), slices.Delete(cells, from, to+1))57 m.nbView().Select(from, false)58 m.note = "Deleted " + cellWords(from, to) + ": z brings them back"59 if from == to {60 m.note = "Deleted " + cellWords(from, to) + ": z brings it back"61 }62}6364// setKind turns the selected cells into code or note cells.65func (m *Model) setKind(k notebook.Kind) {66 v := m.nbView()67 from, to, cells := m.nbRange()68 changed := false69 for i := from; i <= to && i < len(cells); i++ {70 if cells[i].Kind != k {71 cells[i].Kind, changed = k, true72 }73 }74 if changed {75 v.StopEdit()76 m.setCells("change "+cellWords(from, to), cells)77 v.SelectRange(from, to)78 }79}8081// copyCells copies the selected cells, and deletes them with cut.82func (m *Model) copyCells(cut bool) {83 from, to, cells := m.nbRange()84 if len(cells) == 0 {85 return86 }87 m.nb.clip = slices.Clone(cells[from : to+1])88 for i := range m.nb.clip {89 m.nb.clip[i].ID = 090 }91 if cut {92 m.setCells("cut "+cellWords(from, to), slices.Delete(cells, from, to+1))93 m.nbView().Select(from, false)94 m.note = "Cut " + cellWords(from, to) + ": v pastes below, V above"95 return96 }97 m.note = "Copied " + cellWords(from, to) + ": v pastes below, V above"98}99100// pasteCells pastes the cells copied below the selected ones, or above101// them, selecting what it pasted.102func (m *Model) pasteCells(above bool) {103 v := m.nbView()104 from, to, cells := m.nbRange()105 i := from106 if !above && len(cells) > 0 {107 i = to + 1108 }109 cells = slices.Insert(cells, i, m.nb.clip...)110 m.setCells("paste "+plural(len(m.nb.clip), "cell", strconv.Itoa(len(m.nb.clip))+" cells"), cells)111 v.SelectRange(i, i+len(m.nb.clip)-1)112}113114// moveCells moves the selected cells one place up (d -1) or down (1),115// together, still selected.116func (m *Model) moveCells(d int) {117 v := m.nbView()118 from, to, cells := m.nbRange()119 if len(cells) == 0 || from+d < 0 || to+d >= len(cells) {120 return121 }122 v.StopEdit()123 moved := slices.Clone(cells[from : to+1])124 cells = slices.Delete(cells, from, to+1)125 cells = slices.Insert(cells, from+d, moved...)126 m.setCells("move "+cellWords(from, to), cells)127 if sel, _ := v.Selected(); sel == to {128 v.SelectRange(from+d, to+d) // the active cell stays the one it was129 } else {130 v.SelectRange(to+d, from+d)131 }132}133134// clearCellOutputs clears the selected cells' outputs.135func (m *Model) clearCellOutputs() {136 from, to, cells := m.nbRange()137 for i := from; i <= to && i < len(cells); i++ {138 if m.book().Output(cells[i].ID) != nil {139 m.book().SetOutput(cells[i].ID, nil)140 m.feedOutput(cells[i].Name())141 }142 }143 m.note = "Cleared the output of " + cellWords(from, to)144}