diff --git a/internal/console/console.go b/internal/console/console.go index e1a3b4a..511865e 100644 --- a/internal/console/console.go +++ b/internal/console/console.go @@ -39,7 +39,7 @@ func HomeCursor() { } func MoveCursor(x, y int) { - fmt.Printf("\x1b[%d;%dH", y + 1, x + 1) + fmt.Printf("\x1b[%d;%dH", y+1, x+1) } func HideCursor() { @@ -60,13 +60,13 @@ func Size() (int, int) { func runeSize(b byte) int { switch { - case b & 0x80 == 0: + case b&0x80 == 0: return 1 - case b & 0xe0 == 0xc0: + case b&0xe0 == 0xc0: return 2 - case b & 0xf0 == 0xe0: + case b&0xf0 == 0xe0: return 3 - case b & 0xf8 == 0xf0: + case b&0xf8 == 0xf0: return 4 default: return -1 // invalid diff --git a/internal/editor/editor.go b/internal/editor/editor.go index 7673807..af2d018 100644 --- a/internal/editor/editor.go +++ b/internal/editor/editor.go @@ -2,6 +2,7 @@ package editor import ( "strings" + "unicode/utf8" "tea.kareha.org/lab/levi/internal/console" "tea.kareha.org/lab/levi/internal/util" @@ -15,11 +16,13 @@ const ( ) type Editor struct { - scr *Screen - kb *Keyboard - x, y int - line *strings.Builder - mode mode + scr *Screen + kb *Keyboard + col, row int + x, y int + head, tail string + insert *strings.Builder + mode mode } func New() *Editor { @@ -27,33 +30,54 @@ func New() *Editor { kb := NewKeyboard() return &Editor{ - scr: &scr, - kb: &kb, - x: 0, - y: 0, - line: new(strings.Builder), - mode: modeCommand, + scr: &scr, + kb: &kb, + col: 0, + row: 0, + x: 0, + y: 0, + head: "", + tail: "", + insert: new(strings.Builder), + mode: modeCommand, } } func (ed *Editor) addRune(r rune) { - ed.line.WriteRune(r) + ed.insert.WriteRune(r) +} + +func (ed *Editor) drawBuffer() { + switch ed.mode { + case modeCommand: + console.Print(ed.head) + case modeInsert: + console.Print(ed.head) + console.Print(ed.insert.String()) + console.Print(ed.tail) + } } func (ed *Editor) drawStatus() { _, h := console.Size() - console.MoveCursor(0, h - 2) + console.MoveCursor(0, h-2) switch ed.mode { case modeCommand: - console.Print("-- [command] q: quit, i: insert --") + console.Print("-- [command] q: quit, i: insert, a: insert after --") case modeInsert: console.Print("-- [insert] Esc: command mode --") } } -func (ed *Editor) drawBuffer() { - console.Print(ed.line.String()) +func (ed *Editor) updateCursor() { + switch ed.mode { + case modeCommand: + ed.x = util.StringWidth(ed.head, ed.col) + case modeInsert: + ed.x = util.StringWidth(ed.head+ed.insert.String(), ed.col) + } + ed.y = ed.row } func (ed *Editor) repaint() { @@ -65,11 +89,39 @@ func (ed *Editor) repaint() { ed.drawBuffer() ed.drawStatus() + ed.updateCursor() console.MoveCursor(ed.x, ed.y) console.ShowCursor() } +func (ed *Editor) enterInsert() { + rs := []rune(ed.head) + ed.head = string(rs[:ed.col]) + ed.tail = string(rs[ed.col:]) + ed.mode = modeInsert +} + +func (ed *Editor) enterInsertAfter() { + len := utf8.RuneCountInString(ed.head) + if ed.col < len-1 { + ed.moveRight(1) + ed.enterInsert() + return + } else { + ed.col++ + ed.mode = modeInsert + } +} + +func (ed *Editor) moveLeft(n int) { + ed.col = max(ed.col-n, 0) +} + +func (ed *Editor) moveRight(n int) { + ed.col = min(ed.col+n, max(utf8.RuneCountInString(ed.head)-1, 0)) +} + func (ed *Editor) Main() { for { ed.repaint() @@ -81,15 +133,25 @@ func (ed *Editor) Main() { case 'q': return case 'i': - ed.mode = modeInsert + ed.enterInsert() + case 'a': + ed.enterInsertAfter() + case 'h': + ed.moveLeft(1) + case 'l': + ed.moveRight(1) } case modeInsert: switch r { case Esc: + ed.head = ed.head + ed.insert.String() + ed.tail + ed.tail = "" + ed.insert = new(strings.Builder) ed.mode = modeCommand + ed.moveLeft(1) default: ed.addRune(r) - ed.x += util.RuneWidth(r) + ed.col++ } } } diff --git a/internal/editor/keyboard.go b/internal/editor/keyboard.go index 323576c..440878d 100644 --- a/internal/editor/keyboard.go +++ b/internal/editor/keyboard.go @@ -6,7 +6,7 @@ import ( const Esc rune = 0x1b -type Keyboard struct {} +type Keyboard struct{} func NewKeyboard() Keyboard { return Keyboard{} diff --git a/internal/util/util.go b/internal/util/util.go index 97cd773..f7727b6 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -5,8 +5,7 @@ import ( ) func isWide(r rune) bool { - return r >= 0x1100 && ( - r <= 0x115f || // Hangul Jamo + return r >= 0x1100 && (r <= 0x115f || // Hangul Jamo r == 0x2329 || r == 0x232a || (r >= 0x2e80 && r <= 0xa4cf) || (r >= 0xac00 && r <= 0xd7a3) || @@ -47,3 +46,16 @@ func RuneWidth(r rune) int { return 1 } + +func StringWidth(s string, col int) int { + sum := 0 + i := 0 + for _, r := range s { + if i >= col { + break + } + sum += RuneWidth(r) + i++ + } + return sum +}