diff --git a/editor/document.go b/editor/document.go index d56f473..134911c 100644 --- a/editor/document.go +++ b/editor/document.go @@ -5,25 +5,26 @@ import ( "log" "os" "timshome.page/gilo/internal/buffer" + "timshome.page/gilo/internal/gilo" ) -type document struct { +type Document struct { dirty bool filename string - rows []*row + rows []*Row } -func newDocument() *document { - var rows []*row +func NewDocument() *Document { + var rows []*Row - return &document{ + return &Document{ false, "", rows, } } -func (d *document) open(filename string) { +func (d *Document) Open(filename string) { file, err := os.Open(filename) if err != nil { log.Fatalf("failed to open file") @@ -35,13 +36,13 @@ func (d *document) open(filename string) { scanner.Split(bufio.ScanLines) for scanner.Scan() { - d.appendRow(scanner.Text()) + d.AppendRow(scanner.Text()) } d.dirty = false } -func (d *document) save() int { +func (d *Document) Save() int { if d.filename == "" { return 0 } @@ -52,7 +53,7 @@ func (d *document) save() int { } defer file.Close() - fileStr := d.toString() + fileStr := d.ToString() fileLen := len(fileStr) err = file.Truncate(int64(fileLen)) @@ -63,7 +64,7 @@ func (d *document) save() int { size, err := file.WriteString(fileStr) if err != nil { - panic("failed to save document to file") + panic("failed to save Document to file") } if fileLen == size { @@ -75,7 +76,11 @@ func (d *document) save() int { return 0 } -func (d *document) appendRow(s string) { +func (d *Document) Row(at int) *Row { + return d.rows[at] +} + +func (d *Document) AppendRow(s string) { newRow := newRow(s) newRow.update() d.rows = append(d.rows, newRow) @@ -83,7 +88,7 @@ func (d *document) appendRow(s string) { d.dirty = true } -func (d *document) toString() string { +func (d *Document) ToString() string { buf := buffer.NewBuffer() for _, row := range d.rows { @@ -94,6 +99,18 @@ func (d *document) toString() string { return buf.ToString() } -func (d *document) rowCount() int { +func (d *Document) RowCount() int { return len(d.rows) } + +func (d *Document) InsertChar(at *gilo.Point, ch rune) { + d.rows[at.Y].insertRune(ch, at.X) +} + +func (d *Document) DelChar(at *gilo.Point) { + d.rows[at.Y].deleteRune(at.X) +} + +func (d *Document) IsDirty() bool { + return d.dirty +} diff --git a/editor/document_test.go b/editor/document_test.go index e4a3638..a717329 100644 --- a/editor/document_test.go +++ b/editor/document_test.go @@ -3,30 +3,30 @@ package editor import "testing" func TestNewDocument(t *testing.T) { - d := newDocument() + d := NewDocument() if d == nil { - t.Errorf("Failed to create document") + t.Errorf("Failed to create Document") } } func TestAppendRow(t *testing.T) { - d := newDocument() - d.appendRow("Test Row") + d := NewDocument() + d.AppendRow("Test Row") got := len(d.rows) want := 1 if got != want { - t.Errorf("Failed to add a row to the document") + t.Errorf("Failed to add a row to the Document") } } func TestRowCount(t *testing.T) { - d := newDocument() - d.appendRow("Test Row") + d := NewDocument() + d.AppendRow("Test Row") - got := d.rowCount() + got := d.RowCount() want := 1 if got != want { diff --git a/editor/draw.go b/editor/draw.go index a86d072..3a8c198 100644 --- a/editor/draw.go +++ b/editor/draw.go @@ -6,7 +6,7 @@ import ( "strings" "time" "timshome.page/gilo/internal/buffer" - "timshome.page/gilo/terminal" + "timshome.page/gilo/internal/terminal" ) // ---------------------------------------------------------------------------- @@ -25,7 +25,7 @@ func (e *editor) RefreshScreen() { e.drawStatusBar(ab) e.drawMessageBar(ab) - ab.Append(terminal.MoveCursor(e.renderX-e.offset.x, e.cursor.y-e.offset.y)) + ab.Append(terminal.MoveCursor(e.renderX-e.offset.X, e.cursor.Y-e.offset.Y)) ab.Append(terminal.ShowCursor) @@ -35,46 +35,46 @@ func (e *editor) RefreshScreen() { func (e *editor) scroll() { e.renderX = 0 - if e.cursor.y < e.document.rowCount() { - e.renderX = e.document.rows[e.cursor.y].cursorXToRenderX(e.cursor.x) + if e.cursor.Y < e.document.RowCount() { + e.renderX = e.document.Row(e.cursor.Y).cursorXToRenderX(e.cursor.X) } - if e.cursor.y < e.offset.y { - e.offset.y = e.cursor.y + if e.cursor.Y < e.offset.Y { + e.offset.Y = e.cursor.Y } - if e.cursor.y >= e.offset.y+e.screen.Rows { - e.offset.y = e.cursor.y - e.screen.Rows + 1 + if e.cursor.Y >= e.offset.Y+e.screen.Rows { + e.offset.Y = e.cursor.Y - e.screen.Rows + 1 } - if e.renderX < e.offset.x { - e.offset.x = e.renderX + if e.renderX < e.offset.X { + e.offset.X = e.renderX } - if e.renderX >= e.offset.x+e.screen.Cols { - e.offset.x = e.renderX - e.screen.Cols + if e.renderX >= e.offset.X+e.screen.Cols { + e.offset.X = e.renderX - e.screen.Cols } } func (e *editor) drawRows(ab *buffer.Buffer) { for y := 0; y < e.screen.Rows; y++ { - fileRow := y + e.offset.y + fileRow := y + e.offset.Y - if fileRow >= e.document.rowCount() { + if fileRow >= e.document.RowCount() { e.drawPlaceholderRow(y, ab) } else { - rawRow := e.document.rows[fileRow] + rawRow := e.document.Row(fileRow) // If the column offset is greater than the length of the row, // just display an empty row - if e.offset.x > rawRow.size() { + if e.offset.X > rawRow.Size() { ab.Append("") continue } - rowLen := e.document.rows[fileRow].rSize() - e.offset.x - outputRow := truncate(string(e.document.rows[fileRow].render[e.offset.x:]), rowLen) + rowLen := e.document.Row(fileRow).RenderSize() - e.offset.X + outputRow := truncate(string(e.document.Row(fileRow).render[e.offset.X:]), rowLen) ab.Append(outputRow) } @@ -83,7 +83,7 @@ func (e *editor) drawRows(ab *buffer.Buffer) { } func (e *editor) drawPlaceholderRow(y int, ab *buffer.Buffer) { - if e.document.rowCount() == 0 && y == e.screen.Rows/3 { + if e.document.RowCount() == 0 && y == e.screen.Rows/3 { welcome := fmt.Sprintf("Gilo editor -- version %s", KiloVersion) if len(welcome) > e.screen.Cols { welcome = truncate(welcome, e.screen.Cols) @@ -120,7 +120,7 @@ func (e *editor) drawStatusBar(ab *buffer.Buffer) { modified = "(modified)" } - leftStatus := fmt.Sprintf("%.20s - %d lines %s", fileName, e.document.rowCount(), modified) + leftStatus := fmt.Sprintf("%.20s - %d lines %s", fileName, e.document.RowCount(), modified) length := len(leftStatus) if length > cols { leftStatus = truncate(leftStatus, cols) @@ -131,7 +131,7 @@ func (e *editor) drawStatusBar(ab *buffer.Buffer) { return } - rightStatus := fmt.Sprintf("%d/%d", e.cursor.y+1, e.document.rowCount()) + rightStatus := fmt.Sprintf("%d/%d", e.cursor.Y+1, e.document.RowCount()) rlength := len(rightStatus) statusLength := length + rlength diff --git a/editor/editor.go b/editor/editor.go index 637214d..1221b92 100644 --- a/editor/editor.go +++ b/editor/editor.go @@ -4,18 +4,14 @@ package editor import ( "fmt" "time" - "timshome.page/gilo/terminal" + "timshome.page/gilo/internal/gilo" + "timshome.page/gilo/internal/terminal" ) // ---------------------------------------------------------------------------- // !Editor // ---------------------------------------------------------------------------- -type point struct { - x int - y int -} - type statusMsg struct { message string created time.Time @@ -23,9 +19,9 @@ type statusMsg struct { type editor struct { screen *terminal.Screen - cursor *point - offset *point - document *document + cursor *gilo.Point + offset *gilo.Point + document *Document status *statusMsg quitTimes uint8 renderX int @@ -37,9 +33,9 @@ func New() *editor { // Subtract rows for status bar and message bar/prompt screen.Rows -= 2 - cursor := &point{0, 0} - offset := &point{0, 0} - document := newDocument() + cursor := gilo.DefaultPoint() + offset := gilo.DefaultPoint() + document := NewDocument() status := &statusMsg{ "", time.Now(), @@ -57,11 +53,11 @@ func New() *editor { } func (e *editor) Open(filename string) { - e.document.open(filename) + e.document.Open(filename) } func (e *editor) Save() { - size := e.document.save() + size := e.document.Save() if size > 0 { e.SetStatusMessage("%d bytes written to disk", size) @@ -84,23 +80,23 @@ func (e *editor) ProcessKeypress() bool { } func (e *editor) insertChar(ch rune) { - if e.cursor.y == e.document.rowCount() { - e.document.appendRow("") + if e.cursor.Y == e.document.RowCount() { + e.document.AppendRow("") } - e.document.rows[e.cursor.y].insertRune(ch, e.cursor.x) - e.cursor.x += 1 + e.document.Row(e.cursor.Y).insertRune(ch, e.cursor.X) + e.cursor.X += 1 e.document.dirty = true } func (e *editor) delChar() { - if e.cursor.y == e.document.rowCount() { + if e.cursor.Y == e.document.RowCount() { return } - if e.cursor.x > 0 { - e.document.rows[e.cursor.y].deleteRune(e.cursor.x - 1) - e.cursor.x -= 1 + if e.cursor.X > 0 { + e.document.Row(e.cursor.Y).deleteRune(e.cursor.X - 1) + e.cursor.X -= 1 } e.document.dirty = true diff --git a/editor/editor_test.go b/editor/editor_test.go index dde7865..d9f2a2e 100644 --- a/editor/editor_test.go +++ b/editor/editor_test.go @@ -14,12 +14,12 @@ func TestInsertChar(t *testing.T) { e := New() e.insertChar('q') - if e.document.rowCount() != 1 { + if e.document.RowCount() != 1 { t.Errorf("A row was not created when the character was inserted") } - row := e.document.rows[0] - if row.size() != 1 { + row := e.document.Row(0) + if row.Size() != 1 { t.Errorf("Failed to add character to row. Row: %v", row) } } diff --git a/editor/input.go b/editor/input.go index 8313251..1ab138d 100644 --- a/editor/input.go +++ b/editor/input.go @@ -1,8 +1,8 @@ package editor import ( - "timshome.page/gilo/key" - "timshome.page/gilo/terminal" + "timshome.page/gilo/internal/key" + "timshome.page/gilo/internal/terminal" ) /** @@ -81,71 +81,71 @@ func (e *editor) processKeypressStr(key string) { } func (e *editor) moveCursor(key string) { - var row *row - if e.cursor.y >= e.document.rowCount() { + var row *Row + if e.cursor.Y >= e.document.RowCount() { row = nil } else { - row = e.document.rows[e.cursor.y] + row = e.document.Row(e.cursor.Y) } switch key { case keyLeft: - if e.cursor.x != 0 { - e.cursor.x -= 1 + if e.cursor.X != 0 { + e.cursor.X -= 1 } // Move from beginning of current row to end of previous row - if e.cursor.y > 0 { - e.cursor.y -= 1 - e.cursor.x = e.document.rows[e.cursor.y].size() + if e.cursor.Y > 0 { + e.cursor.Y -= 1 + e.cursor.X = e.document.Row(e.cursor.Y).Size() } case keyRight: - if row != nil && e.cursor.x < row.size() { - e.cursor.x += 1 + if row != nil && e.cursor.X < row.Size() { + e.cursor.X += 1 } // Move from end of current line to beginning of next line - if row != nil && e.cursor.x == row.size() && e.cursor.y < e.document.rowCount()-1 { - e.cursor.y += 1 - e.cursor.x = 0 + if row != nil && e.cursor.X == row.Size() && e.cursor.Y < e.document.RowCount()-1 { + e.cursor.Y += 1 + e.cursor.X = 0 } case keyUp: - if e.cursor.y != 0 { - e.cursor.y -= 1 + if e.cursor.Y != 0 { + e.cursor.Y -= 1 } case keyDown: - if e.cursor.y < e.document.rowCount() { - e.cursor.y += 1 + if e.cursor.Y < e.document.RowCount() { + e.cursor.Y += 1 } case keyPageUp: - if e.cursor.y > e.screen.Rows { - e.cursor.y -= e.screen.Rows + if e.cursor.Y > e.screen.Rows { + e.cursor.Y -= e.screen.Rows } else { - e.cursor.y = 0 + e.cursor.Y = 0 } case keyPageDown: - if e.cursor.y+e.screen.Rows > e.document.rowCount() { - e.cursor.y += e.screen.Rows + if e.cursor.Y+e.screen.Rows > e.document.RowCount() { + e.cursor.Y += e.screen.Rows } else { - e.cursor.y = e.document.rowCount() - 1 + e.cursor.Y = e.document.RowCount() - 1 } case keyHome: - e.cursor.x = 0 + e.cursor.X = 0 case keyEnd: if row != nil { - e.cursor.x = row.size() + e.cursor.X = row.Size() } } - if e.cursor.y < e.document.rowCount() { - row = e.document.rows[e.cursor.y] - rowLen := row.size() + if e.cursor.Y < e.document.RowCount() { + row = e.document.Row(e.cursor.Y) + rowLen := row.Size() // Snap to the end of a shorter line from a longer one - if e.cursor.x > rowLen { - e.cursor.x = rowLen + if e.cursor.X > rowLen { + e.cursor.X = rowLen } } } diff --git a/editor/input_test.go b/editor/input_test.go index a00c433..0f170fc 100644 --- a/editor/input_test.go +++ b/editor/input_test.go @@ -2,23 +2,24 @@ package editor import ( "testing" - "timshome.page/gilo/key" + "timshome.page/gilo/internal/gilo" + "timshome.page/gilo/internal/key" ) type moveCursor struct { keys []string withFile bool - cursor *point + cursor *gilo.Point } var cursorTests = []moveCursor{ - {[]string{string(rune(key.Esc))}, false, &point{0, 0}}, - {[]string{keyRight}, true, &point{1, 0}}, - {[]string{keyDown, keyEnd}, true, &point{14, 1}}, - {[]string{keyEnd, keyHome}, true, &point{0, 0}}, - {[]string{keyRight, keyLeft}, true, &point{0, 0}}, - {[]string{keyDown, keyUp}, true, &point{0, 0}}, - // {[]string{keyPageUp}, true, &point{0, 0}}, + {[]string{string(rune(key.Esc))}, false, gilo.DefaultPoint()}, + {[]string{keyRight}, true, gilo.NewPoint(1, 0)}, + {[]string{keyDown, keyEnd}, true, gilo.NewPoint(14, 1)}, + {[]string{keyEnd, keyHome}, true, gilo.DefaultPoint()}, + {[]string{keyRight, keyLeft}, true, gilo.DefaultPoint()}, + {[]string{keyDown, keyUp}, true, gilo.DefaultPoint()}, + // {[]string{keyPageUp}, true, gilo.DefaultPoint()}, } func TestMoveCursor(t *testing.T) { @@ -35,14 +36,14 @@ func TestMoveCursor(t *testing.T) { want := test.cursor got := e.cursor - if got.x != want.x || got.y != want.y { + if got.X != want.X || got.Y != want.Y { t.Errorf("Output %v not equal to expected %v for input %q", got, want, test.keys) } } } type seqTest struct { - input string + input string expected string } diff --git a/editor/row.go b/editor/row.go index 04c99d5..4112fed 100644 --- a/editor/row.go +++ b/editor/row.go @@ -4,12 +4,12 @@ import ( "strings" ) -type row struct { +type Row struct { chars []rune render []rune } -func newRow(s string) *row { +func newRow(s string) *Row { var chars []rune var render []rune @@ -18,21 +18,21 @@ func newRow(s string) *row { render = append(render, ch) } - return &row{chars, render} + return &Row{chars, render} } -func (r *row) size() int { +func (r *Row) Size() int { return len(r.chars) } -func (r *row) rSize() int { +func (r *Row) RenderSize() int { return len(r.render) } -func (r *row) insertRune(ch rune, at int) { +func (r *Row) insertRune(ch rune, at int) { // If insertion index is invalid, just // append the rune to the end of the array - if at < 0 || at >= r.size() { + if at < 0 || at >= r.Size() { r.chars = append(r.chars, ch) r.update() @@ -43,7 +43,7 @@ func (r *row) insertRune(ch rune, at int) { // Split the character array at the insertion point start := r.chars[0:at] - end := r.chars[at:r.size()] + end := r.chars[at:r.Size()] // Splice it back together newSlice = append(newSlice, start...) @@ -54,8 +54,8 @@ func (r *row) insertRune(ch rune, at int) { r.update() } -func (r *row) deleteRune(at int) { - if at < 0 || at >= r.size() { +func (r *Row) deleteRune(at int) { + if at < 0 || at >= r.Size() { return } @@ -63,7 +63,7 @@ func (r *row) deleteRune(at int) { // Split the character array at the insertion point start := r.chars[0:at] - end := r.chars[at+1 : r.size()] // Skip the index in question + end := r.chars[at+1 : r.Size()] // Skip the index in question // Splice it back together newSlice = append(newSlice, start...) @@ -73,7 +73,7 @@ func (r *row) deleteRune(at int) { r.update() } -func (r *row) update() { +func (r *Row) update() { r.render = r.render[:0] replacement := strings.Repeat(" ", KiloTabStop) @@ -83,11 +83,11 @@ func (r *row) update() { } } -func (r *row) toString() string { +func (r *Row) toString() string { return string(r.chars) } -func (r *row) cursorXToRenderX(cursorX int) int { +func (r *Row) cursorXToRenderX(cursorX int) int { renderX := 0 i := 0 diff --git a/editor/row_test.go b/editor/row_test.go index fe9e289..e8e0fbd 100644 --- a/editor/row_test.go +++ b/editor/row_test.go @@ -18,7 +18,7 @@ func TestRowSize(t *testing.T) { str := "abcdefg" row := newRow(str) - got := row.size() + got := row.Size() want := 7 if got != want { @@ -31,7 +31,7 @@ func TestRenderSize(t *testing.T) { row := newRow(str) row.update() - got := row.rSize() + got := row.RenderSize() want := 11 if got != want { @@ -58,8 +58,8 @@ func TestInsertRune(t *testing.T) { row := newRow(test.initial) row.insertRune(test.ch, test.at) - if row.size() != 5 { - t.Errorf("Row size after inserting rune at index [%d] is %d, should be %d", test.at, row.size(), 5) + if row.Size() != 5 { + t.Errorf("Row size after inserting rune at index [%d] is %d, should be %d", test.at, row.Size(), 5) } got := row.toString() diff --git a/gilo.go b/gilo.go index 669e8ac..d803c1c 100644 --- a/gilo.go +++ b/gilo.go @@ -5,7 +5,7 @@ import ( "golang.org/x/term" "os" "timshome.page/gilo/editor" - "timshome.page/gilo/terminal" + "timshome.page/gilo/internal/terminal" ) func cleanup(oldState *term.State) { diff --git a/internal/gilo/shared.go b/internal/gilo/shared.go new file mode 100644 index 0000000..ab2ed7f --- /dev/null +++ b/internal/gilo/shared.go @@ -0,0 +1,14 @@ +package gilo + +type Point struct { + X int + Y int +} + +func DefaultPoint() *Point { + return &Point{0, 0} +} + +func NewPoint(x, y int) *Point { + return &Point{x, y} +} diff --git a/key/key.go b/internal/key/key.go similarity index 100% rename from key/key.go rename to internal/key/key.go diff --git a/key/key_test.go b/internal/key/key_test.go similarity index 100% rename from key/key_test.go rename to internal/key/key_test.go diff --git a/terminal/ansi.go b/internal/terminal/ansi.go similarity index 100% rename from terminal/ansi.go rename to internal/terminal/ansi.go diff --git a/terminal/ansi_test.go b/internal/terminal/ansi_test.go similarity index 100% rename from terminal/ansi_test.go rename to internal/terminal/ansi_test.go diff --git a/terminal/io.go b/internal/terminal/io.go similarity index 100% rename from terminal/io.go rename to internal/terminal/io.go diff --git a/terminal/raw.go b/internal/terminal/raw.go similarity index 100% rename from terminal/raw.go rename to internal/terminal/raw.go diff --git a/terminal/size.go b/internal/terminal/size.go similarity index 100% rename from terminal/size.go rename to internal/terminal/size.go