Refine insert mode and add insert after command

This commit is contained in:
2026-03-25 10:47:11 +09:00
parent 138a877f67
commit e44c7d72bf
4 changed files with 100 additions and 26 deletions

View File

@@ -39,7 +39,7 @@ func HomeCursor() {
} }
func MoveCursor(x, y int) { 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() { func HideCursor() {
@@ -60,13 +60,13 @@ func Size() (int, int) {
func runeSize(b byte) int { func runeSize(b byte) int {
switch { switch {
case b & 0x80 == 0: case b&0x80 == 0:
return 1 return 1
case b & 0xe0 == 0xc0: case b&0xe0 == 0xc0:
return 2 return 2
case b & 0xf0 == 0xe0: case b&0xf0 == 0xe0:
return 3 return 3
case b & 0xf8 == 0xf0: case b&0xf8 == 0xf0:
return 4 return 4
default: default:
return -1 // invalid return -1 // invalid

View File

@@ -2,6 +2,7 @@ package editor
import ( import (
"strings" "strings"
"unicode/utf8"
"tea.kareha.org/lab/levi/internal/console" "tea.kareha.org/lab/levi/internal/console"
"tea.kareha.org/lab/levi/internal/util" "tea.kareha.org/lab/levi/internal/util"
@@ -15,11 +16,13 @@ const (
) )
type Editor struct { type Editor struct {
scr *Screen scr *Screen
kb *Keyboard kb *Keyboard
x, y int col, row int
line *strings.Builder x, y int
mode mode head, tail string
insert *strings.Builder
mode mode
} }
func New() *Editor { func New() *Editor {
@@ -27,33 +30,54 @@ func New() *Editor {
kb := NewKeyboard() kb := NewKeyboard()
return &Editor{ return &Editor{
scr: &scr, scr: &scr,
kb: &kb, kb: &kb,
x: 0, col: 0,
y: 0, row: 0,
line: new(strings.Builder), x: 0,
mode: modeCommand, y: 0,
head: "",
tail: "",
insert: new(strings.Builder),
mode: modeCommand,
} }
} }
func (ed *Editor) addRune(r rune) { 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() { func (ed *Editor) drawStatus() {
_, h := console.Size() _, h := console.Size()
console.MoveCursor(0, h - 2) console.MoveCursor(0, h-2)
switch ed.mode { switch ed.mode {
case modeCommand: case modeCommand:
console.Print("-- [command] q: quit, i: insert --") console.Print("-- [command] q: quit, i: insert, a: insert after --")
case modeInsert: case modeInsert:
console.Print("-- [insert] Esc: command mode --") console.Print("-- [insert] Esc: command mode --")
} }
} }
func (ed *Editor) drawBuffer() { func (ed *Editor) updateCursor() {
console.Print(ed.line.String()) 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() { func (ed *Editor) repaint() {
@@ -65,11 +89,39 @@ func (ed *Editor) repaint() {
ed.drawBuffer() ed.drawBuffer()
ed.drawStatus() ed.drawStatus()
ed.updateCursor()
console.MoveCursor(ed.x, ed.y) console.MoveCursor(ed.x, ed.y)
console.ShowCursor() 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() { func (ed *Editor) Main() {
for { for {
ed.repaint() ed.repaint()
@@ -81,15 +133,25 @@ func (ed *Editor) Main() {
case 'q': case 'q':
return return
case 'i': case 'i':
ed.mode = modeInsert ed.enterInsert()
case 'a':
ed.enterInsertAfter()
case 'h':
ed.moveLeft(1)
case 'l':
ed.moveRight(1)
} }
case modeInsert: case modeInsert:
switch r { switch r {
case Esc: case Esc:
ed.head = ed.head + ed.insert.String() + ed.tail
ed.tail = ""
ed.insert = new(strings.Builder)
ed.mode = modeCommand ed.mode = modeCommand
ed.moveLeft(1)
default: default:
ed.addRune(r) ed.addRune(r)
ed.x += util.RuneWidth(r) ed.col++
} }
} }
} }

View File

@@ -6,7 +6,7 @@ import (
const Esc rune = 0x1b const Esc rune = 0x1b
type Keyboard struct {} type Keyboard struct{}
func NewKeyboard() Keyboard { func NewKeyboard() Keyboard {
return Keyboard{} return Keyboard{}

View File

@@ -5,8 +5,7 @@ import (
) )
func isWide(r rune) bool { func isWide(r rune) bool {
return r >= 0x1100 && ( return r >= 0x1100 && (r <= 0x115f || // Hangul Jamo
r <= 0x115f || // Hangul Jamo
r == 0x2329 || r == 0x232a || r == 0x2329 || r == 0x232a ||
(r >= 0x2e80 && r <= 0xa4cf) || (r >= 0x2e80 && r <= 0xa4cf) ||
(r >= 0xac00 && r <= 0xd7a3) || (r >= 0xac00 && r <= 0xd7a3) ||
@@ -47,3 +46,16 @@ func RuneWidth(r rune) int {
return 1 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
}