Compare commits
No commits in common. "ea4638d815a0d9ce64b0c8412143fcd5c9b201b6" and "db52b63db1bbd173ff318490e0ce4fcbb0ba0db9" have entirely different histories.
ea4638d815
...
db52b63db1
13
FEATURES.md
13
FEATURES.md
@ -74,8 +74,8 @@
|
||||
- [x] `yy` - Yank line (double press)
|
||||
|
||||
### Not Implemented
|
||||
- [x] `c` - Change operator
|
||||
- [x] `cc` - Change line
|
||||
- [ ] `c` - Change operator
|
||||
- [ ] `cc` - Change line
|
||||
- [ ] `>` - Indent right
|
||||
- [ ] `<` - Indent left
|
||||
- [ ] `=` - Auto-indent
|
||||
@ -96,9 +96,9 @@
|
||||
- [x] `A` - Insert at end of line
|
||||
- [x] `o` - Open line below
|
||||
- [x] `O` - Open line above
|
||||
- [x] `s` - Substitute character (delete + insert)
|
||||
- [x] `S` - Substitute line (delete line + insert)
|
||||
- [x] `C` - Change to end of line
|
||||
- [ ] `s` - Substitute character (delete + insert)
|
||||
- [ ] `S` - Substitute line (delete line + insert)
|
||||
- [ ] `C` - Change to end of line
|
||||
- [ ] `gi` - Insert at last insert position
|
||||
|
||||
### Delete Actions
|
||||
@ -415,8 +415,6 @@ Buffers are in-memory representations of files. A buffer exists for each open fi
|
||||
- [x] Delete operator (d, dd)
|
||||
- [x] Yank operator (y, yy)
|
||||
- [x] Paste actions (p, P)
|
||||
- [x] Change operator (c, cc, C)
|
||||
- [x] Substitute action (s, S)
|
||||
- [x] Insert mode entry (i, a, I, A, o, O)
|
||||
- [x] Insert mode editing (enter, backspace, delete, tab, ctrl+w)
|
||||
- [x] Visual modes (v, V, ctrl+v)
|
||||
@ -424,4 +422,3 @@ Buffers are in-memory representations of files. A buffer exists for each open fi
|
||||
- [x] Delete actions (x, D)
|
||||
- [x] Command mode basics
|
||||
- [x] Register behavior
|
||||
|
||||
|
||||
@ -8,21 +8,17 @@ import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
func generateLines(n int) []string {
|
||||
lines := make([]string, n)
|
||||
for i := range n {
|
||||
lines[i] = fmt.Sprintf("line %d", i+1)
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
func main() {
|
||||
m, _ := tea.NewProgram(
|
||||
editor.NewModel([]string{""}, action.Position{Line: 0, Col: 0}),
|
||||
tea.NewProgram(
|
||||
editor.NewModel(generateLines(64), action.Position{Line: 0, Col: 0}),
|
||||
tea.WithAltScreen(),
|
||||
).Run()
|
||||
|
||||
final, ok := m.(*editor.Model)
|
||||
if ok {
|
||||
fmt.Printf("PRINTING WINDOWS: %+v\n", final.Windows())
|
||||
fmt.Printf("PRINTING ACTIVE WINDOW: %+v\n", final.ActiveWindow())
|
||||
for _, win := range final.Windows() {
|
||||
fmt.Printf("\t%+v\n", *win.Buffer)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("PRINTING ALL: %+v\n", m)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -4,6 +4,43 @@ import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
// Mode constants for editor mode
|
||||
type Mode int
|
||||
|
||||
const (
|
||||
NormalMode Mode = iota
|
||||
InsertMode
|
||||
CommandMode
|
||||
VisualMode
|
||||
VisualLineMode
|
||||
VisualBlockMode
|
||||
)
|
||||
|
||||
func (m Mode) ToString() string {
|
||||
switch m {
|
||||
case NormalMode:
|
||||
return "NORMAL"
|
||||
case InsertMode:
|
||||
return "INSERT"
|
||||
case CommandMode:
|
||||
return "COMMAND"
|
||||
case VisualMode:
|
||||
return "VISUAL"
|
||||
case VisualLineMode:
|
||||
return "V-LINE"
|
||||
case VisualBlockMode:
|
||||
return "V-BLOCK"
|
||||
default:
|
||||
return "-----"
|
||||
}
|
||||
}
|
||||
|
||||
func (m Mode) IsVisualMode() bool {
|
||||
return m == VisualMode ||
|
||||
m == VisualLineMode ||
|
||||
m == VisualBlockMode
|
||||
}
|
||||
|
||||
// Model defines the interface for editor state that actions can modify
|
||||
type Model interface {
|
||||
// Text buffer
|
||||
@ -21,11 +58,6 @@ type Model interface {
|
||||
SetCursorY(y int)
|
||||
ClampCursorX()
|
||||
|
||||
// Windows
|
||||
Windows() []*Window
|
||||
ActiveWindowId() int
|
||||
ActiveWindow() *Window
|
||||
|
||||
// Window
|
||||
ScrollY() int
|
||||
SetScrollY(y int)
|
||||
@ -1,111 +0,0 @@
|
||||
package action
|
||||
|
||||
type BufferOptions struct {
|
||||
// tabstop expandtab
|
||||
}
|
||||
|
||||
type Buffer struct {
|
||||
// Buffer data
|
||||
Id int
|
||||
|
||||
// File data
|
||||
Filename string
|
||||
Filetype string
|
||||
Lines []string
|
||||
|
||||
// Flags (not used yet)
|
||||
Modified bool
|
||||
Loaded bool
|
||||
Listed bool
|
||||
|
||||
// Options BufferOptions
|
||||
// UndoTree TODO: This will be big
|
||||
}
|
||||
|
||||
// ==================================================
|
||||
// Helper methods
|
||||
// ==================================================
|
||||
|
||||
// Buffer.Line: Get the line at an index. Returns an empty string if the index
|
||||
// is out of bounds.
|
||||
func (b *Buffer) Line(idx int) string {
|
||||
if idx < 0 || idx >= len(b.Lines) {
|
||||
return ""
|
||||
}
|
||||
return b.Lines[idx]
|
||||
}
|
||||
|
||||
// Buffer.SetLine: Set the content of the line at an index. Does nothing if the
|
||||
// index is out of bounds.
|
||||
func (b *Buffer) SetLine(idx int, content string) {
|
||||
if idx >= 0 && idx < len(b.Lines) {
|
||||
b.Lines[idx] = content
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer.InsertLine: Insert a line with content at an index. The index is clamped
|
||||
// to valid bounds (0 to len(Lines)). The new line is inserted before the line at
|
||||
// the given index.
|
||||
func (b *Buffer) InsertLine(idx int, content string) {
|
||||
if idx < 0 {
|
||||
idx = 0
|
||||
}
|
||||
if idx > len(b.Lines) {
|
||||
idx = len(b.Lines)
|
||||
}
|
||||
b.Lines = append(b.Lines[:idx], append([]string{content}, b.Lines[idx:]...)...)
|
||||
}
|
||||
|
||||
// Buffer.DeleteLine: Delete a line at an index. Does nothing if the index is out
|
||||
// of bounds.
|
||||
func (b *Buffer) DeleteLine(idx int) {
|
||||
if idx >= 0 && idx < len(b.Lines) {
|
||||
b.Lines = append(b.Lines[:idx], b.Lines[idx+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer.LineCount: Get the number of lines in the buffer.
|
||||
func (b *Buffer) LineCount() int {
|
||||
return len(b.Lines)
|
||||
}
|
||||
|
||||
// ==================================================
|
||||
// Setters
|
||||
// ==================================================
|
||||
|
||||
// Buffer.SetFilename: Set the filename associated with this buffer. This is
|
||||
// typically the path to the file on disk that this buffer represents.
|
||||
func (b *Buffer) SetFilename(filename string) {
|
||||
b.Filename = filename
|
||||
}
|
||||
|
||||
// Buffer.SetFiletype: Set the filetype of this buffer. The filetype is used
|
||||
// for syntax highlighting and other language-specific features.
|
||||
func (b *Buffer) SetFiletype(filetype string) {
|
||||
b.Filetype = filetype
|
||||
}
|
||||
|
||||
// Buffer.SetLines: Replace all lines in the buffer with the provided lines.
|
||||
// This is useful when loading a file or resetting buffer content.
|
||||
func (b *Buffer) SetLines(lines []string) {
|
||||
b.Lines = lines
|
||||
}
|
||||
|
||||
// Buffer.SetModified: Set the modified flag for this buffer. A modified buffer
|
||||
// has unsaved changes that differ from the file on disk.
|
||||
func (b *Buffer) SetModified(modified bool) {
|
||||
b.Modified = modified
|
||||
}
|
||||
|
||||
// Buffer.SetLoaded: Set the loaded flag for this buffer. A loaded buffer has
|
||||
// its content in memory, while an unloaded buffer exists only as metadata.
|
||||
func (b *Buffer) SetLoaded(loaded bool) {
|
||||
b.Loaded = loaded
|
||||
}
|
||||
|
||||
// Buffer.SetListed: Set the listed flag for this buffer. A listed buffer appears
|
||||
// in buffer lists (like :ls), while an unlisted buffer is hidden from normal
|
||||
// buffer navigation.
|
||||
func (b *Buffer) SetListed(listed bool) {
|
||||
b.Listed = listed
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
package action
|
||||
|
||||
// Not great, but maybe the best way
|
||||
var CurrentBufferId int = 1
|
||||
|
||||
type BufferBuilder struct {
|
||||
buffer Buffer
|
||||
}
|
||||
|
||||
// NewBufferBuilder: Creates a new buffer builder. The buffer builder implements a
|
||||
// builder pattern to create a buffer with the defined properties and values.
|
||||
func NewBufferBuilder() *BufferBuilder {
|
||||
return &BufferBuilder{
|
||||
buffer: Buffer{
|
||||
Id: 0, // This is set when built
|
||||
Filename: "",
|
||||
Filetype: "",
|
||||
Lines: []string{""},
|
||||
Modified: false,
|
||||
Loaded: false,
|
||||
Listed: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BufferBuilder.WithFilename: Attaches a file name to the buffer that is being built.
|
||||
func (b *BufferBuilder) WithFilename(filename string) *BufferBuilder {
|
||||
b.buffer.Filename = filename
|
||||
return b
|
||||
}
|
||||
|
||||
// BufferBuilder.WithFiletype: Attaches a file type to the buffer that is being built.
|
||||
func (b *BufferBuilder) WithFiletype(filetype string) *BufferBuilder {
|
||||
b.buffer.Filetype = filetype
|
||||
return b
|
||||
}
|
||||
|
||||
// BufferBuilder.WithLines: Attaches a lines to the buffer that is being built.
|
||||
func (b *BufferBuilder) WithLines(lines []string) *BufferBuilder {
|
||||
b.buffer.Lines = lines
|
||||
return b
|
||||
}
|
||||
|
||||
// BufferBuilder.Modified: Sets the modified flag of the buffer being built. By default,
|
||||
// buffers are built with the modified flag being false.
|
||||
func (b *BufferBuilder) Modified() *BufferBuilder {
|
||||
b.buffer.Modified = true
|
||||
return b
|
||||
}
|
||||
|
||||
// BufferBuilder.Loaded: Sets the loaded flag of the buffer being built. By default,
|
||||
// buffers are built with the loaded flag being false.
|
||||
func (b *BufferBuilder) Loaded() *BufferBuilder {
|
||||
b.buffer.Loaded = true
|
||||
return b
|
||||
}
|
||||
|
||||
// BufferBuilder.Listed: Sets the listed flag of the buffer being built. By default,
|
||||
// buffers are built with the listed flag being false.
|
||||
func (b *BufferBuilder) Listed() *BufferBuilder {
|
||||
b.buffer.Listed = true
|
||||
return b
|
||||
}
|
||||
|
||||
// BufferBuilder.Build: Build the final buffer and return it to the caller. Final
|
||||
// step in the process. This is where the ID is set, so many buffers can be "in-progress"
|
||||
// but the ID will be set when they are built. Meaning, this is not thread safe.
|
||||
func (b *BufferBuilder) Build() Buffer {
|
||||
b.buffer.Id = CurrentBufferId
|
||||
CurrentBufferId++
|
||||
|
||||
return b.buffer
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
package action
|
||||
|
||||
// Mode constants for editor mode
|
||||
type Mode int
|
||||
|
||||
const (
|
||||
NormalMode Mode = iota
|
||||
InsertMode
|
||||
CommandMode
|
||||
VisualMode
|
||||
VisualLineMode
|
||||
VisualBlockMode
|
||||
)
|
||||
|
||||
func (m Mode) ToString() string {
|
||||
switch m {
|
||||
case NormalMode:
|
||||
return "NORMAL"
|
||||
case InsertMode:
|
||||
return "INSERT"
|
||||
case CommandMode:
|
||||
return "COMMAND"
|
||||
case VisualMode:
|
||||
return "VISUAL"
|
||||
case VisualLineMode:
|
||||
return "V-LINE"
|
||||
case VisualBlockMode:
|
||||
return "V-BLOCK"
|
||||
default:
|
||||
return "-----"
|
||||
}
|
||||
}
|
||||
|
||||
func (m Mode) IsVisualMode() bool {
|
||||
return m == VisualMode ||
|
||||
m == VisualLineMode ||
|
||||
m == VisualBlockMode
|
||||
}
|
||||
2
internal/action/tmp
Normal file
2
internal/action/tmp
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
hello
|
||||
@ -1,124 +0,0 @@
|
||||
package action
|
||||
|
||||
// TODO: No more global settings, window-wide settings
|
||||
type WinOptions struct {
|
||||
// Number bool
|
||||
// Wrap bool
|
||||
// Relnumber bool
|
||||
}
|
||||
|
||||
type Window struct {
|
||||
Id int
|
||||
Number int // Ignored for now, will be used when splits come into play
|
||||
Buffer *Buffer
|
||||
|
||||
Cursor Position
|
||||
Anchor Position
|
||||
|
||||
ScrollY int
|
||||
Height int
|
||||
Width int
|
||||
|
||||
// Folds // TODO
|
||||
// Options WinOptions
|
||||
}
|
||||
|
||||
// ==================================================
|
||||
// Helper methods
|
||||
// ==================================================
|
||||
|
||||
// Window.ClampCursorX: Clamps the cursor in the X direction to ensure the cursor
|
||||
// does not go into an invalid position. Such as negative values or past the end of
|
||||
// the line.
|
||||
func (w *Window) ClampCursorX() {
|
||||
lineLen := len(w.Buffer.Lines[w.Cursor.Line])
|
||||
if lineLen == 0 {
|
||||
w.Cursor.Col = 0
|
||||
} else if w.Cursor.Col >= lineLen {
|
||||
w.Cursor.Col = lineLen
|
||||
}
|
||||
}
|
||||
|
||||
// ==================================================
|
||||
// Setters
|
||||
// ==================================================
|
||||
|
||||
// Window.SetNumber: Sets the position-based number of this window. Currently ignored
|
||||
// until splits are implemented.
|
||||
func (w *Window) SetNumber(number int) {
|
||||
w.Number = number
|
||||
}
|
||||
|
||||
// Window.SetBuffer: Sets the buffer that this window should display. This is used when
|
||||
// switching between buffers or opening a new file in the current window.
|
||||
func (w *Window) SetBuffer(buffer *Buffer) {
|
||||
w.Buffer = buffer
|
||||
}
|
||||
|
||||
// Window.SetCursor: Sets the cursor position in this window to the given position.
|
||||
func (w *Window) SetCursor(cursor Position) {
|
||||
w.Cursor = cursor
|
||||
}
|
||||
|
||||
// Window.SetCursorLine: Sets the line number of the cursor position.
|
||||
func (w *Window) SetCursorLine(line int) {
|
||||
w.Cursor.Line = line
|
||||
}
|
||||
|
||||
// Window.SetCursorCol: Sets the column number of the cursor position.
|
||||
func (w *Window) SetCursorCol(col int) {
|
||||
w.Cursor.Col = col
|
||||
}
|
||||
|
||||
// Window.SetCursorPos: Sets both the line and column of the cursor position. This is
|
||||
// a convenience method for setting both components at once.
|
||||
func (w *Window) SetCursorPos(line, col int) {
|
||||
w.Cursor.Line = line
|
||||
w.Cursor.Col = col
|
||||
}
|
||||
|
||||
// Window.SetAnchor: Sets the anchor position in this window. The anchor is used for
|
||||
// visual mode selections as the starting point of the selection.
|
||||
func (w *Window) SetAnchor(anchor Position) {
|
||||
w.Anchor = anchor
|
||||
}
|
||||
|
||||
// Window.SetAnchorLine: Sets the line number of the anchor position.
|
||||
func (w *Window) SetAnchorLine(line int) {
|
||||
w.Anchor.Line = line
|
||||
}
|
||||
|
||||
// Window.SetAnchorCol: Sets the column number of the anchor position.
|
||||
func (w *Window) SetAnchorCol(col int) {
|
||||
w.Anchor.Col = col
|
||||
}
|
||||
|
||||
// Window.SetAnchorPos: Sets both the line and column of the anchor position. This is
|
||||
// a convenience method for setting both components at once.
|
||||
func (w *Window) SetAnchorPos(line, col int) {
|
||||
w.Anchor.Line = line
|
||||
w.Anchor.Col = col
|
||||
}
|
||||
|
||||
// Window.SetScrollY: Sets the vertical scroll offset of this window. This determines
|
||||
// which line appears at the top of the visible viewport.
|
||||
func (w *Window) SetScrollY(scrollY int) {
|
||||
w.ScrollY = scrollY
|
||||
}
|
||||
|
||||
// Window.SetHeight: Sets the height of this window in lines.
|
||||
func (w *Window) SetHeight(height int) {
|
||||
w.Height = height
|
||||
}
|
||||
|
||||
// Window.SetWidth: Sets the width of this window in columns.
|
||||
func (w *Window) SetWidth(width int) {
|
||||
w.Width = width
|
||||
}
|
||||
|
||||
// Window.SetDimensions: Sets both the width and height of this window. This is a
|
||||
// convenience method for setting both dimensions at once.
|
||||
func (w *Window) SetDimensions(width, height int) {
|
||||
w.Width = width
|
||||
w.Height = height
|
||||
}
|
||||
@ -1,103 +0,0 @@
|
||||
package action
|
||||
|
||||
// Not great, but maybe the best way
|
||||
var CurrentWindowId int = 1000
|
||||
|
||||
type WindowBuilder struct {
|
||||
window Window
|
||||
}
|
||||
|
||||
// NewWindowBuilder: Creates a new window builder. The window builder implements a
|
||||
// builder pattern to create a window with the defined properties and values.
|
||||
func NewWindowBuilder() *WindowBuilder {
|
||||
return &WindowBuilder{
|
||||
window: Window{
|
||||
Id: 0, // This is set when built
|
||||
Number: 1, // Ignored for now, will be used for splits
|
||||
Buffer: nil,
|
||||
Cursor: Position{Line: 0, Col: 0},
|
||||
Anchor: Position{Line: 0, Col: 0},
|
||||
ScrollY: 0,
|
||||
Height: 0,
|
||||
Width: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WindowBuilder.WithNumber: Attaches a window number to the window that is being built.
|
||||
// Window numbers are position-based and change when windows are rearranged. This is
|
||||
// ignored for now, but will be used when splits are implemented.
|
||||
func (w *WindowBuilder) WithNumber(number int) *WindowBuilder {
|
||||
w.window.Number = number
|
||||
return w
|
||||
}
|
||||
|
||||
// WindowBuilder.WithBuffer: Attaches a buffer to the window that is being built. The
|
||||
// window will display and edit the content of this buffer.
|
||||
func (w *WindowBuilder) WithBuffer(buffer *Buffer) *WindowBuilder {
|
||||
w.window.Buffer = buffer
|
||||
return w
|
||||
}
|
||||
|
||||
// WindowBuilder.WithCursor: Sets the cursor position in the window that is being built.
|
||||
func (w *WindowBuilder) WithCursor(cursor Position) *WindowBuilder {
|
||||
w.window.Cursor = cursor
|
||||
return w
|
||||
}
|
||||
|
||||
// WindowBuilder.WithCursorPos: Sets the cursor position in the window that is being built.
|
||||
// This is an alias for WithCursor that accepts line and column separately.
|
||||
func (w *WindowBuilder) WithCursorPos(line, col int) *WindowBuilder {
|
||||
w.window.Cursor = Position{Line: line, Col: col}
|
||||
return w
|
||||
}
|
||||
|
||||
// WindowBuilder.WithAnchor: Sets the anchor position in the window that is being built.
|
||||
// The anchor is used for visual mode selections.
|
||||
func (w *WindowBuilder) WithAnchor(anchor Position) *WindowBuilder {
|
||||
w.window.Anchor = anchor
|
||||
return w
|
||||
}
|
||||
|
||||
// WindowBuilder.WithAnchorPos: Sets the anchor position in the window that is being built.
|
||||
// This is an alias for WithAnchor that accepts line and column separately.
|
||||
func (w *WindowBuilder) WithAnchorPos(line, col int) *WindowBuilder {
|
||||
w.window.Anchor = Position{Line: line, Col: col}
|
||||
return w
|
||||
}
|
||||
|
||||
// WindowBuilder.WithScrollY: Sets the vertical scroll offset of the window that is being built.
|
||||
func (w *WindowBuilder) WithScrollY(scrollY int) *WindowBuilder {
|
||||
w.window.ScrollY = scrollY
|
||||
return w
|
||||
}
|
||||
|
||||
// WindowBuilder.WithHeight: Sets the height of the window that is being built.
|
||||
func (w *WindowBuilder) WithHeight(height int) *WindowBuilder {
|
||||
w.window.Height = height
|
||||
return w
|
||||
}
|
||||
|
||||
// WindowBuilder.WithWidth: Sets the width of the window that is being built.
|
||||
func (w *WindowBuilder) WithWidth(width int) *WindowBuilder {
|
||||
w.window.Width = width
|
||||
return w
|
||||
}
|
||||
|
||||
// WindowBuilder.WithDimensions: Sets both width and height of the window that is being built.
|
||||
// This is a convenience method for setting dimensions in one call.
|
||||
func (w *WindowBuilder) WithDimensions(width, height int) *WindowBuilder {
|
||||
w.window.Width = width
|
||||
w.window.Height = height
|
||||
return w
|
||||
}
|
||||
|
||||
// WindowBuilder.Build: Build the final window and return it to the caller. Final
|
||||
// step in the process. This is where the ID is set, so many windows can be "in-progress"
|
||||
// but the ID will be set when they are built. Meaning, this is not thread safe.
|
||||
func (w *WindowBuilder) Build() Window {
|
||||
w.window.Id = CurrentWindowId
|
||||
CurrentWindowId++
|
||||
|
||||
return w.window
|
||||
}
|
||||
Binary file not shown.
@ -19,8 +19,8 @@ func TestHelperExamples(t *testing.T) {
|
||||
WithLines([]string{"hello", "world"}),
|
||||
)
|
||||
m := getFinalModel(t, tm)
|
||||
if len(m.Lines()) != 2 {
|
||||
t.Errorf("expected 2 lines, got %d", len(m.Lines()))
|
||||
if len(m.lines) != 2 {
|
||||
t.Errorf("expected 2 lines, got %d", len(m.lines))
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@ -123,8 +123,8 @@ func newTestModelWithTermSize(t *testing.T, lines []string, pos action.Position,
|
||||
}
|
||||
|
||||
// getFinalModel extracts the final model state (sends ctrl+c to quit first)
|
||||
func getFinalModel(t *testing.T, tm *teatest.TestModel) *Model {
|
||||
func getFinalModel(t *testing.T, tm *teatest.TestModel) Model {
|
||||
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlC})
|
||||
fm := tm.FinalModel(t, teatest.WithFinalTimeout(time.Second))
|
||||
return fm.(*Model)
|
||||
return fm.(Model)
|
||||
}
|
||||
|
||||
@ -13,8 +13,8 @@ func TestDeleteChar(t *testing.T) {
|
||||
sendKeys(tm, "x")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "ello" {
|
||||
t.Errorf("lines[0] = %q, want 'ello'", m.Line(0))
|
||||
if m.lines[0] != "ello" {
|
||||
t.Errorf("lines[0] = %q, want 'ello'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -24,8 +24,8 @@ func TestDeleteChar(t *testing.T) {
|
||||
sendKeys(tm, "x")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "helo" {
|
||||
t.Errorf("lines[0] = %q, want 'helo'", m.Line(0))
|
||||
if m.lines[0] != "helo" {
|
||||
t.Errorf("lines[0] = %q, want 'helo'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -35,8 +35,8 @@ func TestDeleteChar(t *testing.T) {
|
||||
sendKeys(tm, "x")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "hell" {
|
||||
t.Errorf("lines[0] = %q, want 'hell'", m.Line(0))
|
||||
if m.lines[0] != "hell" {
|
||||
t.Errorf("lines[0] = %q, want 'hell'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -46,8 +46,8 @@ func TestDeleteChar(t *testing.T) {
|
||||
sendKeys(tm, "x", "x")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "llo" {
|
||||
t.Errorf("lines[0] = %q, want 'llo'", m.Line(0))
|
||||
if m.lines[0] != "llo" {
|
||||
t.Errorf("lines[0] = %q, want 'llo'", m.lines[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -59,8 +59,8 @@ func TestDeleteCharWithCount(t *testing.T) {
|
||||
sendKeys(tm, "3", "x")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "lo" {
|
||||
t.Errorf("lines[0] = %q, want 'lo'", m.Line(0))
|
||||
if m.lines[0] != "lo" {
|
||||
t.Errorf("lines[0] = %q, want 'lo'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -70,8 +70,8 @@ func TestDeleteCharWithCount(t *testing.T) {
|
||||
sendKeys(tm, "1", "0", "x")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "" {
|
||||
t.Errorf("lines[0] = %q, want ''", m.Line(0))
|
||||
if m.lines[0] != "" {
|
||||
t.Errorf("lines[0] = %q, want ''", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -81,8 +81,8 @@ func TestDeleteCharWithCount(t *testing.T) {
|
||||
sendKeys(tm, "2", "x")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "hlo" {
|
||||
t.Errorf("lines[0] = %q, want 'hlo'", m.Line(0))
|
||||
if m.lines[0] != "hlo" {
|
||||
t.Errorf("lines[0] = %q, want 'hlo'", m.lines[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -25,8 +25,8 @@ func TestEnterInsert(t *testing.T) {
|
||||
sendKeys(tm, "i", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "Xhello" {
|
||||
t.Errorf("lines[0] = %q, want 'Xhello'", m.Line(0))
|
||||
if m.lines[0] != "Xhello" {
|
||||
t.Errorf("lines[0] = %q, want 'Xhello'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -36,8 +36,8 @@ func TestEnterInsert(t *testing.T) {
|
||||
sendKeys(tm, "i", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "heXllo" {
|
||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
|
||||
if m.lines[0] != "heXllo" {
|
||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -47,8 +47,8 @@ func TestEnterInsert(t *testing.T) {
|
||||
sendKeys(tm, "i", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorX() != 2 {
|
||||
t.Errorf("cursor.x = %d, want 2", m.CursorX())
|
||||
if m.cursor.x != 2 {
|
||||
t.Errorf("cursor.x = %d, want 2", m.cursor.x)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -70,8 +70,8 @@ func TestEnterInsertAfter(t *testing.T) {
|
||||
sendKeys(tm, "a", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "hXello" {
|
||||
t.Errorf("lines[0] = %q, want 'hXello'", m.Line(0))
|
||||
if m.lines[0] != "hXello" {
|
||||
t.Errorf("lines[0] = %q, want 'hXello'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -81,8 +81,8 @@ func TestEnterInsertAfter(t *testing.T) {
|
||||
sendKeys(tm, "a", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "helXlo" {
|
||||
t.Errorf("lines[0] = %q, want 'helXlo'", m.Line(0))
|
||||
if m.lines[0] != "helXlo" {
|
||||
t.Errorf("lines[0] = %q, want 'helXlo'", m.lines[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -94,8 +94,8 @@ func TestEnterInsertLineStart(t *testing.T) {
|
||||
sendKeys(tm, "I", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "Xhello" {
|
||||
t.Errorf("lines[0] = %q, want 'Xhello'", m.Line(0))
|
||||
if m.lines[0] != "Xhello" {
|
||||
t.Errorf("lines[0] = %q, want 'Xhello'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -105,8 +105,8 @@ func TestEnterInsertLineStart(t *testing.T) {
|
||||
sendKeys(tm, "I", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "Xhello" {
|
||||
t.Errorf("lines[0] = %q, want 'Xhello'", m.Line(0))
|
||||
if m.lines[0] != "Xhello" {
|
||||
t.Errorf("lines[0] = %q, want 'Xhello'", m.lines[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -118,8 +118,8 @@ func TestEnterInsertLineEnd(t *testing.T) {
|
||||
sendKeys(tm, "A", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "helloX" {
|
||||
t.Errorf("lines[0] = %q, want 'helloX'", m.Line(0))
|
||||
if m.lines[0] != "helloX" {
|
||||
t.Errorf("lines[0] = %q, want 'helloX'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -129,8 +129,8 @@ func TestEnterInsertLineEnd(t *testing.T) {
|
||||
sendKeys(tm, "A", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "helloX" {
|
||||
t.Errorf("lines[0] = %q, want 'helloX'", m.Line(0))
|
||||
if m.lines[0] != "helloX" {
|
||||
t.Errorf("lines[0] = %q, want 'helloX'", m.lines[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -144,11 +144,11 @@ func TestOpenLineBelow(t *testing.T) {
|
||||
sendKeys(tm, "o", "n", "e", "w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 3 {
|
||||
t.Errorf("len(lines) = %d, want 3", m.LineCount())
|
||||
if len(m.lines) != 3 {
|
||||
t.Errorf("len(lines) = %d, want 3", len(m.lines))
|
||||
}
|
||||
if m.Line(1) != "new" {
|
||||
t.Errorf("lines[1] = %q, want 'new'", m.Line(1))
|
||||
if m.lines[1] != "new" {
|
||||
t.Errorf("lines[1] = %q, want 'new'", m.lines[1])
|
||||
}
|
||||
})
|
||||
|
||||
@ -158,11 +158,11 @@ func TestOpenLineBelow(t *testing.T) {
|
||||
sendKeys(tm, "o", "n", "e", "w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 4 {
|
||||
t.Errorf("len(lines) = %d, want 4", m.LineCount())
|
||||
if len(m.lines) != 4 {
|
||||
t.Errorf("len(lines) = %d, want 4", len(m.lines))
|
||||
}
|
||||
if m.Line(2) != "new" {
|
||||
t.Errorf("lines[2] = %q, want 'new'", m.Line(2))
|
||||
if m.lines[2] != "new" {
|
||||
t.Errorf("lines[2] = %q, want 'new'", m.lines[2])
|
||||
}
|
||||
})
|
||||
|
||||
@ -172,11 +172,11 @@ func TestOpenLineBelow(t *testing.T) {
|
||||
sendKeys(tm, "o", "n", "e", "w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 3 {
|
||||
t.Errorf("len(lines) = %d, want 3", m.LineCount())
|
||||
if len(m.lines) != 3 {
|
||||
t.Errorf("len(lines) = %d, want 3", len(m.lines))
|
||||
}
|
||||
if m.Line(2) != "new" {
|
||||
t.Errorf("lines[2] = %q, want 'new'", m.Line(2))
|
||||
if m.lines[2] != "new" {
|
||||
t.Errorf("lines[2] = %q, want 'new'", m.lines[2])
|
||||
}
|
||||
})
|
||||
|
||||
@ -186,11 +186,11 @@ func TestOpenLineBelow(t *testing.T) {
|
||||
sendKeys(tm, "o", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorY() != 1 {
|
||||
t.Errorf("cursor.y = %d, want 1", m.CursorY())
|
||||
if m.cursor.y != 1 {
|
||||
t.Errorf("cursor.y = %d, want 1", m.cursor.y)
|
||||
}
|
||||
if m.CursorX() != 0 {
|
||||
t.Errorf("cursor.x = %d, want 0", m.CursorX())
|
||||
if m.cursor.x != 0 {
|
||||
t.Errorf("cursor.x = %d, want 0", m.cursor.x)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -202,12 +202,12 @@ func TestOpenLineBelowWithCount(t *testing.T) {
|
||||
sendKeys(tm, "3", "o", "x", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 4 {
|
||||
t.Errorf("len(lines) = %d, want 4", m.LineCount())
|
||||
if len(m.lines) != 4 {
|
||||
t.Errorf("len(lines) = %d, want 4", len(m.lines))
|
||||
}
|
||||
for i := 1; i <= 3; i++ {
|
||||
if m.Line(i) != "x" {
|
||||
t.Errorf("lines[%d] = %q, want 'x'", i, m.Line(i))
|
||||
if m.lines[i] != "x" {
|
||||
t.Errorf("lines[%d] = %q, want 'x'", i, m.lines[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -218,14 +218,14 @@ func TestOpenLineBelowWithCount(t *testing.T) {
|
||||
sendKeys(tm, "2", "o", "a", "b", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 3 {
|
||||
t.Errorf("len(lines) = %d, want 3", m.LineCount())
|
||||
if len(m.lines) != 3 {
|
||||
t.Errorf("len(lines) = %d, want 3", len(m.lines))
|
||||
}
|
||||
if m.Line(1) != "ab" {
|
||||
t.Errorf("lines[1] = %q, want 'ab'", m.Line(1))
|
||||
if m.lines[1] != "ab" {
|
||||
t.Errorf("lines[1] = %q, want 'ab'", m.lines[1])
|
||||
}
|
||||
if m.Line(2) != "ab" {
|
||||
t.Errorf("lines[2] = %q, want 'ab'", m.Line(2))
|
||||
if m.lines[2] != "ab" {
|
||||
t.Errorf("lines[2] = %q, want 'ab'", m.lines[2])
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -237,11 +237,11 @@ func TestOpenLineAbove(t *testing.T) {
|
||||
sendKeys(tm, "O", "n", "e", "w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 3 {
|
||||
t.Errorf("len(lines) = %d, want 3", m.LineCount())
|
||||
if len(m.lines) != 3 {
|
||||
t.Errorf("len(lines) = %d, want 3", len(m.lines))
|
||||
}
|
||||
if m.Line(1) != "new" {
|
||||
t.Errorf("lines[1] = %q, want 'new'", m.Line(1))
|
||||
if m.lines[1] != "new" {
|
||||
t.Errorf("lines[1] = %q, want 'new'", m.lines[1])
|
||||
}
|
||||
})
|
||||
|
||||
@ -251,11 +251,11 @@ func TestOpenLineAbove(t *testing.T) {
|
||||
sendKeys(tm, "O", "n", "e", "w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 3 {
|
||||
t.Errorf("len(lines) = %d, want 3", m.LineCount())
|
||||
if len(m.lines) != 3 {
|
||||
t.Errorf("len(lines) = %d, want 3", len(m.lines))
|
||||
}
|
||||
if m.Line(0) != "new" {
|
||||
t.Errorf("lines[0] = %q, want 'new'", m.Line(0))
|
||||
if m.lines[0] != "new" {
|
||||
t.Errorf("lines[0] = %q, want 'new'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -265,8 +265,8 @@ func TestOpenLineAbove(t *testing.T) {
|
||||
sendKeys(tm, "O", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorX() != 0 {
|
||||
t.Errorf("cursor.x = %d, want 0", m.CursorX())
|
||||
if m.cursor.x != 0 {
|
||||
t.Errorf("cursor.x = %d, want 0", m.cursor.x)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -278,12 +278,12 @@ func TestOpenLineAboveWithCount(t *testing.T) {
|
||||
sendKeys(tm, "3", "O", "x", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 4 {
|
||||
t.Errorf("len(lines) = %d, want 4", m.LineCount())
|
||||
if len(m.lines) != 4 {
|
||||
t.Errorf("len(lines) = %d, want 4", len(m.lines))
|
||||
}
|
||||
for i := 0; i < 3; i++ {
|
||||
if m.Line(i) != "x" {
|
||||
t.Errorf("lines[%d] = %q, want 'x'", i, m.Line(i))
|
||||
if m.lines[i] != "x" {
|
||||
t.Errorf("lines[%d] = %q, want 'x'", i, m.lines[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -298,14 +298,14 @@ func TestInsertModeEnter(t *testing.T) {
|
||||
sendKeys(tm, "i", "enter", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 2 {
|
||||
t.Errorf("len(lines) = %d, want 2", m.LineCount())
|
||||
if len(m.lines) != 2 {
|
||||
t.Errorf("len(lines) = %d, want 2", len(m.lines))
|
||||
}
|
||||
if m.Line(0) != "hello" {
|
||||
t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
|
||||
if m.lines[0] != "hello" {
|
||||
t.Errorf("lines[0] = %q, want 'hello'", m.lines[0])
|
||||
}
|
||||
if m.Line(1) != " world" {
|
||||
t.Errorf("lines[1] = %q, want ' world'", m.Line(1))
|
||||
if m.lines[1] != " world" {
|
||||
t.Errorf("lines[1] = %q, want ' world'", m.lines[1])
|
||||
}
|
||||
})
|
||||
|
||||
@ -315,14 +315,14 @@ func TestInsertModeEnter(t *testing.T) {
|
||||
sendKeys(tm, "i", "enter", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 2 {
|
||||
t.Errorf("len(lines) = %d, want 2", m.LineCount())
|
||||
if len(m.lines) != 2 {
|
||||
t.Errorf("len(lines) = %d, want 2", len(m.lines))
|
||||
}
|
||||
if m.Line(0) != "hello" {
|
||||
t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
|
||||
if m.lines[0] != "hello" {
|
||||
t.Errorf("lines[0] = %q, want 'hello'", m.lines[0])
|
||||
}
|
||||
if m.Line(1) != "" {
|
||||
t.Errorf("lines[1] = %q, want ''", m.Line(1))
|
||||
if m.lines[1] != "" {
|
||||
t.Errorf("lines[1] = %q, want ''", m.lines[1])
|
||||
}
|
||||
})
|
||||
|
||||
@ -332,14 +332,14 @@ func TestInsertModeEnter(t *testing.T) {
|
||||
sendKeys(tm, "i", "enter", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 2 {
|
||||
t.Errorf("len(lines) = %d, want 2", m.LineCount())
|
||||
if len(m.lines) != 2 {
|
||||
t.Errorf("len(lines) = %d, want 2", len(m.lines))
|
||||
}
|
||||
if m.Line(0) != "" {
|
||||
t.Errorf("lines[0] = %q, want ''", m.Line(0))
|
||||
if m.lines[0] != "" {
|
||||
t.Errorf("lines[0] = %q, want ''", m.lines[0])
|
||||
}
|
||||
if m.Line(1) != "hello" {
|
||||
t.Errorf("lines[1] = %q, want 'hello'", m.Line(1))
|
||||
if m.lines[1] != "hello" {
|
||||
t.Errorf("lines[1] = %q, want 'hello'", m.lines[1])
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -351,8 +351,8 @@ func TestInsertModeBackspace(t *testing.T) {
|
||||
sendKeys(tm, "i", "backspace", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "helo" {
|
||||
t.Errorf("lines[0] = %q, want 'helo'", m.Line(0))
|
||||
if m.lines[0] != "helo" {
|
||||
t.Errorf("lines[0] = %q, want 'helo'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -362,11 +362,11 @@ func TestInsertModeBackspace(t *testing.T) {
|
||||
sendKeys(tm, "i", "backspace", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 1 {
|
||||
t.Errorf("len(lines) = %d, want 1", m.LineCount())
|
||||
if len(m.lines) != 1 {
|
||||
t.Errorf("len(lines) = %d, want 1", len(m.lines))
|
||||
}
|
||||
if m.Line(0) != "helloworld" {
|
||||
t.Errorf("lines[0] = %q, want 'helloworld'", m.Line(0))
|
||||
if m.lines[0] != "helloworld" {
|
||||
t.Errorf("lines[0] = %q, want 'helloworld'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -376,8 +376,8 @@ func TestInsertModeBackspace(t *testing.T) {
|
||||
sendKeys(tm, "i", "backspace", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "hello" {
|
||||
t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
|
||||
if m.lines[0] != "hello" {
|
||||
t.Errorf("lines[0] = %q, want 'hello'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -387,8 +387,8 @@ func TestInsertModeBackspace(t *testing.T) {
|
||||
sendKeys(tm, "i", "backspace", "backspace", "backspace", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "he" {
|
||||
t.Errorf("lines[0] = %q, want 'he'", m.Line(0))
|
||||
if m.lines[0] != "he" {
|
||||
t.Errorf("lines[0] = %q, want 'he'", m.lines[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -400,8 +400,8 @@ func TestInsertModeDelete(t *testing.T) {
|
||||
sendKeys(tm, "i", "delete", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "word" {
|
||||
t.Errorf("lines[0] = %q, want 'word'", m.Line(0))
|
||||
if m.lines[0] != "word" {
|
||||
t.Errorf("lines[0] = %q, want 'word'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -411,11 +411,11 @@ func TestInsertModeDelete(t *testing.T) {
|
||||
sendKeys(tm, "i", "delete", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 1 {
|
||||
t.Errorf("len(lines) = %d, want 1", m.LineCount())
|
||||
if len(m.lines) != 1 {
|
||||
t.Errorf("len(lines) = %d, want 1", len(m.lines))
|
||||
}
|
||||
if m.Line(0) != "helloworld" {
|
||||
t.Errorf("lines[0] = %q, want 'helloworld'", m.Line(0))
|
||||
if m.lines[0] != "helloworld" {
|
||||
t.Errorf("lines[0] = %q, want 'helloworld'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -425,11 +425,11 @@ func TestInsertModeDelete(t *testing.T) {
|
||||
sendKeys(tm, "i", "delete", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.LineCount() != 1 {
|
||||
t.Errorf("len(lines) = %d, want 1", m.LineCount())
|
||||
if len(m.lines) != 1 {
|
||||
t.Errorf("len(lines) = %d, want 1", len(m.lines))
|
||||
}
|
||||
if m.Line(0) != "world" {
|
||||
t.Errorf("lines[0] = %q, want 'world'", m.Line(0))
|
||||
if m.lines[0] != "world" {
|
||||
t.Errorf("lines[0] = %q, want 'world'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -439,8 +439,8 @@ func TestInsertModeDelete(t *testing.T) {
|
||||
sendKeys(tm, "i", "delete", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "hello" {
|
||||
t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
|
||||
if m.lines[0] != "hello" {
|
||||
t.Errorf("lines[0] = %q, want 'hello'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -450,8 +450,8 @@ func TestInsertModeDelete(t *testing.T) {
|
||||
sendKeys(tm, "i", "delete", "delete", "delete", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "ho" {
|
||||
t.Errorf("lines[0] = %q, want 'he'", m.Line(0))
|
||||
if m.lines[0] != "ho" {
|
||||
t.Errorf("lines[0] = %q, want 'he'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -464,8 +464,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
|
||||
sendKeys(tm, "i", "left", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "heXllo" {
|
||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
|
||||
if m.lines[0] != "heXllo" {
|
||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -475,8 +475,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
|
||||
sendKeys(tm, "i", "right", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "heXllo" {
|
||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
|
||||
if m.lines[0] != "heXllo" {
|
||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -486,11 +486,11 @@ func TestInsertModeArrowKeys(t *testing.T) {
|
||||
sendKeys(tm, "i", "up", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "heXllo" {
|
||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
|
||||
if m.lines[0] != "heXllo" {
|
||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.lines[0])
|
||||
}
|
||||
if m.Line(1) != "world" {
|
||||
t.Errorf("lines[1] = %q, want 'world'", m.Line(1))
|
||||
if m.lines[1] != "world" {
|
||||
t.Errorf("lines[1] = %q, want 'world'", m.lines[1])
|
||||
}
|
||||
})
|
||||
|
||||
@ -500,11 +500,11 @@ func TestInsertModeArrowKeys(t *testing.T) {
|
||||
sendKeys(tm, "i", "down", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "hello" {
|
||||
t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
|
||||
if m.lines[0] != "hello" {
|
||||
t.Errorf("lines[0] = %q, want 'hello'", m.lines[0])
|
||||
}
|
||||
if m.Line(1) != "woXrld" {
|
||||
t.Errorf("lines[1] = %q, want 'woXrld'", m.Line(1))
|
||||
if m.lines[1] != "woXrld" {
|
||||
t.Errorf("lines[1] = %q, want 'woXrld'", m.lines[1])
|
||||
}
|
||||
})
|
||||
|
||||
@ -514,8 +514,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
|
||||
sendKeys(tm, "i", "left", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "Xhello" {
|
||||
t.Errorf("lines[0] = %q, want 'Xhello'", m.Line(0))
|
||||
if m.lines[0] != "Xhello" {
|
||||
t.Errorf("lines[0] = %q, want 'Xhello'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -525,8 +525,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
|
||||
sendKeys(tm, "a", "right", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "helloX" {
|
||||
t.Errorf("lines[0] = %q, want 'helloX'", m.Line(0))
|
||||
if m.lines[0] != "helloX" {
|
||||
t.Errorf("lines[0] = %q, want 'helloX'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -536,8 +536,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
|
||||
sendKeys(tm, "i", "up", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "heXllo" {
|
||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
|
||||
if m.lines[0] != "heXllo" {
|
||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -547,8 +547,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
|
||||
sendKeys(tm, "i", "down", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "heXllo" {
|
||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
|
||||
if m.lines[0] != "heXllo" {
|
||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -558,8 +558,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
|
||||
sendKeys(tm, "i", "up", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "hiX" {
|
||||
t.Errorf("lines[0] = %q, want 'hiX'", m.Line(0))
|
||||
if m.lines[0] != "hiX" {
|
||||
t.Errorf("lines[0] = %q, want 'hiX'", m.lines[0])
|
||||
}
|
||||
})
|
||||
|
||||
@ -569,8 +569,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
|
||||
sendKeys(tm, "i", "down", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(1) != "hiX" {
|
||||
t.Errorf("lines[1] = %q, want 'hiX'", m.Line(1))
|
||||
if m.lines[1] != "hiX" {
|
||||
t.Errorf("lines[1] = %q, want 'hiX'", m.lines[1])
|
||||
}
|
||||
})
|
||||
|
||||
@ -580,8 +580,8 @@ func TestInsertModeArrowKeys(t *testing.T) {
|
||||
sendKeys(tm, "i", "right", "right", "down", "X", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(1) != "woXrld" {
|
||||
t.Errorf("lines[1] = %q, want 'woXrld'", m.Line(1))
|
||||
if m.lines[1] != "woXrld" {
|
||||
t.Errorf("lines[1] = %q, want 'woXrld'", m.lines[1])
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -593,8 +593,8 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
|
||||
sendKeys(tm, "a", "ctrl+w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "hello " {
|
||||
t.Errorf("lines[0] = %q, want 'hello '", m.Line(0))
|
||||
if m.lines[0] != "hello " {
|
||||
t.Errorf("lines[0] = %q, want 'hello '", m.lines[0])
|
||||
}
|
||||
if m.CursorX() != 5 {
|
||||
t.Errorf("CursorX() = %d, want '5'", m.CursorX())
|
||||
@ -607,8 +607,8 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
|
||||
sendKeys(tm, "a", "ctrl+w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "" {
|
||||
t.Errorf("lines[0] = %q, want ''", m.Line(0))
|
||||
if m.lines[0] != "" {
|
||||
t.Errorf("lines[0] = %q, want ''", m.lines[0])
|
||||
}
|
||||
if m.CursorX() != 0 {
|
||||
t.Errorf("CursorX() = %d, want '0'", m.CursorX())
|
||||
@ -621,8 +621,8 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
|
||||
sendKeys(tm, "a", "ctrl+w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "hello wo" {
|
||||
t.Errorf("lines[0] = %q, want 'hello wo'", m.Line(0))
|
||||
if m.lines[0] != "hello wo" {
|
||||
t.Errorf("lines[0] = %q, want 'hello wo'", m.lines[0])
|
||||
}
|
||||
if m.CursorX() != 7 {
|
||||
t.Errorf("CursorX() = %d, want '7'", m.CursorX())
|
||||
@ -672,8 +672,8 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
|
||||
sendKeys(tm, "i", "ctrl+w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "hello" {
|
||||
t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
|
||||
if m.lines[0] != "hello" {
|
||||
t.Errorf("lines[0] = %q, want 'hello'", m.lines[0])
|
||||
}
|
||||
if m.CursorX() != 0 {
|
||||
t.Errorf("CursorX() = %d, want 0", m.CursorX())
|
||||
@ -686,8 +686,8 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
|
||||
sendKeys(tm, "i", "ctrl+w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "lo" {
|
||||
t.Errorf("lines[0] = %q, want 'lo'", m.Line(0))
|
||||
if m.lines[0] != "lo" {
|
||||
t.Errorf("lines[0] = %q, want 'lo'", m.lines[0])
|
||||
}
|
||||
if m.CursorX() != 0 {
|
||||
t.Errorf("CursorX() = %d, want 0", m.CursorX())
|
||||
@ -700,8 +700,8 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
|
||||
sendKeys(tm, "a", "ctrl+w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "..." {
|
||||
t.Errorf("lines[0] = %q, want '...'", m.Line(0))
|
||||
if m.lines[0] != "..." {
|
||||
t.Errorf("lines[0] = %q, want '...'", m.lines[0])
|
||||
}
|
||||
if m.CursorX() != 2 {
|
||||
t.Errorf("CursorX() = %d, want 2", m.CursorX())
|
||||
@ -714,8 +714,8 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
|
||||
sendKeys(tm, "a", "ctrl+w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "hello\t" {
|
||||
t.Errorf("lines[0] = %q, want 'hello\\t'", m.Line(0))
|
||||
if m.lines[0] != "hello\t" {
|
||||
t.Errorf("lines[0] = %q, want 'hello\\t'", m.lines[0])
|
||||
}
|
||||
if m.CursorX() != 5 {
|
||||
t.Errorf("CursorX() = %d, want 5", m.CursorX())
|
||||
@ -731,8 +731,8 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
|
||||
if m.LineCount() != 1 {
|
||||
t.Errorf("LineCount() = %d, want 1", m.LineCount())
|
||||
}
|
||||
if m.Line(0) != "helloworld" {
|
||||
t.Errorf("lines[0] = %q, want 'helloworld'", m.Line(0))
|
||||
if m.lines[0] != "helloworld" {
|
||||
t.Errorf("lines[0] = %q, want 'helloworld'", m.lines[0])
|
||||
}
|
||||
if m.CursorX() != 4 {
|
||||
t.Errorf("CursorX() = %d, want 4", m.CursorX())
|
||||
@ -748,8 +748,8 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
|
||||
sendKeys(tm, "a", "ctrl+w", "esc")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "" {
|
||||
t.Errorf("lines[0] = %q, want ''", m.Line(0))
|
||||
if m.lines[0] != "" {
|
||||
t.Errorf("lines[0] = %q, want ''", m.lines[0])
|
||||
}
|
||||
if m.CursorX() != 0 {
|
||||
t.Errorf("CursorX() = %d, want 0", m.CursorX())
|
||||
|
||||
@ -12,8 +12,8 @@ func TestMoveDown(t *testing.T) {
|
||||
sendKeys(tm, "j")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorY() != 1 {
|
||||
t.Errorf("cursor.y = %d, want 1", m.CursorY())
|
||||
if m.cursor.y != 1 {
|
||||
t.Errorf("cursor.y = %d, want 1", m.cursor.y)
|
||||
}
|
||||
})
|
||||
|
||||
@ -22,8 +22,8 @@ func TestMoveDown(t *testing.T) {
|
||||
sendKeys(tm, "j", "j", "j", "j")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorY() != 4 {
|
||||
t.Errorf("cursor.y = %d, want 4", m.CursorY())
|
||||
if m.cursor.y != 4 {
|
||||
t.Errorf("cursor.y = %d, want 4", m.cursor.y)
|
||||
}
|
||||
})
|
||||
|
||||
@ -32,8 +32,8 @@ func TestMoveDown(t *testing.T) {
|
||||
sendKeys(tm, "j", "j", "j", "j", "j", "j", "j", "j", "j")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorY() != 5 {
|
||||
t.Errorf("cursor.y = %d, want 5", m.CursorY())
|
||||
if m.cursor.y != 5 {
|
||||
t.Errorf("cursor.y = %d, want 5", m.cursor.y)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -44,8 +44,8 @@ func TestMoveDownWithCount(t *testing.T) {
|
||||
sendKeys(tm, "3", "j")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorY() != 3 {
|
||||
t.Errorf("cursor.y = %d, want 3", m.CursorY())
|
||||
if m.cursor.y != 3 {
|
||||
t.Errorf("cursor.y = %d, want 3", m.cursor.y)
|
||||
}
|
||||
})
|
||||
|
||||
@ -54,8 +54,8 @@ func TestMoveDownWithCount(t *testing.T) {
|
||||
sendKeys(tm, "1", "0", "j")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorY() != 5 {
|
||||
t.Errorf("cursor.y = %d, want 5", m.CursorY())
|
||||
if m.cursor.y != 5 {
|
||||
t.Errorf("cursor.y = %d, want 5", m.cursor.y)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -69,8 +69,8 @@ func TestMoveDownWithOverflow(t *testing.T) {
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
want := len(lines[1])
|
||||
if m.CursorX() != want {
|
||||
t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
|
||||
if m.cursor.x != want {
|
||||
t.Errorf("cursor.x = %d, want %d", m.cursor.x, want)
|
||||
}
|
||||
})
|
||||
|
||||
@ -79,8 +79,8 @@ func TestMoveDownWithOverflow(t *testing.T) {
|
||||
sendKeys(tm, "j")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorX() != 3 {
|
||||
t.Errorf("cursor.x = %d, want 3", m.CursorX())
|
||||
if m.cursor.x != 3 {
|
||||
t.Errorf("cursor.x = %d, want 3", m.cursor.x)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -91,8 +91,8 @@ func TestMoveUp(t *testing.T) {
|
||||
sendKeys(tm, "k")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorY() != 1 {
|
||||
t.Errorf("cursor.y = %d, want 1", m.CursorY())
|
||||
if m.cursor.y != 1 {
|
||||
t.Errorf("cursor.y = %d, want 1", m.cursor.y)
|
||||
}
|
||||
})
|
||||
|
||||
@ -101,8 +101,8 @@ func TestMoveUp(t *testing.T) {
|
||||
sendKeys(tm, "k", "k", "k", "k")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorY() != 0 {
|
||||
t.Errorf("cursor.y = %d, want 0", m.CursorY())
|
||||
if m.cursor.y != 0 {
|
||||
t.Errorf("cursor.y = %d, want 0", m.cursor.y)
|
||||
}
|
||||
})
|
||||
|
||||
@ -111,8 +111,8 @@ func TestMoveUp(t *testing.T) {
|
||||
sendKeys(tm, "k", "k", "k")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorY() != 0 {
|
||||
t.Errorf("cursor.y = %d, want 0", m.CursorY())
|
||||
if m.cursor.y != 0 {
|
||||
t.Errorf("cursor.y = %d, want 0", m.cursor.y)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -123,8 +123,8 @@ func TestMoveUpWithCount(t *testing.T) {
|
||||
sendKeys(tm, "3", "k")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorY() != 2 {
|
||||
t.Errorf("cursor.y = %d, want 2", m.CursorY())
|
||||
if m.cursor.y != 2 {
|
||||
t.Errorf("cursor.y = %d, want 2", m.cursor.y)
|
||||
}
|
||||
})
|
||||
|
||||
@ -133,8 +133,8 @@ func TestMoveUpWithCount(t *testing.T) {
|
||||
sendKeys(tm, "1", "0", "k")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorY() != 0 {
|
||||
t.Errorf("cursor.y = %d, want 0", m.CursorY())
|
||||
if m.cursor.y != 0 {
|
||||
t.Errorf("cursor.y = %d, want 0", m.cursor.y)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -148,8 +148,8 @@ func TestMoveUpWithOverflow(t *testing.T) {
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
want := len(lines[0])
|
||||
if m.CursorX() != want {
|
||||
t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
|
||||
if m.cursor.x != want {
|
||||
t.Errorf("cursor.x = %d, want %d", m.cursor.x, want)
|
||||
}
|
||||
})
|
||||
|
||||
@ -158,8 +158,8 @@ func TestMoveUpWithOverflow(t *testing.T) {
|
||||
sendKeys(tm, "k")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorX() != 3 {
|
||||
t.Errorf("cursor.x = %d, want 3", m.CursorX())
|
||||
if m.cursor.x != 3 {
|
||||
t.Errorf("cursor.x = %d, want 3", m.cursor.x)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -170,8 +170,8 @@ func TestMoveRight(t *testing.T) {
|
||||
sendKeys(tm, "l")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorX() != 1 {
|
||||
t.Errorf("cursor.x = %d, want 1", m.CursorX())
|
||||
if m.cursor.x != 1 {
|
||||
t.Errorf("cursor.x = %d, want 1", m.cursor.x)
|
||||
}
|
||||
})
|
||||
|
||||
@ -180,8 +180,8 @@ func TestMoveRight(t *testing.T) {
|
||||
sendKeys(tm, "l", "l", "l", "l")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorX() != 4 {
|
||||
t.Errorf("cursor.x = %d, want 4", m.CursorX())
|
||||
if m.cursor.x != 4 {
|
||||
t.Errorf("cursor.x = %d, want 4", m.cursor.x)
|
||||
}
|
||||
})
|
||||
|
||||
@ -192,8 +192,8 @@ func TestMoveRight(t *testing.T) {
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
want := len(lines[0])
|
||||
if m.CursorX() != want {
|
||||
t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
|
||||
if m.cursor.x != want {
|
||||
t.Errorf("cursor.x = %d, want %d", m.cursor.x, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -204,8 +204,8 @@ func TestMoveRightWithCount(t *testing.T) {
|
||||
sendKeys(tm, "3", "l")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorX() != 3 {
|
||||
t.Errorf("cursor.x = %d, want 3", m.CursorX())
|
||||
if m.cursor.x != 3 {
|
||||
t.Errorf("cursor.x = %d, want 3", m.cursor.x)
|
||||
}
|
||||
})
|
||||
|
||||
@ -216,8 +216,8 @@ func TestMoveRightWithCount(t *testing.T) {
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
want := len(lines[0])
|
||||
if m.CursorX() != want {
|
||||
t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
|
||||
if m.cursor.x != want {
|
||||
t.Errorf("cursor.x = %d, want %d", m.cursor.x, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -228,8 +228,8 @@ func TestMoveLeft(t *testing.T) {
|
||||
sendKeys(tm, "h")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorX() != 2 {
|
||||
t.Errorf("cursor.x = %d, want 2", m.CursorX())
|
||||
if m.cursor.x != 2 {
|
||||
t.Errorf("cursor.x = %d, want 2", m.cursor.x)
|
||||
}
|
||||
})
|
||||
|
||||
@ -238,8 +238,8 @@ func TestMoveLeft(t *testing.T) {
|
||||
sendKeys(tm, "h", "h", "h", "h")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorX() != 0 {
|
||||
t.Errorf("cursor.x = %d, want 0", m.CursorX())
|
||||
if m.cursor.x != 0 {
|
||||
t.Errorf("cursor.x = %d, want 0", m.cursor.x)
|
||||
}
|
||||
})
|
||||
|
||||
@ -248,8 +248,8 @@ func TestMoveLeft(t *testing.T) {
|
||||
sendKeys(tm, "h", "h", "h")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorX() != 0 {
|
||||
t.Errorf("cursor.x = %d, want 0", m.CursorX())
|
||||
if m.cursor.x != 0 {
|
||||
t.Errorf("cursor.x = %d, want 0", m.cursor.x)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -260,8 +260,8 @@ func TestMoveLeftWithCount(t *testing.T) {
|
||||
sendKeys(tm, "3", "h")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorX() != 2 {
|
||||
t.Errorf("cursor.x = %d, want 2", m.CursorX())
|
||||
if m.cursor.x != 2 {
|
||||
t.Errorf("cursor.x = %d, want 2", m.cursor.x)
|
||||
}
|
||||
})
|
||||
|
||||
@ -270,8 +270,8 @@ func TestMoveLeftWithCount(t *testing.T) {
|
||||
sendKeys(tm, "1", "0", "h")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.CursorX() != 0 {
|
||||
t.Errorf("cursor.x = %d, want 0", m.CursorX())
|
||||
if m.cursor.x != 0 {
|
||||
t.Errorf("cursor.x = %d, want 0", m.cursor.x)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,72 +9,53 @@ import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
type cursor struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
// Buffers
|
||||
buffers []*action.Buffer
|
||||
//next buffer id?
|
||||
lines []string
|
||||
cursor cursor
|
||||
anchor cursor // starting point for visual modes
|
||||
scrollY int
|
||||
mode action.Mode
|
||||
win_h int
|
||||
win_w int
|
||||
input *input.Handler
|
||||
|
||||
// Windows
|
||||
windows []*action.Window
|
||||
activeWindowId int
|
||||
|
||||
// Editor wide state
|
||||
mode action.Mode
|
||||
|
||||
// Terminal dimensions
|
||||
termWidth int
|
||||
termHeight int
|
||||
|
||||
// Input and key handling
|
||||
input *input.Handler
|
||||
|
||||
// Insert mode state & repetition (applied to active window)
|
||||
// Insert repetition
|
||||
insertCount int
|
||||
insertKeys []string
|
||||
insertAction action.Action
|
||||
|
||||
// Command line state
|
||||
// Command mode
|
||||
command string
|
||||
commandCursor int
|
||||
commandError error
|
||||
commandOutput string
|
||||
|
||||
// Global settings (TODO: This needs to be refactored)
|
||||
// Settings
|
||||
settings action.Settings
|
||||
|
||||
// Registers
|
||||
registers map[rune]action.Register // name -> register
|
||||
}
|
||||
|
||||
func NewModel(lines []string, pos action.Position) *Model {
|
||||
m := Model{
|
||||
func NewModel(lines []string, pos action.Position) Model {
|
||||
return Model{
|
||||
lines: lines,
|
||||
cursor: cursor{
|
||||
x: pos.Col,
|
||||
y: pos.Line,
|
||||
},
|
||||
scrollY: 0,
|
||||
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
|
||||
}
|
||||
|
||||
func (m Model) Init() tea.Cmd {
|
||||
@ -84,74 +65,73 @@ func (m Model) Init() tea.Cmd {
|
||||
// Implement action.Model interface
|
||||
|
||||
func (m *Model) Lines() []string {
|
||||
win := m.ActiveWindow()
|
||||
return win.Buffer.Lines
|
||||
return m.lines
|
||||
}
|
||||
|
||||
func (m *Model) Line(idx int) string {
|
||||
win := m.ActiveWindow()
|
||||
return win.Buffer.Line(idx)
|
||||
if idx < 0 || idx >= len(m.lines) {
|
||||
return ""
|
||||
}
|
||||
return m.lines[idx]
|
||||
}
|
||||
|
||||
func (m *Model) SetLine(idx int, content string) {
|
||||
win := m.ActiveWindow()
|
||||
win.Buffer.SetLine(idx, content)
|
||||
if idx >= 0 && idx < len(m.lines) {
|
||||
m.lines[idx] = content
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) InsertLine(idx int, content string) {
|
||||
win := m.ActiveWindow()
|
||||
win.Buffer.InsertLine(idx, content)
|
||||
if idx < 0 {
|
||||
idx = 0
|
||||
}
|
||||
if idx > len(m.lines) {
|
||||
idx = len(m.lines)
|
||||
}
|
||||
m.lines = append(m.lines[:idx], append([]string{content}, m.lines[idx:]...)...)
|
||||
}
|
||||
|
||||
func (m *Model) DeleteLine(idx int) {
|
||||
win := m.ActiveWindow()
|
||||
win.Buffer.DeleteLine(idx)
|
||||
if idx >= 0 && idx < len(m.lines) {
|
||||
m.lines = append(m.lines[:idx], m.lines[idx+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) LineCount() int {
|
||||
win := m.ActiveWindow()
|
||||
return win.Buffer.LineCount()
|
||||
return len(m.lines)
|
||||
}
|
||||
|
||||
func (m *Model) CursorX() int {
|
||||
win := m.ActiveWindow()
|
||||
return win.Cursor.Col
|
||||
return m.cursor.x
|
||||
}
|
||||
|
||||
func (m *Model) CursorY() int {
|
||||
win := m.ActiveWindow()
|
||||
return win.Cursor.Line
|
||||
return m.cursor.y
|
||||
}
|
||||
|
||||
func (m *Model) SetCursorX(x int) {
|
||||
win := m.ActiveWindow()
|
||||
win.Cursor.Col = x
|
||||
m.cursor.x = x
|
||||
}
|
||||
|
||||
func (m *Model) SetCursorY(y int) {
|
||||
win := m.ActiveWindow()
|
||||
win.Cursor.Line = y
|
||||
m.cursor.y = y
|
||||
}
|
||||
|
||||
// Anchor methods
|
||||
func (m *Model) AnchorX() int {
|
||||
win := m.ActiveWindow()
|
||||
return win.Anchor.Col
|
||||
return m.anchor.x
|
||||
}
|
||||
|
||||
func (m *Model) AnchorY() int {
|
||||
win := m.ActiveWindow()
|
||||
return win.Anchor.Line
|
||||
return m.anchor.y
|
||||
}
|
||||
|
||||
func (m *Model) SetAnchorX(x int) {
|
||||
win := m.ActiveWindow()
|
||||
win.Anchor.Col = x
|
||||
m.anchor.x = x
|
||||
}
|
||||
|
||||
func (m *Model) SetAnchorY(y int) {
|
||||
win := m.ActiveWindow()
|
||||
win.Anchor.Line = y
|
||||
m.anchor.y = y
|
||||
}
|
||||
|
||||
// Insert methods
|
||||
@ -246,33 +226,32 @@ func (m *Model) UpdateDefaultRegister(t action.RegisterType, cnt []string) {
|
||||
|
||||
// Window
|
||||
func (m *Model) ScrollY() int {
|
||||
win := m.ActiveWindow()
|
||||
return win.ScrollY
|
||||
return m.scrollY
|
||||
}
|
||||
|
||||
func (m *Model) SetScrollY(y int) {
|
||||
win := m.ActiveWindow()
|
||||
win.ScrollY = y
|
||||
m.scrollY = y
|
||||
}
|
||||
|
||||
func (m *Model) WinH() int {
|
||||
win := m.ActiveWindow()
|
||||
return win.Height
|
||||
return m.win_h
|
||||
}
|
||||
|
||||
func (m *Model) WinW() int {
|
||||
win := m.ActiveWindow()
|
||||
return win.Width
|
||||
return m.win_w
|
||||
}
|
||||
|
||||
func (m *Model) ViewPortH() int {
|
||||
win := m.ActiveWindow()
|
||||
return win.Height - 2
|
||||
return m.win_h - 2 // -2 for status bar and commmand bar
|
||||
}
|
||||
|
||||
func (m *Model) ClampCursorX() {
|
||||
win := m.ActiveWindow()
|
||||
win.ClampCursorX()
|
||||
lineLen := len(m.lines[m.cursor.y])
|
||||
if lineLen == 0 {
|
||||
m.cursor.x = 0
|
||||
} else if m.cursor.x >= lineLen {
|
||||
m.cursor.x = lineLen
|
||||
}
|
||||
}
|
||||
|
||||
// AdjustScroll ensures the cursor stays within the viewport with scrollOff margins.
|
||||
@ -301,25 +280,6 @@ func (m *Model) AdjustScroll() {
|
||||
m.SetScrollY(max(0, min(m.ScrollY(), maxScroll)))
|
||||
}
|
||||
|
||||
// Windows
|
||||
func (m *Model) Windows() []*action.Window {
|
||||
return m.windows
|
||||
}
|
||||
|
||||
func (m *Model) ActiveWindowId() int {
|
||||
return m.activeWindowId
|
||||
}
|
||||
|
||||
func (m *Model) ActiveWindow() *action.Window {
|
||||
winId := m.ActiveWindowId()
|
||||
for i := range m.Windows() {
|
||||
if m.windows[i].Id == winId {
|
||||
return m.windows[i]
|
||||
}
|
||||
}
|
||||
panic("Could not find window")
|
||||
}
|
||||
|
||||
func (m *Model) Mode() action.Mode {
|
||||
return m.mode
|
||||
}
|
||||
@ -334,29 +294,24 @@ func (m *Model) SetInsertRecording(count int, act action.Action) {
|
||||
m.insertAction = act
|
||||
}
|
||||
|
||||
func (m *Model) GetCursorPosition() *action.Position {
|
||||
// Return a copy of the position
|
||||
win := m.ActiveWindow()
|
||||
pos := win.Cursor
|
||||
return &pos
|
||||
func (m *Model) GetCursorPosition() action.Position {
|
||||
return action.Position{Line: m.cursor.y, Col: m.cursor.x}
|
||||
}
|
||||
|
||||
func (m *Model) replayInsert() {
|
||||
win := m.ActiveWindow()
|
||||
|
||||
// Replay (count - 1) more times
|
||||
for i := 1; i < m.insertCount; i++ {
|
||||
// For 'o' and 'O', we need to create a new line first
|
||||
switch m.insertAction.(type) {
|
||||
case action.OpenLineBelow:
|
||||
pos := win.Cursor.Line
|
||||
win.Buffer.Lines = append(win.Buffer.Lines[:pos+1], append([]string{""}, win.Buffer.Lines[pos+1:]...)...)
|
||||
win.Cursor.Line++
|
||||
win.Cursor.Col = 0
|
||||
pos := m.cursor.y
|
||||
m.lines = append(m.lines[:pos+1], append([]string{""}, m.lines[pos+1:]...)...)
|
||||
m.cursor.y++
|
||||
m.cursor.x = 0
|
||||
case action.OpenLineAbove:
|
||||
pos := win.Cursor.Line
|
||||
win.Buffer.Lines = append(win.Buffer.Lines[:pos], append([]string{""}, win.Buffer.Lines[pos:]...)...)
|
||||
win.Cursor.Col = 0
|
||||
pos := m.cursor.y
|
||||
m.lines = append(m.lines[:pos], append([]string{""}, m.lines[pos:]...)...)
|
||||
m.cursor.x = 0
|
||||
// 'i' and 'a' don't need setup - just replay keys
|
||||
}
|
||||
|
||||
@ -368,12 +323,11 @@ func (m *Model) replayInsert() {
|
||||
}
|
||||
|
||||
func (m *Model) ExitInsertMode() {
|
||||
win := m.ActiveWindow()
|
||||
if m.insertCount > 1 {
|
||||
m.replayInsert()
|
||||
}
|
||||
if win.Cursor.Col > 0 {
|
||||
win.Cursor.Col--
|
||||
if m.cursor.x > 0 {
|
||||
m.cursor.x--
|
||||
}
|
||||
m.mode = action.NormalMode
|
||||
m.insertCount = 0
|
||||
|
||||
@ -4,47 +4,14 @@ import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmd tea.Cmd
|
||||
|
||||
switch msg := msg.(type) {
|
||||
|
||||
case tea.WindowSizeMsg:
|
||||
m.termHeight = msg.Height
|
||||
m.termWidth = msg.Width
|
||||
|
||||
// TODO: Implement a layout method that handles this
|
||||
//
|
||||
// func (m *Model) layoutWindows() {
|
||||
// if len(m.windows) == 0 {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if len(m.windows) == 1 {
|
||||
// // Single window - full screen
|
||||
// m.windows[0].Width = m.termWidth
|
||||
// m.windows[0].Height = m.termHeight
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // Multiple windows - distribute space
|
||||
// // This is where you'd implement split layout logic
|
||||
// // For example, horizontal split:
|
||||
// halfHeight := m.termHeight / 2
|
||||
// for i, win := range m.windows {
|
||||
// win.Width = m.termWidth
|
||||
// if i < len(m.windows)-1 {
|
||||
// win.Height = halfHeight
|
||||
// } else {
|
||||
// // Last window gets remainder
|
||||
// win.Height = m.termHeight - (halfHeight * (len(m.windows) - 1))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
for i := range m.windows {
|
||||
m.windows[i].Height = msg.Height
|
||||
m.windows[i].Width = msg.Width
|
||||
}
|
||||
m.win_h = msg.Height
|
||||
m.win_w = msg.Width
|
||||
|
||||
case tea.KeyMsg:
|
||||
// TODO: This needs to be removed, but for now its required for the tests.
|
||||
@ -52,7 +19,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if msg.Type == tea.KeyCtrlC {
|
||||
return m, tea.Quit
|
||||
}
|
||||
cmd = m.input.Handle(m, msg.String())
|
||||
cmd = m.input.Handle(&m, msg.String())
|
||||
}
|
||||
|
||||
// Keep cursor in view after any update
|
||||
|
||||
@ -146,7 +146,7 @@ func drawStatusBar(m Model) string {
|
||||
left := leftBar(m)
|
||||
right := rightBar(m)
|
||||
|
||||
diff := m.termWidth - (len(left) + len(right))
|
||||
diff := m.win_w - (len(left) + len(right))
|
||||
|
||||
// This happens when the terminal spawns
|
||||
if diff <= 0 {
|
||||
|
||||
@ -16,7 +16,7 @@ const (
|
||||
|
||||
// PositionGetter is used to get cursor position for operator ranges
|
||||
type PositionGetter interface {
|
||||
GetCursorPosition() *action.Position
|
||||
GetCursorPosition() action.Position
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
@ -205,7 +205,7 @@ func (h *Handler) handleAfterOperator(m action.Model, kind string, binding any,
|
||||
start := pg.GetCursorPosition()
|
||||
mot.Execute(m)
|
||||
end := pg.GetCursorPosition()
|
||||
cmd := h.operator.Operate(m, *start, *end, mot.Type())
|
||||
cmd := h.operator.Operate(m, start, end, mot.Type())
|
||||
h.Reset()
|
||||
return cmd
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user