151 lines
2.8 KiB
Go
151 lines
2.8 KiB
Go
package main
|
|
|
|
import tea "github.com/charmbracelet/bubbletea"
|
|
|
|
type mode int
|
|
|
|
const (
|
|
NormalMode mode = iota
|
|
InsertMode
|
|
CommandMode
|
|
)
|
|
|
|
type cursor struct {
|
|
x int
|
|
y int
|
|
}
|
|
|
|
type model struct {
|
|
lines []string
|
|
cursor cursor
|
|
s_gutter int
|
|
mode mode
|
|
win_h int
|
|
win_w int
|
|
command string
|
|
input *InputHandler
|
|
|
|
// Insert repetition
|
|
insertCount int
|
|
insertKeys []string
|
|
insertAction Action
|
|
}
|
|
|
|
func newModel(lines []string) model {
|
|
return model{
|
|
lines: lines,
|
|
cursor: cursor{
|
|
x: 0,
|
|
y: 0,
|
|
},
|
|
s_gutter: 5,
|
|
mode: NormalMode,
|
|
command: "",
|
|
input: NewInputHandler(),
|
|
}
|
|
}
|
|
|
|
func newModelWithPos(lines []string, pos Position) model {
|
|
return model{
|
|
lines: lines,
|
|
cursor: cursor{
|
|
x: pos.Col,
|
|
y: pos.Line,
|
|
},
|
|
s_gutter: 5,
|
|
mode: NormalMode,
|
|
command: "",
|
|
input: NewInputHandler(),
|
|
}
|
|
}
|
|
|
|
func (m model) Init() tea.Cmd {
|
|
return nil
|
|
}
|
|
|
|
func (m *model) clampCursorX() {
|
|
lineLen := len(m.lines[m.cursor.y])
|
|
if lineLen == 0 {
|
|
m.cursor.x = 0
|
|
} else if m.cursor.x >= lineLen {
|
|
m.cursor.x = lineLen
|
|
}
|
|
}
|
|
|
|
func (m model) getCursorPosition() Position {
|
|
return Position{Line: m.cursor.y, Col: m.cursor.x}
|
|
}
|
|
|
|
func (m *model) replayInsert() {
|
|
// Replay (count - 1) more times
|
|
for i := 1; i < m.insertCount; i++ {
|
|
// For 'o' and 'O', we need to create a new line first
|
|
switch m.insertAction.(type) {
|
|
case OpenLineBelow:
|
|
pos := m.cursor.y
|
|
m.lines = append(m.lines[:pos+1], append([]string{""}, m.lines[pos+1:]...)...)
|
|
m.cursor.y++
|
|
m.cursor.x = 0
|
|
case OpenLineAbove:
|
|
pos := m.cursor.y
|
|
m.lines = append(m.lines[:pos], append([]string{""}, m.lines[pos:]...)...)
|
|
m.cursor.x = 0
|
|
// 'i' and 'a' don't need setup - just replay keys
|
|
}
|
|
|
|
// Replay each recorded keystroke
|
|
for _, key := range m.insertKeys {
|
|
m.processInsertKey(key)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *model) processInsertKey(key string) {
|
|
switch key {
|
|
case "enter":
|
|
y := m.cursor.y
|
|
x := m.cursor.x
|
|
|
|
// Simple case, at end, just create a line
|
|
if x == len(m.lines[y]) {
|
|
m.lines = append(m.lines[:y+1], append([]string{""}, m.lines[y+1:]...)...)
|
|
|
|
// otherwise, splice
|
|
} else {
|
|
l := m.lines[y]
|
|
m.lines[y] = l[:x]
|
|
m.lines = append(m.lines[:y+1], append([]string{l[x:]}, m.lines[y+1:]...)...)
|
|
}
|
|
|
|
m.cursor.y++
|
|
m.cursor.x = 0
|
|
|
|
case "backspace":
|
|
x := m.cursor.x
|
|
y := m.cursor.y
|
|
l := m.lines[y]
|
|
if x > 0 {
|
|
m.lines[y] = l[:x-1] + l[x:]
|
|
m.cursor.x--
|
|
} else if y > 0 {
|
|
newX := len(m.lines[y-1])
|
|
m.lines[y-1] = m.lines[y-1] + l
|
|
m.lines = append(m.lines[:y], m.lines[y+1:]...)
|
|
m.cursor.y--
|
|
m.cursor.x = newX
|
|
}
|
|
|
|
default:
|
|
// Regular character
|
|
x := m.cursor.x
|
|
y := m.cursor.y
|
|
l := m.lines[y]
|
|
if x < len(l) {
|
|
m.lines[y] = l[:x] + key + l[x:]
|
|
} else {
|
|
m.lines[y] = l + key
|
|
}
|
|
m.cursor.x += len(key)
|
|
}
|
|
}
|