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)
|
- [x] `yy` - Yank line (double press)
|
||||||
|
|
||||||
### Not Implemented
|
### Not Implemented
|
||||||
- [x] `c` - Change operator
|
- [ ] `c` - Change operator
|
||||||
- [x] `cc` - Change line
|
- [ ] `cc` - Change line
|
||||||
- [ ] `>` - Indent right
|
- [ ] `>` - Indent right
|
||||||
- [ ] `<` - Indent left
|
- [ ] `<` - Indent left
|
||||||
- [ ] `=` - Auto-indent
|
- [ ] `=` - Auto-indent
|
||||||
@ -96,9 +96,9 @@
|
|||||||
- [x] `A` - Insert at end of line
|
- [x] `A` - Insert at end of line
|
||||||
- [x] `o` - Open line below
|
- [x] `o` - Open line below
|
||||||
- [x] `O` - Open line above
|
- [x] `O` - Open line above
|
||||||
- [x] `s` - Substitute character (delete + insert)
|
- [ ] `s` - Substitute character (delete + insert)
|
||||||
- [x] `S` - Substitute line (delete line + insert)
|
- [ ] `S` - Substitute line (delete line + insert)
|
||||||
- [x] `C` - Change to end of line
|
- [ ] `C` - Change to end of line
|
||||||
- [ ] `gi` - Insert at last insert position
|
- [ ] `gi` - Insert at last insert position
|
||||||
|
|
||||||
### Delete Actions
|
### 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] Delete operator (d, dd)
|
||||||
- [x] Yank operator (y, yy)
|
- [x] Yank operator (y, yy)
|
||||||
- [x] Paste actions (p, P)
|
- [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 entry (i, a, I, A, o, O)
|
||||||
- [x] Insert mode editing (enter, backspace, delete, tab, ctrl+w)
|
- [x] Insert mode editing (enter, backspace, delete, tab, ctrl+w)
|
||||||
- [x] Visual modes (v, V, ctrl+v)
|
- [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] Delete actions (x, D)
|
||||||
- [x] Command mode basics
|
- [x] Command mode basics
|
||||||
- [x] Register behavior
|
- [x] Register behavior
|
||||||
|
|
||||||
|
|||||||
@ -8,21 +8,17 @@ import (
|
|||||||
tea "github.com/charmbracelet/bubbletea"
|
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() {
|
func main() {
|
||||||
m, _ := tea.NewProgram(
|
tea.NewProgram(
|
||||||
editor.NewModel([]string{""}, action.Position{Line: 0, Col: 0}),
|
editor.NewModel(generateLines(64), action.Position{Line: 0, Col: 0}),
|
||||||
tea.WithAltScreen(),
|
tea.WithAltScreen(),
|
||||||
).Run()
|
).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"
|
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
|
// Model defines the interface for editor state that actions can modify
|
||||||
type Model interface {
|
type Model interface {
|
||||||
// Text buffer
|
// Text buffer
|
||||||
@ -21,11 +58,6 @@ type Model interface {
|
|||||||
SetCursorY(y int)
|
SetCursorY(y int)
|
||||||
ClampCursorX()
|
ClampCursorX()
|
||||||
|
|
||||||
// Windows
|
|
||||||
Windows() []*Window
|
|
||||||
ActiveWindowId() int
|
|
||||||
ActiveWindow() *Window
|
|
||||||
|
|
||||||
// Window
|
// Window
|
||||||
ScrollY() int
|
ScrollY() int
|
||||||
SetScrollY(y 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"}),
|
WithLines([]string{"hello", "world"}),
|
||||||
)
|
)
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if len(m.Lines()) != 2 {
|
if len(m.lines) != 2 {
|
||||||
t.Errorf("expected 2 lines, got %d", len(m.Lines()))
|
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)
|
// 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})
|
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlC})
|
||||||
fm := tm.FinalModel(t, teatest.WithFinalTimeout(time.Second))
|
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")
|
sendKeys(tm, "x")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "ello" {
|
if m.lines[0] != "ello" {
|
||||||
t.Errorf("lines[0] = %q, want 'ello'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'ello'", m.lines[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.Line(0) != "helo" {
|
if m.lines[0] != "helo" {
|
||||||
t.Errorf("lines[0] = %q, want 'helo'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'helo'", m.lines[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.Line(0) != "hell" {
|
if m.lines[0] != "hell" {
|
||||||
t.Errorf("lines[0] = %q, want 'hell'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'hell'", m.lines[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.Line(0) != "llo" {
|
if m.lines[0] != "llo" {
|
||||||
t.Errorf("lines[0] = %q, want 'llo'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'llo'", m.lines[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.Line(0) != "lo" {
|
if m.lines[0] != "lo" {
|
||||||
t.Errorf("lines[0] = %q, want 'lo'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'lo'", m.lines[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.Line(0) != "" {
|
if m.lines[0] != "" {
|
||||||
t.Errorf("lines[0] = %q, want ''", m.Line(0))
|
t.Errorf("lines[0] = %q, want ''", m.lines[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.Line(0) != "hlo" {
|
if m.lines[0] != "hlo" {
|
||||||
t.Errorf("lines[0] = %q, want 'hlo'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'hlo'", m.lines[0])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.Line(0) != "Xhello" {
|
if m.lines[0] != "Xhello" {
|
||||||
t.Errorf("lines[0] = %q, want 'Xhello'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'Xhello'", m.lines[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.Line(0) != "heXllo" {
|
if m.lines[0] != "heXllo" {
|
||||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'heXllo'", m.lines[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.CursorX() != 2 {
|
if m.cursor.x != 2 {
|
||||||
t.Errorf("cursor.x = %d, want 2", m.CursorX())
|
t.Errorf("cursor.x = %d, want 2", m.cursor.x)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -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.Line(0) != "hXello" {
|
if m.lines[0] != "hXello" {
|
||||||
t.Errorf("lines[0] = %q, want 'hXello'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'hXello'", m.lines[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.Line(0) != "helXlo" {
|
if m.lines[0] != "helXlo" {
|
||||||
t.Errorf("lines[0] = %q, want 'helXlo'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'helXlo'", m.lines[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.Line(0) != "Xhello" {
|
if m.lines[0] != "Xhello" {
|
||||||
t.Errorf("lines[0] = %q, want 'Xhello'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'Xhello'", m.lines[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.Line(0) != "Xhello" {
|
if m.lines[0] != "Xhello" {
|
||||||
t.Errorf("lines[0] = %q, want 'Xhello'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'Xhello'", m.lines[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.Line(0) != "helloX" {
|
if m.lines[0] != "helloX" {
|
||||||
t.Errorf("lines[0] = %q, want 'helloX'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'helloX'", m.lines[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.Line(0) != "helloX" {
|
if m.lines[0] != "helloX" {
|
||||||
t.Errorf("lines[0] = %q, want 'helloX'", m.Line(0))
|
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")
|
sendKeys(tm, "o", "n", "e", "w", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.LineCount() != 3 {
|
if len(m.lines) != 3 {
|
||||||
t.Errorf("len(lines) = %d, want 3", m.LineCount())
|
t.Errorf("len(lines) = %d, want 3", len(m.lines))
|
||||||
}
|
}
|
||||||
if m.Line(1) != "new" {
|
if m.lines[1] != "new" {
|
||||||
t.Errorf("lines[1] = %q, want 'new'", m.Line(1))
|
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")
|
sendKeys(tm, "o", "n", "e", "w", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.LineCount() != 4 {
|
if len(m.lines) != 4 {
|
||||||
t.Errorf("len(lines) = %d, want 4", m.LineCount())
|
t.Errorf("len(lines) = %d, want 4", len(m.lines))
|
||||||
}
|
}
|
||||||
if m.Line(2) != "new" {
|
if m.lines[2] != "new" {
|
||||||
t.Errorf("lines[2] = %q, want 'new'", m.Line(2))
|
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")
|
sendKeys(tm, "o", "n", "e", "w", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.LineCount() != 3 {
|
if len(m.lines) != 3 {
|
||||||
t.Errorf("len(lines) = %d, want 3", m.LineCount())
|
t.Errorf("len(lines) = %d, want 3", len(m.lines))
|
||||||
}
|
}
|
||||||
if m.Line(2) != "new" {
|
if m.lines[2] != "new" {
|
||||||
t.Errorf("lines[2] = %q, want 'new'", m.Line(2))
|
t.Errorf("lines[2] = %q, want 'new'", m.lines[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.CursorY() != 1 {
|
if m.cursor.y != 1 {
|
||||||
t.Errorf("cursor.y = %d, want 1", m.CursorY())
|
t.Errorf("cursor.y = %d, want 1", m.cursor.y)
|
||||||
}
|
}
|
||||||
if m.CursorX() != 0 {
|
if m.cursor.x != 0 {
|
||||||
t.Errorf("cursor.x = %d, want 0", m.CursorX())
|
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")
|
sendKeys(tm, "3", "o", "x", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.LineCount() != 4 {
|
if len(m.lines) != 4 {
|
||||||
t.Errorf("len(lines) = %d, want 4", m.LineCount())
|
t.Errorf("len(lines) = %d, want 4", len(m.lines))
|
||||||
}
|
}
|
||||||
for i := 1; i <= 3; i++ {
|
for i := 1; i <= 3; i++ {
|
||||||
if m.Line(i) != "x" {
|
if m.lines[i] != "x" {
|
||||||
t.Errorf("lines[%d] = %q, want 'x'", i, m.Line(i))
|
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")
|
sendKeys(tm, "2", "o", "a", "b", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.LineCount() != 3 {
|
if len(m.lines) != 3 {
|
||||||
t.Errorf("len(lines) = %d, want 3", m.LineCount())
|
t.Errorf("len(lines) = %d, want 3", len(m.lines))
|
||||||
}
|
}
|
||||||
if m.Line(1) != "ab" {
|
if m.lines[1] != "ab" {
|
||||||
t.Errorf("lines[1] = %q, want 'ab'", m.Line(1))
|
t.Errorf("lines[1] = %q, want 'ab'", m.lines[1])
|
||||||
}
|
}
|
||||||
if m.Line(2) != "ab" {
|
if m.lines[2] != "ab" {
|
||||||
t.Errorf("lines[2] = %q, want 'ab'", m.Line(2))
|
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")
|
sendKeys(tm, "O", "n", "e", "w", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.LineCount() != 3 {
|
if len(m.lines) != 3 {
|
||||||
t.Errorf("len(lines) = %d, want 3", m.LineCount())
|
t.Errorf("len(lines) = %d, want 3", len(m.lines))
|
||||||
}
|
}
|
||||||
if m.Line(1) != "new" {
|
if m.lines[1] != "new" {
|
||||||
t.Errorf("lines[1] = %q, want 'new'", m.Line(1))
|
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")
|
sendKeys(tm, "O", "n", "e", "w", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.LineCount() != 3 {
|
if len(m.lines) != 3 {
|
||||||
t.Errorf("len(lines) = %d, want 3", m.LineCount())
|
t.Errorf("len(lines) = %d, want 3", len(m.lines))
|
||||||
}
|
}
|
||||||
if m.Line(0) != "new" {
|
if m.lines[0] != "new" {
|
||||||
t.Errorf("lines[0] = %q, want 'new'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'new'", m.lines[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.CursorX() != 0 {
|
if m.cursor.x != 0 {
|
||||||
t.Errorf("cursor.x = %d, want 0", m.CursorX())
|
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")
|
sendKeys(tm, "3", "O", "x", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.LineCount() != 4 {
|
if len(m.lines) != 4 {
|
||||||
t.Errorf("len(lines) = %d, want 4", m.LineCount())
|
t.Errorf("len(lines) = %d, want 4", len(m.lines))
|
||||||
}
|
}
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
if m.Line(i) != "x" {
|
if m.lines[i] != "x" {
|
||||||
t.Errorf("lines[%d] = %q, want 'x'", i, m.Line(i))
|
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")
|
sendKeys(tm, "i", "enter", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.LineCount() != 2 {
|
if len(m.lines) != 2 {
|
||||||
t.Errorf("len(lines) = %d, want 2", m.LineCount())
|
t.Errorf("len(lines) = %d, want 2", len(m.lines))
|
||||||
}
|
}
|
||||||
if m.Line(0) != "hello" {
|
if m.lines[0] != "hello" {
|
||||||
t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'hello'", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.Line(1) != " world" {
|
if m.lines[1] != " world" {
|
||||||
t.Errorf("lines[1] = %q, want ' world'", m.Line(1))
|
t.Errorf("lines[1] = %q, want ' world'", m.lines[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.LineCount() != 2 {
|
if len(m.lines) != 2 {
|
||||||
t.Errorf("len(lines) = %d, want 2", m.LineCount())
|
t.Errorf("len(lines) = %d, want 2", len(m.lines))
|
||||||
}
|
}
|
||||||
if m.Line(0) != "hello" {
|
if m.lines[0] != "hello" {
|
||||||
t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'hello'", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.Line(1) != "" {
|
if m.lines[1] != "" {
|
||||||
t.Errorf("lines[1] = %q, want ''", m.Line(1))
|
t.Errorf("lines[1] = %q, want ''", m.lines[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.LineCount() != 2 {
|
if len(m.lines) != 2 {
|
||||||
t.Errorf("len(lines) = %d, want 2", m.LineCount())
|
t.Errorf("len(lines) = %d, want 2", len(m.lines))
|
||||||
}
|
}
|
||||||
if m.Line(0) != "" {
|
if m.lines[0] != "" {
|
||||||
t.Errorf("lines[0] = %q, want ''", m.Line(0))
|
t.Errorf("lines[0] = %q, want ''", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.Line(1) != "hello" {
|
if m.lines[1] != "hello" {
|
||||||
t.Errorf("lines[1] = %q, want 'hello'", m.Line(1))
|
t.Errorf("lines[1] = %q, want 'hello'", m.lines[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.Line(0) != "helo" {
|
if m.lines[0] != "helo" {
|
||||||
t.Errorf("lines[0] = %q, want 'helo'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'helo'", m.lines[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.LineCount() != 1 {
|
if len(m.lines) != 1 {
|
||||||
t.Errorf("len(lines) = %d, want 1", m.LineCount())
|
t.Errorf("len(lines) = %d, want 1", len(m.lines))
|
||||||
}
|
}
|
||||||
if m.Line(0) != "helloworld" {
|
if m.lines[0] != "helloworld" {
|
||||||
t.Errorf("lines[0] = %q, want 'helloworld'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'helloworld'", m.lines[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.Line(0) != "hello" {
|
if m.lines[0] != "hello" {
|
||||||
t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
|
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")
|
sendKeys(tm, "i", "backspace", "backspace", "backspace", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "he" {
|
if m.lines[0] != "he" {
|
||||||
t.Errorf("lines[0] = %q, want 'he'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'he'", m.lines[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.Line(0) != "word" {
|
if m.lines[0] != "word" {
|
||||||
t.Errorf("lines[0] = %q, want 'word'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'word'", m.lines[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.LineCount() != 1 {
|
if len(m.lines) != 1 {
|
||||||
t.Errorf("len(lines) = %d, want 1", m.LineCount())
|
t.Errorf("len(lines) = %d, want 1", len(m.lines))
|
||||||
}
|
}
|
||||||
if m.Line(0) != "helloworld" {
|
if m.lines[0] != "helloworld" {
|
||||||
t.Errorf("lines[0] = %q, want 'helloworld'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'helloworld'", m.lines[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.LineCount() != 1 {
|
if len(m.lines) != 1 {
|
||||||
t.Errorf("len(lines) = %d, want 1", m.LineCount())
|
t.Errorf("len(lines) = %d, want 1", len(m.lines))
|
||||||
}
|
}
|
||||||
if m.Line(0) != "world" {
|
if m.lines[0] != "world" {
|
||||||
t.Errorf("lines[0] = %q, want 'world'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'world'", m.lines[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.Line(0) != "hello" {
|
if m.lines[0] != "hello" {
|
||||||
t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
|
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")
|
sendKeys(tm, "i", "delete", "delete", "delete", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "ho" {
|
if m.lines[0] != "ho" {
|
||||||
t.Errorf("lines[0] = %q, want 'he'", m.Line(0))
|
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")
|
sendKeys(tm, "i", "left", "X", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "heXllo" {
|
if m.lines[0] != "heXllo" {
|
||||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
|
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")
|
sendKeys(tm, "i", "right", "X", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "heXllo" {
|
if m.lines[0] != "heXllo" {
|
||||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
|
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")
|
sendKeys(tm, "i", "up", "X", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "heXllo" {
|
if m.lines[0] != "heXllo" {
|
||||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'heXllo'", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.Line(1) != "world" {
|
if m.lines[1] != "world" {
|
||||||
t.Errorf("lines[1] = %q, want 'world'", m.Line(1))
|
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")
|
sendKeys(tm, "i", "down", "X", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "hello" {
|
if m.lines[0] != "hello" {
|
||||||
t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'hello'", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.Line(1) != "woXrld" {
|
if m.lines[1] != "woXrld" {
|
||||||
t.Errorf("lines[1] = %q, want 'woXrld'", m.Line(1))
|
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")
|
sendKeys(tm, "i", "left", "X", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "Xhello" {
|
if m.lines[0] != "Xhello" {
|
||||||
t.Errorf("lines[0] = %q, want 'Xhello'", m.Line(0))
|
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")
|
sendKeys(tm, "a", "right", "X", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "helloX" {
|
if m.lines[0] != "helloX" {
|
||||||
t.Errorf("lines[0] = %q, want 'helloX'", m.Line(0))
|
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")
|
sendKeys(tm, "i", "up", "X", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "heXllo" {
|
if m.lines[0] != "heXllo" {
|
||||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
|
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")
|
sendKeys(tm, "i", "down", "X", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "heXllo" {
|
if m.lines[0] != "heXllo" {
|
||||||
t.Errorf("lines[0] = %q, want 'heXllo'", m.Line(0))
|
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")
|
sendKeys(tm, "i", "up", "X", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "hiX" {
|
if m.lines[0] != "hiX" {
|
||||||
t.Errorf("lines[0] = %q, want 'hiX'", m.Line(0))
|
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")
|
sendKeys(tm, "i", "down", "X", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(1) != "hiX" {
|
if m.lines[1] != "hiX" {
|
||||||
t.Errorf("lines[1] = %q, want 'hiX'", m.Line(1))
|
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")
|
sendKeys(tm, "i", "right", "right", "down", "X", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(1) != "woXrld" {
|
if m.lines[1] != "woXrld" {
|
||||||
t.Errorf("lines[1] = %q, want 'woXrld'", m.Line(1))
|
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")
|
sendKeys(tm, "a", "ctrl+w", "esc")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.Line(0) != "hello " {
|
if m.lines[0] != "hello " {
|
||||||
t.Errorf("lines[0] = %q, want 'hello '", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'hello '", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.CursorX() != 5 {
|
if m.CursorX() != 5 {
|
||||||
t.Errorf("CursorX() = %d, want '5'", m.CursorX())
|
t.Errorf("CursorX() = %d, want '5'", m.CursorX())
|
||||||
@ -607,8 +607,8 @@ 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.Line(0) != "" {
|
if m.lines[0] != "" {
|
||||||
t.Errorf("lines[0] = %q, want ''", m.Line(0))
|
t.Errorf("lines[0] = %q, want ''", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.CursorX() != 0 {
|
if m.CursorX() != 0 {
|
||||||
t.Errorf("CursorX() = %d, want '0'", m.CursorX())
|
t.Errorf("CursorX() = %d, want '0'", m.CursorX())
|
||||||
@ -621,8 +621,8 @@ 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.Line(0) != "hello wo" {
|
if m.lines[0] != "hello wo" {
|
||||||
t.Errorf("lines[0] = %q, want 'hello wo'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'hello wo'", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.CursorX() != 7 {
|
if m.CursorX() != 7 {
|
||||||
t.Errorf("CursorX() = %d, want '7'", m.CursorX())
|
t.Errorf("CursorX() = %d, want '7'", m.CursorX())
|
||||||
@ -672,8 +672,8 @@ 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.Line(0) != "hello" {
|
if m.lines[0] != "hello" {
|
||||||
t.Errorf("lines[0] = %q, want 'hello'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'hello'", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.CursorX() != 0 {
|
if m.CursorX() != 0 {
|
||||||
t.Errorf("CursorX() = %d, want 0", m.CursorX())
|
t.Errorf("CursorX() = %d, want 0", m.CursorX())
|
||||||
@ -686,8 +686,8 @@ 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.Line(0) != "lo" {
|
if m.lines[0] != "lo" {
|
||||||
t.Errorf("lines[0] = %q, want 'lo'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'lo'", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.CursorX() != 0 {
|
if m.CursorX() != 0 {
|
||||||
t.Errorf("CursorX() = %d, want 0", m.CursorX())
|
t.Errorf("CursorX() = %d, want 0", m.CursorX())
|
||||||
@ -700,8 +700,8 @@ 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.Line(0) != "..." {
|
if m.lines[0] != "..." {
|
||||||
t.Errorf("lines[0] = %q, want '...'", m.Line(0))
|
t.Errorf("lines[0] = %q, want '...'", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.CursorX() != 2 {
|
if m.CursorX() != 2 {
|
||||||
t.Errorf("CursorX() = %d, want 2", m.CursorX())
|
t.Errorf("CursorX() = %d, want 2", m.CursorX())
|
||||||
@ -714,8 +714,8 @@ 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.Line(0) != "hello\t" {
|
if m.lines[0] != "hello\t" {
|
||||||
t.Errorf("lines[0] = %q, want 'hello\\t'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'hello\\t'", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.CursorX() != 5 {
|
if m.CursorX() != 5 {
|
||||||
t.Errorf("CursorX() = %d, want 5", m.CursorX())
|
t.Errorf("CursorX() = %d, want 5", m.CursorX())
|
||||||
@ -731,8 +731,8 @@ func TestInsertModeDeletePreviousWord(t *testing.T) {
|
|||||||
if m.LineCount() != 1 {
|
if m.LineCount() != 1 {
|
||||||
t.Errorf("LineCount() = %d, want 1", m.LineCount())
|
t.Errorf("LineCount() = %d, want 1", m.LineCount())
|
||||||
}
|
}
|
||||||
if m.Line(0) != "helloworld" {
|
if m.lines[0] != "helloworld" {
|
||||||
t.Errorf("lines[0] = %q, want 'helloworld'", m.Line(0))
|
t.Errorf("lines[0] = %q, want 'helloworld'", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.CursorX() != 4 {
|
if m.CursorX() != 4 {
|
||||||
t.Errorf("CursorX() = %d, want 4", m.CursorX())
|
t.Errorf("CursorX() = %d, want 4", m.CursorX())
|
||||||
@ -748,8 +748,8 @@ 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.Line(0) != "" {
|
if m.lines[0] != "" {
|
||||||
t.Errorf("lines[0] = %q, want ''", m.Line(0))
|
t.Errorf("lines[0] = %q, want ''", m.lines[0])
|
||||||
}
|
}
|
||||||
if m.CursorX() != 0 {
|
if m.CursorX() != 0 {
|
||||||
t.Errorf("CursorX() = %d, want 0", m.CursorX())
|
t.Errorf("CursorX() = %d, want 0", m.CursorX())
|
||||||
|
|||||||
@ -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.CursorY() != 1 {
|
if m.cursor.y != 1 {
|
||||||
t.Errorf("cursor.y = %d, want 1", m.CursorY())
|
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")
|
sendKeys(tm, "j", "j", "j", "j")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.CursorY() != 4 {
|
if m.cursor.y != 4 {
|
||||||
t.Errorf("cursor.y = %d, want 4", m.CursorY())
|
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")
|
sendKeys(tm, "j", "j", "j", "j", "j", "j", "j", "j", "j")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.CursorY() != 5 {
|
if m.cursor.y != 5 {
|
||||||
t.Errorf("cursor.y = %d, want 5", m.CursorY())
|
t.Errorf("cursor.y = %d, want 5", m.cursor.y)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -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.CursorY() != 3 {
|
if m.cursor.y != 3 {
|
||||||
t.Errorf("cursor.y = %d, want 3", m.CursorY())
|
t.Errorf("cursor.y = %d, want 3", m.cursor.y)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -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.CursorY() != 5 {
|
if m.cursor.y != 5 {
|
||||||
t.Errorf("cursor.y = %d, want 5", m.CursorY())
|
t.Errorf("cursor.y = %d, want 5", m.cursor.y)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -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.CursorX() != want {
|
if m.cursor.x != want {
|
||||||
t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
|
t.Errorf("cursor.x = %d, want %d", m.cursor.x, 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.CursorX() != 3 {
|
if m.cursor.x != 3 {
|
||||||
t.Errorf("cursor.x = %d, want 3", m.CursorX())
|
t.Errorf("cursor.x = %d, want 3", m.cursor.x)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -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.CursorY() != 1 {
|
if m.cursor.y != 1 {
|
||||||
t.Errorf("cursor.y = %d, want 1", m.CursorY())
|
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")
|
sendKeys(tm, "k", "k", "k", "k")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.CursorY() != 0 {
|
if m.cursor.y != 0 {
|
||||||
t.Errorf("cursor.y = %d, want 0", m.CursorY())
|
t.Errorf("cursor.y = %d, want 0", m.cursor.y)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -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.CursorY() != 0 {
|
if m.cursor.y != 0 {
|
||||||
t.Errorf("cursor.y = %d, want 0", m.CursorY())
|
t.Errorf("cursor.y = %d, want 0", m.cursor.y)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -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.CursorY() != 2 {
|
if m.cursor.y != 2 {
|
||||||
t.Errorf("cursor.y = %d, want 2", m.CursorY())
|
t.Errorf("cursor.y = %d, want 2", m.cursor.y)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -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.CursorY() != 0 {
|
if m.cursor.y != 0 {
|
||||||
t.Errorf("cursor.y = %d, want 0", m.CursorY())
|
t.Errorf("cursor.y = %d, want 0", m.cursor.y)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -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.CursorX() != want {
|
if m.cursor.x != want {
|
||||||
t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
|
t.Errorf("cursor.x = %d, want %d", m.cursor.x, 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.CursorX() != 3 {
|
if m.cursor.x != 3 {
|
||||||
t.Errorf("cursor.x = %d, want 3", m.CursorX())
|
t.Errorf("cursor.x = %d, want 3", m.cursor.x)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -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.CursorX() != 1 {
|
if m.cursor.x != 1 {
|
||||||
t.Errorf("cursor.x = %d, want 1", m.CursorX())
|
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")
|
sendKeys(tm, "l", "l", "l", "l")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.CursorX() != 4 {
|
if m.cursor.x != 4 {
|
||||||
t.Errorf("cursor.x = %d, want 4", m.CursorX())
|
t.Errorf("cursor.x = %d, want 4", m.cursor.x)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -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.CursorX() != want {
|
if m.cursor.x != want {
|
||||||
t.Errorf("cursor.x = %d, want %d", m.CursorX(), 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")
|
sendKeys(tm, "3", "l")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.CursorX() != 3 {
|
if m.cursor.x != 3 {
|
||||||
t.Errorf("cursor.x = %d, want 3", m.CursorX())
|
t.Errorf("cursor.x = %d, want 3", m.cursor.x)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -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.CursorX() != want {
|
if m.cursor.x != want {
|
||||||
t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
|
t.Errorf("cursor.x = %d, want %d", m.cursor.x, 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.CursorX() != 2 {
|
if m.cursor.x != 2 {
|
||||||
t.Errorf("cursor.x = %d, want 2", m.CursorX())
|
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")
|
sendKeys(tm, "h", "h", "h", "h")
|
||||||
|
|
||||||
m := getFinalModel(t, tm)
|
m := getFinalModel(t, tm)
|
||||||
if m.CursorX() != 0 {
|
if m.cursor.x != 0 {
|
||||||
t.Errorf("cursor.x = %d, want 0", m.CursorX())
|
t.Errorf("cursor.x = %d, want 0", m.cursor.x)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -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.CursorX() != 0 {
|
if m.cursor.x != 0 {
|
||||||
t.Errorf("cursor.x = %d, want 0", m.CursorX())
|
t.Errorf("cursor.x = %d, want 0", m.cursor.x)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -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.CursorX() != 2 {
|
if m.cursor.x != 2 {
|
||||||
t.Errorf("cursor.x = %d, want 2", m.CursorX())
|
t.Errorf("cursor.x = %d, want 2", m.cursor.x)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -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.CursorX() != 0 {
|
if m.cursor.x != 0 {
|
||||||
t.Errorf("cursor.x = %d, want 0", m.CursorX())
|
t.Errorf("cursor.x = %d, want 0", m.cursor.x)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,72 +9,53 @@ import (
|
|||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type cursor struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
// Buffers
|
lines []string
|
||||||
buffers []*action.Buffer
|
cursor cursor
|
||||||
//next buffer id?
|
anchor cursor // starting point for visual modes
|
||||||
|
scrollY int
|
||||||
|
mode action.Mode
|
||||||
|
win_h int
|
||||||
|
win_w int
|
||||||
|
input *input.Handler
|
||||||
|
|
||||||
// Windows
|
// Insert repetition
|
||||||
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)
|
|
||||||
insertCount int
|
insertCount int
|
||||||
insertKeys []string
|
insertKeys []string
|
||||||
insertAction action.Action
|
insertAction action.Action
|
||||||
|
|
||||||
// Command line state
|
// Command mode
|
||||||
command string
|
command string
|
||||||
commandCursor int
|
commandCursor int
|
||||||
commandError error
|
commandError error
|
||||||
commandOutput string
|
commandOutput string
|
||||||
|
|
||||||
// Global settings (TODO: This needs to be refactored)
|
// Settings
|
||||||
settings action.Settings
|
settings action.Settings
|
||||||
|
|
||||||
// Registers
|
// Registers
|
||||||
registers map[rune]action.Register // name -> register
|
registers map[rune]action.Register // name -> register
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModel(lines []string, pos action.Position) *Model {
|
func NewModel(lines []string, pos action.Position) Model {
|
||||||
m := Model{
|
return Model{
|
||||||
|
lines: lines,
|
||||||
|
cursor: cursor{
|
||||||
|
x: pos.Col,
|
||||||
|
y: pos.Line,
|
||||||
|
},
|
||||||
|
scrollY: 0,
|
||||||
mode: action.NormalMode,
|
mode: action.NormalMode,
|
||||||
command: "",
|
command: "",
|
||||||
input: input.NewHandler(),
|
input: input.NewHandler(),
|
||||||
settings: action.NewDefaultSettings(),
|
settings: action.NewDefaultSettings(),
|
||||||
registers: action.DefaultRegisters(),
|
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 {
|
func (m Model) Init() tea.Cmd {
|
||||||
@ -84,74 +65,73 @@ func (m Model) Init() tea.Cmd {
|
|||||||
// Implement action.Model interface
|
// Implement action.Model interface
|
||||||
|
|
||||||
func (m *Model) Lines() []string {
|
func (m *Model) Lines() []string {
|
||||||
win := m.ActiveWindow()
|
return m.lines
|
||||||
return win.Buffer.Lines
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) Line(idx int) string {
|
func (m *Model) Line(idx int) string {
|
||||||
win := m.ActiveWindow()
|
if idx < 0 || idx >= len(m.lines) {
|
||||||
return win.Buffer.Line(idx)
|
return ""
|
||||||
|
}
|
||||||
|
return m.lines[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) SetLine(idx int, content string) {
|
func (m *Model) SetLine(idx int, content string) {
|
||||||
win := m.ActiveWindow()
|
if idx >= 0 && idx < len(m.lines) {
|
||||||
win.Buffer.SetLine(idx, content)
|
m.lines[idx] = content
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) InsertLine(idx int, content string) {
|
func (m *Model) InsertLine(idx int, content string) {
|
||||||
win := m.ActiveWindow()
|
if idx < 0 {
|
||||||
win.Buffer.InsertLine(idx, content)
|
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) {
|
func (m *Model) DeleteLine(idx int) {
|
||||||
win := m.ActiveWindow()
|
if idx >= 0 && idx < len(m.lines) {
|
||||||
win.Buffer.DeleteLine(idx)
|
m.lines = append(m.lines[:idx], m.lines[idx+1:]...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) LineCount() int {
|
func (m *Model) LineCount() int {
|
||||||
win := m.ActiveWindow()
|
return len(m.lines)
|
||||||
return win.Buffer.LineCount()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) CursorX() int {
|
func (m *Model) CursorX() int {
|
||||||
win := m.ActiveWindow()
|
return m.cursor.x
|
||||||
return win.Cursor.Col
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) CursorY() int {
|
func (m *Model) CursorY() int {
|
||||||
win := m.ActiveWindow()
|
return m.cursor.y
|
||||||
return win.Cursor.Line
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) SetCursorX(x int) {
|
func (m *Model) SetCursorX(x int) {
|
||||||
win := m.ActiveWindow()
|
m.cursor.x = x
|
||||||
win.Cursor.Col = x
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) SetCursorY(y int) {
|
func (m *Model) SetCursorY(y int) {
|
||||||
win := m.ActiveWindow()
|
m.cursor.y = y
|
||||||
win.Cursor.Line = y
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anchor methods
|
// Anchor methods
|
||||||
func (m *Model) AnchorX() int {
|
func (m *Model) AnchorX() int {
|
||||||
win := m.ActiveWindow()
|
return m.anchor.x
|
||||||
return win.Anchor.Col
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) AnchorY() int {
|
func (m *Model) AnchorY() int {
|
||||||
win := m.ActiveWindow()
|
return m.anchor.y
|
||||||
return win.Anchor.Line
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) SetAnchorX(x int) {
|
func (m *Model) SetAnchorX(x int) {
|
||||||
win := m.ActiveWindow()
|
m.anchor.x = x
|
||||||
win.Anchor.Col = x
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) SetAnchorY(y int) {
|
func (m *Model) SetAnchorY(y int) {
|
||||||
win := m.ActiveWindow()
|
m.anchor.y = y
|
||||||
win.Anchor.Line = y
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert methods
|
// Insert methods
|
||||||
@ -246,33 +226,32 @@ func (m *Model) UpdateDefaultRegister(t action.RegisterType, cnt []string) {
|
|||||||
|
|
||||||
// Window
|
// Window
|
||||||
func (m *Model) ScrollY() int {
|
func (m *Model) ScrollY() int {
|
||||||
win := m.ActiveWindow()
|
return m.scrollY
|
||||||
return win.ScrollY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) SetScrollY(y int) {
|
func (m *Model) SetScrollY(y int) {
|
||||||
win := m.ActiveWindow()
|
m.scrollY = y
|
||||||
win.ScrollY = y
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) WinH() int {
|
func (m *Model) WinH() int {
|
||||||
win := m.ActiveWindow()
|
return m.win_h
|
||||||
return win.Height
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) WinW() int {
|
func (m *Model) WinW() int {
|
||||||
win := m.ActiveWindow()
|
return m.win_w
|
||||||
return win.Width
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) ViewPortH() int {
|
func (m *Model) ViewPortH() int {
|
||||||
win := m.ActiveWindow()
|
return m.win_h - 2 // -2 for status bar and commmand bar
|
||||||
return win.Height - 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) ClampCursorX() {
|
func (m *Model) ClampCursorX() {
|
||||||
win := m.ActiveWindow()
|
lineLen := len(m.lines[m.cursor.y])
|
||||||
win.ClampCursorX()
|
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.
|
// 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)))
|
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 {
|
func (m *Model) Mode() action.Mode {
|
||||||
return m.mode
|
return m.mode
|
||||||
}
|
}
|
||||||
@ -334,29 +294,24 @@ func (m *Model) SetInsertRecording(count int, act action.Action) {
|
|||||||
m.insertAction = act
|
m.insertAction = act
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) GetCursorPosition() *action.Position {
|
func (m *Model) GetCursorPosition() action.Position {
|
||||||
// Return a copy of the position
|
return action.Position{Line: m.cursor.y, Col: m.cursor.x}
|
||||||
win := m.ActiveWindow()
|
|
||||||
pos := win.Cursor
|
|
||||||
return &pos
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) replayInsert() {
|
func (m *Model) replayInsert() {
|
||||||
win := m.ActiveWindow()
|
|
||||||
|
|
||||||
// Replay (count - 1) more times
|
// Replay (count - 1) more times
|
||||||
for i := 1; i < m.insertCount; i++ {
|
for i := 1; i < m.insertCount; i++ {
|
||||||
// For 'o' and 'O', we need to create a new line first
|
// For 'o' and 'O', we need to create a new line first
|
||||||
switch m.insertAction.(type) {
|
switch m.insertAction.(type) {
|
||||||
case action.OpenLineBelow:
|
case action.OpenLineBelow:
|
||||||
pos := win.Cursor.Line
|
pos := m.cursor.y
|
||||||
win.Buffer.Lines = append(win.Buffer.Lines[:pos+1], append([]string{""}, win.Buffer.Lines[pos+1:]...)...)
|
m.lines = append(m.lines[:pos+1], append([]string{""}, m.lines[pos+1:]...)...)
|
||||||
win.Cursor.Line++
|
m.cursor.y++
|
||||||
win.Cursor.Col = 0
|
m.cursor.x = 0
|
||||||
case action.OpenLineAbove:
|
case action.OpenLineAbove:
|
||||||
pos := win.Cursor.Line
|
pos := m.cursor.y
|
||||||
win.Buffer.Lines = append(win.Buffer.Lines[:pos], append([]string{""}, win.Buffer.Lines[pos:]...)...)
|
m.lines = append(m.lines[:pos], append([]string{""}, m.lines[pos:]...)...)
|
||||||
win.Cursor.Col = 0
|
m.cursor.x = 0
|
||||||
// 'i' and 'a' don't need setup - just replay keys
|
// 'i' and 'a' don't need setup - just replay keys
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,12 +323,11 @@ func (m *Model) replayInsert() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) ExitInsertMode() {
|
func (m *Model) ExitInsertMode() {
|
||||||
win := m.ActiveWindow()
|
|
||||||
if m.insertCount > 1 {
|
if m.insertCount > 1 {
|
||||||
m.replayInsert()
|
m.replayInsert()
|
||||||
}
|
}
|
||||||
if win.Cursor.Col > 0 {
|
if m.cursor.x > 0 {
|
||||||
win.Cursor.Col--
|
m.cursor.x--
|
||||||
}
|
}
|
||||||
m.mode = action.NormalMode
|
m.mode = action.NormalMode
|
||||||
m.insertCount = 0
|
m.insertCount = 0
|
||||||
|
|||||||
@ -4,47 +4,14 @@ import (
|
|||||||
tea "github.com/charmbracelet/bubbletea"
|
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
|
var cmd tea.Cmd
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
|
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
m.termHeight = msg.Height
|
m.win_h = msg.Height
|
||||||
m.termWidth = msg.Width
|
m.win_w = 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
|
|
||||||
}
|
|
||||||
|
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
// TODO: This needs to be removed, but for now its required for the tests.
|
// 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 {
|
if msg.Type == tea.KeyCtrlC {
|
||||||
return m, tea.Quit
|
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
|
// Keep cursor in view after any update
|
||||||
|
|||||||
@ -146,7 +146,7 @@ func drawStatusBar(m Model) string {
|
|||||||
left := leftBar(m)
|
left := leftBar(m)
|
||||||
right := rightBar(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
|
// This happens when the terminal spawns
|
||||||
if diff <= 0 {
|
if diff <= 0 {
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const (
|
|||||||
|
|
||||||
// PositionGetter is used to get cursor position for operator ranges
|
// PositionGetter is used to get cursor position for operator ranges
|
||||||
type PositionGetter interface {
|
type PositionGetter interface {
|
||||||
GetCursorPosition() *action.Position
|
GetCursorPosition() action.Position
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
@ -205,7 +205,7 @@ func (h *Handler) handleAfterOperator(m action.Model, kind string, binding any,
|
|||||||
start := pg.GetCursorPosition()
|
start := pg.GetCursorPosition()
|
||||||
mot.Execute(m)
|
mot.Execute(m)
|
||||||
end := pg.GetCursorPosition()
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user