Compare commits

..

No commits in common. "b1b3edf81037fd047906b890bb5a75f9df395588" and "770cbcceb7d61950a6b15480b655b4084f10672d" have entirely different histories.

33 changed files with 2244 additions and 2600 deletions

View File

@ -9,20 +9,10 @@ import (
) )
func main() { func main() {
buf := action.NewBufferBuilder(). m, _ := tea.NewProgram(
Build() editor.NewModel([]string{""}, action.Position{Line: 0, Col: 0}),
tea.WithAltScreen(),
win := action.NewWindowBuilder(). ).Run()
WithBuffer(&buf).
Build()
model := editor.NewModelBuilder().
AddBuffer(&buf).
AddWindow(&win).
WithActiveWindowId(win.Id).
Build()
m, _ := tea.NewProgram(model, tea.WithAltScreen()).Run()
final, ok := m.(*editor.Model) final, ok := m.(*editor.Model)
if ok { if ok {
@ -34,4 +24,5 @@ func main() {
} else { } else {
fmt.Printf("PRINTING ALL: %+v\n", m) fmt.Printf("PRINTING ALL: %+v\n", m)
} }
} }

View File

@ -8,11 +8,8 @@ type ChangeToEndOfLine struct {
} }
func (a ChangeToEndOfLine) Execute(m Model) tea.Cmd { func (a ChangeToEndOfLine) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() pos := m.CursorX()
buf := m.ActiveBuffer() line := m.Line(m.CursorY())
pos := win.Cursor.Col
line := buf.Lines[win.Cursor.Line]
// Save deleted text to register // Save deleted text to register
if pos < len(line) { if pos < len(line) {
@ -20,7 +17,7 @@ func (a ChangeToEndOfLine) Execute(m Model) tea.Cmd {
} }
// Delete to end of line // Delete to end of line
buf.SetLine(win.Cursor.Line, line[:pos]) m.SetLine(m.CursorY(), line[:pos])
// Enter insert mode // Enter insert mode
m.SetMode(InsertMode) m.SetMode(InsertMode)
@ -41,11 +38,8 @@ type SubstituteChar struct {
} }
func (a SubstituteChar) Execute(m Model) tea.Cmd { func (a SubstituteChar) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() pos := m.CursorX()
buf := m.ActiveBuffer() line := m.Line(m.CursorY())
pos := win.Cursor.Col
line := buf.Lines[win.Cursor.Line]
// Calculate how many chars to delete (limited by line length) // Calculate how many chars to delete (limited by line length)
count := min(a.Count, len(line)-pos) count := min(a.Count, len(line)-pos)
@ -55,7 +49,7 @@ func (a SubstituteChar) Execute(m Model) tea.Cmd {
m.UpdateDefaultRegister(CharwiseRegister, []string{line[pos : pos+count]}) m.UpdateDefaultRegister(CharwiseRegister, []string{line[pos : pos+count]})
// Delete the characters // Delete the characters
buf.SetLine(win.Cursor.Line, line[:pos]+line[pos+count:]) m.SetLine(m.CursorY(), line[:pos]+line[pos+count:])
} }
// Enter insert mode // Enter insert mode
@ -77,31 +71,29 @@ type SubstituteLine struct {
} }
func (a SubstituteLine) Execute(m Model) tea.Cmd { func (a SubstituteLine) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() y := m.CursorY()
buf := m.ActiveBuffer()
y := win.Cursor.Line
// Calculate how many lines to substitute // Calculate how many lines to substitute
count := min(a.Count, buf.LineCount()-y) count := min(a.Count, m.LineCount()-y)
var lines []string var lines []string
// Collect and delete lines // Collect and delete lines
for range count { for range count {
lines = append(lines, buf.Lines[y]) lines = append(lines, m.Line(y))
buf.DeleteLine(y) m.DeleteLine(y)
} }
// Save deleted lines to register // Save deleted lines to register
m.UpdateDefaultRegister(LinewiseRegister, lines) m.UpdateDefaultRegister(LinewiseRegister, lines)
// Insert empty line at original position // Insert empty line at original position
insertY := min(y, buf.LineCount()) insertY := min(y, m.LineCount())
buf.InsertLine(insertY, "") m.InsertLine(insertY, "")
// Position cursor // Position cursor
win.SetCursorPos(insertY, 0) m.SetCursorY(insertY)
m.SetCursorX(0)
// Enter insert mode // Enter insert mode
m.SetMode(InsertMode) m.SetMode(InsertMode)

View File

@ -8,14 +8,11 @@ type DeleteChar struct {
} }
func (a DeleteChar) Execute(m Model) tea.Cmd { func (a DeleteChar) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() pos := m.CursorX()
buf := m.ActiveBuffer() line := m.Line(m.CursorY())
pos := win.Cursor.Col
line := buf.Lines[win.Cursor.Line]
for i := 0; i < a.Count && pos < len(line); i++ { for i := 0; i < a.Count && pos < len(line); i++ {
line = line[:pos] + line[pos+1:] line = line[:pos] + line[pos+1:]
buf.SetLine(win.Cursor.Line, line) m.SetLine(m.CursorY(), line)
} }
return nil return nil
@ -30,47 +27,46 @@ type DeleteToEndOfLine struct {
} }
func (a DeleteToEndOfLine) Execute(m Model) tea.Cmd { func (a DeleteToEndOfLine) Execute(m Model) tea.Cmd {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
// Delete to end of line // Delete to end of line
pos := win.Cursor.Col pos := m.CursorX()
line := buf.Lines[win.Cursor.Line] line := m.Line(m.CursorY())
buf.SetLine(win.Cursor.Line, line[:pos]) m.SetLine(m.CursorY(), line[:pos])
win.SetCursorCol(pos - 1) m.SetCursorX(pos - 1)
// If count is greater, than we will delete the next N - 1 lines below // If count is greater, than we will delete the next N - 1 lines below
initY := win.Cursor.Line initY := m.CursorY()
if a.Count > 1 { if a.Count > 1 {
// Copied from `internal/operator/delete.go` // Copied from `internal/operator/delete.go`
opCount := min(a.Count-1, buf.LineCount()-win.Cursor.Line) opCount := min(a.Count-1, m.LineCount()-m.CursorY())
// Down one // Down one
win.SetCursorLine(initY + 1) m.SetCursorY(initY + 1)
for range opCount { for range opCount {
y := win.Cursor.Line // Changed from the copied code y := m.CursorY() // Changed from the copied code
// Stop if were on the starting line // Stop if were on the starting line
if y == initY { if y == initY {
break break
} }
buf.DeleteLine(y) m.DeleteLine(y)
if buf.LineCount() == 0 { if m.LineCount() == 0 {
buf.InsertLine(0, "") m.InsertLine(0, "")
} }
if y >= buf.LineCount() { if y >= m.LineCount() {
y = buf.LineCount() - 1 y = m.LineCount() - 1
} }
win.SetCursorLine(y) m.SetCursorY(y)
m.ClampCursorX()
} }
} }
win.SetCursorLine(initY) m.SetCursorY(initY)
m.ClampCursorX()
return nil return nil
} }

View File

@ -28,8 +28,8 @@ type EnterInsertAfter struct {
} }
func (a EnterInsertAfter) Execute(m Model) tea.Cmd { func (a EnterInsertAfter) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() m.SetCursorX(m.CursorX() + 1)
win.SetCursorCol(win.Cursor.Col + 1) m.ClampCursorX()
// Start recording // Start recording
m.SetInsertRecording(a.Count, a) m.SetInsertRecording(a.Count, a)
@ -47,8 +47,8 @@ type EnterInsertLineStart struct {
} }
func (a EnterInsertLineStart) Execute(m Model) tea.Cmd { func (a EnterInsertLineStart) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() m.SetCursorX(0)
win.SetCursorCol(0) m.ClampCursorX()
// Start recording // Start recording
m.SetInsertRecording(a.Count, a) m.SetInsertRecording(a.Count, a)
@ -66,9 +66,8 @@ type EnterInsertLineEnd struct {
} }
func (a EnterInsertLineEnd) Execute(m Model) tea.Cmd { func (a EnterInsertLineEnd) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() m.SetCursorX(len(m.Line(m.CursorY())))
buf := m.ActiveBuffer() m.ClampCursorX()
win.SetCursorCol(len(buf.Lines[win.Cursor.Line]))
// Start recording // Start recording
m.SetInsertRecording(a.Count, a) m.SetInsertRecording(a.Count, a)
@ -86,18 +85,16 @@ type OpenLineBelow struct {
} }
func (a OpenLineBelow) Execute(m Model) tea.Cmd { func (a OpenLineBelow) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() pos := m.CursorY()
buf := m.ActiveBuffer()
pos := win.Cursor.Line if pos >= m.LineCount() {
m.InsertLine(m.LineCount(), "")
if pos >= buf.LineCount() {
buf.InsertLine(buf.LineCount(), "")
} else { } else {
buf.InsertLine(pos+1, "") m.InsertLine(pos+1, "")
} }
win.SetCursorPos(win.Cursor.Line+1, 0) m.SetCursorY(m.CursorY() + 1)
m.SetCursorX(0)
// Start recording // Start recording
m.SetInsertRecording(a.Count, a) m.SetInsertRecording(a.Count, a)
@ -115,12 +112,9 @@ type OpenLineAbove struct {
} }
func (a OpenLineAbove) Execute(m Model) tea.Cmd { func (a OpenLineAbove) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() pos := m.CursorY()
buf := m.ActiveBuffer() m.InsertLine(pos, "")
m.SetCursorX(0)
pos := win.Cursor.Line
buf.InsertLine(pos, "")
win.SetCursorCol(0)
// Start recording // Start recording
m.SetInsertRecording(a.Count, a) m.SetInsertRecording(a.Count, a)
@ -140,17 +134,14 @@ type InsertChar struct {
} }
func (a InsertChar) Execute(m Model) tea.Cmd { func (a InsertChar) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() x, y := m.CursorX(), m.CursorY()
buf := m.ActiveBuffer() l := m.Line(y)
x, y := win.Cursor.Col, win.Cursor.Line
l := buf.Lines[y]
if x < len(l) { if x < len(l) {
buf.SetLine(y, l[:x]+a.Char+l[x:]) m.SetLine(y, l[:x]+a.Char+l[x:])
} else { } else {
buf.SetLine(y, l+a.Char) m.SetLine(y, l+a.Char)
} }
win.SetCursorCol(x + len(a.Char)) m.SetCursorX(x + len(a.Char))
return nil return nil
} }
@ -158,18 +149,16 @@ func (a InsertChar) Execute(m Model) tea.Cmd {
type InsertNewline struct{} type InsertNewline struct{}
func (a InsertNewline) Execute(m Model) tea.Cmd { func (a InsertNewline) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() x, y := m.CursorX(), m.CursorY()
buf := m.ActiveBuffer() l := m.Line(y)
x, y := win.Cursor.Col, win.Cursor.Line
l := buf.Lines[y]
if x == len(l) { if x == len(l) {
buf.InsertLine(y+1, "") m.InsertLine(y+1, "")
} else { } else {
buf.SetLine(y, l[:x]) m.SetLine(y, l[:x])
buf.InsertLine(y+1, l[x:]) m.InsertLine(y+1, l[x:])
} }
win.SetCursorPos(y+1, 0) m.SetCursorY(y + 1)
m.SetCursorX(0)
return nil return nil
} }
@ -177,20 +166,18 @@ func (a InsertNewline) Execute(m Model) tea.Cmd {
type InsertBackspace struct{} type InsertBackspace struct{}
func (a InsertBackspace) Execute(m Model) tea.Cmd { func (a InsertBackspace) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() x, y := m.CursorX(), m.CursorY()
buf := m.ActiveBuffer() l := m.Line(y)
x, y := win.Cursor.Col, win.Cursor.Line
l := buf.Lines[y]
if x > 0 { if x > 0 {
buf.SetLine(y, l[:x-1]+l[x:]) m.SetLine(y, l[:x-1]+l[x:])
win.SetCursorCol(x - 1) m.SetCursorX(x - 1)
} else if y > 0 { } else if y > 0 {
prevLine := buf.Lines[y-1] prevLine := m.Line(y - 1)
newX := len(prevLine) newX := len(prevLine)
buf.SetLine(y-1, prevLine+l) m.SetLine(y-1, prevLine+l)
buf.DeleteLine(y) m.DeleteLine(y)
win.SetCursorPos(y-1, newX) m.SetCursorY(y - 1)
m.SetCursorX(newX)
} }
return nil return nil
} }
@ -199,17 +186,14 @@ func (a InsertBackspace) Execute(m Model) tea.Cmd {
type InsertDelete struct{} type InsertDelete struct{}
func (a InsertDelete) Execute(m Model) tea.Cmd { func (a InsertDelete) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() x, y := m.CursorX(), m.CursorY()
buf := m.ActiveBuffer() l := m.Line(y)
if x == len(l) && y < m.LineCount()-1 {
x, y := win.Cursor.Col, win.Cursor.Line nextLine := m.Line(y + 1)
l := buf.Lines[y] m.SetLine(y, l+nextLine)
if x == len(l) && y < buf.LineCount()-1 { m.DeleteLine(y + 1)
nextLine := buf.Lines[y+1]
buf.SetLine(y, l+nextLine)
buf.DeleteLine(y + 1)
} else if x < len(l) { } else if x < len(l) {
buf.SetLine(y, l[:x]+l[x+1:]) m.SetLine(y, l[:x]+l[x+1:])
} }
return nil return nil
} }
@ -218,18 +202,15 @@ func (a InsertDelete) Execute(m Model) tea.Cmd {
type InsertTab struct{} type InsertTab struct{}
func (a InsertTab) Execute(m Model) tea.Cmd { func (a InsertTab) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() x, y := m.CursorX(), m.CursorY()
buf := m.ActiveBuffer() l := m.Line(y)
x, y := win.Cursor.Col, win.Cursor.Line
l := buf.Lines[y]
tabs := strings.Repeat(" ", m.Settings().TabSize) tabs := strings.Repeat(" ", m.Settings().TabSize)
if x < len(l) { if x < len(l) {
buf.SetLine(y, l[:x]+tabs+l[x:]) m.SetLine(y, l[:x]+tabs+l[x:])
} else { } else {
buf.SetLine(y, l+tabs) m.SetLine(y, l+tabs)
} }
win.SetCursorCol(x + len(tabs)) m.SetCursorX(x + len(tabs))
return nil return nil
} }
@ -248,20 +229,18 @@ func isPunctuation(c byte) bool {
} }
func (a InsertDeletePreviousWord) Execute(m Model) tea.Cmd { func (a InsertDeletePreviousWord) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() x, y := m.CursorX(), m.CursorY()
buf := m.ActiveBuffer() line := m.Line(y)
x, y := win.Cursor.Col, win.Cursor.Line
line := buf.Lines[y]
// At start of line: merge with previous line (same as backspace) // At start of line: merge with previous line (same as backspace)
if x == 0 { if x == 0 {
if y > 0 { if y > 0 {
prevLine := buf.Lines[y-1] prevLine := m.Line(y - 1)
newX := len(prevLine) newX := len(prevLine)
buf.SetLine(y-1, prevLine+line) m.SetLine(y-1, prevLine+line)
buf.DeleteLine(y) m.DeleteLine(y)
win.SetCursorPos(y-1, newX) m.SetCursorY(y - 1)
m.SetCursorX(newX)
} }
return nil return nil
} }
@ -275,8 +254,8 @@ func (a InsertDeletePreviousWord) Execute(m Model) tea.Cmd {
newX-- newX--
} }
buf.SetLine(y, line[:newX]+line[x:]) m.SetLine(y, line[:newX]+line[x:])
win.SetCursorCol(newX) m.SetCursorX(newX)
return nil return nil
} }
@ -292,8 +271,8 @@ func (a InsertDeletePreviousWord) Execute(m Model) tea.Cmd {
} }
// Delete everything from newX up to x in one operation // Delete everything from newX up to x in one operation
buf.SetLine(y, line[:newX]+line[x:]) m.SetLine(y, line[:newX]+line[x:])
win.SetCursorCol(newX) m.SetCursorX(newX)
return nil return nil
} }

View File

@ -25,9 +25,8 @@ func (a EnterComandMode) Execute(m Model) tea.Cmd {
type EnterVisualMode struct{} type EnterVisualMode struct{}
func (a EnterVisualMode) Execute(m Model) tea.Cmd { func (a EnterVisualMode) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() m.SetAnchorX(m.CursorX())
win.SetAnchorCol(win.Cursor.Col) m.SetAnchorY(m.CursorY())
win.SetAnchorLine(win.Cursor.Line)
m.SetMode(VisualMode) m.SetMode(VisualMode)
return nil return nil
} }
@ -36,9 +35,8 @@ func (a EnterVisualMode) Execute(m Model) tea.Cmd {
type EnterVisualLineMode struct{} type EnterVisualLineMode struct{}
func (a EnterVisualLineMode) Execute(m Model) tea.Cmd { func (a EnterVisualLineMode) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() m.SetAnchorX(m.CursorX())
win.SetAnchorCol(win.Cursor.Col) m.SetAnchorY(m.CursorY())
win.SetAnchorLine(win.Cursor.Line)
m.SetMode(VisualLineMode) m.SetMode(VisualLineMode)
return nil return nil
} }
@ -47,9 +45,8 @@ func (a EnterVisualLineMode) Execute(m Model) tea.Cmd {
type EnterVisualBlockMode struct{} type EnterVisualBlockMode struct{}
func (a EnterVisualBlockMode) Execute(m Model) tea.Cmd { func (a EnterVisualBlockMode) Execute(m Model) tea.Cmd {
win := m.ActiveWindow() m.SetAnchorX(m.CursorX())
win.SetAnchorCol(win.Cursor.Col) m.SetAnchorY(m.CursorY())
win.SetAnchorLine(win.Cursor.Line)
m.SetMode(VisualBlockMode) m.SetMode(VisualBlockMode)
return nil return nil
} }

View File

@ -13,9 +13,6 @@ type Paste struct {
} }
func (a Paste) Execute(m Model) tea.Cmd { func (a Paste) Execute(m Model) tea.Cmd {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
// Get reg // Get reg
reg, found := m.GetRegister('"') reg, found := m.GetRegister('"')
if !found { if !found {
@ -31,20 +28,20 @@ func (a Paste) Execute(m Model) tea.Cmd {
switch reg.Type { switch reg.Type {
case LinewiseRegister: case LinewiseRegister:
{ {
initY := win.Cursor.Line initY := m.CursorY()
lines := reg.Content lines := reg.Content
insertPos := initY + 1 insertPos := initY + 1
// Run count times // Run count times
for range a.Count { for range a.Count {
for _, line := range lines { for _, line := range lines {
buf.InsertLine(insertPos, line) m.InsertLine(insertPos, line)
insertPos++ insertPos++
} }
} }
if buf.LineCount() > 1 { if m.LineCount() > 1 {
win.SetCursorLine(initY + 1) m.SetCursorY(initY + 1)
} }
} }
case CharwiseRegister: case CharwiseRegister:
@ -57,18 +54,19 @@ func (a Paste) Execute(m Model) tea.Cmd {
break break
} }
x := win.Cursor.Col x := m.CursorX()
y := win.Cursor.Line y := m.CursorY()
cnt := strings.Repeat(lines[0], max(1, a.Count)) cnt := strings.Repeat(lines[0], max(1, a.Count))
curLine := buf.Lines[y] curLine := m.Line(y)
// Catch edge cases, end of line, start of blank line // Catch edge cases, end of line, start of blank line
insertAt := min(x+1, len(curLine)) insertAt := min(x+1, len(curLine))
newLine := curLine[:insertAt] + cnt + curLine[insertAt:] newLine := curLine[:insertAt] + cnt + curLine[insertAt:]
buf.SetLine(y, newLine) m.SetLine(y, newLine)
win.SetCursorCol(x + len(cnt)) m.SetCursorX(x + len(cnt))
m.ClampCursorX()
} }
default: default:
m.SetCommandError(fmt.Errorf("Register type is not implemented.")) m.SetCommandError(fmt.Errorf("Register type is not implemented."))
@ -90,9 +88,6 @@ type PasteBefore struct {
} }
func (a PasteBefore) Execute(m Model) tea.Cmd { func (a PasteBefore) Execute(m Model) tea.Cmd {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
// Get reg // Get reg
reg, found := m.GetRegister('"') reg, found := m.GetRegister('"')
if !found { if !found {
@ -103,14 +98,14 @@ func (a PasteBefore) Execute(m Model) tea.Cmd {
switch reg.Type { switch reg.Type {
case LinewiseRegister: case LinewiseRegister:
{ {
initY := win.Cursor.Line initY := m.CursorY()
lines := reg.Content lines := reg.Content
insertPos := initY // Leave here, this will effectively move the lines below insertPos := initY // Leave here, this will effectively move the lines below
// Run count times // Run count times
for range a.Count { for range a.Count {
for _, line := range lines { for _, line := range lines {
buf.InsertLine(insertPos, line) m.InsertLine(insertPos, line)
insertPos++ insertPos++
} }
} }
@ -125,18 +120,19 @@ func (a PasteBefore) Execute(m Model) tea.Cmd {
break break
} }
x := win.Cursor.Col x := m.CursorX()
y := win.Cursor.Line y := m.CursorY()
cnt := strings.Repeat(lines[0], max(1, a.Count)) cnt := strings.Repeat(lines[0], max(1, a.Count))
curLine := buf.Lines[y] curLine := m.Line(y)
// Catch edge cases, end of line, start of blank line // Catch edge cases, end of line, start of blank line
insertAt := min(x, len(curLine)) insertAt := min(x, len(curLine))
newLine := curLine[:insertAt] + cnt + curLine[insertAt:] newLine := curLine[:insertAt] + cnt + curLine[insertAt:]
buf.SetLine(y, newLine) m.SetLine(y, newLine)
win.SetCursorCol(x + len(cnt)) m.SetCursorX(x + len(cnt))
m.ClampCursorX()
} }
default: default:
m.SetCommandError(fmt.Errorf("Register type is not implemented.")) m.SetCommandError(fmt.Errorf("Register type is not implemented."))
@ -187,10 +183,11 @@ func (a VisualPaste) Execute(m Model) tea.Cmd {
// normalizeSelection returns start and end positions with start always before end // normalizeSelection returns start and end positions with start always before end
func normalizeSelection(m Model) (Position, Position) { func normalizeSelection(m Model) (Position, Position) {
win := m.ActiveWindow() anchorX, anchorY := m.AnchorX(), m.AnchorY()
cursorX, cursorY := m.CursorX(), m.CursorY()
start := Position{Line: win.Anchor.Line, Col: win.Anchor.Col} start := Position{Line: anchorY, Col: anchorX}
end := Position{Line: win.Cursor.Line, Col: win.Cursor.Col} end := Position{Line: cursorY, Col: cursorX}
// Normalize so start is always before end // Normalize so start is always before end
if start.Line > end.Line || (start.Line == end.Line && start.Col > end.Col) { if start.Line > end.Line || (start.Line == end.Line && start.Col > end.Col) {
@ -202,9 +199,6 @@ func normalizeSelection(m Model) (Position, Position) {
// visualCharPaste handles paste in visual (character) mode // visualCharPaste handles paste in visual (character) mode
func visualCharPaste(m Model, reg Register, start, end Position) { func visualCharPaste(m Model, reg Register, start, end Position) {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
// First, extract the text that will be deleted (to save to register) // First, extract the text that will be deleted (to save to register)
deletedText := extractCharSelection(m, start, end) deletedText := extractCharSelection(m, start, end)
@ -217,14 +211,14 @@ func visualCharPaste(m Model, reg Register, start, end Position) {
} else if reg.Type == CharwiseRegister { } else if reg.Type == CharwiseRegister {
// Charwise paste: insert text at cursor position // Charwise paste: insert text at cursor position
if len(reg.Content) == 1 { if len(reg.Content) == 1 {
line := buf.Lines[start.Line] line := m.Line(start.Line)
insertAt := min(start.Col, len(line)) insertAt := min(start.Col, len(line))
newLine := line[:insertAt] + reg.Content[0] + line[insertAt:] newLine := line[:insertAt] + reg.Content[0] + line[insertAt:]
buf.SetLine(start.Line, newLine) m.SetLine(start.Line, newLine)
// Cursor at end of pasted text // Cursor at end of pasted text
win.SetCursorCol(insertAt + len(reg.Content[0]) - 1) m.SetCursorX(insertAt + len(reg.Content[0]) - 1)
win.SetCursorLine(start.Line) m.SetCursorY(start.Line)
} }
} else if reg.Type == LinewiseRegister { } else if reg.Type == LinewiseRegister {
// Linewise paste in visual char mode: replace selection with lines // Linewise paste in visual char mode: replace selection with lines
@ -232,39 +226,38 @@ func visualCharPaste(m Model, reg Register, start, end Position) {
for i, content := range reg.Content { for i, content := range reg.Content {
if i == 0 { if i == 0 {
// First line: insert at start position // First line: insert at start position
line := buf.Lines[start.Line] line := m.Line(start.Line)
insertAt := min(start.Col, len(line)) insertAt := min(start.Col, len(line))
newLine := line[:insertAt] + content newLine := line[:insertAt] + content
if len(reg.Content) == 1 { if len(reg.Content) == 1 {
// Single line register - append rest of line // Single line register - append rest of line
newLine += line[insertAt:] newLine += line[insertAt:]
} }
buf.SetLine(start.Line, newLine) m.SetLine(start.Line, newLine)
} else { } else {
// Subsequent lines: insert new lines // Subsequent lines: insert new lines
buf.InsertLine(start.Line+i, content) m.InsertLine(start.Line+i, content)
} }
} }
win.SetCursorLine(start.Line) m.SetCursorY(start.Line)
win.SetCursorCol(start.Col) m.SetCursorX(start.Col)
} }
m.ClampCursorX()
// Update register with deleted text // Update register with deleted text
m.UpdateDefaultRegister(CharwiseRegister, []string{deletedText}) m.UpdateDefaultRegister(CharwiseRegister, []string{deletedText})
} }
// visualBlockPaste handles paste in visual block mode // visualBlockPaste handles paste in visual block mode
func visualBlockPaste(m Model, reg Register, start, end Position) { func visualBlockPaste(m Model, reg Register, start, end Position) {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
startCol := min(start.Col, end.Col) startCol := min(start.Col, end.Col)
endCol := max(start.Col, end.Col) endCol := max(start.Col, end.Col)
// Extract deleted text (for register) // Extract deleted text (for register)
var deletedLines []string var deletedLines []string
for y := start.Line; y <= end.Line; y++ { for y := start.Line; y <= end.Line; y++ {
line := buf.Lines[y] line := m.Line(y)
if startCol < len(line) { if startCol < len(line) {
ec := min(endCol+1, len(line)) ec := min(endCol+1, len(line))
deletedLines = append(deletedLines, line[startCol:ec]) deletedLines = append(deletedLines, line[startCol:ec])
@ -275,12 +268,12 @@ func visualBlockPaste(m Model, reg Register, start, end Position) {
// Delete the block selection // Delete the block selection
for y := start.Line; y <= end.Line; y++ { for y := start.Line; y <= end.Line; y++ {
line := buf.Lines[y] line := m.Line(y)
if startCol >= len(line) { if startCol >= len(line) {
continue continue
} }
ec := min(endCol+1, len(line)) ec := min(endCol+1, len(line))
buf.SetLine(y, line[:startCol]+line[ec:]) m.SetLine(y, line[:startCol]+line[ec:])
} }
// Insert register content // Insert register content
@ -291,19 +284,20 @@ func visualBlockPaste(m Model, reg Register, start, end Position) {
} }
for y := start.Line; y <= end.Line; y++ { for y := start.Line; y <= end.Line; y++ {
line := buf.Lines[y] line := m.Line(y)
insertAt := min(startCol, len(line)) insertAt := min(startCol, len(line))
// Pad with spaces if needed // Pad with spaces if needed
for len(line) < insertAt { for len(line) < insertAt {
line += " " line += " "
} }
newLine := line[:insertAt] + pasteContent + line[insertAt:] newLine := line[:insertAt] + pasteContent + line[insertAt:]
buf.SetLine(y, newLine) m.SetLine(y, newLine)
} }
} }
win.SetCursorLine(start.Line) m.SetCursorY(start.Line)
win.SetCursorCol(startCol) m.SetCursorX(startCol)
m.ClampCursorX()
// Update register with deleted block text (joined) // Update register with deleted block text (joined)
m.UpdateDefaultRegister(CharwiseRegister, []string{strings.Join(deletedLines, "\n")}) m.UpdateDefaultRegister(CharwiseRegister, []string{strings.Join(deletedLines, "\n")})
@ -311,50 +305,48 @@ func visualBlockPaste(m Model, reg Register, start, end Position) {
// visualLinePaste handles paste in visual line mode // visualLinePaste handles paste in visual line mode
func visualLinePaste(m Model, reg Register, start, end Position) { func visualLinePaste(m Model, reg Register, start, end Position) {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
// Extract deleted lines (for register) // Extract deleted lines (for register)
var deletedLines []string var deletedLines []string
for y := start.Line; y <= end.Line; y++ { for y := start.Line; y <= end.Line; y++ {
deletedLines = append(deletedLines, buf.Lines[y]) deletedLines = append(deletedLines, m.Line(y))
} }
// Delete the selected lines (from end to start to preserve indices) // Delete the selected lines (from end to start to preserve indices)
for y := end.Line; y >= start.Line; y-- { for y := end.Line; y >= start.Line; y-- {
buf.DeleteLine(y) m.DeleteLine(y)
} }
// Insert register content // Insert register content
if len(reg.Content) == 0 { if len(reg.Content) == 0 {
// Empty register - ensure at least one empty line exists // Empty register - ensure at least one empty line exists
if buf.LineCount() == 0 { if m.LineCount() == 0 {
buf.InsertLine(0, "") m.InsertLine(0, "")
} }
} else if reg.Type == LinewiseRegister { } else if reg.Type == LinewiseRegister {
// Linewise register: insert each line // Linewise register: insert each line
insertPos := start.Line insertPos := start.Line
for _, content := range reg.Content { for _, content := range reg.Content {
buf.InsertLine(insertPos, content) m.InsertLine(insertPos, content)
insertPos++ insertPos++
} }
} else { } else {
// Charwise register: insert as a single line // Charwise register: insert as a single line
buf.InsertLine(start.Line, reg.Content[0]) m.InsertLine(start.Line, reg.Content[0])
} }
// Ensure we have at least one line // Ensure we have at least one line
if buf.LineCount() == 0 { if m.LineCount() == 0 {
buf.InsertLine(0, "") m.InsertLine(0, "")
} }
// Position cursor at start of pasted content // Position cursor at start of pasted content
y := start.Line y := start.Line
if y >= buf.LineCount() { if y >= m.LineCount() {
y = buf.LineCount() - 1 y = m.LineCount() - 1
} }
win.SetCursorLine(y) m.SetCursorY(y)
win.SetCursorCol(0) m.SetCursorX(0)
m.ClampCursorX()
// Update register with deleted lines // Update register with deleted lines
m.UpdateDefaultRegister(LinewiseRegister, deletedLines) m.UpdateDefaultRegister(LinewiseRegister, deletedLines)
@ -362,10 +354,8 @@ func visualLinePaste(m Model, reg Register, start, end Position) {
// extractCharSelection extracts text from a character selection // extractCharSelection extracts text from a character selection
func extractCharSelection(m Model, start, end Position) string { func extractCharSelection(m Model, start, end Position) string {
buf := m.ActiveBuffer()
if start.Line == end.Line { if start.Line == end.Line {
line := buf.Lines[start.Line] line := m.Line(start.Line)
endCol := min(end.Col+1, len(line)) endCol := min(end.Col+1, len(line))
startCol := min(start.Col, len(line)) startCol := min(start.Col, len(line))
if startCol >= endCol { if startCol >= endCol {
@ -378,7 +368,7 @@ func extractCharSelection(m Model, start, end Position) string {
var result strings.Builder var result strings.Builder
// First line: from start.Col to end // First line: from start.Col to end
firstLine := buf.Lines[start.Line] firstLine := m.Line(start.Line)
if start.Col < len(firstLine) { if start.Col < len(firstLine) {
result.WriteString(firstLine[start.Col:]) result.WriteString(firstLine[start.Col:])
} }
@ -386,12 +376,12 @@ func extractCharSelection(m Model, start, end Position) string {
// Middle lines: entire lines // Middle lines: entire lines
for y := start.Line + 1; y < end.Line; y++ { for y := start.Line + 1; y < end.Line; y++ {
result.WriteString(buf.Lines[y]) result.WriteString(m.Line(y))
result.WriteString("\n") result.WriteString("\n")
} }
// Last line: from beginning to end.Col // Last line: from beginning to end.Col
lastLine := buf.Lines[end.Line] lastLine := m.Line(end.Line)
endCol := min(end.Col+1, len(lastLine)) endCol := min(end.Col+1, len(lastLine))
result.WriteString(lastLine[:endCol]) result.WriteString(lastLine[:endCol])
@ -400,16 +390,13 @@ func extractCharSelection(m Model, start, end Position) string {
// deleteCharSelectionForPaste deletes a character selection (similar to operator/delete.go) // deleteCharSelectionForPaste deletes a character selection (similar to operator/delete.go)
func deleteCharSelectionForPaste(m Model, start, end Position) { func deleteCharSelectionForPaste(m Model, start, end Position) {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
if start.Line == end.Line { if start.Line == end.Line {
line := buf.Lines[start.Line] line := m.Line(start.Line)
endCol := min(end.Col+1, len(line)) endCol := min(end.Col+1, len(line))
buf.SetLine(start.Line, line[:start.Col]+line[endCol:]) m.SetLine(start.Line, line[:start.Col]+line[endCol:])
} else { } else {
startLine := buf.Lines[start.Line] startLine := m.Line(start.Line)
endLine := buf.Lines[end.Line] endLine := m.Line(end.Line)
prefix := "" prefix := ""
if start.Col < len(startLine) { if start.Col < len(startLine) {
@ -423,13 +410,13 @@ func deleteCharSelectionForPaste(m Model, start, end Position) {
// Delete from end back to start to preserve indices // Delete from end back to start to preserve indices
for i := end.Line; i >= start.Line; i-- { for i := end.Line; i >= start.Line; i-- {
buf.DeleteLine(i) m.DeleteLine(i)
} }
buf.InsertLine(start.Line, prefix+suffix) m.InsertLine(start.Line, prefix+suffix)
} }
win.SetCursorLine(start.Line) m.SetCursorY(start.Line)
win.SetCursorCol(start.Col) m.SetCursorX(start.Col)
} }
// Ensure VisualPaste implements Repeatable // Ensure VisualPaste implements Repeatable

View File

@ -80,13 +80,6 @@ func (w *Window) AdjustScroll() {
w.ScrollY = max(0, min(w.ScrollY, maxScroll)) w.ScrollY = max(0, min(w.ScrollY, maxScroll))
} }
// ==================================================
// View methods
// ==================================================
func (w *Window) View() {
}
// ================================================== // ==================================================
// Setters // Setters
// ================================================== // ==================================================

BIN
internal/editor/gim Executable file

Binary file not shown.

View File

@ -19,9 +19,8 @@ func TestHelperExamples(t *testing.T) {
WithLines([]string{"hello", "world"}), WithLines([]string{"hello", "world"}),
) )
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
buf := m.ActiveBuffer() if len(m.Lines()) != 2 {
if buf.LineCount() != 2 { t.Errorf("expected 2 lines, got %d", len(m.Lines()))
t.Errorf("expected 2 lines, got %d", buf.LineCount())
} }
}) })
@ -30,9 +29,8 @@ func TestHelperExamples(t *testing.T) {
WithCursorPos(action.Position{Line: 2, Col: 3}), WithCursorPos(action.Position{Line: 2, Col: 3}),
) )
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
win := m.ActiveWindow() if m.CursorY() != 2 || m.CursorX() != 3 {
if win.Cursor.Line != 2 || win.Cursor.Col != 3 { t.Errorf("expected cursor at (2,3), got (%d,%d)", m.CursorY(), m.CursorX())
t.Errorf("expected cursor at (2,3), got (%d,%d)", win.Cursor.Line, win.Cursor.Col)
} }
}) })
@ -41,9 +39,8 @@ func TestHelperExamples(t *testing.T) {
WithTermSize(120, 40), WithTermSize(120, 40),
) )
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
win := m.ActiveWindow() if m.WinW() != 120 || m.WinH() != 40 {
if win.Width != 120 || win.Height != 40 { t.Errorf("expected size 120x40, got %dx%d", m.WinW(), m.WinH())
t.Errorf("expected size 120x40, got %dx%d", win.Width, win.Height)
} }
}) })
@ -76,17 +73,14 @@ func TestHelperExamples(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Verify all options were applied // Verify all options were applied
win := m.ActiveWindow() if len(m.Lines()) != 3 {
buf := m.ActiveBuffer() t.Errorf("expected 3 lines, got %d", len(m.Lines()))
if buf.LineCount() != 3 {
t.Errorf("expected 3 lines, got %d", buf.LineCount())
} }
if win.Cursor.Line != 1 || win.Cursor.Col != 5 { if m.CursorY() != 1 || m.CursorX() != 5 {
t.Errorf("expected cursor at (1,5), got (%d,%d)", win.Cursor.Line, win.Cursor.Col) t.Errorf("expected cursor at (1,5), got (%d,%d)", m.CursorY(), m.CursorX())
} }
if win.Width != 100 || win.Height != 30 { if m.WinW() != 100 || m.WinH() != 30 {
t.Errorf("expected size 100x30, got %dx%d", win.Width, win.Height) t.Errorf("expected size 100x30, got %dx%d", m.WinW(), m.WinH())
} }
reg, ok := m.GetRegister('"') reg, ok := m.GetRegister('"')
@ -99,29 +93,25 @@ func TestHelperExamples(t *testing.T) {
// Old style helpers still work for existing tests // Old style helpers still work for existing tests
tm1 := newTestModelWithLines(t, []string{"a", "b"}) tm1 := newTestModelWithLines(t, []string{"a", "b"})
m1 := getFinalModel(t, tm1) m1 := getFinalModel(t, tm1)
buf1 := m1.ActiveBuffer() if len(m1.Lines()) != 2 {
if buf1.LineCount() != 2 {
t.Error("newTestModelWithLines failed") t.Error("newTestModelWithLines failed")
} }
tm2 := newTestModelWithCursorPos(t, action.Position{Line: 1, Col: 2}) tm2 := newTestModelWithCursorPos(t, action.Position{Line: 1, Col: 2})
m2 := getFinalModel(t, tm2) m2 := getFinalModel(t, tm2)
win2 := m2.ActiveWindow() if m2.CursorY() != 1 {
if win2.Cursor.Line != 1 {
t.Error("newTestModelWithCursorPos failed") t.Error("newTestModelWithCursorPos failed")
} }
tm3 := newTestModelWithLinesAndCursorPos(t, []string{"x"}, action.Position{Line: 0, Col: 0}) tm3 := newTestModelWithLinesAndCursorPos(t, []string{"x"}, action.Position{Line: 0, Col: 0})
m3 := getFinalModel(t, tm3) m3 := getFinalModel(t, tm3)
buf3 := m3.ActiveBuffer() if len(m3.Lines()) != 1 {
if buf3.LineCount() != 1 {
t.Error("newTestModelWithLinesAndCursorPos failed") t.Error("newTestModelWithLinesAndCursorPos failed")
} }
tm4 := newTestModelWithTermSize(t, []string{"y"}, action.Position{Line: 0, Col: 0}, 50, 20) tm4 := newTestModelWithTermSize(t, []string{"y"}, action.Position{Line: 0, Col: 0}, 50, 20)
m4 := getFinalModel(t, tm4) m4 := getFinalModel(t, tm4)
win4 := m4.ActiveWindow() if m4.WinW() != 50 {
if win4.Width != 50 {
t.Error("newTestModelWithTermSize failed") t.Error("newTestModelWithTermSize failed")
} }
}) })

View File

@ -9,8 +9,6 @@ import (
"github.com/charmbracelet/x/exp/teatest" "github.com/charmbracelet/x/exp/teatest"
) )
// NOTE: Lots of this actually sucks ass, but it works for now...
// sendKeys sends a sequence of keys to the test model // sendKeys sends a sequence of keys to the test model
func sendKeys(tm *teatest.TestModel, keys ...string) { func sendKeys(tm *teatest.TestModel, keys ...string) {
for _, key := range keys { for _, key := range keys {
@ -96,31 +94,12 @@ func newTestModel(t *testing.T, opts ...TestModelOption) *teatest.TestModel {
opt(&cfg) opt(&cfg)
} }
buf := action.NewBufferBuilder(). // Create model
WithLines(cfg.lines). m := NewModel(cfg.lines, cfg.pos)
Build()
win := action.NewWindowBuilder(). // Set register if provided
WithBuffer(&buf).
WithCursorPos(cfg.pos.Line, cfg.pos.Col).
WithDimensions(cfg.width, cfg.height).
Build()
// Create model with default registers
m := NewModelBuilder().
AddBuffer(&buf).
AddWindow(&win).
WithActiveWindowId(win.Id).
WithTermSize(cfg.width, cfg.height).
Build()
// Set register if provided (must be done AFTER Build because SetRegister
// requires the register to already exist in the default register map)
if cfg.regContent != nil { if cfg.regContent != nil {
err := m.SetRegister(cfg.regName, cfg.regType, cfg.regContent) m.SetRegister(cfg.regName, cfg.regType, cfg.regContent)
if err != nil {
t.Fatalf("Failed to set register %c: %v", cfg.regName, err)
}
} }
return teatest.NewTestModel(t, m, teatest.WithInitialTermSize(cfg.width, cfg.height)) return teatest.NewTestModel(t, m, teatest.WithInitialTermSize(cfg.width, cfg.height))

View File

@ -13,8 +13,8 @@ func TestDeleteChar(t *testing.T) {
sendKeys(tm, "x") sendKeys(tm, "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "ello" { if m.Line(0) != "ello" {
t.Errorf("lines[0] = %q, want 'ello'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'ello'", m.Line(0))
} }
}) })
@ -24,8 +24,8 @@ func TestDeleteChar(t *testing.T) {
sendKeys(tm, "x") sendKeys(tm, "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "helo" { if m.Line(0) != "helo" {
t.Errorf("lines[0] = %q, want 'helo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'helo'", m.Line(0))
} }
}) })
@ -35,8 +35,8 @@ func TestDeleteChar(t *testing.T) {
sendKeys(tm, "x") sendKeys(tm, "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hell" { if m.Line(0) != "hell" {
t.Errorf("lines[0] = %q, want 'hell'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hell'", m.Line(0))
} }
}) })
@ -46,8 +46,8 @@ func TestDeleteChar(t *testing.T) {
sendKeys(tm, "x", "x") sendKeys(tm, "x", "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "llo" { if m.Line(0) != "llo" {
t.Errorf("lines[0] = %q, want 'llo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'llo'", m.Line(0))
} }
}) })
} }
@ -59,8 +59,8 @@ func TestDeleteCharWithCount(t *testing.T) {
sendKeys(tm, "3", "x") sendKeys(tm, "3", "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "lo" { if m.Line(0) != "lo" {
t.Errorf("lines[0] = %q, want 'lo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'lo'", m.Line(0))
} }
}) })
@ -70,8 +70,8 @@ func TestDeleteCharWithCount(t *testing.T) {
sendKeys(tm, "1", "0", "x") sendKeys(tm, "1", "0", "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("lines[0] = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want ''", m.Line(0))
} }
}) })
@ -81,8 +81,8 @@ func TestDeleteCharWithCount(t *testing.T) {
sendKeys(tm, "2", "x") sendKeys(tm, "2", "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hlo" { if m.Line(0) != "hlo" {
t.Errorf("lines[0] = %q, want 'hlo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hlo'", m.Line(0))
} }
}) })
} }
@ -94,11 +94,11 @@ func TestDeleteCharEdgeCases(t *testing.T) {
sendKeys(tm, "x") sendKeys(tm, "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -108,8 +108,8 @@ func TestDeleteCharEdgeCases(t *testing.T) {
sendKeys(tm, "x") sendKeys(tm, "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -119,8 +119,8 @@ func TestDeleteCharEdgeCases(t *testing.T) {
sendKeys(tm, "x") sendKeys(tm, "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "a" { if m.Line(0) != "a" {
t.Errorf("Line(0) = %q, want 'a'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'a'", m.Line(0))
} }
}) })
@ -130,8 +130,8 @@ func TestDeleteCharEdgeCases(t *testing.T) {
sendKeys(tm, "x") sendKeys(tm, "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "ab c" { if m.Line(0) != "ab c" {
t.Errorf("Line(0) = %q, want 'ab c'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'ab c'", m.Line(0))
} }
}) })
@ -141,11 +141,11 @@ func TestDeleteCharEdgeCases(t *testing.T) {
sendKeys(tm, "x") sendKeys(tm, "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 2 { if m.LineCount() != 2 {
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 2", m.LineCount())
} }
if m.ActiveBuffer().Lines[1] != "world" { if m.Line(1) != "world" {
t.Errorf("Line(1) = %q, want 'world'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'world'", m.Line(1))
} }
}) })
@ -155,8 +155,8 @@ func TestDeleteCharEdgeCases(t *testing.T) {
sendKeys(tm, "x", "x", "x") sendKeys(tm, "x", "x", "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "lo" { if m.Line(0) != "lo" {
t.Errorf("Line(0) = %q, want 'lo'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'lo'", m.Line(0))
} }
}) })
@ -166,8 +166,8 @@ func TestDeleteCharEdgeCases(t *testing.T) {
sendKeys(tm, "x") sendKeys(tm, "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "ab" { if m.Line(0) != "ab" {
t.Errorf("Line(0) = %q, want 'ab'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'ab'", m.Line(0))
} }
}) })
@ -177,8 +177,8 @@ func TestDeleteCharEdgeCases(t *testing.T) {
sendKeys(tm, "5", "x") sendKeys(tm, "5", "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -188,8 +188,8 @@ func TestDeleteCharEdgeCases(t *testing.T) {
sendKeys(tm, "x") sendKeys(tm, "x")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "abde" { if m.Line(0) != "abde" {
t.Errorf("Line(0) = %q, want 'abde'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'abde'", m.Line(0))
} }
}) })
} }
@ -201,8 +201,8 @@ func TestDeleteToEndOfLine(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello" { if m.Line(0) != "hello" {
t.Errorf("Line(0) = %q, want 'hello'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello'", m.Line(0))
} }
}) })
@ -212,8 +212,8 @@ func TestDeleteToEndOfLine(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -223,8 +223,8 @@ func TestDeleteToEndOfLine(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hell" { if m.Line(0) != "hell" {
t.Errorf("Line(0) = %q, want 'hell'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hell'", m.Line(0))
} }
}) })
@ -235,8 +235,8 @@ func TestDeleteToEndOfLine(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Cursor should move to last character of remaining text // Cursor should move to last character of remaining text
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -246,8 +246,8 @@ func TestDeleteToEndOfLine(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -257,14 +257,14 @@ func TestDeleteToEndOfLine(t *testing.T) {
sendKeys(tm, "2", "D") sendKeys(tm, "2", "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %q, want '3'", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %q, want '3'", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "he" { if m.Line(0) != "he" {
t.Errorf("Line(0) = %q, want 'he'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'he'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "hi" { if m.Line(1) != "hi" {
t.Errorf("Line(1) = %q, want 'hi'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'hi'", m.Line(1))
} }
}) })
@ -274,11 +274,11 @@ func TestDeleteToEndOfLine(t *testing.T) {
sendKeys(tm, "8", "D") sendKeys(tm, "8", "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %q, want '1'", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %q, want '1'", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "he" { if m.Line(0) != "he" {
t.Errorf("Line(0) = %q, want 'he'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'he'", m.Line(0))
} }
}) })
} }
@ -290,8 +290,8 @@ func TestDeleteToEndOfLineEdgeCases(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -301,11 +301,11 @@ func TestDeleteToEndOfLineEdgeCases(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 2 { if m.LineCount() != 2 {
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 2", m.LineCount())
} }
if m.ActiveBuffer().Lines[1] != "" { if m.Line(1) != "" {
t.Errorf("Line(1) = %q, want ''", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want ''", m.Line(1))
} }
}) })
@ -315,11 +315,11 @@ func TestDeleteToEndOfLineEdgeCases(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "line 1" { if m.Line(0) != "line 1" {
t.Errorf("Line(0) = %q, want 'line 1'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'line 1'", m.Line(0))
} }
if m.ActiveBuffer().Lines[2] != "line 3" { if m.Line(2) != "line 3" {
t.Errorf("Line(2) = %q, want 'line 3'", m.ActiveBuffer().Lines[2]) t.Errorf("Line(2) = %q, want 'line 3'", m.Line(2))
} }
}) })
@ -329,12 +329,12 @@ func TestDeleteToEndOfLineEdgeCases(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "he" { if m.Line(0) != "he" {
t.Errorf("Line(0) = %q, want 'he'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'he'", m.Line(0))
} }
// Cursor should clamp to last char // Cursor should clamp to last char
if m.ActiveWindow().Cursor.Col != 1 { if m.CursorX() != 1 {
t.Errorf("CursorX() = %d, want 1", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 1", m.CursorX())
} }
}) })
@ -344,8 +344,8 @@ func TestDeleteToEndOfLineEdgeCases(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -355,8 +355,8 @@ func TestDeleteToEndOfLineEdgeCases(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != " " { if m.Line(0) != " " {
t.Errorf("Line(0) = %q, want ' '", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ' '", m.Line(0))
} }
}) })
@ -366,8 +366,8 @@ func TestDeleteToEndOfLineEdgeCases(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello" { if m.Line(0) != "hello" {
t.Errorf("Line(0) = %q, want 'hello'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello'", m.Line(0))
} }
}) })
@ -377,8 +377,8 @@ func TestDeleteToEndOfLineEdgeCases(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "a" { if m.Line(0) != "a" {
t.Errorf("Line(0) = %q, want 'a'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'a'", m.Line(0))
} }
}) })
@ -388,8 +388,8 @@ func TestDeleteToEndOfLineEdgeCases(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[1] != "world" { if m.Line(1) != "world" {
t.Errorf("Line(1) = %q, want 'world'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'world'", m.Line(1))
} }
}) })
@ -399,11 +399,11 @@ func TestDeleteToEndOfLineEdgeCases(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "first" { if m.Line(0) != "first" {
t.Errorf("Line(0) = %q, want 'first'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'first'", m.Line(0))
} }
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
}) })
@ -413,8 +413,8 @@ func TestDeleteToEndOfLineEdgeCases(t *testing.T) {
sendKeys(tm, "D") sendKeys(tm, "D")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 1 { if m.CursorY() != 1 {
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 1", m.CursorY())
} }
}) })
} }

View File

@ -25,8 +25,8 @@ func TestEnterInsert(t *testing.T) {
sendKeys(tm, "i", "X", "esc") sendKeys(tm, "i", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "Xhello" { if m.Line(0) != "Xhello" {
t.Errorf("lines[0] = %q, want 'Xhello'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'Xhello'", m.Line(0))
} }
}) })
@ -36,8 +36,8 @@ func TestEnterInsert(t *testing.T) {
sendKeys(tm, "i", "X", "esc") sendKeys(tm, "i", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "heXllo" { if m.Line(0) != "heXllo" {
t.Errorf("lines[0] = %q, want 'heXllo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
} }
}) })
@ -47,8 +47,8 @@ func TestEnterInsert(t *testing.T) {
sendKeys(tm, "i", "X", "esc") sendKeys(tm, "i", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 2 { if m.CursorX() != 2 {
t.Errorf("cursor.x = %d, want 2", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 2", m.CursorX())
} }
}) })
} }
@ -70,8 +70,8 @@ func TestEnterInsertAfter(t *testing.T) {
sendKeys(tm, "a", "X", "esc") sendKeys(tm, "a", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hXello" { if m.Line(0) != "hXello" {
t.Errorf("lines[0] = %q, want 'hXello'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hXello'", m.Line(0))
} }
}) })
@ -81,8 +81,8 @@ func TestEnterInsertAfter(t *testing.T) {
sendKeys(tm, "a", "X", "esc") sendKeys(tm, "a", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "helXlo" { if m.Line(0) != "helXlo" {
t.Errorf("lines[0] = %q, want 'helXlo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'helXlo'", m.Line(0))
} }
}) })
} }
@ -94,8 +94,8 @@ func TestEnterInsertLineStart(t *testing.T) {
sendKeys(tm, "I", "X", "esc") sendKeys(tm, "I", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "Xhello" { if m.Line(0) != "Xhello" {
t.Errorf("lines[0] = %q, want 'Xhello'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'Xhello'", m.Line(0))
} }
}) })
@ -105,8 +105,8 @@ func TestEnterInsertLineStart(t *testing.T) {
sendKeys(tm, "I", "X", "esc") sendKeys(tm, "I", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "Xhello" { if m.Line(0) != "Xhello" {
t.Errorf("lines[0] = %q, want 'Xhello'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'Xhello'", m.Line(0))
} }
}) })
} }
@ -118,8 +118,8 @@ func TestEnterInsertLineEnd(t *testing.T) {
sendKeys(tm, "A", "X", "esc") sendKeys(tm, "A", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "helloX" { if m.Line(0) != "helloX" {
t.Errorf("lines[0] = %q, want 'helloX'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'helloX'", m.Line(0))
} }
}) })
@ -129,8 +129,8 @@ func TestEnterInsertLineEnd(t *testing.T) {
sendKeys(tm, "A", "X", "esc") sendKeys(tm, "A", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "helloX" { if m.Line(0) != "helloX" {
t.Errorf("lines[0] = %q, want 'helloX'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'helloX'", m.Line(0))
} }
}) })
} }
@ -144,11 +144,11 @@ func TestOpenLineBelow(t *testing.T) {
sendKeys(tm, "o", "n", "e", "w", "esc") sendKeys(tm, "o", "n", "e", "w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("len(lines) = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[1] != "new" { if m.Line(1) != "new" {
t.Errorf("lines[1] = %q, want 'new'", m.ActiveBuffer().Lines[1]) t.Errorf("lines[1] = %q, want 'new'", m.Line(1))
} }
}) })
@ -158,11 +158,11 @@ func TestOpenLineBelow(t *testing.T) {
sendKeys(tm, "o", "n", "e", "w", "esc") sendKeys(tm, "o", "n", "e", "w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 4 { if m.LineCount() != 4 {
t.Errorf("len(lines) = %d, want 4", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 4", m.LineCount())
} }
if m.ActiveBuffer().Lines[2] != "new" { if m.Line(2) != "new" {
t.Errorf("lines[2] = %q, want 'new'", m.ActiveBuffer().Lines[2]) t.Errorf("lines[2] = %q, want 'new'", m.Line(2))
} }
}) })
@ -172,11 +172,11 @@ func TestOpenLineBelow(t *testing.T) {
sendKeys(tm, "o", "n", "e", "w", "esc") sendKeys(tm, "o", "n", "e", "w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("len(lines) = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[2] != "new" { if m.Line(2) != "new" {
t.Errorf("lines[2] = %q, want 'new'", m.ActiveBuffer().Lines[2]) t.Errorf("lines[2] = %q, want 'new'", m.Line(2))
} }
}) })
@ -186,11 +186,11 @@ func TestOpenLineBelow(t *testing.T) {
sendKeys(tm, "o", "esc") sendKeys(tm, "o", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 1 { if m.CursorY() != 1 {
t.Errorf("cursor.y = %d, want 1", m.ActiveWindow().Cursor.Line) t.Errorf("cursor.y = %d, want 1", m.CursorY())
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("cursor.x = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 0", m.CursorX())
} }
}) })
} }
@ -202,12 +202,12 @@ func TestOpenLineBelowWithCount(t *testing.T) {
sendKeys(tm, "3", "o", "x", "esc") sendKeys(tm, "3", "o", "x", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 4 { if m.LineCount() != 4 {
t.Errorf("len(lines) = %d, want 4", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 4", m.LineCount())
} }
for i := 1; i <= 3; i++ { for i := 1; i <= 3; i++ {
if m.ActiveBuffer().Lines[i] != "x" { if m.Line(i) != "x" {
t.Errorf("lines[%d] = %q, want 'x'", i, m.ActiveBuffer().Lines[i]) t.Errorf("lines[%d] = %q, want 'x'", i, m.Line(i))
} }
} }
}) })
@ -218,14 +218,14 @@ func TestOpenLineBelowWithCount(t *testing.T) {
sendKeys(tm, "2", "o", "a", "b", "esc") sendKeys(tm, "2", "o", "a", "b", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("len(lines) = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[1] != "ab" { if m.Line(1) != "ab" {
t.Errorf("lines[1] = %q, want 'ab'", m.ActiveBuffer().Lines[1]) t.Errorf("lines[1] = %q, want 'ab'", m.Line(1))
} }
if m.ActiveBuffer().Lines[2] != "ab" { if m.Line(2) != "ab" {
t.Errorf("lines[2] = %q, want 'ab'", m.ActiveBuffer().Lines[2]) t.Errorf("lines[2] = %q, want 'ab'", m.Line(2))
} }
}) })
} }
@ -237,11 +237,11 @@ func TestOpenLineAbove(t *testing.T) {
sendKeys(tm, "O", "n", "e", "w", "esc") sendKeys(tm, "O", "n", "e", "w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("len(lines) = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[1] != "new" { if m.Line(1) != "new" {
t.Errorf("lines[1] = %q, want 'new'", m.ActiveBuffer().Lines[1]) t.Errorf("lines[1] = %q, want 'new'", m.Line(1))
} }
}) })
@ -251,11 +251,11 @@ func TestOpenLineAbove(t *testing.T) {
sendKeys(tm, "O", "n", "e", "w", "esc") sendKeys(tm, "O", "n", "e", "w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("len(lines) = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "new" { if m.Line(0) != "new" {
t.Errorf("lines[0] = %q, want 'new'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'new'", m.Line(0))
} }
}) })
@ -265,8 +265,8 @@ func TestOpenLineAbove(t *testing.T) {
sendKeys(tm, "O", "esc") sendKeys(tm, "O", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("cursor.x = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 0", m.CursorX())
} }
}) })
} }
@ -278,12 +278,12 @@ func TestOpenLineAboveWithCount(t *testing.T) {
sendKeys(tm, "3", "O", "x", "esc") sendKeys(tm, "3", "O", "x", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 4 { if m.LineCount() != 4 {
t.Errorf("len(lines) = %d, want 4", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 4", m.LineCount())
} }
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
if m.ActiveBuffer().Lines[i] != "x" { if m.Line(i) != "x" {
t.Errorf("lines[%d] = %q, want 'x'", i, m.ActiveBuffer().Lines[i]) t.Errorf("lines[%d] = %q, want 'x'", i, m.Line(i))
} }
} }
}) })
@ -298,14 +298,14 @@ func TestInsertModeEnter(t *testing.T) {
sendKeys(tm, "i", "enter", "esc") sendKeys(tm, "i", "enter", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 2 { if m.LineCount() != 2 {
t.Errorf("len(lines) = %d, want 2", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 2", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "hello" { if m.Line(0) != "hello" {
t.Errorf("lines[0] = %q, want 'hello'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != " world" { if m.Line(1) != " world" {
t.Errorf("lines[1] = %q, want ' world'", m.ActiveBuffer().Lines[1]) t.Errorf("lines[1] = %q, want ' world'", m.Line(1))
} }
}) })
@ -315,14 +315,14 @@ func TestInsertModeEnter(t *testing.T) {
sendKeys(tm, "i", "enter", "esc") sendKeys(tm, "i", "enter", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 2 { if m.LineCount() != 2 {
t.Errorf("len(lines) = %d, want 2", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 2", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "hello" { if m.Line(0) != "hello" {
t.Errorf("lines[0] = %q, want 'hello'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "" { if m.Line(1) != "" {
t.Errorf("lines[1] = %q, want ''", m.ActiveBuffer().Lines[1]) t.Errorf("lines[1] = %q, want ''", m.Line(1))
} }
}) })
@ -332,14 +332,14 @@ func TestInsertModeEnter(t *testing.T) {
sendKeys(tm, "i", "enter", "esc") sendKeys(tm, "i", "enter", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 2 { if m.LineCount() != 2 {
t.Errorf("len(lines) = %d, want 2", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 2", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("lines[0] = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want ''", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "hello" { if m.Line(1) != "hello" {
t.Errorf("lines[1] = %q, want 'hello'", m.ActiveBuffer().Lines[1]) t.Errorf("lines[1] = %q, want 'hello'", m.Line(1))
} }
}) })
} }
@ -351,8 +351,8 @@ func TestInsertModeBackspace(t *testing.T) {
sendKeys(tm, "i", "backspace", "esc") sendKeys(tm, "i", "backspace", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "helo" { if m.Line(0) != "helo" {
t.Errorf("lines[0] = %q, want 'helo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'helo'", m.Line(0))
} }
}) })
@ -362,11 +362,11 @@ func TestInsertModeBackspace(t *testing.T) {
sendKeys(tm, "i", "backspace", "esc") sendKeys(tm, "i", "backspace", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("len(lines) = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "helloworld" { if m.Line(0) != "helloworld" {
t.Errorf("lines[0] = %q, want 'helloworld'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'helloworld'", m.Line(0))
} }
}) })
@ -376,8 +376,8 @@ func TestInsertModeBackspace(t *testing.T) {
sendKeys(tm, "i", "backspace", "esc") sendKeys(tm, "i", "backspace", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello" { if m.Line(0) != "hello" {
t.Errorf("lines[0] = %q, want 'hello'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
} }
}) })
@ -387,8 +387,8 @@ func TestInsertModeBackspace(t *testing.T) {
sendKeys(tm, "i", "backspace", "backspace", "backspace", "esc") sendKeys(tm, "i", "backspace", "backspace", "backspace", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "he" { if m.Line(0) != "he" {
t.Errorf("lines[0] = %q, want 'he'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'he'", m.Line(0))
} }
}) })
} }
@ -400,8 +400,8 @@ func TestInsertModeDelete(t *testing.T) {
sendKeys(tm, "i", "delete", "esc") sendKeys(tm, "i", "delete", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "word" { if m.Line(0) != "word" {
t.Errorf("lines[0] = %q, want 'word'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'word'", m.Line(0))
} }
}) })
@ -411,11 +411,11 @@ func TestInsertModeDelete(t *testing.T) {
sendKeys(tm, "i", "delete", "esc") sendKeys(tm, "i", "delete", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("len(lines) = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "helloworld" { if m.Line(0) != "helloworld" {
t.Errorf("lines[0] = %q, want 'helloworld'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'helloworld'", m.Line(0))
} }
}) })
@ -425,11 +425,11 @@ func TestInsertModeDelete(t *testing.T) {
sendKeys(tm, "i", "delete", "esc") sendKeys(tm, "i", "delete", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("len(lines) = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("len(lines) = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "world" { if m.Line(0) != "world" {
t.Errorf("lines[0] = %q, want 'world'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'world'", m.Line(0))
} }
}) })
@ -439,8 +439,8 @@ func TestInsertModeDelete(t *testing.T) {
sendKeys(tm, "i", "delete", "esc") sendKeys(tm, "i", "delete", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello" { if m.Line(0) != "hello" {
t.Errorf("lines[0] = %q, want 'hello'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
} }
}) })
@ -450,8 +450,8 @@ func TestInsertModeDelete(t *testing.T) {
sendKeys(tm, "i", "delete", "delete", "delete", "esc") sendKeys(tm, "i", "delete", "delete", "delete", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "ho" { if m.Line(0) != "ho" {
t.Errorf("lines[0] = %q, want 'he'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'he'", m.Line(0))
} }
}) })
@ -464,8 +464,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
sendKeys(tm, "i", "left", "X", "esc") sendKeys(tm, "i", "left", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "heXllo" { if m.Line(0) != "heXllo" {
t.Errorf("lines[0] = %q, want 'heXllo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
} }
}) })
@ -475,8 +475,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
sendKeys(tm, "i", "right", "X", "esc") sendKeys(tm, "i", "right", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "heXllo" { if m.Line(0) != "heXllo" {
t.Errorf("lines[0] = %q, want 'heXllo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
} }
}) })
@ -486,11 +486,11 @@ func TestInsertModeArrowKeys(t *testing.T) {
sendKeys(tm, "i", "up", "X", "esc") sendKeys(tm, "i", "up", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "heXllo" { if m.Line(0) != "heXllo" {
t.Errorf("lines[0] = %q, want 'heXllo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "world" { if m.Line(1) != "world" {
t.Errorf("lines[1] = %q, want 'world'", m.ActiveBuffer().Lines[1]) t.Errorf("lines[1] = %q, want 'world'", m.Line(1))
} }
}) })
@ -500,11 +500,11 @@ func TestInsertModeArrowKeys(t *testing.T) {
sendKeys(tm, "i", "down", "X", "esc") sendKeys(tm, "i", "down", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello" { if m.Line(0) != "hello" {
t.Errorf("lines[0] = %q, want 'hello'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "woXrld" { if m.Line(1) != "woXrld" {
t.Errorf("lines[1] = %q, want 'woXrld'", m.ActiveBuffer().Lines[1]) t.Errorf("lines[1] = %q, want 'woXrld'", m.Line(1))
} }
}) })
@ -514,8 +514,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
sendKeys(tm, "i", "left", "X", "esc") sendKeys(tm, "i", "left", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "Xhello" { if m.Line(0) != "Xhello" {
t.Errorf("lines[0] = %q, want 'Xhello'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'Xhello'", m.Line(0))
} }
}) })
@ -525,8 +525,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
sendKeys(tm, "a", "right", "X", "esc") sendKeys(tm, "a", "right", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "helloX" { if m.Line(0) != "helloX" {
t.Errorf("lines[0] = %q, want 'helloX'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'helloX'", m.Line(0))
} }
}) })
@ -536,8 +536,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
sendKeys(tm, "i", "up", "X", "esc") sendKeys(tm, "i", "up", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "heXllo" { if m.Line(0) != "heXllo" {
t.Errorf("lines[0] = %q, want 'heXllo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
} }
}) })
@ -547,8 +547,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
sendKeys(tm, "i", "down", "X", "esc") sendKeys(tm, "i", "down", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "heXllo" { if m.Line(0) != "heXllo" {
t.Errorf("lines[0] = %q, want 'heXllo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
} }
}) })
@ -558,8 +558,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
sendKeys(tm, "i", "up", "X", "esc") sendKeys(tm, "i", "up", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hiX" { if m.Line(0) != "hiX" {
t.Errorf("lines[0] = %q, want 'hiX'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hiX'", m.Line(0))
} }
}) })
@ -569,8 +569,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
sendKeys(tm, "i", "down", "X", "esc") sendKeys(tm, "i", "down", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[1] != "hiX" { if m.Line(1) != "hiX" {
t.Errorf("lines[1] = %q, want 'hiX'", m.ActiveBuffer().Lines[1]) t.Errorf("lines[1] = %q, want 'hiX'", m.Line(1))
} }
}) })
@ -580,8 +580,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
sendKeys(tm, "i", "right", "right", "down", "X", "esc") sendKeys(tm, "i", "right", "right", "down", "X", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[1] != "woXrld" { if m.Line(1) != "woXrld" {
t.Errorf("lines[1] = %q, want 'woXrld'", m.ActiveBuffer().Lines[1]) t.Errorf("lines[1] = %q, want 'woXrld'", m.Line(1))
} }
}) })
} }
@ -593,11 +593,11 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
sendKeys(tm, "a", "ctrl+w", "esc") sendKeys(tm, "a", "ctrl+w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello " { if m.Line(0) != "hello " {
t.Errorf("lines[0] = %q, want 'hello '", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hello '", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 5 { if m.CursorX() != 5 {
t.Errorf("CursorX() = %d, want '5'", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want '5'", m.CursorX())
} }
}) })
@ -607,11 +607,11 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
sendKeys(tm, "a", "ctrl+w", "esc") sendKeys(tm, "a", "ctrl+w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("lines[0] = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want ''", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want '0'", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want '0'", m.CursorX())
} }
}) })
@ -621,11 +621,11 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
sendKeys(tm, "a", "ctrl+w", "esc") sendKeys(tm, "a", "ctrl+w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello wo" { if m.Line(0) != "hello wo" {
t.Errorf("lines[0] = %q, want 'hello wo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hello wo'", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 7 { if m.CursorX() != 7 {
t.Errorf("CursorX() = %d, want '7'", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want '7'", m.CursorX())
} }
}) })
@ -635,14 +635,14 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
sendKeys(tm, "a", "ctrl+w", "esc") sendKeys(tm, "a", "ctrl+w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want '1'", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want '1'", m.LineCount())
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want '0'", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want '0'", m.CursorX())
} }
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want '0'", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want '0'", m.CursorY())
} }
}) })
@ -652,17 +652,17 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
sendKeys(tm, "a", "ctrl+w", "esc") sendKeys(tm, "a", "ctrl+w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want '1'", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want '1'", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %s, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %s, want ''", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want '0'", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want '0'", m.CursorX())
} }
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want '0'", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want '0'", m.CursorY())
} }
}) })
@ -672,11 +672,11 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
sendKeys(tm, "i", "ctrl+w", "esc") sendKeys(tm, "i", "ctrl+w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello" { if m.Line(0) != "hello" {
t.Errorf("lines[0] = %q, want 'hello'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -686,11 +686,11 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
sendKeys(tm, "i", "ctrl+w", "esc") sendKeys(tm, "i", "ctrl+w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "lo" { if m.Line(0) != "lo" {
t.Errorf("lines[0] = %q, want 'lo'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'lo'", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -700,11 +700,11 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
sendKeys(tm, "a", "ctrl+w", "esc") sendKeys(tm, "a", "ctrl+w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "..." { if m.Line(0) != "..." {
t.Errorf("lines[0] = %q, want '...'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want '...'", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 2 { if m.CursorX() != 2 {
t.Errorf("CursorX() = %d, want 2", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 2", m.CursorX())
} }
}) })
@ -714,11 +714,11 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
sendKeys(tm, "a", "ctrl+w", "esc") sendKeys(tm, "a", "ctrl+w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello\t" { if m.Line(0) != "hello\t" {
t.Errorf("lines[0] = %q, want 'hello\\t'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'hello\\t'", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 5 { if m.CursorX() != 5 {
t.Errorf("CursorX() = %d, want 5", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 5", m.CursorX())
} }
}) })
@ -728,17 +728,17 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
sendKeys(tm, "i", "ctrl+w", "esc") sendKeys(tm, "i", "ctrl+w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "helloworld" { if m.Line(0) != "helloworld" {
t.Errorf("lines[0] = %q, want 'helloworld'", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want 'helloworld'", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
@ -748,11 +748,11 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
sendKeys(tm, "a", "ctrl+w", "esc") sendKeys(tm, "a", "ctrl+w", "esc")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("lines[0] = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("lines[0] = %q, want ''", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
} }

View File

@ -12,8 +12,8 @@ func TestMoveDown(t *testing.T) {
sendKeys(tm, "j") sendKeys(tm, "j")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 1 { if m.CursorY() != 1 {
t.Errorf("cursor.y = %d, want 1", m.ActiveWindow().Cursor.Line) t.Errorf("cursor.y = %d, want 1", m.CursorY())
} }
}) })
@ -22,8 +22,8 @@ func TestMoveDown(t *testing.T) {
sendKeys(tm, "j", "j", "j", "j") sendKeys(tm, "j", "j", "j", "j")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 4 { if m.CursorY() != 4 {
t.Errorf("cursor.y = %d, want 4", m.ActiveWindow().Cursor.Line) t.Errorf("cursor.y = %d, want 4", m.CursorY())
} }
}) })
@ -32,8 +32,8 @@ func TestMoveDown(t *testing.T) {
sendKeys(tm, "j", "j", "j", "j", "j", "j", "j", "j", "j") sendKeys(tm, "j", "j", "j", "j", "j", "j", "j", "j", "j")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 5 { if m.CursorY() != 5 {
t.Errorf("cursor.y = %d, want 5", m.ActiveWindow().Cursor.Line) t.Errorf("cursor.y = %d, want 5", m.CursorY())
} }
}) })
} }
@ -44,8 +44,8 @@ func TestMoveDownWithCount(t *testing.T) {
sendKeys(tm, "3", "j") sendKeys(tm, "3", "j")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 3 { if m.CursorY() != 3 {
t.Errorf("cursor.y = %d, want 3", m.ActiveWindow().Cursor.Line) t.Errorf("cursor.y = %d, want 3", m.CursorY())
} }
}) })
@ -54,8 +54,8 @@ func TestMoveDownWithCount(t *testing.T) {
sendKeys(tm, "1", "0", "j") sendKeys(tm, "1", "0", "j")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 5 { if m.CursorY() != 5 {
t.Errorf("cursor.y = %d, want 5", m.ActiveWindow().Cursor.Line) t.Errorf("cursor.y = %d, want 5", m.CursorY())
} }
}) })
} }
@ -69,8 +69,8 @@ func TestMoveDownWithOverflow(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
want := len(lines[1]) want := len(lines[1])
if m.ActiveWindow().Cursor.Col != want { if m.CursorX() != want {
t.Errorf("cursor.x = %d, want %d", m.ActiveWindow().Cursor.Col, want) t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
} }
}) })
@ -79,8 +79,8 @@ func TestMoveDownWithOverflow(t *testing.T) {
sendKeys(tm, "j") sendKeys(tm, "j")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 3 { if m.CursorX() != 3 {
t.Errorf("cursor.x = %d, want 3", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 3", m.CursorX())
} }
}) })
} }
@ -91,8 +91,8 @@ func TestMoveUp(t *testing.T) {
sendKeys(tm, "k") sendKeys(tm, "k")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 1 { if m.CursorY() != 1 {
t.Errorf("cursor.y = %d, want 1", m.ActiveWindow().Cursor.Line) t.Errorf("cursor.y = %d, want 1", m.CursorY())
} }
}) })
@ -101,8 +101,8 @@ func TestMoveUp(t *testing.T) {
sendKeys(tm, "k", "k", "k", "k") sendKeys(tm, "k", "k", "k", "k")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("cursor.y = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("cursor.y = %d, want 0", m.CursorY())
} }
}) })
@ -111,8 +111,8 @@ func TestMoveUp(t *testing.T) {
sendKeys(tm, "k", "k", "k") sendKeys(tm, "k", "k", "k")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("cursor.y = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("cursor.y = %d, want 0", m.CursorY())
} }
}) })
} }
@ -123,8 +123,8 @@ func TestMoveUpWithCount(t *testing.T) {
sendKeys(tm, "3", "k") sendKeys(tm, "3", "k")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 2 { if m.CursorY() != 2 {
t.Errorf("cursor.y = %d, want 2", m.ActiveWindow().Cursor.Line) t.Errorf("cursor.y = %d, want 2", m.CursorY())
} }
}) })
@ -133,8 +133,8 @@ func TestMoveUpWithCount(t *testing.T) {
sendKeys(tm, "1", "0", "k") sendKeys(tm, "1", "0", "k")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("cursor.y = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("cursor.y = %d, want 0", m.CursorY())
} }
}) })
} }
@ -148,8 +148,8 @@ func TestMoveUpWithOverflow(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
want := len(lines[0]) want := len(lines[0])
if m.ActiveWindow().Cursor.Col != want { if m.CursorX() != want {
t.Errorf("cursor.x = %d, want %d", m.ActiveWindow().Cursor.Col, want) t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
} }
}) })
@ -158,8 +158,8 @@ func TestMoveUpWithOverflow(t *testing.T) {
sendKeys(tm, "k") sendKeys(tm, "k")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 3 { if m.CursorX() != 3 {
t.Errorf("cursor.x = %d, want 3", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 3", m.CursorX())
} }
}) })
} }
@ -170,8 +170,8 @@ func TestMoveRight(t *testing.T) {
sendKeys(tm, "l") sendKeys(tm, "l")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 1 { if m.CursorX() != 1 {
t.Errorf("cursor.x = %d, want 1", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 1", m.CursorX())
} }
}) })
@ -180,8 +180,8 @@ func TestMoveRight(t *testing.T) {
sendKeys(tm, "l", "l", "l", "l") sendKeys(tm, "l", "l", "l", "l")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("cursor.x = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 4", m.CursorX())
} }
}) })
@ -192,8 +192,8 @@ func TestMoveRight(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
want := len(lines[0]) want := len(lines[0])
if m.ActiveWindow().Cursor.Col != want { if m.CursorX() != want {
t.Errorf("cursor.x = %d, want %d", m.ActiveWindow().Cursor.Col, want) t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
} }
}) })
} }
@ -204,8 +204,8 @@ func TestMoveRightWithCount(t *testing.T) {
sendKeys(tm, "3", "l") sendKeys(tm, "3", "l")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 3 { if m.CursorX() != 3 {
t.Errorf("cursor.x = %d, want 3", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 3", m.CursorX())
} }
}) })
@ -216,8 +216,8 @@ func TestMoveRightWithCount(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
want := len(lines[0]) want := len(lines[0])
if m.ActiveWindow().Cursor.Col != want { if m.CursorX() != want {
t.Errorf("cursor.x = %d, want %d", m.ActiveWindow().Cursor.Col, want) t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
} }
}) })
} }
@ -228,8 +228,8 @@ func TestMoveLeft(t *testing.T) {
sendKeys(tm, "h") sendKeys(tm, "h")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 2 { if m.CursorX() != 2 {
t.Errorf("cursor.x = %d, want 2", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 2", m.CursorX())
} }
}) })
@ -238,8 +238,8 @@ func TestMoveLeft(t *testing.T) {
sendKeys(tm, "h", "h", "h", "h") sendKeys(tm, "h", "h", "h", "h")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("cursor.x = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 0", m.CursorX())
} }
}) })
@ -248,8 +248,8 @@ func TestMoveLeft(t *testing.T) {
sendKeys(tm, "h", "h", "h") sendKeys(tm, "h", "h", "h")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("cursor.x = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 0", m.CursorX())
} }
}) })
} }
@ -260,8 +260,8 @@ func TestMoveLeftWithCount(t *testing.T) {
sendKeys(tm, "3", "h") sendKeys(tm, "3", "h")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 2 { if m.CursorX() != 2 {
t.Errorf("cursor.x = %d, want 2", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 2", m.CursorX())
} }
}) })
@ -270,8 +270,8 @@ func TestMoveLeftWithCount(t *testing.T) {
sendKeys(tm, "1", "0", "h") sendKeys(tm, "1", "0", "h")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("cursor.x = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("cursor.x = %d, want 0", m.CursorX())
} }
}) })
} }

View File

@ -14,8 +14,8 @@ func TestMoveToBottom(t *testing.T) {
sendKeys(tm, "G") sendKeys(tm, "G")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 5 { if m.CursorY() != 5 {
t.Errorf("CursorY() = %d, want 5", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 5", m.CursorY())
} }
}) })
@ -24,8 +24,8 @@ func TestMoveToBottom(t *testing.T) {
sendKeys(tm, "G") sendKeys(tm, "G")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 5 { if m.CursorY() != 5 {
t.Errorf("CursorY() = %d, want 5", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 5", m.CursorY())
} }
}) })
@ -34,8 +34,8 @@ func TestMoveToBottom(t *testing.T) {
sendKeys(tm, "G") sendKeys(tm, "G")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 5 { if m.CursorY() != 5 {
t.Errorf("CursorY() = %d, want 5", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 5", m.CursorY())
} }
}) })
@ -45,12 +45,12 @@ func TestMoveToBottom(t *testing.T) {
sendKeys(tm, "G") sendKeys(tm, "G")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 1 { if m.CursorY() != 1 {
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 1", m.CursorY())
} }
want := len(lines[1]) want := len(lines[1])
if m.ActiveWindow().Cursor.Col != want { if m.CursorX() != want {
t.Errorf("CursorX() = %d, want %d", m.ActiveWindow().Cursor.Col, want) t.Errorf("CursorX() = %d, want %d", m.CursorX(), want)
} }
}) })
@ -60,8 +60,8 @@ func TestMoveToBottom(t *testing.T) {
sendKeys(tm, "G") sendKeys(tm, "G")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
} }
@ -72,8 +72,8 @@ func TestMoveToTop(t *testing.T) {
sendKeys(tm, "g", "g") sendKeys(tm, "g", "g")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
@ -82,8 +82,8 @@ func TestMoveToTop(t *testing.T) {
sendKeys(tm, "g", "g") sendKeys(tm, "g", "g")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
@ -92,8 +92,8 @@ func TestMoveToTop(t *testing.T) {
sendKeys(tm, "g", "g") sendKeys(tm, "g", "g")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
@ -103,12 +103,12 @@ func TestMoveToTop(t *testing.T) {
sendKeys(tm, "g", "g") sendKeys(tm, "g", "g")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
want := len(lines[0]) want := len(lines[0])
if m.ActiveWindow().Cursor.Col != want { if m.CursorX() != want {
t.Errorf("CursorX() = %d, want %d", m.ActiveWindow().Cursor.Col, want) t.Errorf("CursorX() = %d, want %d", m.CursorX(), want)
} }
}) })
} }
@ -121,8 +121,8 @@ func TestMoveToLineStart(t *testing.T) {
sendKeys(tm, "0") sendKeys(tm, "0")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -132,8 +132,8 @@ func TestMoveToLineStart(t *testing.T) {
sendKeys(tm, "0") sendKeys(tm, "0")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -142,8 +142,8 @@ func TestMoveToLineStart(t *testing.T) {
sendKeys(tm, "0") sendKeys(tm, "0")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -153,8 +153,8 @@ func TestMoveToLineStart(t *testing.T) {
sendKeys(tm, "0") sendKeys(tm, "0")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -163,8 +163,8 @@ func TestMoveToLineStart(t *testing.T) {
sendKeys(tm, "0") sendKeys(tm, "0")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 2 { if m.CursorY() != 2 {
t.Errorf("CursorY() = %d, want 2", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 2", m.CursorY())
} }
}) })
} }
@ -177,8 +177,8 @@ func TestMoveToLineEnd(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
want := len(lines[0]) want := len(lines[0])
if m.ActiveWindow().Cursor.Col != want { if m.CursorX() != want {
t.Errorf("CursorX() = %d, want %d", m.ActiveWindow().Cursor.Col, want) t.Errorf("CursorX() = %d, want %d", m.CursorX(), want)
} }
}) })
@ -189,8 +189,8 @@ func TestMoveToLineEnd(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
want := len(lines[0]) want := len(lines[0])
if m.ActiveWindow().Cursor.Col != want { if m.CursorX() != want {
t.Errorf("CursorX() = %d, want %d", m.ActiveWindow().Cursor.Col, want) t.Errorf("CursorX() = %d, want %d", m.CursorX(), want)
} }
}) })
@ -201,8 +201,8 @@ func TestMoveToLineEnd(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
want := len(lines[0]) want := len(lines[0])
if m.ActiveWindow().Cursor.Col != want { if m.CursorX() != want {
t.Errorf("CursorX() = %d, want %d", m.ActiveWindow().Cursor.Col, want) t.Errorf("CursorX() = %d, want %d", m.CursorX(), want)
} }
}) })
@ -212,8 +212,8 @@ func TestMoveToLineEnd(t *testing.T) {
sendKeys(tm, "$") sendKeys(tm, "$")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -222,8 +222,8 @@ func TestMoveToLineEnd(t *testing.T) {
sendKeys(tm, "$") sendKeys(tm, "$")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 2 { if m.CursorY() != 2 {
t.Errorf("CursorY() = %d, want 2", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 2", m.CursorY())
} }
}) })
} }
@ -235,8 +235,8 @@ func TestMoveToLineContentStart(t *testing.T) {
sendKeys(tm, "_") sendKeys(tm, "_")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -246,8 +246,8 @@ func TestMoveToLineContentStart(t *testing.T) {
sendKeys(tm, "_") sendKeys(tm, "_")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -257,8 +257,8 @@ func TestMoveToLineContentStart(t *testing.T) {
sendKeys(tm, "_") sendKeys(tm, "_")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -268,8 +268,8 @@ func TestMoveToLineContentStart(t *testing.T) {
sendKeys(tm, "_") sendKeys(tm, "_")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -279,8 +279,8 @@ func TestMoveToLineContentStart(t *testing.T) {
sendKeys(tm, "_") sendKeys(tm, "_")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -290,8 +290,8 @@ func TestMoveToLineContentStart(t *testing.T) {
sendKeys(tm, "_") sendKeys(tm, "_")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -301,8 +301,8 @@ func TestMoveToLineContentStart(t *testing.T) {
sendKeys(tm, "_") sendKeys(tm, "_")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
} }
@ -314,8 +314,8 @@ func TestMoveToLineContentStartAlias(t *testing.T) {
sendKeys(tm, "^") sendKeys(tm, "^")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -325,8 +325,8 @@ func TestMoveToLineContentStartAlias(t *testing.T) {
sendKeys(tm, "^") sendKeys(tm, "^")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -336,8 +336,8 @@ func TestMoveToLineContentStartAlias(t *testing.T) {
sendKeys(tm, "^") sendKeys(tm, "^")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -347,8 +347,8 @@ func TestMoveToLineContentStartAlias(t *testing.T) {
sendKeys(tm, "^") sendKeys(tm, "^")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -358,8 +358,8 @@ func TestMoveToLineContentStartAlias(t *testing.T) {
sendKeys(tm, "^") sendKeys(tm, "^")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -369,8 +369,8 @@ func TestMoveToLineContentStartAlias(t *testing.T) {
sendKeys(tm, "^") sendKeys(tm, "^")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -380,8 +380,8 @@ func TestMoveToLineContentStartAlias(t *testing.T) {
sendKeys(tm, "^") sendKeys(tm, "^")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
} }
@ -400,8 +400,8 @@ func TestMoveToColumn(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// | with no count = 1| = column 1 = index 0 // | with no count = 1| = column 1 = index 0
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -411,8 +411,8 @@ func TestMoveToColumn(t *testing.T) {
sendKeys(tm, "1", "|") sendKeys(tm, "1", "|")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -423,8 +423,8 @@ func TestMoveToColumn(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Column 5 = index 4 (the 'o' in hello) // Column 5 = index 4 (the 'o' in hello)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -435,8 +435,8 @@ func TestMoveToColumn(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Column 10 = index 9 (the 'l' in world) // Column 10 = index 9 (the 'l' in world)
if m.ActiveWindow().Cursor.Col != 9 { if m.CursorX() != 9 {
t.Errorf("CursorX() = %d, want 9", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 9", m.CursorX())
} }
}) })
@ -446,8 +446,8 @@ func TestMoveToColumn(t *testing.T) {
sendKeys(tm, "|") sendKeys(tm, "|")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -457,8 +457,8 @@ func TestMoveToColumn(t *testing.T) {
sendKeys(tm, "5", "|") sendKeys(tm, "5", "|")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
} }
@ -471,8 +471,8 @@ func TestMoveToColumnClamp(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Column 20 exceeds line length, should clamp to last char (index 4) // Column 20 exceeds line length, should clamp to last char (index 4)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -483,8 +483,8 @@ func TestMoveToColumnClamp(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Should clamp to last char (index 10) // Should clamp to last char (index 10)
if m.ActiveWindow().Cursor.Col != 10 { if m.CursorX() != 10 {
t.Errorf("CursorX() = %d, want 10", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 10", m.CursorX())
} }
}) })
@ -495,8 +495,8 @@ func TestMoveToColumnClamp(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Column 6 = index 5, but line only has 5 chars (max index 4) // Column 6 = index 5, but line only has 5 chars (max index 4)
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -506,8 +506,8 @@ func TestMoveToColumnClamp(t *testing.T) {
sendKeys(tm, "|") sendKeys(tm, "|")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -517,8 +517,8 @@ func TestMoveToColumnClamp(t *testing.T) {
sendKeys(tm, "5", "|") sendKeys(tm, "5", "|")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -529,8 +529,8 @@ func TestMoveToColumnClamp(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Column 3 = index 2, but line only has 2 chars (max index 1) // Column 3 = index 2, but line only has 2 chars (max index 1)
if m.ActiveWindow().Cursor.Col != 1 { if m.CursorX() != 1 {
t.Errorf("CursorX() = %d, want 1", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 1", m.CursorX())
} }
}) })
} }
@ -542,8 +542,8 @@ func TestMoveToColumnPreservesLine(t *testing.T) {
sendKeys(tm, "|") sendKeys(tm, "|")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 1 { if m.CursorY() != 1 {
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 1", m.CursorY())
} }
}) })
@ -553,8 +553,8 @@ func TestMoveToColumnPreservesLine(t *testing.T) {
sendKeys(tm, "5", "|") sendKeys(tm, "5", "|")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 2 { if m.CursorY() != 2 {
t.Errorf("CursorY() = %d, want 2", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 2", m.CursorY())
} }
}) })
@ -564,11 +564,11 @@ func TestMoveToColumnPreservesLine(t *testing.T) {
sendKeys(tm, "|") sendKeys(tm, "|")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 1 { if m.CursorY() != 1 {
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 1", m.CursorY())
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
} }
@ -581,8 +581,8 @@ func TestMoveToColumnWithWhitespace(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Column 5 = index 4 = 'h' in " hello" // Column 5 = index 4 = 'h' in " hello"
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -593,8 +593,8 @@ func TestMoveToColumnWithWhitespace(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Column 3 = index 2 = third space // Column 3 = index 2 = third space
if m.ActiveWindow().Cursor.Col != 2 { if m.CursorX() != 2 {
t.Errorf("CursorX() = %d, want 2", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 2", m.CursorX())
} }
}) })
@ -605,8 +605,8 @@ func TestMoveToColumnWithWhitespace(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// | goes to column 1 = index 0 = the tab // | goes to column 1 = index 0 = the tab
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -617,8 +617,8 @@ func TestMoveToColumnWithWhitespace(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Column 2 = index 1 = 'h' in "\thello" // Column 2 = index 1 = 'h' in "\thello"
if m.ActiveWindow().Cursor.Col != 1 { if m.CursorX() != 1 {
t.Errorf("CursorX() = %d, want 1", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 1", m.CursorX())
} }
}) })
} }
@ -633,8 +633,8 @@ func TestMoveToColumnWithOperator(t *testing.T) {
// Deletes from column 1 to current position (exclusive), so "hello" deleted // Deletes from column 1 to current position (exclusive), so "hello" deleted
// Result depends on inclusive/exclusive behavior // Result depends on inclusive/exclusive behavior
// In Vim: d| from col 5 deletes chars 0-4, leaving " world" // In Vim: d| from col 5 deletes chars 0-4, leaving " world"
if m.ActiveBuffer().Lines[0] != " world" { if m.Line(0) != " world" {
t.Errorf("Line(0) = %q, want ' world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ' world'", m.Line(0))
} }
}) })
@ -646,8 +646,8 @@ func TestMoveToColumnWithOperator(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Deletes from cursor (0) to column 5 (index 4), so "hell" deleted // Deletes from cursor (0) to column 5 (index 4), so "hell" deleted
// Result: "o world" // Result: "o world"
if m.ActiveBuffer().Lines[0] != "o world" { if m.Line(0) != "o world" {
t.Errorf("Line(0) = %q, want 'o world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'o world'", m.Line(0))
} }
}) })
@ -691,11 +691,11 @@ func TestMoveToColumnInVisualMode(t *testing.T) {
sendKeys(tm, "v", "5", "|") sendKeys(tm, "v", "5", "|")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Col != 0 { if m.AnchorX() != 0 {
t.Errorf("AnchorX() = %d, want 0", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 0", m.AnchorX())
} }
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -705,11 +705,11 @@ func TestMoveToColumnInVisualMode(t *testing.T) {
sendKeys(tm, "v", "|") sendKeys(tm, "v", "|")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Col != 5 { if m.AnchorX() != 5 {
t.Errorf("AnchorX() = %d, want 5", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 5", m.AnchorX())
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -720,8 +720,8 @@ func TestMoveToColumnInVisualMode(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Visual selection from 0 to 4 inclusive, delete "hello" // Visual selection from 0 to 4 inclusive, delete "hello"
if m.ActiveBuffer().Lines[0] != " world" { if m.Line(0) != " world" {
t.Errorf("Line(0) = %q, want ' world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ' world'", m.Line(0))
} }
}) })
} }

File diff suppressed because it is too large Load Diff

View File

@ -22,15 +22,15 @@ func TestChangeLine(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().LineCount() != 2 { if m.LineCount() != 2 {
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 2", m.LineCount())
} }
// First line should be empty (ready for insert) // First line should be empty (ready for insert)
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "world" { if m.Line(1) != "world" {
t.Errorf("Line(1) = %q, want 'world'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'world'", m.Line(1))
} }
}) })
@ -45,14 +45,14 @@ func TestChangeLine(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "line one" { if m.Line(0) != "line one" {
t.Errorf("Line(0) = %q, want 'line one'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'line one'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "" { if m.Line(1) != "" {
t.Errorf("Line(1) = %q, want ''", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want ''", m.Line(1))
} }
if m.ActiveBuffer().Lines[2] != "line three" { if m.Line(2) != "line three" {
t.Errorf("Line(2) = %q, want 'line three'", m.ActiveBuffer().Lines[2]) t.Errorf("Line(2) = %q, want 'line three'", m.Line(2))
} }
}) })
@ -67,11 +67,11 @@ func TestChangeLine(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "hello" { if m.Line(0) != "hello" {
t.Errorf("Line(0) = %q, want 'hello'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "" { if m.Line(1) != "" {
t.Errorf("Line(1) = %q, want ''", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want ''", m.Line(1))
} }
}) })
@ -106,11 +106,11 @@ func TestChangeLine(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -123,11 +123,11 @@ func TestChangeLine(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Cursor should be at column 0 on the empty line // Cursor should be at column 0 on the empty line
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
} }
@ -145,14 +145,14 @@ func TestChangeLineWithCount(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// Should have 3 lines: empty + line three + line four // Should have 3 lines: empty + line three + line four
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "line three" { if m.Line(1) != "line three" {
t.Errorf("Line(1) = %q, want 'line three'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'line three'", m.Line(1))
} }
}) })
@ -168,17 +168,17 @@ func TestChangeLineWithCount(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// Should have 3 lines: one + empty + five // Should have 3 lines: one + empty + five
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "one" { if m.Line(0) != "one" {
t.Errorf("Line(0) = %q, want 'one'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'one'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "" { if m.Line(1) != "" {
t.Errorf("Line(1) = %q, want ''", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want ''", m.Line(1))
} }
if m.ActiveBuffer().Lines[2] != "five" { if m.Line(2) != "five" {
t.Errorf("Line(2) = %q, want 'five'", m.ActiveBuffer().Lines[2]) t.Errorf("Line(2) = %q, want 'five'", m.Line(2))
} }
}) })
@ -193,11 +193,11 @@ func TestChangeLineWithCount(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -235,8 +235,8 @@ func TestChangeWithHorizontalMotion(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "ello world" { if m.Line(0) != "ello world" {
t.Errorf("Line(0) = %q, want 'ello world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'ello world'", m.Line(0))
} }
}) })
@ -251,8 +251,8 @@ func TestChangeWithHorizontalMotion(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "llo world" { if m.Line(0) != "llo world" {
t.Errorf("Line(0) = %q, want 'llo world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'llo world'", m.Line(0))
} }
}) })
@ -267,8 +267,8 @@ func TestChangeWithHorizontalMotion(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "hell world" { if m.Line(0) != "hell world" {
t.Errorf("Line(0) = %q, want 'hell world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hell world'", m.Line(0))
} }
}) })
@ -283,8 +283,8 @@ func TestChangeWithHorizontalMotion(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "hello " { if m.Line(0) != "hello " {
t.Errorf("Line(0) = %q, want 'hello '", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello '", m.Line(0))
} }
}) })
@ -299,8 +299,8 @@ func TestChangeWithHorizontalMotion(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "world" { if m.Line(0) != "world" {
t.Errorf("Line(0) = %q, want 'world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'world'", m.Line(0))
} }
}) })
@ -317,8 +317,8 @@ func TestChangeWithHorizontalMotion(t *testing.T) {
} }
// ^ is exclusive motion, so position 8 (space) is not included // ^ is exclusive motion, so position 8 (space) is not included
// Delete positions 3-7 ("hello"), leaving " " + " world" = " world" // Delete positions 3-7 ("hello"), leaving " " + " world" = " world"
if m.ActiveBuffer().Lines[0] != " world" { if m.Line(0) != " world" {
t.Errorf("Line(0) = %q, want ' world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ' world'", m.Line(0))
} }
}) })
} }
@ -339,8 +339,8 @@ func TestChangeWithWordMotion(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "world" { if m.Line(0) != "world" {
t.Errorf("Line(0) = %q, want 'world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'world'", m.Line(0))
} }
}) })
@ -355,8 +355,8 @@ func TestChangeWithWordMotion(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "heworld" { if m.Line(0) != "heworld" {
t.Errorf("Line(0) = %q, want 'heworld'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'heworld'", m.Line(0))
} }
}) })
@ -371,8 +371,8 @@ func TestChangeWithWordMotion(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != " world" { if m.Line(0) != " world" {
t.Errorf("Line(0) = %q, want ' world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ' world'", m.Line(0))
} }
}) })
@ -387,8 +387,8 @@ func TestChangeWithWordMotion(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "world" { if m.Line(0) != "world" {
t.Errorf("Line(0) = %q, want 'world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'world'", m.Line(0))
} }
}) })
@ -403,8 +403,8 @@ func TestChangeWithWordMotion(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "three four" { if m.Line(0) != "three four" {
t.Errorf("Line(0) = %q, want 'three four'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'three four'", m.Line(0))
} }
}) })
@ -419,8 +419,8 @@ func TestChangeWithWordMotion(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "next" { if m.Line(0) != "next" {
t.Errorf("Line(0) = %q, want 'next'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'next'", m.Line(0))
} }
}) })
@ -435,8 +435,8 @@ func TestChangeWithWordMotion(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != " next" { if m.Line(0) != " next" {
t.Errorf("Line(0) = %q, want ' next'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ' next'", m.Line(0))
} }
}) })
} }
@ -458,14 +458,14 @@ func TestChangeWithVerticalMotion(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// Should have empty line + line three // Should have empty line + line three
if m.ActiveBuffer().LineCount() != 2 { if m.LineCount() != 2 {
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 2", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "line three" { if m.Line(1) != "line three" {
t.Errorf("Line(1) = %q, want 'line three'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'line three'", m.Line(1))
} }
}) })
@ -481,14 +481,14 @@ func TestChangeWithVerticalMotion(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// Should have empty line + line three // Should have empty line + line three
if m.ActiveBuffer().LineCount() != 2 { if m.LineCount() != 2 {
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 2", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "line three" { if m.Line(1) != "line three" {
t.Errorf("Line(1) = %q, want 'line three'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'line three'", m.Line(1))
} }
}) })
@ -504,14 +504,14 @@ func TestChangeWithVerticalMotion(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// Should have empty + four + five // Should have empty + four + five
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "four" { if m.Line(1) != "four" {
t.Errorf("Line(1) = %q, want 'four'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'four'", m.Line(1))
} }
}) })
} }
@ -533,11 +533,11 @@ func TestChangeWithJumpMotion(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// All lines should be replaced with one empty line // All lines should be replaced with one empty line
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -553,11 +553,11 @@ func TestChangeWithJumpMotion(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// All lines should be replaced with one empty line // All lines should be replaced with one empty line
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -573,14 +573,14 @@ func TestChangeWithJumpMotion(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// Should have line one + empty // Should have line one + empty
if m.ActiveBuffer().LineCount() != 2 { if m.LineCount() != 2 {
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 2", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "line one" { if m.Line(0) != "line one" {
t.Errorf("Line(0) = %q, want 'line one'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'line one'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "" { if m.Line(1) != "" {
t.Errorf("Line(1) = %q, want ''", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want ''", m.Line(1))
} }
}) })
} }
@ -601,8 +601,8 @@ func TestChangeToEndOfLine(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "hello " { if m.Line(0) != "hello " {
t.Errorf("Line(0) = %q, want 'hello '", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello '", m.Line(0))
} }
}) })
@ -617,8 +617,8 @@ func TestChangeToEndOfLine(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -634,8 +634,8 @@ func TestChangeToEndOfLine(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// Should delete last char // Should delete last char
if m.ActiveBuffer().Lines[0] != "hell" { if m.Line(0) != "hell" {
t.Errorf("Line(0) = %q, want 'hell'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hell'", m.Line(0))
} }
}) })
@ -673,8 +673,8 @@ func TestSubstituteCharacter(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "ello" { if m.Line(0) != "ello" {
t.Errorf("Line(0) = %q, want 'ello'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'ello'", m.Line(0))
} }
}) })
@ -689,8 +689,8 @@ func TestSubstituteCharacter(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "llo" { if m.Line(0) != "llo" {
t.Errorf("Line(0) = %q, want 'llo'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'llo'", m.Line(0))
} }
}) })
@ -705,8 +705,8 @@ func TestSubstituteCharacter(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "hell" { if m.Line(0) != "hell" {
t.Errorf("Line(0) = %q, want 'hell'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hell'", m.Line(0))
} }
}) })
} }
@ -727,8 +727,8 @@ func TestSubstituteLine(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -740,14 +740,14 @@ func TestSubstituteLine(t *testing.T) {
sendKeys(tm, "S") sendKeys(tm, "S")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "line one" { if m.Line(0) != "line one" {
t.Errorf("Line(0) = %q, want 'line one'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'line one'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "" { if m.Line(1) != "" {
t.Errorf("Line(1) = %q, want ''", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want ''", m.Line(1))
} }
if m.ActiveBuffer().Lines[2] != "line three" { if m.Line(2) != "line three" {
t.Errorf("Line(2) = %q, want 'line three'", m.ActiveBuffer().Lines[2]) t.Errorf("Line(2) = %q, want 'line three'", m.Line(2))
} }
}) })
@ -763,14 +763,14 @@ func TestSubstituteLine(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// Should have empty + three + four // Should have empty + three + four
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "three" { if m.Line(1) != "three" {
t.Errorf("Line(1) = %q, want 'three'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'three'", m.Line(1))
} }
}) })
} }
@ -791,8 +791,8 @@ func TestVisualModeChange(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "ello world" { if m.Line(0) != "ello world" {
t.Errorf("Line(0) = %q, want 'ello world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'ello world'", m.Line(0))
} }
}) })
@ -807,8 +807,8 @@ func TestVisualModeChange(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != " world" { if m.Line(0) != " world" {
t.Errorf("Line(0) = %q, want ' world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ' world'", m.Line(0))
} }
}) })
@ -823,8 +823,8 @@ func TestVisualModeChange(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "hello " { if m.Line(0) != "hello " {
t.Errorf("Line(0) = %q, want 'hello '", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello '", m.Line(0))
} }
}) })
@ -840,8 +840,8 @@ func TestVisualModeChange(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// Should merge lines with selection removed // Should merge lines with selection removed
if m.ActiveBuffer().LineCount() != 2 { if m.LineCount() != 2 {
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 2", m.LineCount())
} }
}) })
@ -875,11 +875,11 @@ func TestVisualLineModeChange(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[1] != "" { if m.Line(1) != "" {
t.Errorf("Line(1) = %q, want ''", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want ''", m.Line(1))
} }
}) })
@ -895,17 +895,17 @@ func TestVisualLineModeChange(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// Should have: line one, empty, line four // Should have: line one, empty, line four
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "line one" { if m.Line(0) != "line one" {
t.Errorf("Line(0) = %q, want 'line one'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'line one'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "" { if m.Line(1) != "" {
t.Errorf("Line(1) = %q, want ''", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want ''", m.Line(1))
} }
if m.ActiveBuffer().Lines[2] != "line four" { if m.Line(2) != "line four" {
t.Errorf("Line(2) = %q, want 'line four'", m.ActiveBuffer().Lines[2]) t.Errorf("Line(2) = %q, want 'line four'", m.Line(2))
} }
}) })
@ -946,8 +946,8 @@ func TestChangeEdgeCases(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -963,8 +963,8 @@ func TestChangeEdgeCases(t *testing.T) {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
// cw on last word should change to end of line // cw on last word should change to end of line
if m.ActiveBuffer().Lines[0] != "hello " { if m.Line(0) != "hello " {
t.Errorf("Line(0) = %q, want 'hello '", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello '", m.Line(0))
} }
}) })
@ -979,8 +979,8 @@ func TestChangeEdgeCases(t *testing.T) {
if m.Mode() != action.InsertMode { if m.Mode() != action.InsertMode {
t.Errorf("Mode() = %v, want InsertMode", m.Mode()) t.Errorf("Mode() = %v, want InsertMode", m.Mode())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -26,8 +26,8 @@ func TestScrollBasic(t *testing.T) {
sendKeys(tm, "G") // go to bottom sendKeys(tm, "G") // go to bottom
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY != 0 { if m.ScrollY() != 0 {
t.Errorf("ScrollY() = %d, want 0", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 0", m.ScrollY())
} }
}) })
@ -45,14 +45,14 @@ func TestScrollBasic(t *testing.T) {
} }
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 15 { if m.CursorY() != 15 {
t.Errorf("CursorY() = %d, want 15", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 15", m.CursorY())
} }
// With scrollOff=8, viewport=19, cursor at 15 means: // With scrollOff=8, viewport=19, cursor at 15 means:
// cursor should be at position 10 from top (19-1-8=10) // cursor should be at position 10 from top (19-1-8=10)
// so scrollY = 15 - 10 = 5 // so scrollY = 15 - 10 = 5
if m.ActiveWindow().ScrollY < 1 { if m.ScrollY() < 1 {
t.Errorf("ScrollY() = %d, want > 0 (should have scrolled)", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want > 0 (should have scrolled)", m.ScrollY())
} }
}) })
@ -68,13 +68,13 @@ func TestScrollBasic(t *testing.T) {
} }
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 5 { if m.CursorY() != 5 {
t.Errorf("CursorY() = %d, want 5", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 5", m.CursorY())
} }
// Cursor at line 5 with scrollOff=8 means scrollY should be 0 // Cursor at line 5 with scrollOff=8 means scrollY should be 0
// (can't scroll negative, and cursor is within safe zone from top) // (can't scroll negative, and cursor is within safe zone from top)
if m.ActiveWindow().ScrollY != 0 { if m.ScrollY() != 0 {
t.Errorf("ScrollY() = %d, want 0", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 0", m.ScrollY())
} }
}) })
@ -84,13 +84,13 @@ func TestScrollBasic(t *testing.T) {
sendKeys(tm, "G") sendKeys(tm, "G")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 99 { if m.CursorY() != 99 {
t.Errorf("CursorY() = %d, want 99", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 99", m.CursorY())
} }
// With 100 lines and viewport 18 (height - 2 for status + command bar), // With 100 lines and viewport 18 (height - 2 for status + command bar),
// max scrollY = 100 - 18 = 82 // max scrollY = 100 - 18 = 82
if m.ActiveWindow().ScrollY != 82 { if m.ScrollY() != 82 {
t.Errorf("ScrollY() = %d, want 82", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 82", m.ScrollY())
} }
}) })
@ -100,11 +100,11 @@ func TestScrollBasic(t *testing.T) {
sendKeys(tm, "g", "g") sendKeys(tm, "g", "g")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
if m.ActiveWindow().ScrollY != 0 { if m.ScrollY() != 0 {
t.Errorf("ScrollY() = %d, want 0", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 0", m.ScrollY())
} }
}) })
} }
@ -120,8 +120,8 @@ func TestScrollEdgeCases(t *testing.T) {
} }
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY < 0 { if m.ScrollY() < 0 {
t.Errorf("ScrollY() = %d, want >= 0", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want >= 0", m.ScrollY())
} }
}) })
@ -133,8 +133,8 @@ func TestScrollEdgeCases(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// 30 lines, viewport 18 (height - 2) -> maxScroll = 30 - 18 = 12 // 30 lines, viewport 18 (height - 2) -> maxScroll = 30 - 18 = 12
maxScroll := 30 - 18 maxScroll := 30 - 18
if m.ActiveWindow().ScrollY > maxScroll { if m.ScrollY() > maxScroll {
t.Errorf("ScrollY() = %d, want <= %d", m.ActiveWindow().ScrollY, maxScroll) t.Errorf("ScrollY() = %d, want <= %d", m.ScrollY(), maxScroll)
} }
}) })
@ -148,9 +148,9 @@ func TestScrollEdgeCases(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Cursor should still be visible // Cursor should still be visible
viewportHeight := 19 viewportHeight := 19
if m.ActiveWindow().Cursor.Line < m.ActiveWindow().ScrollY || m.ActiveWindow().Cursor.Line >= m.ActiveWindow().ScrollY+viewportHeight { if m.CursorY() < m.ScrollY() || m.CursorY() >= m.ScrollY()+viewportHeight {
t.Errorf("Cursor at %d not visible in viewport [%d, %d)", t.Errorf("Cursor at %d not visible in viewport [%d, %d)",
m.ActiveWindow().Cursor.Line, m.ActiveWindow().ScrollY, m.ActiveWindow().ScrollY+viewportHeight) m.CursorY(), m.ScrollY(), m.ScrollY()+viewportHeight)
} }
}) })
} }
@ -166,11 +166,11 @@ func TestHalfPageScrollDown(t *testing.T) {
sendKeys(tm, "ctrl+d") sendKeys(tm, "ctrl+d")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY != 14 { if m.ScrollY() != 14 {
t.Errorf("ScrollY() = %d, want 14", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 14", m.ScrollY())
} }
if m.ActiveWindow().Cursor.Line != 29 { if m.CursorY() != 29 {
t.Errorf("CursorY() = %d, want 29", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 29", m.CursorY())
} }
}) })
@ -181,7 +181,7 @@ func TestHalfPageScrollDown(t *testing.T) {
sendKeys(tm, "ctrl+d") sendKeys(tm, "ctrl+d")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
relY := m.ActiveWindow().Cursor.Line - m.ActiveWindow().ScrollY relY := m.CursorY() - m.ScrollY()
if relY != 15 { if relY != 15 {
t.Errorf("relative position = %d, want 15", relY) t.Errorf("relative position = %d, want 15", relY)
} }
@ -195,11 +195,11 @@ func TestHalfPageScrollDown(t *testing.T) {
sendKeys(tm, "ctrl+d") sendKeys(tm, "ctrl+d")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY != 14 { if m.ScrollY() != 14 {
t.Errorf("ScrollY() = %d, want 14", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 14", m.ScrollY())
} }
if m.ActiveWindow().Cursor.Line != 22 { if m.CursorY() != 22 {
t.Errorf("CursorY() = %d, want 22", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 22", m.CursorY())
} }
}) })
@ -213,11 +213,11 @@ func TestHalfPageScrollDown(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
maxScroll := 40 - 28 maxScroll := 40 - 28
if m.ActiveWindow().ScrollY > maxScroll { if m.ScrollY() > maxScroll {
t.Errorf("ScrollY() = %d, want <= %d", m.ActiveWindow().ScrollY, maxScroll) t.Errorf("ScrollY() = %d, want <= %d", m.ScrollY(), maxScroll)
} }
if m.ActiveWindow().Cursor.Line != 31 { if m.CursorY() != 31 {
t.Errorf("CursorY() = %d, want 31", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 31", m.CursorY())
} }
}) })
@ -229,11 +229,11 @@ func TestHalfPageScrollDown(t *testing.T) {
sendKeys(tm, "ctrl+d") sendKeys(tm, "ctrl+d")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY != 0 { if m.ScrollY() != 0 {
t.Errorf("ScrollY() = %d, want 0", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 0", m.ScrollY())
} }
if m.ActiveWindow().Cursor.Line != 8 { if m.CursorY() != 8 {
t.Errorf("CursorY() = %d, want 8", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 8", m.CursorY())
} }
}) })
@ -253,11 +253,11 @@ func TestHalfPageScrollDown(t *testing.T) {
sendKeys(tm, "ctrl+d") sendKeys(tm, "ctrl+d")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 22 { if m.CursorY() != 22 {
t.Errorf("CursorY() = %d, want 22", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 22", m.CursorY())
} }
if m.ActiveWindow().Cursor.Col > len(m.ActiveBuffer().Lines[m.ActiveWindow().Cursor.Line]) { if m.CursorX() > len(m.Line(m.CursorY())) {
t.Errorf("CursorX() = %d exceeds line length %d", m.ActiveWindow().Cursor.Col, len(m.ActiveBuffer().Lines[m.ActiveWindow().Cursor.Line])) t.Errorf("CursorX() = %d exceeds line length %d", m.CursorX(), len(m.Line(m.CursorY())))
} }
}) })
@ -270,11 +270,11 @@ func TestHalfPageScrollDown(t *testing.T) {
sendKeys(tm, "ctrl+d", "ctrl+d") sendKeys(tm, "ctrl+d", "ctrl+d")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY != 28 { if m.ScrollY() != 28 {
t.Errorf("ScrollY() = %d, want 28", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 28", m.ScrollY())
} }
if m.ActiveWindow().Cursor.Line != 43 { if m.CursorY() != 43 {
t.Errorf("CursorY() = %d, want 43", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 43", m.CursorY())
} }
}) })
} }
@ -288,11 +288,11 @@ func TestHalfPageScrollUp(t *testing.T) {
sendKeys(tm, "ctrl+u") sendKeys(tm, "ctrl+u")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY != 17 { if m.ScrollY() != 17 {
t.Errorf("ScrollY() = %d, want 17", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 17", m.ScrollY())
} }
if m.ActiveWindow().Cursor.Line != 36 { if m.CursorY() != 36 {
t.Errorf("CursorY() = %d, want 36", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 36", m.CursorY())
} }
}) })
@ -303,7 +303,7 @@ func TestHalfPageScrollUp(t *testing.T) {
sendKeys(tm, "ctrl+u") sendKeys(tm, "ctrl+u")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
relY := m.ActiveWindow().Cursor.Line - m.ActiveWindow().ScrollY relY := m.CursorY() - m.ScrollY()
if relY != 19 { if relY != 19 {
t.Errorf("relative position = %d, want 19", relY) t.Errorf("relative position = %d, want 19", relY)
} }
@ -317,14 +317,14 @@ func TestHalfPageScrollUp(t *testing.T) {
sendKeys(tm, "ctrl+u") sendKeys(tm, "ctrl+u")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY < 0 { if m.ScrollY() < 0 {
t.Errorf("ScrollY() = %d, want >= 0", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want >= 0", m.ScrollY())
} }
if m.ActiveWindow().ScrollY != 0 { if m.ScrollY() != 0 {
t.Errorf("ScrollY() = %d, want 0", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 0", m.ScrollY())
} }
if m.ActiveWindow().Cursor.Line != 10 { if m.CursorY() != 10 {
t.Errorf("CursorY() = %d, want 10", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 10", m.CursorY())
} }
}) })
@ -336,11 +336,11 @@ func TestHalfPageScrollUp(t *testing.T) {
sendKeys(tm, "ctrl+u") sendKeys(tm, "ctrl+u")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY != 0 { if m.ScrollY() != 0 {
t.Errorf("ScrollY() = %d, want 0", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 0", m.ScrollY())
} }
if m.ActiveWindow().Cursor.Line != 8 { if m.CursorY() != 8 {
t.Errorf("CursorY() = %d, want 8", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 8", m.CursorY())
} }
}) })
@ -353,11 +353,11 @@ func TestHalfPageScrollUp(t *testing.T) {
sendKeys(tm, "ctrl+u", "ctrl+u") sendKeys(tm, "ctrl+u", "ctrl+u")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY != 33 { if m.ScrollY() != 33 {
t.Errorf("ScrollY() = %d, want 33", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 33", m.ScrollY())
} }
if m.ActiveWindow().Cursor.Line != 52 { if m.CursorY() != 52 {
t.Errorf("CursorY() = %d, want 52", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 52", m.CursorY())
} }
}) })
} }
@ -372,11 +372,11 @@ func TestHalfPageScrollRoundTrip(t *testing.T) {
sendKeys(tm, "ctrl+d", "ctrl+u") sendKeys(tm, "ctrl+d", "ctrl+u")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY != 0 { if m.ScrollY() != 0 {
t.Errorf("ScrollY() = %d, want 0", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 0", m.ScrollY())
} }
if m.ActiveWindow().Cursor.Line != 15 { if m.CursorY() != 15 {
t.Errorf("CursorY() = %d, want 15", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 15", m.CursorY())
} }
}) })
@ -389,11 +389,11 @@ func TestHalfPageScrollRoundTrip(t *testing.T) {
sendKeys(tm, "ctrl+u", "ctrl+d") sendKeys(tm, "ctrl+u", "ctrl+d")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY != 31 { if m.ScrollY() != 31 {
t.Errorf("ScrollY() = %d, want 31", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 31", m.ScrollY())
} }
if m.ActiveWindow().Cursor.Line != 50 { if m.CursorY() != 50 {
t.Errorf("CursorY() = %d, want 50", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 50", m.CursorY())
} }
}) })
@ -403,11 +403,11 @@ func TestHalfPageScrollRoundTrip(t *testing.T) {
sendKeys(tm, "ctrl+d", "ctrl+u", "ctrl+d", "ctrl+u") sendKeys(tm, "ctrl+d", "ctrl+u", "ctrl+d", "ctrl+u")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().ScrollY != 0 { if m.ScrollY() != 0 {
t.Errorf("ScrollY() = %d, want 0 after 2 round trips", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want 0 after 2 round trips", m.ScrollY())
} }
if m.ActiveWindow().Cursor.Line != 15 { if m.CursorY() != 15 {
t.Errorf("CursorY() = %d, want 15 after 2 round trips", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 15 after 2 round trips", m.CursorY())
} }
}) })
} }
@ -420,12 +420,12 @@ func TestScrollWithCount(t *testing.T) {
sendKeys(tm, "1", "0", "j") // move down 10 lines sendKeys(tm, "1", "0", "j") // move down 10 lines
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 15 { if m.CursorY() != 15 {
t.Errorf("CursorY() = %d, want 15", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 15", m.CursorY())
} }
// Should have scrolled since we moved past the safe zone // Should have scrolled since we moved past the safe zone
if m.ActiveWindow().ScrollY == 0 { if m.ScrollY() == 0 {
t.Errorf("ScrollY() = %d, want > 0", m.ActiveWindow().ScrollY) t.Errorf("ScrollY() = %d, want > 0", m.ScrollY())
} }
}) })
@ -436,8 +436,8 @@ func TestScrollWithCount(t *testing.T) {
sendKeys(tm, "1", "5", "k") // move up 15 lines sendKeys(tm, "1", "5", "k") // move up 15 lines
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 10 { if m.CursorY() != 10 {
t.Errorf("CursorY() = %d, want 10", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 10", m.CursorY())
} }
}) })
} }

View File

@ -20,11 +20,11 @@ func TestVisualModeSelectionState(t *testing.T) {
if m.Mode() != action.VisualMode { if m.Mode() != action.VisualMode {
t.Errorf("Mode() = %v, want VisualMode", m.Mode()) t.Errorf("Mode() = %v, want VisualMode", m.Mode())
} }
if m.ActiveWindow().Anchor.Col != 3 { if m.AnchorX() != 3 {
t.Errorf("AnchorX() = %d, want 3", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 3", m.AnchorX())
} }
if m.ActiveWindow().Anchor.Line != 0 { if m.AnchorY() != 0 {
t.Errorf("AnchorY() = %d, want 0", m.ActiveWindow().Anchor.Line) t.Errorf("AnchorY() = %d, want 0", m.AnchorY())
} }
}) })
@ -34,11 +34,11 @@ func TestVisualModeSelectionState(t *testing.T) {
sendKeys(tm, "v", "l") sendKeys(tm, "v", "l")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Col != 0 { if m.AnchorX() != 0 {
t.Errorf("AnchorX() = %d, want 0", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 0", m.AnchorX())
} }
if m.ActiveWindow().Cursor.Col != 1 { if m.CursorX() != 1 {
t.Errorf("CursorX() = %d, want 1", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 1", m.CursorX())
} }
}) })
@ -48,11 +48,11 @@ func TestVisualModeSelectionState(t *testing.T) {
sendKeys(tm, "v", "h") sendKeys(tm, "v", "h")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Col != 3 { if m.AnchorX() != 3 {
t.Errorf("AnchorX() = %d, want 3", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 3", m.AnchorX())
} }
if m.ActiveWindow().Cursor.Col != 2 { if m.CursorX() != 2 {
t.Errorf("CursorX() = %d, want 2", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 2", m.CursorX())
} }
}) })
@ -62,14 +62,14 @@ func TestVisualModeSelectionState(t *testing.T) {
sendKeys(tm, "v", "j") sendKeys(tm, "v", "j")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Col != 2 { if m.AnchorX() != 2 {
t.Errorf("AnchorX() = %d, want 2", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 2", m.AnchorX())
} }
if m.ActiveWindow().Anchor.Line != 0 { if m.AnchorY() != 0 {
t.Errorf("AnchorY() = %d, want 0", m.ActiveWindow().Anchor.Line) t.Errorf("AnchorY() = %d, want 0", m.AnchorY())
} }
if m.ActiveWindow().Cursor.Line != 1 { if m.CursorY() != 1 {
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 1", m.CursorY())
} }
}) })
@ -82,8 +82,8 @@ func TestVisualModeSelectionState(t *testing.T) {
if m.Mode() != action.VisualLineMode { if m.Mode() != action.VisualLineMode {
t.Errorf("Mode() = %v, want VisualLineMode", m.Mode()) t.Errorf("Mode() = %v, want VisualLineMode", m.Mode())
} }
if m.ActiveWindow().Anchor.Line != 1 { if m.AnchorY() != 1 {
t.Errorf("AnchorY() = %d, want 1", m.ActiveWindow().Anchor.Line) t.Errorf("AnchorY() = %d, want 1", m.AnchorY())
} }
}) })
@ -96,11 +96,11 @@ func TestVisualModeSelectionState(t *testing.T) {
if m.Mode() != action.VisualBlockMode { if m.Mode() != action.VisualBlockMode {
t.Errorf("Mode() = %v, want VisualBlockMode", m.Mode()) t.Errorf("Mode() = %v, want VisualBlockMode", m.Mode())
} }
if m.ActiveWindow().Anchor.Col != 2 { if m.AnchorX() != 2 {
t.Errorf("AnchorX() = %d, want 2", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 2", m.AnchorX())
} }
if m.ActiveWindow().Anchor.Line != 1 { if m.AnchorY() != 1 {
t.Errorf("AnchorY() = %d, want 1", m.ActiveWindow().Anchor.Line) t.Errorf("AnchorY() = %d, want 1", m.AnchorY())
} }
}) })
@ -125,11 +125,11 @@ func TestVisualModeDelete(t *testing.T) {
sendKeys(tm, "v", "d") sendKeys(tm, "v", "d")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "ello" { if m.Line(0) != "ello" {
t.Errorf("Line(0) = %q, want \"ello\"", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want \"ello\"", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -139,11 +139,11 @@ func TestVisualModeDelete(t *testing.T) {
sendKeys(tm, "v", "l", "l", "l", "d") sendKeys(tm, "v", "l", "l", "l", "d")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "o world" { if m.Line(0) != "o world" {
t.Errorf("Line(0) = %q, want \"o world\"", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want \"o world\"", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -154,11 +154,11 @@ func TestVisualModeDelete(t *testing.T) {
// anchor=3, cursor=1 → normalized start=1, end=3 → delete "ell" → "ho" // anchor=3, cursor=1 → normalized start=1, end=3 → delete "ell" → "ho"
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "ho" { if m.Line(0) != "ho" {
t.Errorf("Line(0) = %q, want \"ho\"", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want \"ho\"", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 1 { if m.CursorX() != 1 {
t.Errorf("CursorX() = %d, want 1", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 1", m.CursorX())
} }
}) })
@ -169,17 +169,17 @@ func TestVisualModeDelete(t *testing.T) {
// start=(2,0), end=(2,1) → prefix="he", suffix="ld" → "held" // start=(2,0), end=(2,1) → prefix="he", suffix="ld" → "held"
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "held" { if m.Line(0) != "held" {
t.Errorf("Line(0) = %q, want \"held\"", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want \"held\"", m.Line(0))
} }
if m.ActiveWindow().Cursor.Col != 2 { if m.CursorX() != 2 {
t.Errorf("CursorX() = %d, want 2", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 2", m.CursorX())
} }
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
@ -189,14 +189,14 @@ func TestVisualModeDelete(t *testing.T) {
sendKeys(tm, "V", "d") sendKeys(tm, "V", "d")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "world" { if m.Line(0) != "world" {
t.Errorf("Line(0) = %q, want \"world\"", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want \"world\"", m.Line(0))
} }
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
@ -206,14 +206,14 @@ func TestVisualModeDelete(t *testing.T) {
sendKeys(tm, "V", "j", "d") sendKeys(tm, "V", "j", "d")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "testing" { if m.Line(0) != "testing" {
t.Errorf("Line(0) = %q, want \"testing\"", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want \"testing\"", m.Line(0))
} }
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
@ -224,11 +224,11 @@ func TestVisualModeDelete(t *testing.T) {
// anchor=line2, cursor=line1 → normalized start=line1, end=line2 → delete both // anchor=line2, cursor=line1 → normalized start=line1, end=line2 → delete both
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "hello" { if m.Line(0) != "hello" {
t.Errorf("Line(0) = %q, want \"hello\"", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want \"hello\"", m.Line(0))
} }
}) })
@ -241,17 +241,17 @@ func TestVisualModeDelete(t *testing.T) {
// "hello"[:0]+"hello"[2:] = "llo" // "hello"[:0]+"hello"[2:] = "llo"
// "world"[:0]+"world"[2:] = "rld" // "world"[:0]+"world"[2:] = "rld"
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "llo" { if m.Line(0) != "llo" {
t.Errorf("Line(0) = %q, want \"llo\"", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want \"llo\"", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "rld" { if m.Line(1) != "rld" {
t.Errorf("Line(1) = %q, want \"rld\"", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want \"rld\"", m.Line(1))
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
@ -264,11 +264,11 @@ func TestVisualModeDelete(t *testing.T) {
// "hello"[:1]+"hello"[4:] = "h"+"o" = "ho" // "hello"[:1]+"hello"[4:] = "h"+"o" = "ho"
// "world"[:1]+"world"[4:] = "w"+"d" = "wd" // "world"[:1]+"world"[4:] = "w"+"d" = "wd"
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "ho" { if m.Line(0) != "ho" {
t.Errorf("Line(0) = %q, want \"ho\"", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want \"ho\"", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "wd" { if m.Line(1) != "wd" {
t.Errorf("Line(1) = %q, want \"wd\"", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want \"wd\"", m.Line(1))
} }
}) })
} }
@ -284,12 +284,12 @@ func TestVisualModeWordMotions(t *testing.T) {
sendKeys(tm, "v", "w") sendKeys(tm, "v", "w")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Col != 0 { if m.AnchorX() != 0 {
t.Errorf("AnchorX() = %d, want 0", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 0", m.AnchorX())
} }
// w moves to start of "world" at col 6 // w moves to start of "world" at col 6
if m.ActiveWindow().Cursor.Col != 6 { if m.CursorX() != 6 {
t.Errorf("CursorX() = %d, want 6", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 6", m.CursorX())
} }
}) })
@ -302,8 +302,8 @@ func TestVisualModeWordMotions(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Deletes from 0 to 6 inclusive = "hello w", leaves "orld" // Deletes from 0 to 6 inclusive = "hello w", leaves "orld"
if m.ActiveBuffer().Lines[0] != "orld" { if m.Line(0) != "orld" {
t.Errorf("Line(0) = %q, want 'orld'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'orld'", m.Line(0))
} }
}) })
@ -315,12 +315,12 @@ func TestVisualModeWordMotions(t *testing.T) {
sendKeys(tm, "v", "e") sendKeys(tm, "v", "e")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Col != 0 { if m.AnchorX() != 0 {
t.Errorf("AnchorX() = %d, want 0", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 0", m.AnchorX())
} }
// e moves to end of "hello" at col 4 // e moves to end of "hello" at col 4
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -333,8 +333,8 @@ func TestVisualModeWordMotions(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Deletes "hello" // Deletes "hello"
if m.ActiveBuffer().Lines[0] != " world" { if m.Line(0) != " world" {
t.Errorf("Line(0) = %q, want ' world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ' world'", m.Line(0))
} }
}) })
@ -346,12 +346,12 @@ func TestVisualModeWordMotions(t *testing.T) {
sendKeys(tm, "v", "b") sendKeys(tm, "v", "b")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Col != 6 { if m.AnchorX() != 6 {
t.Errorf("AnchorX() = %d, want 6", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 6", m.AnchorX())
} }
// b moves to start of "hello" at col 0 // b moves to start of "hello" at col 0
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -364,8 +364,8 @@ func TestVisualModeWordMotions(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Deletes from "h" (0) to "w" (6) inclusive // Deletes from "h" (0) to "w" (6) inclusive
if m.ActiveBuffer().Lines[0] != "orld" { if m.Line(0) != "orld" {
t.Errorf("Line(0) = %q, want 'orld'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'orld'", m.Line(0))
} }
}) })
@ -378,8 +378,8 @@ func TestVisualModeWordMotions(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// 2w moves past "one " and "two " to start of "three" at col 8 // 2w moves past "one " and "two " to start of "three" at col 8
if m.ActiveWindow().Cursor.Col != 8 { if m.CursorX() != 8 {
t.Errorf("CursorX() = %d, want 8", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 8", m.CursorX())
} }
}) })
} }
@ -395,12 +395,12 @@ func TestVisualModeJumpMotions(t *testing.T) {
sendKeys(tm, "v", "$") sendKeys(tm, "v", "$")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Col != 0 { if m.AnchorX() != 0 {
t.Errorf("AnchorX() = %d, want 0", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 0", m.AnchorX())
} }
// $ moves past end of line // $ moves past end of line
if m.ActiveWindow().Cursor.Col != 11 { if m.CursorX() != 11 {
t.Errorf("CursorX() = %d, want 11", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 11", m.CursorX())
} }
}) })
@ -412,8 +412,8 @@ func TestVisualModeJumpMotions(t *testing.T) {
sendKeys(tm, "v", "$", "d") sendKeys(tm, "v", "$", "d")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello " { if m.Line(0) != "hello " {
t.Errorf("Line(0) = %q, want 'hello '", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello '", m.Line(0))
} }
}) })
@ -425,11 +425,11 @@ func TestVisualModeJumpMotions(t *testing.T) {
sendKeys(tm, "v", "0") sendKeys(tm, "v", "0")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Col != 6 { if m.AnchorX() != 6 {
t.Errorf("AnchorX() = %d, want 6", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 6", m.AnchorX())
} }
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -442,8 +442,8 @@ func TestVisualModeJumpMotions(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// Deletes from 'h' (0) to 'w' (6) inclusive // Deletes from 'h' (0) to 'w' (6) inclusive
if m.ActiveBuffer().Lines[0] != "orld" { if m.Line(0) != "orld" {
t.Errorf("Line(0) = %q, want 'orld'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'orld'", m.Line(0))
} }
}) })
@ -455,12 +455,12 @@ func TestVisualModeJumpMotions(t *testing.T) {
sendKeys(tm, "v", "_") sendKeys(tm, "v", "_")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Col != 10 { if m.AnchorX() != 10 {
t.Errorf("AnchorX() = %d, want 10", m.ActiveWindow().Anchor.Col) t.Errorf("AnchorX() = %d, want 10", m.AnchorX())
} }
// _ moves to first non-ws at col 4 // _ moves to first non-ws at col 4
if m.ActiveWindow().Cursor.Col != 4 { if m.CursorX() != 4 {
t.Errorf("CursorX() = %d, want 4", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 4", m.CursorX())
} }
}) })
@ -472,11 +472,11 @@ func TestVisualModeJumpMotions(t *testing.T) {
sendKeys(tm, "v", "G") sendKeys(tm, "v", "G")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Line != 0 { if m.AnchorY() != 0 {
t.Errorf("AnchorY() = %d, want 0", m.ActiveWindow().Anchor.Line) t.Errorf("AnchorY() = %d, want 0", m.AnchorY())
} }
if m.ActiveWindow().Cursor.Line != 2 { if m.CursorY() != 2 {
t.Errorf("CursorY() = %d, want 2", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 2", m.CursorY())
} }
}) })
@ -490,11 +490,11 @@ func TestVisualModeJumpMotions(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// G goes to last line at same col, deletes from (0,3) to (2,3) // G goes to last line at same col, deletes from (0,3) to (2,3)
// Keeps "lin" from first line + "e 3" from last line = "lin 3" // Keeps "lin" from first line + "e 3" from last line = "lin 3"
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "lin 3" { if m.Line(0) != "lin 3" {
t.Errorf("Line(0) = %q, want 'lin 3'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'lin 3'", m.Line(0))
} }
}) })
@ -506,11 +506,11 @@ func TestVisualModeJumpMotions(t *testing.T) {
sendKeys(tm, "v", "g", "g") sendKeys(tm, "v", "g", "g")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Line != 2 { if m.AnchorY() != 2 {
t.Errorf("AnchorY() = %d, want 2", m.ActiveWindow().Anchor.Line) t.Errorf("AnchorY() = %d, want 2", m.AnchorY())
} }
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
@ -524,11 +524,11 @@ func TestVisualModeJumpMotions(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// gg goes to first line at same col, deletes selection // gg goes to first line at same col, deletes selection
// Keeps "lin" from first line + " 3" from last line = "lin 3" // Keeps "lin" from first line + " 3" from last line = "lin 3"
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "lin 3" { if m.Line(0) != "lin 3" {
t.Errorf("Line(0) = %q, want 'lin 3'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'lin 3'", m.Line(0))
} }
}) })
} }
@ -544,11 +544,11 @@ func TestVisualLineModeJumpMotions(t *testing.T) {
sendKeys(tm, "V", "G") sendKeys(tm, "V", "G")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Line != 0 { if m.AnchorY() != 0 {
t.Errorf("AnchorY() = %d, want 0", m.ActiveWindow().Anchor.Line) t.Errorf("AnchorY() = %d, want 0", m.AnchorY())
} }
if m.ActiveWindow().Cursor.Line != 2 { if m.CursorY() != 2 {
t.Errorf("CursorY() = %d, want 2", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 2", m.CursorY())
} }
}) })
@ -561,11 +561,11 @@ func TestVisualLineModeJumpMotions(t *testing.T) {
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
// All lines deleted, should have empty buffer // All lines deleted, should have empty buffer
if m.ActiveBuffer().LineCount() != 1 { if m.LineCount() != 1 {
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 1", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "" { if m.Line(0) != "" {
t.Errorf("Line(0) = %q, want ''", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want ''", m.Line(0))
} }
}) })
@ -577,11 +577,11 @@ func TestVisualLineModeJumpMotions(t *testing.T) {
sendKeys(tm, "V", "g", "g") sendKeys(tm, "V", "g", "g")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Anchor.Line != 2 { if m.AnchorY() != 2 {
t.Errorf("AnchorY() = %d, want 2", m.ActiveWindow().Anchor.Line) t.Errorf("AnchorY() = %d, want 2", m.AnchorY())
} }
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
} }

View File

@ -43,17 +43,17 @@ func TestYankLineBasic(t *testing.T) {
sendKeys(tm, "y", "y") sendKeys(tm, "y", "y")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "line 1" { if m.Line(0) != "line 1" {
t.Errorf("Line(0) = %q, want 'line 1'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'line 1'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "line 2" { if m.Line(1) != "line 2" {
t.Errorf("Line(1) = %q, want 'line 2'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'line 2'", m.Line(1))
} }
if m.ActiveBuffer().Lines[2] != "line 3" { if m.Line(2) != "line 3" {
t.Errorf("Line(2) = %q, want 'line 3'", m.ActiveBuffer().Lines[2]) t.Errorf("Line(2) = %q, want 'line 3'", m.Line(2))
} }
}) })
@ -65,11 +65,11 @@ func TestYankLineBasic(t *testing.T) {
sendKeys(tm, "y", "y") sendKeys(tm, "y", "y")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 1 { if m.CursorY() != 1 {
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 1", m.CursorY())
} }
if m.ActiveWindow().Cursor.Col != 3 { if m.CursorX() != 3 {
t.Errorf("CursorX() = %d, want 3", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 3", m.CursorX())
} }
}) })
@ -174,8 +174,8 @@ func TestYankLineWithCount(t *testing.T) {
sendKeys(tm, "3", "y", "y") sendKeys(tm, "3", "y", "y")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
}) })
} }
@ -376,8 +376,8 @@ func TestYankWithLinewiseMotions(t *testing.T) {
sendKeys(tm, "y", "j") sendKeys(tm, "y", "j")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Line != 0 { if m.CursorY() != 0 {
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line) t.Errorf("CursorY() = %d, want 0", m.CursorY())
} }
}) })
@ -389,8 +389,8 @@ func TestYankWithLinewiseMotions(t *testing.T) {
sendKeys(tm, "y", "G") sendKeys(tm, "y", "G")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
}) })
} }
@ -570,8 +570,8 @@ func TestYankWithCharwiseMotions(t *testing.T) {
sendKeys(tm, "y", "w") sendKeys(tm, "y", "w")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveWindow().Cursor.Col != 0 { if m.CursorX() != 0 {
t.Errorf("CursorX() = %d, want 0", m.ActiveWindow().Cursor.Col) t.Errorf("CursorX() = %d, want 0", m.CursorX())
} }
}) })
@ -583,8 +583,8 @@ func TestYankWithCharwiseMotions(t *testing.T) {
sendKeys(tm, "y", "w") sendKeys(tm, "y", "w")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello world" { if m.Line(0) != "hello world" {
t.Errorf("Line(0) = %q, want 'hello world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello world'", m.Line(0))
} }
}) })
} }
@ -659,8 +659,8 @@ func TestYankVisualCharwise(t *testing.T) {
sendKeys(tm, "v", "l", "l", "l", "l", "y") sendKeys(tm, "v", "l", "l", "l", "l", "y")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello world" { if m.Line(0) != "hello world" {
t.Errorf("Line(0) = %q, want 'hello world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello world'", m.Line(0))
} }
}) })
} }
@ -901,11 +901,11 @@ func TestYankRegisterBehavior(t *testing.T) {
sendKeys(tm, "p") // paste sendKeys(tm, "p") // paste
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[1] != "to copy" { if m.Line(1) != "to copy" {
t.Errorf("Line(1) = %q, want 'to copy'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'to copy'", m.Line(1))
} }
}) })
} }
@ -1053,8 +1053,8 @@ func TestVisualYankPasteRoundTrip(t *testing.T) {
sendKeys(tm, "v", "l", "l", "l", "l", "y", "$", "p") sendKeys(tm, "v", "l", "l", "l", "l", "y", "$", "p")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello worldhello" { if m.Line(0) != "hello worldhello" {
t.Errorf("Line(0) = %q, want 'hello worldhello'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello worldhello'", m.Line(0))
} }
}) })
@ -1067,8 +1067,8 @@ func TestVisualYankPasteRoundTrip(t *testing.T) {
sendKeys(tm, "v", "$", "y", "0", "P") sendKeys(tm, "v", "$", "y", "0", "P")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "worldhello world" { if m.Line(0) != "worldhello world" {
t.Errorf("Line(0) = %q, want 'worldhello world'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'worldhello world'", m.Line(0))
} }
}) })
@ -1081,11 +1081,11 @@ func TestVisualYankPasteRoundTrip(t *testing.T) {
sendKeys(tm, "V", "y", "j", "p") sendKeys(tm, "V", "y", "j", "p")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 4 { if m.LineCount() != 4 {
t.Errorf("LineCount() = %d, want 4", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 4", m.LineCount())
} }
if m.ActiveBuffer().Lines[2] != "line 1" { if m.Line(2) != "line 1" {
t.Errorf("Line(2) = %q, want 'line 1'", m.ActiveBuffer().Lines[2]) t.Errorf("Line(2) = %q, want 'line 1'", m.Line(2))
} }
}) })
@ -1098,14 +1098,14 @@ func TestVisualYankPasteRoundTrip(t *testing.T) {
sendKeys(tm, "V", "j", "y", "G", "p") sendKeys(tm, "V", "j", "y", "G", "p")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 6 { if m.LineCount() != 6 {
t.Errorf("LineCount() = %d, want 6", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 6", m.LineCount())
} }
if m.ActiveBuffer().Lines[4] != "line 1" { if m.Line(4) != "line 1" {
t.Errorf("Line(4) = %q, want 'line 1'", m.ActiveBuffer().Lines[4]) t.Errorf("Line(4) = %q, want 'line 1'", m.Line(4))
} }
if m.ActiveBuffer().Lines[5] != "line 2" { if m.Line(5) != "line 2" {
t.Errorf("Line(5) = %q, want 'line 2'", m.ActiveBuffer().Lines[5]) t.Errorf("Line(5) = %q, want 'line 2'", m.Line(5))
} }
}) })
@ -1118,14 +1118,14 @@ func TestVisualYankPasteRoundTrip(t *testing.T) {
sendKeys(tm, "V", "y", "g", "g", "P") sendKeys(tm, "V", "y", "g", "g", "P")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 4 { if m.LineCount() != 4 {
t.Errorf("LineCount() = %d, want 4", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 4", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "line 3" { if m.Line(0) != "line 3" {
t.Errorf("Line(0) = %q, want 'line 3'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'line 3'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "line 1" { if m.Line(1) != "line 1" {
t.Errorf("Line(1) = %q, want 'line 1'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'line 1'", m.Line(1))
} }
}) })
@ -1137,17 +1137,17 @@ func TestVisualYankPasteRoundTrip(t *testing.T) {
sendKeys(tm, "y", "y", "p") sendKeys(tm, "y", "y", "p")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "original" { if m.Line(0) != "original" {
t.Errorf("Line(0) = %q, want 'original'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'original'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "original" { if m.Line(1) != "original" {
t.Errorf("Line(1) = %q, want 'original'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'original'", m.Line(1))
} }
if m.ActiveBuffer().Lines[2] != "other" { if m.Line(2) != "other" {
t.Errorf("Line(2) = %q, want 'other'", m.ActiveBuffer().Lines[2]) t.Errorf("Line(2) = %q, want 'other'", m.Line(2))
} }
}) })
@ -1159,17 +1159,17 @@ func TestVisualYankPasteRoundTrip(t *testing.T) {
sendKeys(tm, "y", "y", "P") sendKeys(tm, "y", "y", "P")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "original" { if m.Line(0) != "original" {
t.Errorf("Line(0) = %q, want 'original'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'original'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "other" { if m.Line(1) != "other" {
t.Errorf("Line(1) = %q, want 'other'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'other'", m.Line(1))
} }
if m.ActiveBuffer().Lines[2] != "other" { if m.Line(2) != "other" {
t.Errorf("Line(2) = %q, want 'other'", m.ActiveBuffer().Lines[2]) t.Errorf("Line(2) = %q, want 'other'", m.Line(2))
} }
}) })
@ -1182,8 +1182,8 @@ func TestVisualYankPasteRoundTrip(t *testing.T) {
sendKeys(tm, "y", "w", "$", "p") sendKeys(tm, "y", "w", "$", "p")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello worldhello " { if m.Line(0) != "hello worldhello " {
t.Errorf("Line(0) = %q, want 'hello worldhello '", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello worldhello '", m.Line(0))
} }
}) })
@ -1196,8 +1196,8 @@ func TestVisualYankPasteRoundTrip(t *testing.T) {
sendKeys(tm, "y", "e", "$", "p") sendKeys(tm, "y", "e", "$", "p")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "hello worldhello" { if m.Line(0) != "hello worldhello" {
t.Errorf("Line(0) = %q, want 'hello worldhello'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'hello worldhello'", m.Line(0))
} }
}) })
@ -1210,8 +1210,8 @@ func TestVisualYankPasteRoundTrip(t *testing.T) {
sendKeys(tm, "v", "l", "l", "y", "$", "p") sendKeys(tm, "v", "l", "l", "y", "$", "p")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().Lines[0] != "abcdefghcde" { if m.Line(0) != "abcdefghcde" {
t.Errorf("Line(0) = %q, want 'abcdefghcde'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'abcdefghcde'", m.Line(0))
} }
}) })
@ -1243,17 +1243,17 @@ func TestVisualYankPasteRoundTrip(t *testing.T) {
sendKeys(tm, "d", "d", "p") sendKeys(tm, "d", "d", "p")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 3 { if m.LineCount() != 3 {
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 3", m.LineCount())
} }
if m.ActiveBuffer().Lines[0] != "line 2" { if m.Line(0) != "line 2" {
t.Errorf("Line(0) = %q, want 'line 2'", m.ActiveBuffer().Lines[0]) t.Errorf("Line(0) = %q, want 'line 2'", m.Line(0))
} }
if m.ActiveBuffer().Lines[1] != "line 1" { if m.Line(1) != "line 1" {
t.Errorf("Line(1) = %q, want 'line 1'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'line 1'", m.Line(1))
} }
if m.ActiveBuffer().Lines[2] != "line 3" { if m.Line(2) != "line 3" {
t.Errorf("Line(2) = %q, want 'line 3'", m.ActiveBuffer().Lines[2]) t.Errorf("Line(2) = %q, want 'line 3'", m.Line(2))
} }
}) })
@ -1266,21 +1266,21 @@ func TestVisualYankPasteRoundTrip(t *testing.T) {
sendKeys(tm, "2", "y", "y", "2", "p") sendKeys(tm, "2", "y", "y", "2", "p")
m := getFinalModel(t, tm) m := getFinalModel(t, tm)
if m.ActiveBuffer().LineCount() != 7 { if m.LineCount() != 7 {
t.Errorf("LineCount() = %d, want 7", m.ActiveBuffer().LineCount()) t.Errorf("LineCount() = %d, want 7", m.LineCount())
} }
// Original + 2 copies of 2 lines = 3 + 4 = 7 // Original + 2 copies of 2 lines = 3 + 4 = 7
if m.ActiveBuffer().Lines[1] != "line 1" { if m.Line(1) != "line 1" {
t.Errorf("Line(1) = %q, want 'line 1'", m.ActiveBuffer().Lines[1]) t.Errorf("Line(1) = %q, want 'line 1'", m.Line(1))
} }
if m.ActiveBuffer().Lines[2] != "line 2" { if m.Line(2) != "line 2" {
t.Errorf("Line(2) = %q, want 'line 2'", m.ActiveBuffer().Lines[2]) t.Errorf("Line(2) = %q, want 'line 2'", m.Line(2))
} }
if m.ActiveBuffer().Lines[3] != "line 1" { if m.Line(3) != "line 1" {
t.Errorf("Line(3) = %q, want 'line 1'", m.ActiveBuffer().Lines[3]) t.Errorf("Line(3) = %q, want 'line 1'", m.Line(3))
} }
if m.ActiveBuffer().Lines[4] != "line 2" { if m.Line(4) != "line 2" {
t.Errorf("Line(4) = %q, want 'line 2'", m.ActiveBuffer().Lines[4]) t.Errorf("Line(4) = %q, want 'line 2'", m.Line(4))
} }
}) })
} }

View File

@ -44,9 +44,37 @@ type Model struct {
// Registers // Registers
registers map[rune]action.Register // name -> register registers map[rune]action.Register // name -> register
}
// Visual styles func NewModel(lines []string, pos action.Position) *Model {
styles Styles m := Model{
mode: action.NormalMode,
command: "",
input: input.NewHandler(),
settings: action.NewDefaultSettings(),
registers: action.DefaultRegisters(),
windows: []*action.Window{},
}
// TODO: Temporary: Build the single buffer and window
buf := action.
NewBufferBuilder().
WithLines(lines).
Build()
m.buffers = append(m.buffers, &buf)
win := action.
NewWindowBuilder().
WithBuffer(&buf).
WithCursor(pos).
Build()
m.windows = append(m.windows, &win)
m.activeWindowId = win.Id
return &m
} }
// Model.Init: Initialize the model and start any commands that may need to run. Required // Model.Init: Initialize the model and start any commands that may need to run. Required
@ -194,11 +222,13 @@ func (m *Model) processInsertKey(key string) {
case "up": case "up":
if line > 0 { if line > 0 {
win.SetCursorLine(line - 1) win.SetCursorLine(line - 1)
win.ClampCursorX()
} }
case "down": case "down":
if line+1 < buf.LineCount() { if line+1 < buf.LineCount() {
win.SetCursorLine(line + 1) win.SetCursorLine(line + 1)
win.ClampCursorX()
} }
case "left": case "left":
@ -288,16 +318,6 @@ func (m *Model) SetSettings(s action.Settings) {
m.settings = s m.settings = s
} }
// Model.Styles: Returns the visual styles used for rendering.
func (m *Model) Styles() Styles {
return m.styles
}
// Model.SetStyles: Sets the visual styles used for rendering.
func (m *Model) SetStyles(s Styles) {
m.styles = s
}
// ================================================== // ==================================================
// Registers // Registers
// ================================================== // ==================================================

View File

@ -1,139 +0,0 @@
package editor
import (
"git.gophernest.net/azpect/TextEditor/internal/action"
"git.gophernest.net/azpect/TextEditor/internal/input"
)
type ModelBuilder struct {
model Model
}
func NewModelBuilder() *ModelBuilder {
return &ModelBuilder{
model: Model{
buffers: []*action.Buffer{},
windows: []*action.Window{},
activeWindowId: -1,
mode: action.NormalMode,
termWidth: 0,
termHeight: 0,
input: input.NewHandler(),
insertCount: 0,
insertKeys: []string{},
insertAction: nil,
command: "",
commandCursor: 0,
commandError: nil,
commandOutput: "",
settings: action.NewDefaultSettings(),
registers: action.DefaultRegisters(),
styles: DefaultStyles(),
},
}
}
// ModelBuilder.WithBuffers: Set the buffers for the model. Buffers represent
// the in-memory text content of files being edited.
func (mb *ModelBuilder) WithBuffers(buffers []*action.Buffer) *ModelBuilder {
mb.model.buffers = buffers
return mb
}
// ModelBuilder.AddBuffer: Add a single buffer to the model's buffer list.
func (mb *ModelBuilder) AddBuffer(buffer *action.Buffer) *ModelBuilder {
mb.model.buffers = append(mb.model.buffers, buffer)
return mb
}
// ModelBuilder.WithWindows: Set the windows for the model. Windows are viewports
// that display buffer content with their own cursor position and scroll state.
func (mb *ModelBuilder) WithWindows(windows []*action.Window) *ModelBuilder {
mb.model.windows = windows
return mb
}
// ModelBuilder.AddWindow: Add a single window to the model's window list.
func (mb *ModelBuilder) AddWindow(window *action.Window) *ModelBuilder {
mb.model.windows = append(mb.model.windows, window)
return mb
}
// ModelBuilder.WithActiveWindowId: Set the ID of the currently active window.
// This determines which window receives input and displays the cursor.
func (mb *ModelBuilder) WithActiveWindowId(id int) *ModelBuilder {
mb.model.activeWindowId = id
return mb
}
// ModelBuilder.WithMode: Set the editor mode (Normal, Insert, Visual, etc).
func (mb *ModelBuilder) WithMode(mode action.Mode) *ModelBuilder {
mb.model.mode = mode
return mb
}
// ModelBuilder.WithTermSize: Set the terminal dimensions in columns and rows.
func (mb *ModelBuilder) WithTermSize(width, height int) *ModelBuilder {
mb.model.termWidth = width
mb.model.termHeight = height
return mb
}
// ModelBuilder.WithTermWidth: Set the terminal width in columns.
func (mb *ModelBuilder) WithTermWidth(width int) *ModelBuilder {
mb.model.termWidth = width
return mb
}
// ModelBuilder.WithTermHeight: Set the terminal height in rows.
func (mb *ModelBuilder) WithTermHeight(height int) *ModelBuilder {
mb.model.termHeight = height
return mb
}
// ModelBuilder.WithSettings: Set the editor settings (tabstop, scrolloff, etc).
func (mb *ModelBuilder) WithSettings(settings action.Settings) *ModelBuilder {
mb.model.settings = settings
return mb
}
// ModelBuilder.WithRegisters: Set the register map for yank/delete/paste operations.
func (mb *ModelBuilder) WithRegisters(registers map[rune]action.Register) *ModelBuilder {
mb.model.registers = registers
return mb
}
// ModelBuilder.WithCommand: Set the command line text.
func (mb *ModelBuilder) WithCommand(command string) *ModelBuilder {
mb.model.command = command
return mb
}
// ModelBuilder.WithCommandCursor: Set the cursor position in the command line.
func (mb *ModelBuilder) WithCommandCursor(cursor int) *ModelBuilder {
mb.model.commandCursor = cursor
return mb
}
// ModelBuilder.WithCommandError: Set the command line error state.
func (mb *ModelBuilder) WithCommandError(err error) *ModelBuilder {
mb.model.commandError = err
return mb
}
// ModelBuilder.WithCommandOutput: Set the command line output text.
func (mb *ModelBuilder) WithCommandOutput(output string) *ModelBuilder {
mb.model.commandOutput = output
return mb
}
// ModelBuilder.WithStyles: Set the visual styling for the editor.
func (mb *ModelBuilder) WithStyles(styles Styles) *ModelBuilder {
mb.model.styles = styles
return mb
}
// ModelBuilder.Build: Build and return the configured Model instance.
func (mb *ModelBuilder) Build() *Model {
return &mb.model
}

View File

@ -5,121 +5,48 @@ import (
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
) )
// Styles holds all the visual styling for the editor. func (m Model) cursorStyle() lipgloss.Style {
// Passed to Window.View() so windows can render themselves. switch m.mode {
type Styles struct { case action.NormalMode,
// Cursor styles by mode action.VisualMode,
CursorNormal lipgloss.Style action.VisualBlockMode,
CursorInsert lipgloss.Style action.VisualLineMode:
CursorCommand lipgloss.Style // Block cursor for normal mode
return lipgloss.NewStyle().Reverse(true)
// Gutter (line numbers)
Gutter lipgloss.Style
GutterCurrentLine lipgloss.Style
// Visual mode
VisualHighlight lipgloss.Style
VisualAnchor lipgloss.Style // debugging
// Status bar
StatusBar lipgloss.Style
StatusBarActive lipgloss.Style
// Command line
CommandError lipgloss.Style
}
// DefaultStyles returns the default editor color scheme.
func DefaultStyles() Styles {
return Styles{
CursorNormal: lipgloss.NewStyle().Reverse(true),
CursorInsert: lipgloss.NewStyle().Underline(true),
CursorCommand: lipgloss.NewStyle().Reverse(true),
Gutter: lipgloss.NewStyle().
Background(lipgloss.Color("236")).
Foreground(lipgloss.Color("243")),
GutterCurrentLine: lipgloss.NewStyle().
Background(lipgloss.Color("236")).
Foreground(lipgloss.Color("#d69d00")),
VisualHighlight: lipgloss.NewStyle().
Background(lipgloss.Color("#7a6a00")),
VisualAnchor: lipgloss.NewStyle().
Background(lipgloss.Color("#a89020")),
StatusBar: lipgloss.NewStyle().
Background(lipgloss.Color("236")).
Foreground(lipgloss.Color("243")),
StatusBarActive: lipgloss.NewStyle().
Background(lipgloss.Color("62")).
Foreground(lipgloss.Color("230")),
CommandError: lipgloss.NewStyle().
Foreground(lipgloss.Color("#e3203a")),
}
}
// CursorStyle returns the appropriate cursor style for the given mode.
func (s Styles) CursorStyle(mode action.Mode) lipgloss.Style {
switch mode {
case action.InsertMode: case action.InsertMode:
return s.CursorInsert // Bar/underline for insert mode
return lipgloss.NewStyle().Underline(true)
case action.CommandMode: case action.CommandMode:
return s.CursorCommand return lipgloss.NewStyle().Reverse(true)
default: default:
return s.CursorNormal return lipgloss.NewStyle().Reverse(true)
} }
} }
// ================================================== // DEBUGGING STYLE
// Deprecated func (m Model) visualAnchorStyle() lipgloss.Style {
// ================================================== bg := lipgloss.Color("#a89020")
// func (m Model) cursorStyle() lipgloss.Style { return lipgloss.NewStyle().Background(bg)
// switch m.mode { }
// case action.NormalMode,
// action.VisualMode, func (m Model) gutterStyle(currentLine bool) lipgloss.Style {
// action.VisualBlockMode, bg := lipgloss.Color("236")
// action.VisualLineMode: fg := lipgloss.Color("243")
// // Block cursor for normal mode if currentLine {
// return lipgloss.NewStyle().Reverse(true) fg = lipgloss.Color("#d69d00")
// case action.InsertMode: }
// // Bar/underline for insert mode return lipgloss.NewStyle().
// return lipgloss.NewStyle().Underline(true) Width(m.Settings().GutterSize).
// case action.CommandMode: Background(bg).
// return lipgloss.NewStyle().Reverse(true) Foreground(fg)
// default: }
// return lipgloss.NewStyle().Reverse(true)
// } func (m Model) visualHighlightStyle() lipgloss.Style {
// } bg := lipgloss.Color("#7a6a00")
// return lipgloss.NewStyle().Background(bg)
// // DEBUGGING STYLE }
// func (m Model) visualAnchorStyle() lipgloss.Style {
// bg := lipgloss.Color("#a89020") func (m Model) commandErrorStyle() lipgloss.Style {
// return lipgloss.NewStyle().Background(bg) fg := lipgloss.Color("#e3203a")
// } return lipgloss.NewStyle().Foreground(fg)
// }
// func (m Model) gutterStyle(currentLine bool) lipgloss.Style {
// bg := lipgloss.Color("236")
// fg := lipgloss.Color("243")
// if currentLine {
// fg = lipgloss.Color("#d69d00")
// }
// return lipgloss.NewStyle().
// Width(m.Settings().GutterSize).
// Background(bg).
// Foreground(fg)
// }
//
// func (m Model) visualHighlightStyle() lipgloss.Style {
// bg := lipgloss.Color("#7a6a00")
// return lipgloss.NewStyle().Background(bg)
// }
//
// func (m Model) commandErrorStyle() lipgloss.Style {
// fg := lipgloss.Color("#e3203a")
// return lipgloss.NewStyle().Foreground(fg)
// }

View File

@ -56,8 +56,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} }
// Keep cursor in view after any update // Keep cursor in view after any update
win := m.ActiveWindow() m.AdjustScroll()
win.AdjustScroll()
return m, cmd return m, cmd
} }

View File

@ -8,20 +8,18 @@ import (
) )
func posInsideSelection(m Model, col, line int) bool { func posInsideSelection(m Model, col, line int) bool {
win := m.ActiveWindow()
switch m.Mode() { switch m.Mode() {
case action.VisualLineMode: case action.VisualLineMode:
startY := min(win.Anchor.Line, win.Cursor.Line) startY := min(m.AnchorY(), m.CursorY())
endY := max(win.Anchor.Line, win.Cursor.Line) endY := max(m.AnchorY(), m.CursorY())
return line >= startY && line <= endY return line >= startY && line <= endY
case action.VisualMode: case action.VisualMode:
ax := win.Anchor.Col ax := m.AnchorX()
ay := win.Anchor.Line ay := m.AnchorY()
cx := win.Cursor.Col cx := m.CursorX()
cy := win.Cursor.Line cy := m.CursorY()
// Normalize so start is always before end in document order // Normalize so start is always before end in document order
var startX, startY, endX, endY int var startX, startY, endX, endY int
@ -39,10 +37,10 @@ func posInsideSelection(m Model, col, line int) bool {
return afterStart && beforeEnd return afterStart && beforeEnd
case action.VisualBlockMode: case action.VisualBlockMode:
startX := min(win.Anchor.Col, win.Cursor.Col) startX := min(m.AnchorX(), m.CursorX())
startY := min(win.Anchor.Line, win.Cursor.Line) startY := min(m.AnchorY(), m.CursorY())
endX := max(win.Anchor.Col, win.Cursor.Col) endX := max(m.AnchorX(), m.CursorX())
endY := max(win.Anchor.Line, win.Cursor.Line) endY := max(m.AnchorY(), m.CursorY())
return col >= startX && col <= endX && return col >= startX && col <= endX &&
line >= startY && line <= endY line >= startY && line <= endY
@ -53,25 +51,21 @@ func posInsideSelection(m Model, col, line int) bool {
} }
func posIsAnchor(m Model, col, line int) bool { func posIsAnchor(m Model, col, line int) bool {
win := m.ActiveWindow() ax := m.AnchorX()
ax := win.Anchor.Col ay := m.AnchorY()
ay := win.Anchor.Line
return col == ax && line == ay return col == ax && line == ay
} }
func (m Model) View() string { func (m Model) View() string {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
var view strings.Builder var view strings.Builder
viewportHeight := win.Height - 2 viewportHeight := m.ViewPortH()
start := win.ScrollY start := m.ScrollY()
end := win.ScrollY + viewportHeight end := m.ScrollY() + viewportHeight
for i := start; i < end; i++ { for i := start; i < end; i++ {
if i < buf.LineCount() { if i < m.LineCount() {
if m.Settings().Number || m.Settings().RelativeNumber { if m.Settings().Number || m.Settings().RelativeNumber {
var ( var (
@ -82,11 +76,11 @@ func (m Model) View() string {
if m.Settings().RelativeNumber { if m.Settings().RelativeNumber {
// Relative line numbers: show distance from cursor, current line shows absolute // Relative line numbers: show distance from cursor, current line shows absolute
if i > win.Cursor.Line { if i > m.CursorY() {
lineNumber = i - win.Cursor.Line lineNumber = i - m.CursorY()
gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber) gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber)
} else if i < win.Cursor.Line { } else if i < m.CursorY() {
lineNumber = win.Cursor.Line - i lineNumber = m.CursorY() - i
gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber) gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber)
} else { } else {
// Current line: show absolute number if Number is also set, otherwise show 0 // Current line: show absolute number if Number is also set, otherwise show 0
@ -101,35 +95,31 @@ func (m Model) View() string {
} else if m.Settings().Number { } else if m.Settings().Number {
// Absolute line numbers only // Absolute line numbers only
lineNumber = i + 1 lineNumber = i + 1
currentLine = (i == win.Cursor.Line) currentLine = (i == m.CursorY())
gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber) gutter = fmt.Sprintf("%*d ", m.Settings().GutterSize-1, lineNumber)
} }
if currentLine { view.WriteString(m.gutterStyle(currentLine).Render(gutter))
view.WriteString(m.Styles().GutterCurrentLine.Render(gutter))
} else {
view.WriteString(m.Styles().Gutter.Render(gutter))
}
} }
runes := []rune(buf.Lines[i]) runes := []rune(m.Line(i))
for x := 0; x <= len(runes); x++ { for x := 0; x <= len(runes); x++ {
if win.Cursor.Line == i && win.Cursor.Col == x { if m.CursorY() == i && m.CursorX() == x {
if x < len(runes) { if x < len(runes) {
view.WriteString(m.Styles().CursorStyle(m.Mode()).Render(string(runes[x]))) view.WriteString(m.cursorStyle().Render(string(runes[x])))
} else { } else {
view.WriteString(m.Styles().CursorStyle(m.Mode()).Render(" ")) view.WriteString(m.cursorStyle().Render(" "))
} }
} else if x < len(runes) { } else if x < len(runes) {
if m.Mode().IsVisualMode() && posIsAnchor(m, x, i) { if m.Mode().IsVisualMode() && posIsAnchor(m, x, i) {
view.WriteString(m.Styles().VisualAnchor.Render(string(runes[x]))) view.WriteString(m.visualAnchorStyle().Render(string(runes[x])))
} else if m.Mode().IsVisualMode() && posInsideSelection(m, x, i) { } else if m.Mode().IsVisualMode() && posInsideSelection(m, x, i) {
view.WriteString(m.Styles().VisualHighlight.Render(string(runes[x]))) view.WriteString(m.visualHighlightStyle().Render(string(runes[x])))
} else { } else {
view.WriteRune(runes[x]) view.WriteRune(runes[x])
} }
// To highlight blank lines when in visual mode // To highlight blank lines when in visual mode
} else if m.Mode().IsVisualMode() && posInsideSelection(m, x, i) { } else if m.Mode().IsVisualMode() && posInsideSelection(m, x, i) {
view.WriteString(m.Styles().VisualHighlight.Render(" ")) view.WriteString(m.visualHighlightStyle().Render(" "))
} }
} }
} else { } else {
@ -172,13 +162,11 @@ func leftBar(m Model) string {
} }
func rightBar(m Model) (bar string) { func rightBar(m Model) (bar string) {
win := m.ActiveWindow()
if m.Mode().IsVisualMode() { if m.Mode().IsVisualMode() {
lineCount := max(win.Anchor.Line, win.Cursor.Line) - min(win.Anchor.Line, win.Cursor.Line) + 1 lineCount := max(m.AnchorY(), m.CursorY()) - min(m.AnchorY(), m.CursorY()) + 1
bar = fmt.Sprintf("%d:%d <%d>", win.Cursor.Line, win.Cursor.Col, lineCount) bar = fmt.Sprintf("%d:%d <%d>", m.CursorY(), m.CursorX(), lineCount)
} else { } else {
bar = fmt.Sprintf("%d:%d ", win.Cursor.Line, win.Cursor.Col) bar = fmt.Sprintf("%d:%d ", m.CursorY(), m.CursorX())
} }
return return
} }
@ -190,18 +178,18 @@ func drawCommandBar(m Model) (bar string) {
cur := m.CommandCursor() cur := m.CommandCursor()
for i := 0; i < len(cmd); i++ { for i := 0; i < len(cmd); i++ {
if i == cur { if i == cur {
bar += m.Styles().CursorStyle(m.Mode()).Render(string(cmd[i])) bar += m.cursorStyle().Render(string(cmd[i]))
} else { } else {
bar += string(cmd[i]) bar += string(cmd[i])
} }
} }
// Cursor at end of command // Cursor at end of command
if cur >= len(cmd) { if cur >= len(cmd) {
bar += m.Styles().CursorStyle(m.Mode()).Render(" ") bar += m.cursorStyle().Render(" ")
} }
// bar = fmt.Sprintf("%s %d", bar, cur) // bar = fmt.Sprintf("%s %d", bar, cur)
} else if m.CommandError() != nil { } else if m.CommandError() != nil {
bar = m.Styles().CommandError.Render(m.CommandError().Error()) bar = m.commandErrorStyle().Render(m.CommandError().Error())
} else if strings.TrimSpace(m.CommandOutput()) != "" { } else if strings.TrimSpace(m.CommandOutput()) != "" {
bar = m.CommandOutput() bar = m.CommandOutput()
} else if strings.TrimSpace(m.Command()) != "" { } else if strings.TrimSpace(m.Command()) != "" {

View File

@ -14,6 +14,11 @@ const (
StateMotionCount StateMotionCount
) )
// PositionGetter is used to get cursor position for operator ranges
type PositionGetter interface {
GetCursorPosition() *action.Position
}
type Handler struct { type Handler struct {
state InputState state InputState
count1 int count1 int
@ -176,7 +181,6 @@ func (h *Handler) handleInitial(m action.Model, kind string, binding any, key st
func (h *Handler) handleAfterOperator(m action.Model, kind string, binding any, key string) tea.Cmd { func (h *Handler) handleAfterOperator(m action.Model, kind string, binding any, key string) tea.Cmd {
count := h.effectiveCount() count := h.effectiveCount()
win := m.ActiveWindow()
// dd, yy, cc - same operator key pressed twice // dd, yy, cc - same operator key pressed twice
if kind == "operator" && key == h.operatorKey { if kind == "operator" && key == h.operatorKey {
@ -197,10 +201,11 @@ func (h *Handler) handleAfterOperator(m action.Model, kind string, binding any,
mot = r.WithCount(count).(action.Motion) mot = r.WithCount(count).(action.Motion)
} }
// Get range and motion type // Get range and motion type
start := win.Cursor pg := m.(PositionGetter)
start := pg.GetCursorPosition()
mot.Execute(m) mot.Execute(m)
end := win.Cursor end := pg.GetCursorPosition()
cmd := h.operator.Operate(m, start, end, mot.Type()) cmd := h.operator.Operate(m, *start, *end, mot.Type())
h.Reset() h.Reset()
return cmd return cmd
} }
@ -297,9 +302,8 @@ func (h *Handler) handleCommandKey(m action.Model, key string) tea.Cmd {
} }
func normalizeVisualSelection(m action.Model) (action.Position, action.Position) { func normalizeVisualSelection(m action.Model) (action.Position, action.Position) {
win := m.ActiveWindow() a := action.Position{Line: m.AnchorY(), Col: m.AnchorX()}
a := action.Position{Line: win.Anchor.Line, Col: win.Anchor.Col} c := action.Position{Line: m.CursorY(), Col: m.CursorX()}
c := action.Position{Line: win.Cursor.Line, Col: win.Cursor.Col}
if a.Line < c.Line || (a.Line == c.Line && a.Col <= c.Col) { if a.Line < c.Line || (a.Line == c.Line && a.Col <= c.Col) {
return a, c return a, c
} }

View File

@ -1,8 +1,8 @@
package motion package motion
import ( import (
"git.gophernest.net/azpect/TextEditor/internal/action"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"git.gophernest.net/azpect/TextEditor/internal/action"
) )
// MoveDown implements Motion (j) - linewise // MoveDown implements Motion (j) - linewise
@ -11,11 +11,10 @@ type MoveDown struct {
} }
func (a MoveDown) Execute(m action.Model) tea.Cmd { func (a MoveDown) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() for i := 0; i < a.Count && m.CursorY() < m.LineCount()-1; i++ {
buf := m.ActiveBuffer() m.SetCursorY(m.CursorY() + 1)
for i := 0; i < a.Count && win.Cursor.Line < buf.LineCount()-1; i++ {
win.SetCursorLine(win.Cursor.Line + 1)
} }
m.ClampCursorX()
return nil return nil
} }
@ -31,10 +30,10 @@ type MoveUp struct {
} }
func (a MoveUp) Execute(m action.Model) tea.Cmd { func (a MoveUp) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() for i := 0; i < a.Count && m.CursorY() > 0; i++ {
for i := 0; i < a.Count && win.Cursor.Line > 0; i++ { m.SetCursorY(m.CursorY() - 1)
win.SetCursorLine(win.Cursor.Line - 1)
} }
m.ClampCursorX()
return nil return nil
} }
@ -50,10 +49,10 @@ type MoveLeft struct {
} }
func (a MoveLeft) Execute(m action.Model) tea.Cmd { func (a MoveLeft) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() for i := 0; i < a.Count && m.CursorX() > 0; i++ {
for i := 0; i < a.Count && win.Cursor.Col > 0; i++ { m.SetCursorX(m.CursorX() - 1)
win.SetCursorCol(win.Cursor.Col - 1)
} }
m.ClampCursorX()
return nil return nil
} }
@ -69,12 +68,11 @@ type MoveRight struct {
} }
func (a MoveRight) Execute(m action.Model) tea.Cmd { func (a MoveRight) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() lineLen := len(m.Line(m.CursorY()))
buf := m.ActiveBuffer() for i := 0; i < a.Count && m.CursorX() <= lineLen; i++ {
lineLen := len(buf.Lines[win.Cursor.Line]) m.SetCursorX(m.CursorX() + 1)
for i := 0; i < a.Count && win.Cursor.Col <= lineLen; i++ {
win.SetCursorCol(win.Cursor.Col + 1)
} }
m.ClampCursorX()
return nil return nil
} }

View File

@ -9,8 +9,8 @@ import (
type MoveToTop struct{} type MoveToTop struct{}
func (a MoveToTop) Execute(m action.Model) tea.Cmd { func (a MoveToTop) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() m.SetCursorY(0)
win.SetCursorLine(0) m.ClampCursorX()
return nil return nil
} }
@ -20,9 +20,8 @@ func (a MoveToTop) Type() action.MotionType { return action.Linewise }
type MoveToBottom struct{} type MoveToBottom struct{}
func (a MoveToBottom) Execute(m action.Model) tea.Cmd { func (a MoveToBottom) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() m.SetCursorY(m.LineCount() - 1)
buf := m.ActiveBuffer() m.ClampCursorX()
win.SetCursorLine(buf.LineCount() - 1)
return nil return nil
} }
@ -32,8 +31,8 @@ func (a MoveToBottom) Type() action.MotionType { return action.Linewise }
type MoveToLineStart struct{} type MoveToLineStart struct{}
func (a MoveToLineStart) Execute(m action.Model) tea.Cmd { func (a MoveToLineStart) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() m.SetCursorX(0)
win.SetCursorCol(0) m.ClampCursorX()
return nil return nil
} }
@ -43,9 +42,8 @@ func (a MoveToLineStart) Type() action.MotionType { return action.CharwiseExclus
type MoveToLineEnd struct{} type MoveToLineEnd struct{}
func (a MoveToLineEnd) Execute(m action.Model) tea.Cmd { func (a MoveToLineEnd) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() m.SetCursorX(len(m.Line(m.CursorY())))
buf := m.ActiveBuffer() m.ClampCursorX()
win.SetCursorCol(len(buf.Lines[win.Cursor.Line]))
return nil return nil
} }
@ -55,10 +53,7 @@ func (a MoveToLineEnd) Type() action.MotionType { return action.CharwiseInclusiv
type MoveToLineContentStart struct{} type MoveToLineContentStart struct{}
func (a MoveToLineContentStart) Execute(m action.Model) tea.Cmd { func (a MoveToLineContentStart) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() line := m.Line(m.CursorY())
buf := m.ActiveBuffer()
line := buf.Lines[win.Cursor.Line]
x := 0 x := 0
for x < len(line) { for x < len(line) {
ch := line[x] ch := line[x]
@ -73,7 +68,7 @@ func (a MoveToLineContentStart) Execute(m action.Model) tea.Cmd {
x-- x--
} }
win.SetCursorCol(x) m.SetCursorX(x)
return nil return nil
} }
@ -85,13 +80,11 @@ type MoveToColumn struct {
} }
func (a MoveToColumn) Execute(m action.Model) tea.Cmd { func (a MoveToColumn) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() line := m.Line(m.CursorY())
buf := m.ActiveBuffer()
line := buf.Lines[win.Cursor.Line]
col := min(a.Count-1, len(line)-1) col := min(a.Count-1, len(line)-1)
win.SetCursorCol(col) m.SetCursorX(col)
m.ClampCursorX()
return nil return nil
} }
@ -107,25 +100,22 @@ func (a MoveToColumn) WithCount(n int) action.Action {
type ScrollDownHalfPage struct{} type ScrollDownHalfPage struct{}
func (a ScrollDownHalfPage) Execute(m action.Model) tea.Cmd { func (a ScrollDownHalfPage) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() viewportHeight := m.ViewPortH()
buf := m.ActiveBuffer()
viewportHeight := win.Height - 2
if viewportHeight <= 0 { if viewportHeight <= 0 {
return nil return nil
} }
scroll := viewportHeight / 2 scroll := viewportHeight / 2
scrollOff := win.Options.ScrollOff scrollOff := m.Settings().ScrollOff
// Current relative position in viewport // Current relative position in viewport
relY := win.Cursor.Line - win.ScrollY relY := m.CursorY() - m.ScrollY()
// Scroll down, clamped to valid range // Scroll down, clamped to valid range
newScrollY := win.ScrollY + scroll newScrollY := m.ScrollY() + scroll
maxScroll := max(0, buf.LineCount()-viewportHeight) maxScroll := max(0, m.LineCount()-viewportHeight)
newScrollY = min(newScrollY, maxScroll) newScrollY = min(newScrollY, maxScroll)
win.SetScrollY(newScrollY) m.SetScrollY(newScrollY)
// Maintain relative position, respecting scrollOff // Maintain relative position, respecting scrollOff
if relY < scrollOff { if relY < scrollOff {
@ -136,8 +126,9 @@ func (a ScrollDownHalfPage) Execute(m action.Model) tea.Cmd {
} }
newCursorY := newScrollY + relY newCursorY := newScrollY + relY
newCursorY = max(0, min(newCursorY, buf.LineCount()-1)) newCursorY = max(0, min(newCursorY, m.LineCount()-1))
win.SetCursorLine(newCursorY) m.SetCursorY(newCursorY)
m.ClampCursorX()
return nil return nil
} }
@ -148,23 +139,20 @@ func (a ScrollDownHalfPage) Type() action.MotionType { return action.Linewise }
type ScrollUpHalfPage struct{} type ScrollUpHalfPage struct{}
func (a ScrollUpHalfPage) Execute(m action.Model) tea.Cmd { func (a ScrollUpHalfPage) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() viewportHeight := m.ViewPortH()
buf := m.ActiveBuffer()
viewportHeight := win.Height - 2
if viewportHeight <= 0 { if viewportHeight <= 0 {
return nil return nil
} }
scroll := viewportHeight / 2 scroll := viewportHeight / 2
scrollOff := win.Options.ScrollOff scrollOff := m.Settings().ScrollOff
// Current relative position in viewport // Current relative position in viewport
relY := win.Cursor.Line - win.ScrollY relY := m.CursorY() - m.ScrollY()
// Scroll up, clamped to valid range // Scroll up, clamped to valid range
newScrollY := win.ScrollY - scroll newScrollY := m.ScrollY() - scroll
newScrollY = max(0, newScrollY) newScrollY = max(0, newScrollY)
win.SetScrollY(newScrollY) m.SetScrollY(newScrollY)
// Maintain relative position, respecting scrollOff // Maintain relative position, respecting scrollOff
if relY < scrollOff { if relY < scrollOff {
@ -175,8 +163,9 @@ func (a ScrollUpHalfPage) Execute(m action.Model) tea.Cmd {
} }
newCursorY := newScrollY + relY newCursorY := newScrollY + relY
newCursorY = max(0, min(newCursorY, buf.LineCount()-1)) newCursorY = max(0, min(newCursorY, m.LineCount()-1))
win.SetCursorLine(newCursorY) m.SetCursorY(newCursorY)
m.ClampCursorX()
return nil return nil
} }

View File

@ -16,8 +16,8 @@ func isWordPunctuation(c byte) bool {
return c != ' ' && c != '\t' && !isWordChar(c) return c != ' ' && c != '\t' && !isWordChar(c)
} }
func nextWordStart(buf *action.Buffer, x, y int) (int, int) { func nextWordStart(m action.Model, x, y int) (int, int) {
line := buf.Lines[y] line := m.Line(y)
// Skip current class // Skip current class
if x < len(line) { if x < len(line) {
@ -46,13 +46,13 @@ func nextWordStart(buf *action.Buffer, x, y int) (int, int) {
} }
// If next line is the end of the file, exit now // If next line is the end of the file, exit now
if y+1 >= buf.LineCount() { if y+1 >= m.LineCount() {
return x, y return x, y
} }
// Move to first char of next line // Move to first char of next line
y++ y++
line = buf.Lines[y] line = m.Line(y)
x = 0 x = 0
// If the first char of the new line is no whitespace, stay here! // If the first char of the new line is no whitespace, stay here!
@ -64,8 +64,8 @@ func nextWordStart(buf *action.Buffer, x, y int) (int, int) {
return x, y return x, y
} }
func nextWORDStart(buf *action.Buffer, x, y int) (int, int) { func nextWORDStart(m action.Model, x, y int) (int, int) {
line := buf.Lines[y] line := m.Line(y)
// Skip current WORD (all non-whitespace is one class for W) // Skip current WORD (all non-whitespace is one class for W)
for x < len(line) && line[x] != ' ' && line[x] != '\t' { for x < len(line) && line[x] != ' ' && line[x] != '\t' {
@ -85,13 +85,13 @@ func nextWORDStart(buf *action.Buffer, x, y int) (int, int) {
} }
// If next line is the end of the file, exit now // If next line is the end of the file, exit now
if y+1 >= buf.LineCount() { if y+1 >= m.LineCount() {
return x, y return x, y
} }
// Move to first char of next line // Move to first char of next line
y++ y++
line = buf.Lines[y] line = m.Line(y)
x = 0 x = 0
// If the first char of the new line is no whitespace, stay here! // If the first char of the new line is no whitespace, stay here!
@ -103,21 +103,21 @@ func nextWORDStart(buf *action.Buffer, x, y int) (int, int) {
return x, y return x, y
} }
func nextWordEnd(buf *action.Buffer, x, y int) (int, int) { func nextWordEnd(m action.Model, x, y int) (int, int) {
line := buf.Lines[y] line := m.Line(y)
// Advance once to avoid being stuck on the current end // Advance once to avoid being stuck on the current end
x++ x++
if x >= len(line) { if x >= len(line) {
// At last line of file, pin cursor to end of file // At last line of file, pin cursor to end of file
if y+1 >= buf.LineCount() { if y+1 >= m.LineCount() {
return len(line) - 1, y return len(line) - 1, y
} }
// Otherwise, move to next line // Otherwise, move to next line
y++ y++
x = 0 x = 0
line = buf.Lines[y] line = m.Line(y)
} }
// Skip whitespace and cross lines if needed // Skip whitespace and cross lines if needed
@ -133,13 +133,13 @@ func nextWordEnd(buf *action.Buffer, x, y int) (int, int) {
} }
// If next line is the end of the file, exit now // If next line is the end of the file, exit now
if y+1 >= buf.LineCount() { if y+1 >= m.LineCount() {
return x, y return x, y
} }
// Move to first char of next line // Move to first char of next line
y++ y++
line = buf.Lines[y] line = m.Line(y)
x = 0 x = 0
} }
@ -160,21 +160,21 @@ func nextWordEnd(buf *action.Buffer, x, y int) (int, int) {
return x, y return x, y
} }
func nextWORDEnd(buf *action.Buffer, x, y int) (int, int) { func nextWORDEnd(m action.Model, x, y int) (int, int) {
line := buf.Lines[y] line := m.Line(y)
// Advance once to avoid being stuck on the current end // Advance once to avoid being stuck on the current end
x++ x++
if x >= len(line) { if x >= len(line) {
// At last line of file, pin cursor to end of file // At last line of file, pin cursor to end of file
if y+1 >= buf.LineCount() { if y+1 >= m.LineCount() {
return len(line) - 1, y return len(line) - 1, y
} }
// Otherwise, move to next line // Otherwise, move to next line
y++ y++
x = 0 x = 0
line = buf.Lines[y] line = m.Line(y)
} }
// Skip whitespace and cross lines if needed // Skip whitespace and cross lines if needed
@ -190,13 +190,13 @@ func nextWORDEnd(buf *action.Buffer, x, y int) (int, int) {
} }
// If next line is the end of the file, exit now // If next line is the end of the file, exit now
if y+1 >= buf.LineCount() { if y+1 >= m.LineCount() {
return x, y return x, y
} }
// Move to first char of next line // Move to first char of next line
y++ y++
line = buf.Lines[y] line = m.Line(y)
x = 0 x = 0
} }
@ -208,8 +208,8 @@ func nextWORDEnd(buf *action.Buffer, x, y int) (int, int) {
return x, y return x, y
} }
func prevWordStart(buf *action.Buffer, x, y int) (int, int) { func prevWordStart(m action.Model, x, y int) (int, int) {
line := buf.Lines[y] line := m.Line(y)
// Back one to avoid being stuck on the current start // Back one to avoid being stuck on the current start
x-- x--
@ -218,7 +218,7 @@ func prevWordStart(buf *action.Buffer, x, y int) (int, int) {
return 0, 0 // beginning of file, stay put return 0, 0 // beginning of file, stay put
} }
y-- y--
line = buf.Lines[y] line = m.Line(y)
x = len(line) - 1 x = len(line) - 1
if x < 0 { if x < 0 {
return 0, y // landed on an empty line return 0, y // landed on an empty line
@ -237,7 +237,7 @@ func prevWordStart(buf *action.Buffer, x, y int) (int, int) {
return 0, 0 return 0, 0
} }
y-- y--
line = buf.Lines[y] line = m.Line(y)
x = len(line) - 1 x = len(line) - 1
if len(line) == 0 { if len(line) == 0 {
return 0, y // empty line acts as a word boundary return 0, y // empty line acts as a word boundary
@ -264,16 +264,13 @@ type MoveForwardWord struct {
} }
func (a MoveForwardWord) Execute(m action.Model) tea.Cmd { func (a MoveForwardWord) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() x := m.CursorX()
buf := m.ActiveBuffer() y := m.CursorY()
x := win.Cursor.Col
y := win.Cursor.Line
for i := 0; i < a.Count; i++ { for i := 0; i < a.Count; i++ {
x, y = nextWordStart(buf, x, y) x, y = nextWordStart(m, x, y)
} }
win.SetCursorCol(x) m.SetCursorX(x)
win.SetCursorLine(y) m.SetCursorY(y)
return nil return nil
} }
@ -289,16 +286,13 @@ type MoveForwardWORD struct {
} }
func (a MoveForwardWORD) Execute(m action.Model) tea.Cmd { func (a MoveForwardWORD) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() x := m.CursorX()
buf := m.ActiveBuffer() y := m.CursorY()
x := win.Cursor.Col
y := win.Cursor.Line
for i := 0; i < a.Count; i++ { for i := 0; i < a.Count; i++ {
x, y = nextWORDStart(buf, x, y) x, y = nextWORDStart(m, x, y)
} }
win.SetCursorCol(x) m.SetCursorX(x)
win.SetCursorLine(y) m.SetCursorY(y)
return nil return nil
} }
@ -314,16 +308,13 @@ type MoveForwardWordEnd struct {
} }
func (a MoveForwardWordEnd) Execute(m action.Model) tea.Cmd { func (a MoveForwardWordEnd) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() x := m.CursorX()
buf := m.ActiveBuffer() y := m.CursorY()
x := win.Cursor.Col
y := win.Cursor.Line
for i := 0; i < a.Count; i++ { for i := 0; i < a.Count; i++ {
x, y = nextWordEnd(buf, x, y) x, y = nextWordEnd(m, x, y)
} }
win.SetCursorCol(x) m.SetCursorX(x)
win.SetCursorLine(y) m.SetCursorY(y)
return nil return nil
} }
@ -339,16 +330,13 @@ type MoveForwardWORDEnd struct {
} }
func (a MoveForwardWORDEnd) Execute(m action.Model) tea.Cmd { func (a MoveForwardWORDEnd) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() x := m.CursorX()
buf := m.ActiveBuffer() y := m.CursorY()
x := win.Cursor.Col
y := win.Cursor.Line
for i := 0; i < a.Count; i++ { for i := 0; i < a.Count; i++ {
x, y = nextWORDEnd(buf, x, y) x, y = nextWORDEnd(m, x, y)
} }
win.SetCursorCol(x) m.SetCursorX(x)
win.SetCursorLine(y) m.SetCursorY(y)
return nil return nil
} }
@ -364,16 +352,13 @@ type MoveBackwardWord struct {
} }
func (a MoveBackwardWord) Execute(m action.Model) tea.Cmd { func (a MoveBackwardWord) Execute(m action.Model) tea.Cmd {
win := m.ActiveWindow() x := m.CursorX()
buf := m.ActiveBuffer() y := m.CursorY()
x := win.Cursor.Col
y := win.Cursor.Line
for i := 0; i < a.Count; i++ { for i := 0; i < a.Count; i++ {
x, y = prevWordStart(buf, x, y) x, y = prevWordStart(m, x, y)
} }
win.SetCursorCol(x) m.SetCursorX(x)
win.SetCursorLine(y) m.SetCursorY(y)
return nil return nil
} }

View File

@ -58,24 +58,21 @@ func changeNormalMode(m action.Model, start, end action.Position, mtype action.M
} }
func changeCharSelection(m action.Model, start, end action.Position) { func changeCharSelection(m action.Model, start, end action.Position) {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
var deletedText string var deletedText string
if start.Line == end.Line { if start.Line == end.Line {
line := buf.Lines[start.Line] line := m.Line(start.Line)
endCol := min(end.Col+1, len(line)) endCol := min(end.Col+1, len(line))
deletedText = line[start.Col:endCol] deletedText = line[start.Col:endCol]
buf.SetLine(start.Line, line[:start.Col]+line[endCol:]) m.SetLine(start.Line, line[:start.Col]+line[endCol:])
} else { } else {
startLine := buf.Lines[start.Line] startLine := m.Line(start.Line)
endLine := buf.Lines[end.Line] endLine := m.Line(end.Line)
// Extract deleted text // Extract deleted text
deletedText = startLine[start.Col:] + "\n" deletedText = startLine[start.Col:] + "\n"
for y := start.Line + 1; y < end.Line; y++ { for y := start.Line + 1; y < end.Line; y++ {
deletedText += buf.Lines[y] + "\n" deletedText += m.Line(y) + "\n"
} }
endCol := min(end.Col+1, len(endLine)) endCol := min(end.Col+1, len(endLine))
deletedText += endLine[:endCol] deletedText += endLine[:endCol]
@ -88,13 +85,14 @@ func changeCharSelection(m action.Model, start, end action.Position) {
// Delete from end back to start to preserve indices // Delete from end back to start to preserve indices
for i := end.Line; i >= start.Line; i-- { for i := end.Line; i >= start.Line; i-- {
buf.DeleteLine(i) m.DeleteLine(i)
} }
buf.InsertLine(start.Line, prefix+suffix) m.InsertLine(start.Line, prefix+suffix)
} }
win.SetCursorLine(start.Line) m.SetCursorY(start.Line)
win.SetCursorCol(start.Col) m.SetCursorX(start.Col)
m.ClampCursorX()
m.SetMode(action.InsertMode) m.SetMode(action.InsertMode)
// Update register with deleted text // Update register with deleted text
@ -102,22 +100,19 @@ func changeCharSelection(m action.Model, start, end action.Position) {
} }
func changeLineSelection(m action.Model, start, end action.Position) { func changeLineSelection(m action.Model, start, end action.Position) {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
var lines []string var lines []string
for i := end.Line; i >= start.Line; i-- { for i := end.Line; i >= start.Line; i-- {
lines = append([]string{buf.Lines[i]}, lines...) lines = append([]string{m.Line(i)}, lines...)
buf.DeleteLine(i) m.DeleteLine(i)
} }
// Insert an empty line for editing // Insert an empty line for editing
insertY := min(start.Line, buf.LineCount()) insertY := min(start.Line, m.LineCount())
buf.InsertLine(insertY, "") m.InsertLine(insertY, "")
win.SetCursorLine(insertY) m.SetCursorY(insertY)
win.SetCursorCol(0) m.SetCursorX(0)
m.SetMode(action.InsertMode) m.SetMode(action.InsertMode)
// Update registers // Update registers
@ -125,23 +120,21 @@ func changeLineSelection(m action.Model, start, end action.Position) {
} }
func changeBlockSelection(m action.Model, start, end action.Position) { func changeBlockSelection(m action.Model, start, end action.Position) {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
startCol := min(start.Col, end.Col) startCol := min(start.Col, end.Col)
endCol := max(start.Col, end.Col) endCol := max(start.Col, end.Col)
for y := start.Line; y <= end.Line; y++ { for y := start.Line; y <= end.Line; y++ {
line := buf.Lines[y] line := m.Line(y)
if startCol >= len(line) { if startCol >= len(line) {
continue continue
} }
ec := min(endCol+1, len(line)) ec := min(endCol+1, len(line))
buf.SetLine(y, line[:startCol]+line[ec:]) m.SetLine(y, line[:startCol]+line[ec:])
} }
win.SetCursorLine(start.Line) m.SetCursorY(start.Line)
win.SetCursorCol(startCol) m.SetCursorX(startCol)
m.ClampCursorX()
m.SetMode(action.InsertMode) m.SetMode(action.InsertMode)
} }
@ -150,20 +143,17 @@ var _ action.DoublePresser = ChangeOperator{}
// Double press handles cc - change the entire line // Double press handles cc - change the entire line
func (o ChangeOperator) DoublePress(m action.Model, count int) tea.Cmd { func (o ChangeOperator) DoublePress(m action.Model, count int) tea.Cmd {
win := m.ActiveWindow() startY := m.CursorY()
buf := m.ActiveBuffer()
startY := win.Cursor.Line
// If we have a higher value than lines remaining, we can only run so many times // If we have a higher value than lines remaining, we can only run so many times
opCount := min(count, buf.LineCount()-startY) opCount := min(count, m.LineCount()-startY)
var lines []string var lines []string
// Collect lines to delete (always delete at startY since lines shift up) // Collect lines to delete (always delete at startY since lines shift up)
for range opCount { for range opCount {
lines = append(lines, buf.Lines[startY]) lines = append(lines, m.Line(startY))
buf.DeleteLine(startY) m.DeleteLine(startY)
} }
// Put deleted lines in register // Put deleted lines in register
@ -171,12 +161,12 @@ func (o ChangeOperator) DoublePress(m action.Model, count int) tea.Cmd {
// Insert empty line at the original position for editing // Insert empty line at the original position for editing
// If we deleted everything, startY might be past end, so clamp it // If we deleted everything, startY might be past end, so clamp it
insertY := min(startY, buf.LineCount()) insertY := min(startY, m.LineCount())
buf.InsertLine(insertY, "") m.InsertLine(insertY, "")
// Position cursor on the new empty line // Position cursor on the new empty line
win.SetCursorLine(insertY) m.SetCursorY(insertY)
win.SetCursorCol(0) m.SetCursorX(0)
m.SetMode(action.InsertMode) m.SetMode(action.InsertMode)
return nil return nil

View File

@ -27,29 +27,27 @@ var _ action.DoublePresser = DeleteOperator{}
// Double press handles dd - delete the entire line // Double press handles dd - delete the entire line
func (o DeleteOperator) DoublePress(m action.Model, count int) tea.Cmd { func (o DeleteOperator) DoublePress(m action.Model, count int) tea.Cmd {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
// If we have a higher value than lines remaining, we can only run so many times // If we have a higher value than lines remaining, we can only run so many times
opCount := min(count, buf.LineCount()-win.Cursor.Line) opCount := min(count, m.LineCount()-m.CursorY())
var lines []string var lines []string
for range opCount { for range opCount {
y := win.Cursor.Line y := m.CursorY()
lines = append(lines, buf.Lines[y]) lines = append(lines, m.Line(y))
buf.DeleteLine(y) m.DeleteLine(y)
if buf.LineCount() == 0 { if m.LineCount() == 0 {
buf.InsertLine(0, "") m.InsertLine(0, "")
} }
if y >= buf.LineCount() { if y >= m.LineCount() {
y = buf.LineCount() - 1 y = m.LineCount() - 1
} }
win.SetCursorLine(y) m.SetCursorY(y)
m.ClampCursorX()
} }
// Put her in the register! // Put her in the register!
@ -91,16 +89,13 @@ func deleteNormalMode(m action.Model, start, end action.Position, mtype action.M
} }
func deleteCharSelection(m action.Model, start, end action.Position) { func deleteCharSelection(m action.Model, start, end action.Position) {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
if start.Line == end.Line { if start.Line == end.Line {
line := buf.Lines[start.Line] line := m.Line(start.Line)
endCol := min(end.Col+1, len(line)) endCol := min(end.Col+1, len(line))
buf.SetLine(start.Line, line[:start.Col]+line[endCol:]) m.SetLine(start.Line, line[:start.Col]+line[endCol:])
} else { } else {
startLine := buf.Lines[start.Line] startLine := m.Line(start.Line)
endLine := buf.Lines[end.Line] endLine := m.Line(end.Line)
prefix := startLine[:start.Col] prefix := startLine[:start.Col]
suffix := "" suffix := ""
@ -110,57 +105,54 @@ func deleteCharSelection(m action.Model, start, end action.Position) {
// Delete from end back to start to preserve indices // Delete from end back to start to preserve indices
for i := end.Line; i >= start.Line; i-- { for i := end.Line; i >= start.Line; i-- {
buf.DeleteLine(i) m.DeleteLine(i)
} }
buf.InsertLine(start.Line, prefix+suffix) m.InsertLine(start.Line, prefix+suffix)
} }
win.SetCursorLine(start.Line) m.SetCursorY(start.Line)
win.SetCursorCol(start.Col) m.SetCursorX(start.Col)
m.ClampCursorX()
} }
func deleteLineSelection(m action.Model, start, end action.Position) { func deleteLineSelection(m action.Model, start, end action.Position) {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
var lines []string var lines []string
for i := end.Line; i >= start.Line; i-- { for i := end.Line; i >= start.Line; i-- {
lines = append(lines, buf.Lines[i]) lines = append(lines, m.Line(i))
buf.DeleteLine(i) m.DeleteLine(i)
} }
if buf.LineCount() == 0 { if m.LineCount() == 0 {
buf.InsertLine(0, "") m.InsertLine(0, "")
} }
y := start.Line y := start.Line
if y >= buf.LineCount() { if y >= m.LineCount() {
y = buf.LineCount() - 1 y = m.LineCount() - 1
} }
win.SetCursorLine(y) m.SetCursorY(y)
m.ClampCursorX()
// Update registers // Update registers
m.UpdateDefaultRegister(action.LinewiseRegister, lines) m.UpdateDefaultRegister(action.LinewiseRegister, lines)
} }
func deleteBlockSelection(m action.Model, start, end action.Position) { func deleteBlockSelection(m action.Model, start, end action.Position) {
win := m.ActiveWindow()
buf := m.ActiveBuffer()
startCol := min(start.Col, end.Col) startCol := min(start.Col, end.Col)
endCol := max(start.Col, end.Col) endCol := max(start.Col, end.Col)
for y := start.Line; y <= end.Line; y++ { for y := start.Line; y <= end.Line; y++ {
line := buf.Lines[y] line := m.Line(y)
if startCol >= len(line) { if startCol >= len(line) {
continue continue
} }
ec := min(endCol+1, len(line)) ec := min(endCol+1, len(line))
buf.SetLine(y, line[:startCol]+line[ec:]) m.SetLine(y, line[:startCol]+line[ec:])
} }
win.SetCursorLine(start.Line) m.SetCursorY(start.Line)
win.SetCursorCol(startCol) m.SetCursorX(startCol)
m.ClampCursorX()
} }

View File

@ -11,8 +11,6 @@ import (
type YankOperator struct{} type YankOperator struct{}
func (o YankOperator) Operate(m action.Model, start, end action.Position, mtype action.MotionType) tea.Cmd { func (o YankOperator) Operate(m action.Model, start, end action.Position, mtype action.MotionType) tea.Cmd {
win := m.ActiveWindow()
switch m.Mode() { switch m.Mode() {
case action.VisualMode: case action.VisualMode:
yankVisualMode(m, start, end) yankVisualMode(m, start, end)
@ -26,8 +24,8 @@ func (o YankOperator) Operate(m action.Model, start, end action.Position, mtype
m.SetCommandError(fmt.Errorf("'y' operator not yet implemented.")) m.SetCommandError(fmt.Errorf("'y' operator not yet implemented."))
} }
win.SetCursorCol(start.Col) m.SetCursorX(start.Col)
win.SetCursorLine(start.Line) m.SetCursorY(start.Line)
return nil return nil
} }
@ -35,18 +33,15 @@ func (o YankOperator) Operate(m action.Model, start, end action.Position, mtype
var _ action.DoublePresser = YankOperator{} var _ action.DoublePresser = YankOperator{}
func (o YankOperator) DoublePress(m action.Model, count int) tea.Cmd { func (o YankOperator) DoublePress(m action.Model, count int) tea.Cmd {
win := m.ActiveWindow() y := m.CursorY()
buf := m.ActiveBuffer()
y := win.Cursor.Line
// If we have a higher value than lines remaining, we can only run so many times // If we have a higher value than lines remaining, we can only run so many times
opCount := min(count, buf.LineCount()-y) opCount := min(count, m.LineCount()-y)
var lines []string var lines []string
for i := range opCount { for i := range opCount {
lines = append(lines, buf.Lines[y+i]) lines = append(lines, m.Line(y+i))
} }
// Put her in the register! // Put her in the register!
@ -56,8 +51,6 @@ func (o YankOperator) DoublePress(m action.Model, count int) tea.Cmd {
} }
func yankNormalMode(m action.Model, start, end action.Position, mtype action.MotionType) { func yankNormalMode(m action.Model, start, end action.Position, mtype action.MotionType) {
buf := m.ActiveBuffer()
switch { switch {
case mtype.IsCharwise(): case mtype.IsCharwise():
// This shouldn't happen // This shouldn't happen
@ -66,7 +59,7 @@ func yankNormalMode(m action.Model, start, end action.Position, mtype action.Mot
return return
} }
line := buf.Lines[start.Line] line := m.Line(start.Line)
startX := min(start.Col, end.Col) startX := min(start.Col, end.Col)
endX := max(start.Col, end.Col) endX := max(start.Col, end.Col)
@ -92,14 +85,12 @@ func yankNormalMode(m action.Model, start, end action.Position, mtype action.Mot
startY := min(start.Line, end.Line) startY := min(start.Line, end.Line)
endY := max(start.Line, end.Line) endY := max(start.Line, end.Line)
cnt := buf.Lines[startY : endY+1] cnt := m.Lines()[startY : endY+1]
m.UpdateDefaultRegister(action.LinewiseRegister, cnt) m.UpdateDefaultRegister(action.LinewiseRegister, cnt)
} }
} }
func yankVisualMode(m action.Model, start, end action.Position) { func yankVisualMode(m action.Model, start, end action.Position) {
buf := m.ActiveBuffer()
// Normalize so start is before end // Normalize so start is before end
if start.Line > end.Line || (start.Line == end.Line && start.Col > end.Col) { if start.Line > end.Line || (start.Line == end.Line && start.Col > end.Col) {
start, end = end, start start, end = end, start
@ -107,7 +98,7 @@ func yankVisualMode(m action.Model, start, end action.Position) {
// Single line selection // Single line selection
if start.Line == end.Line { if start.Line == end.Line {
line := buf.Lines[start.Line] line := m.Line(start.Line)
endCol := min(end.Col+1, len(line)) // +1 because visual selection is inclusive endCol := min(end.Col+1, len(line)) // +1 because visual selection is inclusive
startCol := min(start.Col, len(line)) startCol := min(start.Col, len(line))
cnt := line[startCol:endCol] cnt := line[startCol:endCol]
@ -119,17 +110,17 @@ func yankVisualMode(m action.Model, start, end action.Position) {
var content []string var content []string
// First line: from start.Col to end of line // First line: from start.Col to end of line
firstLine := buf.Lines[start.Line] firstLine := m.Line(start.Line)
startCol := min(start.Col, len(firstLine)) startCol := min(start.Col, len(firstLine))
content = append(content, firstLine[startCol:]) content = append(content, firstLine[startCol:])
// Middle lines: entire lines // Middle lines: entire lines
for y := start.Line + 1; y < end.Line; y++ { for y := start.Line + 1; y < end.Line; y++ {
content = append(content, buf.Lines[y]) content = append(content, m.Line(y))
} }
// Last line: from beginning to end.Col (inclusive) // Last line: from beginning to end.Col (inclusive)
lastLine := buf.Lines[end.Line] lastLine := m.Line(end.Line)
endCol := min(end.Col+1, len(lastLine)) endCol := min(end.Col+1, len(lastLine))
content = append(content, lastLine[:endCol]) content = append(content, lastLine[:endCol])
@ -137,8 +128,6 @@ func yankVisualMode(m action.Model, start, end action.Position) {
} }
func yankVisualLineMode(m action.Model, start, end action.Position) { func yankVisualLineMode(m action.Model, start, end action.Position) {
buf := m.ActiveBuffer()
// This shouldn't happen // This shouldn't happen
if start.Col != end.Col { if start.Col != end.Col {
m.SetCommandError(fmt.Errorf("Start column and end column must match for linewise yank operations.")) m.SetCommandError(fmt.Errorf("Start column and end column must match for linewise yank operations."))
@ -149,14 +138,12 @@ func yankVisualLineMode(m action.Model, start, end action.Position) {
startY := min(start.Line, end.Line) startY := min(start.Line, end.Line)
endY := max(start.Line, end.Line) endY := max(start.Line, end.Line)
cnt := buf.Lines[startY : endY+1] cnt := m.Lines()[startY : endY+1]
m.UpdateDefaultRegister(action.LinewiseRegister, cnt) m.UpdateDefaultRegister(action.LinewiseRegister, cnt)
} }
func yankVisualBlockMode(m action.Model, start, end action.Position) { func yankVisualBlockMode(m action.Model, start, end action.Position) {
buf := m.ActiveBuffer()
// Normalize so startY <= endY and startX <= endX // Normalize so startY <= endY and startX <= endX
startY := min(start.Line, end.Line) startY := min(start.Line, end.Line)
endY := max(start.Line, end.Line) endY := max(start.Line, end.Line)
@ -166,7 +153,7 @@ func yankVisualBlockMode(m action.Model, start, end action.Position) {
var content []string var content []string
for y := startY; y <= endY; y++ { for y := startY; y <= endY; y++ {
line := buf.Lines[y] line := m.Line(y)
// Handle lines shorter than the block selection // Handle lines shorter than the block selection
if startX >= len(line) { if startX >= len(line) {