package main import tea "github.com/charmbracelet/bubbletea" // MoveDown implements Motion type MoveDown struct { count int } func (a MoveDown) Execute(m *model) tea.Cmd { for i := 0; i < a.count && m.cursor.y < len(m.lines)-1; i++ { m.cursor.y++ } m.clampCursorX() return nil } func (a MoveDown) WithCount(n int) Action { return MoveDown{count: n} } // MoveUp implements Motion type MoveUp struct { count int } func (a MoveUp) Execute(m *model) tea.Cmd { for i := 0; i < a.count && m.cursor.y > 0; i++ { m.cursor.y-- } m.clampCursorX() return nil } func (a MoveUp) WithCount(n int) Action { return MoveUp{count: n} } type MoveLeft struct { count int } func (a MoveLeft) Execute(m *model) tea.Cmd { for i := 0; i < a.count && m.cursor.x > 0; i++ { m.cursor.x-- } m.clampCursorX() return nil } func (a MoveLeft) WithCount(n int) Action { return MoveLeft{count: n} } type MoveRight struct { count int } func (a MoveRight) Execute(m *model) tea.Cmd { lineLen := len(m.lines[m.cursor.y]) for i := 0; i < a.count && m.cursor.x <= lineLen; i++ { m.cursor.x++ } m.clampCursorX() return nil } func (a MoveRight) WithCount(n int) Action { return MoveRight{count: n} } type EnterInsert struct{} func (a EnterInsert) Execute(m *model) tea.Cmd { m.mode = InsertMode return nil } type EnterInsertAfter struct{} func (a EnterInsertAfter) Execute(m *model) tea.Cmd { m.cursor.x++ m.clampCursorX() m.mode = InsertMode return nil } type Quit struct{} func (a Quit) Execute(m *model) tea.Cmd { return tea.Quit } // MoveToTop implements Motion (gg) type MoveToTop struct{} func (a MoveToTop) Execute(m *model) tea.Cmd { m.cursor.y = 0 m.clampCursorX() return nil } // MoveToBottom implements Motion (G) type MoveToBottom struct{} func (a MoveToBottom) Execute(m *model) tea.Cmd { m.cursor.y = len(m.lines) - 1 m.clampCursorX() return nil } type EnterInsertLineStart struct{} func (a EnterInsertLineStart) Execute(m *model) tea.Cmd { m.cursor.x = 0 m.clampCursorX() m.mode = InsertMode return nil } type EnterInsertLineEnd struct{} func (a EnterInsertLineEnd) Execute(m *model) tea.Cmd { m.cursor.x = len(m.lines[m.cursor.y]) m.clampCursorX() m.mode = InsertMode return nil } type MoveToLineStart struct{} func (a MoveToLineStart) Execute(m *model) tea.Cmd { m.cursor.x = 0 m.clampCursorX() return nil } type MoveToLineEnd struct{} func (a MoveToLineEnd) Execute(m *model) tea.Cmd { m.cursor.x = len(m.lines[m.cursor.y]) m.clampCursorX() return nil } // TODO: Count type OpenLineBelow struct{} func (a OpenLineBelow) Execute(m *model) tea.Cmd { pos := m.cursor.y if pos >= len(m.lines) { m.lines = append(m.lines, "") } else { m.lines = append(m.lines[:pos+1], append([]string{""}, m.lines[pos+1:]...)...) } m.cursor.y++ m.clampCursorX() m.mode = InsertMode return nil } // TODO: Count type OpenLineAbove struct{} func (a OpenLineAbove) Execute(m *model) tea.Cmd { pos := m.cursor.y if pos <= 0 { m.lines = append([]string{""}, m.lines...) } else { m.lines = append(m.lines[:pos], append([]string{""}, m.lines[pos:]...)...) } m.clampCursorX() m.mode = InsertMode return nil } // TODO: Visual mode type DeleteChar struct { count int } func (a DeleteChar) Execute(m *model) tea.Cmd { pos := m.cursor.x for i := 0; i < a.count && m.cursor.x < len(m.lines[m.cursor.y]); i++ { line := m.lines[m.cursor.y] m.lines[m.cursor.y] = line[:pos] + line[pos+1:] } return nil } func (a DeleteChar) WithCount(n int) Action { return DeleteChar{count: n} }