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
|
win_w int
|
||||||
command string
|
command string
|
||||||
input *InputHandler
|
input *InputHandler
|
||||||
|
|
||||||
|
// Insert repetition
|
||||||
|
insertCount int
|
||||||
|
insertKeys []string
|
||||||
|
insertAction Action
|
||||||
}
|
}
|
||||||
|
|
||||||
func newModel() model {
|
func newModel() model {
|
||||||
@ -63,3 +68,76 @@ func (m *model) clampCursorX() {
|
|||||||
func (m model) getCursorPosition() Position {
|
func (m model) getCursorPosition() Position {
|
||||||
return Position{Line: m.cursor.y, Col: m.cursor.x}
|
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}
|
return MoveRight{count: n}
|
||||||
}
|
}
|
||||||
|
|
||||||
type EnterInsert struct{}
|
type EnterInsert struct {
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
func (a EnterInsert) Execute(m *model) tea.Cmd {
|
func (a EnterInsert) Execute(m *model) tea.Cmd {
|
||||||
|
// Start recording
|
||||||
|
m.insertCount = a.count
|
||||||
|
m.insertKeys = []string{}
|
||||||
|
m.insertAction = a
|
||||||
|
|
||||||
m.mode = InsertMode
|
m.mode = InsertMode
|
||||||
return nil
|
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 {
|
func (a EnterInsertAfter) Execute(m *model) tea.Cmd {
|
||||||
m.cursor.x++
|
m.cursor.x++
|
||||||
m.clampCursorX()
|
m.clampCursorX()
|
||||||
|
|
||||||
|
// Start recording
|
||||||
|
m.insertCount = a.count
|
||||||
|
m.insertKeys = []string{}
|
||||||
|
m.insertAction = a
|
||||||
|
|
||||||
m.mode = InsertMode
|
m.mode = InsertMode
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a EnterInsertAfter) WithCount(n int) Action {
|
||||||
|
return EnterInsertAfter{count: n}
|
||||||
|
}
|
||||||
|
|
||||||
type Quit struct{}
|
type Quit struct{}
|
||||||
|
|
||||||
func (a Quit) Execute(m *model) tea.Cmd {
|
func (a Quit) Execute(m *model) tea.Cmd {
|
||||||
@ -109,24 +132,48 @@ func (a MoveToBottom) Execute(m *model) tea.Cmd {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type EnterInsertLineStart struct{}
|
type EnterInsertLineStart struct {
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
func (a EnterInsertLineStart) Execute(m *model) tea.Cmd {
|
func (a EnterInsertLineStart) Execute(m *model) tea.Cmd {
|
||||||
m.cursor.x = 0
|
m.cursor.x = 0
|
||||||
m.clampCursorX()
|
m.clampCursorX()
|
||||||
|
|
||||||
|
// Start recording
|
||||||
|
m.insertCount = a.count
|
||||||
|
m.insertKeys = []string{}
|
||||||
|
m.insertAction = a
|
||||||
|
|
||||||
m.mode = InsertMode
|
m.mode = InsertMode
|
||||||
return nil
|
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 {
|
func (a EnterInsertLineEnd) Execute(m *model) tea.Cmd {
|
||||||
m.cursor.x = len(m.lines[m.cursor.y])
|
m.cursor.x = len(m.lines[m.cursor.y])
|
||||||
m.clampCursorX()
|
m.clampCursorX()
|
||||||
|
|
||||||
|
// Start recording
|
||||||
|
m.insertCount = a.count
|
||||||
|
m.insertKeys = []string{}
|
||||||
|
m.insertAction = a
|
||||||
|
|
||||||
m.mode = InsertMode
|
m.mode = InsertMode
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a EnterInsertLineEnd) WithCount(n int) Action {
|
||||||
|
return EnterInsertLineEnd{count: n}
|
||||||
|
}
|
||||||
|
|
||||||
type MoveToLineStart struct{}
|
type MoveToLineStart struct{}
|
||||||
|
|
||||||
func (a MoveToLineStart) Execute(m *model) tea.Cmd {
|
func (a MoveToLineStart) Execute(m *model) tea.Cmd {
|
||||||
@ -144,7 +191,9 @@ func (a MoveToLineEnd) Execute(m *model) tea.Cmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Count
|
// TODO: Count
|
||||||
type OpenLineBelow struct{}
|
type OpenLineBelow struct {
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
func (a OpenLineBelow) Execute(m *model) tea.Cmd {
|
func (a OpenLineBelow) Execute(m *model) tea.Cmd {
|
||||||
pos := m.cursor.y
|
pos := m.cursor.y
|
||||||
@ -156,13 +205,24 @@ func (a OpenLineBelow) Execute(m *model) tea.Cmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.cursor.y++
|
m.cursor.y++
|
||||||
m.clampCursorX()
|
m.cursor.x = 0
|
||||||
|
|
||||||
|
// Start recording
|
||||||
|
m.insertCount = a.count
|
||||||
|
m.insertKeys = []string{}
|
||||||
|
m.insertAction = a
|
||||||
|
|
||||||
m.mode = InsertMode
|
m.mode = InsertMode
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Count
|
func (a OpenLineBelow) WithCount(n int) Action {
|
||||||
type OpenLineAbove struct{}
|
return OpenLineBelow{count: n}
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenLineAbove struct {
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
func (a OpenLineAbove) Execute(m *model) tea.Cmd {
|
func (a OpenLineAbove) Execute(m *model) tea.Cmd {
|
||||||
pos := m.cursor.y
|
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.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
|
m.mode = InsertMode
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a OpenLineAbove) WithCount(n int) Action {
|
||||||
|
return OpenLineAbove{count: n}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Visual mode
|
// TODO: Visual mode
|
||||||
type DeleteChar struct {
|
type DeleteChar struct {
|
||||||
count int
|
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:
|
case NormalMode:
|
||||||
return m, m.input.Handle(&m, msg.String())
|
return m, m.input.Handle(&m, msg.String())
|
||||||
|
|
||||||
|
// TODO: This should be handled elsewhere
|
||||||
case InsertMode:
|
case InsertMode:
|
||||||
switch msg.String() {
|
key := msg.String()
|
||||||
case "ctrl+c", "ctrl+d":
|
|
||||||
return m, tea.Quit
|
|
||||||
|
|
||||||
|
switch key {
|
||||||
case "esc":
|
case "esc":
|
||||||
|
if m.insertCount > 1 {
|
||||||
|
m.replayInsert()
|
||||||
|
}
|
||||||
|
|
||||||
// Allow i to step back, but a to stay put
|
// Allow i to step back, but a to stay put
|
||||||
if m.cursor.x > 0 {
|
if m.cursor.x > 0 {
|
||||||
m.cursor.x--
|
m.cursor.x--
|
||||||
}
|
}
|
||||||
m.mode = NormalMode
|
m.mode = NormalMode
|
||||||
|
m.insertCount = 0
|
||||||
|
m.insertKeys = nil
|
||||||
|
|
||||||
case "enter":
|
case "ctrl+c", "ctrl+d":
|
||||||
y := m.cursor.y
|
return m, tea.Quit
|
||||||
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])
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
x := m.cursor.x
|
// Record and process
|
||||||
y := m.cursor.y
|
m.insertKeys = append(m.insertKeys, key)
|
||||||
l := m.lines[y]
|
m.processInsertKey(key)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
case CommandMode:
|
case CommandMode:
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
|
|||||||
4
view.go
4
view.go
@ -69,9 +69,9 @@ func (m model) View() string {
|
|||||||
|
|
||||||
var bar string
|
var bar string
|
||||||
if m.mode == CommandMode {
|
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 {
|
} 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)
|
view.WriteString(bar)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user