gilo/editor/editor.go
Timothy Warren ae65be003c
All checks were successful
timw4mail/gilo/pipeline/head This commit looks good
Fix some offset errors for cursor operations
2021-03-31 14:32:43 -04:00

228 lines
3.8 KiB
Go

package editor
import (
"bufio"
"log"
"os"
"timshome.page/gilo/terminal"
)
// ----------------------------------------------------------------------------
// !Editor
// ----------------------------------------------------------------------------
type point struct {
x int
y int
}
type editor struct {
screen *terminal.Screen
cursor *point
offset *point
rows []*row
}
func New() *editor {
screen := terminal.Size()
cursor := &point { 0, 0 }
offset := &point { 0, 0 }
var rows []*row
return &editor{
screen,
cursor,
offset,
rows,
}
}
func (e *editor) Open(filename string) {
file, err := os.Open(filename)
if err != nil {
log.Fatalf("failed to open file")
}
defer file.Close()
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
e.appendRow(scanner.Text())
}
}
func (e *editor) ProcessKeypress() bool {
var str string
ch, _ := terminal.ReadKey()
if isCtrl(ch) {
switch ch {
case ctrl('q'):
// Clean up on exit
terminal.Write(terminal.ClearScreen + terminal.ResetCursor)
return false
}
}
if ch == '\x1b' {
str = parseEscapeSequence()
} else {
str = string(ch)
}
switch str {
case keyUp, keyDown, keyLeft, keyRight, keyPageUp, keyPageDown, keyHome, keyEnd:
e.moveCursor(str)
return true
default:
// Do something later
terminal.Write("Code: %v", str)
}
return true
}
func (e *editor) moveCursor (key string) {
var row *row
if e.cursor.y >= len(e.rows) {
row = nil
} else {
row = e.rows[e.cursor.y]
}
switch key {
case keyLeft:
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.rows[e.cursor.y].size()
}
case keyRight:
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 < len(e.rows) - 1 {
e.cursor.y += 1
e.cursor.x = 0
}
case keyUp:
if e.cursor.y != 0 {
e.cursor.y -= 1
}
case keyDown:
if e.cursor.y < (len(e.rows) - 1) {
e.cursor.y += 1
}
case keyPageUp:
if e.cursor.y > e.screen.Rows {
e.cursor.y -= e.screen.Rows
} else {
e.cursor.y = 0
}
case keyPageDown:
if e.cursor.y + e.screen.Rows > len(e.rows) {
e.cursor.y += e.screen.Rows
} else {
e.cursor.y = len(e.rows) - 1
}
case keyHome:
e.cursor.x = 0
case keyEnd:
if row != nil {
e.cursor.x = row.size()
}
}
if e.cursor.y > len(e.rows) {
row = nil
} else {
row = e.rows[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
}
}
}
func (e *editor) appendRow(s string) {
newRow := newRow(s)
newRow.update()
e.rows = append(e.rows, newRow)
}
// Convert the raw ANSI escape sequences to the type of key input
func parseEscapeSequence () string {
var runes []rune
for i := 0; i < 3; i++ {
ch, size := terminal.ReadKey()
if size == 0 {
return "\x1b"
}
runes = append(runes, ch)
if i == 1 && runes[1] >= 'A' {
// \eOH \eOF
if runes[0] == 'O' {
switch runes[1] {
case 'H':
return keyHome
case 'F':
return keyEnd
}
}
// \e[A
if runes[0] == '[' {
switch runes[1] {
case 'A':
return keyUp
case 'B':
return keyDown
case 'C':
return keyRight
case 'D':
return keyLeft
case 'H':
return keyHome
case 'F':
return keyEnd
}
}
}
// \e[1~
if i == 2 && runes[0] == '[' && runes[2] == '~' {
switch runes[1] {
case '1':
return keyHome
case '3':
return keyDelete
case '4':
return keyEnd
case '5':
return keyPageUp
case '6':
return keyPageDown
case '7':
return keyHome
case '8':
return keyEnd
}
}
}
return string('\x1b')
}