Implemented counts for insert elements.
This commit is contained in:
parent
6c0c289b52
commit
e7405a8d19
78
model.go
78
model.go
@ -24,6 +24,11 @@ type model struct {
|
||||
win_w int
|
||||
command string
|
||||
input *InputHandler
|
||||
|
||||
// Insert repetition
|
||||
insertCount int
|
||||
insertKeys []string
|
||||
insertAction Action
|
||||
}
|
||||
|
||||
func newModel() model {
|
||||
@ -63,3 +68,76 @@ func (m *model) clampCursorX() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
88
motion.go
88
motion.go
@ -69,22 +69,45 @@ func (a MoveRight) WithCount(n int) Action {
|
||||
return MoveRight{count: n}
|
||||
}
|
||||
|
||||
type EnterInsert struct{}
|
||||
type EnterInsert struct {
|
||||
count int
|
||||
}
|
||||
|
||||
func (a EnterInsert) Execute(m *model) tea.Cmd {
|
||||
// Start recording
|
||||
m.insertCount = a.count
|
||||
m.insertKeys = []string{}
|
||||
m.insertAction = a
|
||||
|
||||
m.mode = InsertMode
|
||||
return nil
|
||||
}
|
||||
|
||||
type EnterInsertAfter struct{}
|
||||
func (a EnterInsert) WithCount(n int) Action {
|
||||
return EnterInsert{count: n}
|
||||
}
|
||||
|
||||
type EnterInsertAfter struct {
|
||||
count int
|
||||
}
|
||||
|
||||
func (a EnterInsertAfter) Execute(m *model) tea.Cmd {
|
||||
m.cursor.x++
|
||||
m.clampCursorX()
|
||||
|
||||
// Start recording
|
||||
m.insertCount = a.count
|
||||
m.insertKeys = []string{}
|
||||
m.insertAction = a
|
||||
|
||||
m.mode = InsertMode
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a EnterInsertAfter) WithCount(n int) Action {
|
||||
return EnterInsertAfter{count: n}
|
||||
}
|
||||
|
||||
type Quit struct{}
|
||||
|
||||
func (a Quit) Execute(m *model) tea.Cmd {
|
||||
@ -109,24 +132,48 @@ func (a MoveToBottom) Execute(m *model) tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
type EnterInsertLineStart struct{}
|
||||
type EnterInsertLineStart struct {
|
||||
count int
|
||||
}
|
||||
|
||||
func (a EnterInsertLineStart) Execute(m *model) tea.Cmd {
|
||||
m.cursor.x = 0
|
||||
m.clampCursorX()
|
||||
|
||||
// Start recording
|
||||
m.insertCount = a.count
|
||||
m.insertKeys = []string{}
|
||||
m.insertAction = a
|
||||
|
||||
m.mode = InsertMode
|
||||
return nil
|
||||
}
|
||||
|
||||
type EnterInsertLineEnd struct{}
|
||||
func (a EnterInsertLineStart) WithCount(n int) Action {
|
||||
return EnterInsertLineStart{count: n}
|
||||
}
|
||||
|
||||
type EnterInsertLineEnd struct {
|
||||
count int
|
||||
}
|
||||
|
||||
func (a EnterInsertLineEnd) Execute(m *model) tea.Cmd {
|
||||
m.cursor.x = len(m.lines[m.cursor.y])
|
||||
m.clampCursorX()
|
||||
|
||||
// Start recording
|
||||
m.insertCount = a.count
|
||||
m.insertKeys = []string{}
|
||||
m.insertAction = a
|
||||
|
||||
m.mode = InsertMode
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a EnterInsertLineEnd) WithCount(n int) Action {
|
||||
return EnterInsertLineEnd{count: n}
|
||||
}
|
||||
|
||||
type MoveToLineStart struct{}
|
||||
|
||||
func (a MoveToLineStart) Execute(m *model) tea.Cmd {
|
||||
@ -144,7 +191,9 @@ func (a MoveToLineEnd) Execute(m *model) tea.Cmd {
|
||||
}
|
||||
|
||||
// TODO: Count
|
||||
type OpenLineBelow struct{}
|
||||
type OpenLineBelow struct {
|
||||
count int
|
||||
}
|
||||
|
||||
func (a OpenLineBelow) Execute(m *model) tea.Cmd {
|
||||
pos := m.cursor.y
|
||||
@ -156,13 +205,24 @@ func (a OpenLineBelow) Execute(m *model) tea.Cmd {
|
||||
}
|
||||
|
||||
m.cursor.y++
|
||||
m.clampCursorX()
|
||||
m.cursor.x = 0
|
||||
|
||||
// Start recording
|
||||
m.insertCount = a.count
|
||||
m.insertKeys = []string{}
|
||||
m.insertAction = a
|
||||
|
||||
m.mode = InsertMode
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Count
|
||||
type OpenLineAbove struct{}
|
||||
func (a OpenLineBelow) WithCount(n int) Action {
|
||||
return OpenLineBelow{count: n}
|
||||
}
|
||||
|
||||
type OpenLineAbove struct {
|
||||
count int
|
||||
}
|
||||
|
||||
func (a OpenLineAbove) Execute(m *model) tea.Cmd {
|
||||
pos := m.cursor.y
|
||||
@ -173,11 +233,21 @@ func (a OpenLineAbove) Execute(m *model) tea.Cmd {
|
||||
m.lines = append(m.lines[:pos], append([]string{""}, m.lines[pos:]...)...)
|
||||
}
|
||||
|
||||
m.clampCursorX()
|
||||
m.cursor.x = 0
|
||||
|
||||
// Start recording
|
||||
m.insertCount = a.count
|
||||
m.insertKeys = []string{}
|
||||
m.insertAction = a
|
||||
|
||||
m.mode = InsertMode
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a OpenLineAbove) WithCount(n int) Action {
|
||||
return OpenLineAbove{count: n}
|
||||
}
|
||||
|
||||
// TODO: Visual mode
|
||||
type DeleteChar struct {
|
||||
count int
|
||||
|
||||
89
update.go
89
update.go
@ -14,92 +14,31 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case NormalMode:
|
||||
return m, m.input.Handle(&m, msg.String())
|
||||
|
||||
// TODO: This should be handled elsewhere
|
||||
case InsertMode:
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "ctrl+d":
|
||||
return m, tea.Quit
|
||||
key := msg.String()
|
||||
|
||||
switch key {
|
||||
case "esc":
|
||||
if m.insertCount > 1 {
|
||||
m.replayInsert()
|
||||
}
|
||||
|
||||
// Allow i to step back, but a to stay put
|
||||
if m.cursor.x > 0 {
|
||||
m.cursor.x--
|
||||
}
|
||||
m.mode = NormalMode
|
||||
m.insertCount = 0
|
||||
m.insertKeys = nil
|
||||
|
||||
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 m.cursor.x > 0 {
|
||||
m.lines[y] = l[:x-1] + l[x:]
|
||||
m.cursor.x--
|
||||
} else if m.cursor.y > 0 {
|
||||
new_x := 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 = new_x
|
||||
}
|
||||
|
||||
case "left":
|
||||
if m.cursor.x > 0 {
|
||||
m.cursor.x--
|
||||
}
|
||||
|
||||
case "right":
|
||||
if m.cursor.x < len(m.lines[m.cursor.y]) {
|
||||
m.cursor.x++
|
||||
}
|
||||
|
||||
case "up":
|
||||
if m.cursor.y > 0 {
|
||||
m.cursor.y--
|
||||
}
|
||||
|
||||
if m.cursor.x > len(m.lines[m.cursor.y])-1 {
|
||||
m.cursor.x = len(m.lines[m.cursor.y])
|
||||
}
|
||||
|
||||
case "down":
|
||||
if m.cursor.y < len(m.lines)-1 {
|
||||
m.cursor.y++
|
||||
}
|
||||
|
||||
if m.cursor.x > len(m.lines[m.cursor.y])-1 {
|
||||
m.cursor.x = len(m.lines[m.cursor.y])
|
||||
}
|
||||
case "ctrl+c", "ctrl+d":
|
||||
return m, tea.Quit
|
||||
|
||||
default:
|
||||
x := m.cursor.x
|
||||
y := m.cursor.y
|
||||
l := m.lines[y]
|
||||
ch := msg.String()
|
||||
if x < len(l) {
|
||||
m.lines[y] = l[:x] + ch + l[x:]
|
||||
} else {
|
||||
m.lines[y] = l + ch
|
||||
}
|
||||
m.cursor.x += len(ch)
|
||||
// Record and process
|
||||
m.insertKeys = append(m.insertKeys, key)
|
||||
m.processInsertKey(key)
|
||||
}
|
||||
case CommandMode:
|
||||
switch msg.String() {
|
||||
|
||||
4
view.go
4
view.go
@ -69,9 +69,9 @@ func (m model) View() string {
|
||||
|
||||
var bar string
|
||||
if m.mode == CommandMode {
|
||||
bar = fmt.Sprintf(" %6s | %d:%d (%d:%d) %s | %s", modeString, m.cursor.x, m.cursor.y, m.win_w, m.win_h, m.command, m.input.buffer)
|
||||
bar = fmt.Sprintf(" %6s | %d:%d (%d:%d) %s ", modeString, m.cursor.x, m.cursor.y, m.win_w, m.win_h, m.command)
|
||||
} else {
|
||||
bar = fmt.Sprintf(" %6s | %d:%d (%d:%d) | %s", modeString, m.cursor.x, m.cursor.y, m.win_w, m.win_h, m.input.buffer)
|
||||
bar = fmt.Sprintf(" %6s | %d:%d (%d:%d) | %s | %+v | %d", modeString, m.cursor.x, m.cursor.y, m.win_w, m.win_h, m.input.buffer, m.insertKeys, m.insertCount)
|
||||
}
|
||||
|
||||
view.WriteString(bar)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user