Refactor
This commit is contained in:
@@ -5,7 +5,7 @@ func (ed *Editor) Insert() {
|
||||
if ed.mode == ModeInsert {
|
||||
panic("invalid state")
|
||||
}
|
||||
ed.ins.Enter(ed.lines[ed.row], ed.col)
|
||||
ed.ins.Init(ed.CurrentLine(), ed.col)
|
||||
ed.mode = ModeInsert
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@ func (ed *Editor) MoveLeft(n int) {
|
||||
if ed.mode != ModeCommand {
|
||||
panic("invalid state")
|
||||
}
|
||||
ed.col = max(ed.col-n, 0)
|
||||
ed.col -= n
|
||||
ed.Confine()
|
||||
}
|
||||
|
||||
// key: l
|
||||
@@ -36,7 +37,8 @@ func (ed *Editor) MoveRight(n int) {
|
||||
if ed.mode != ModeCommand {
|
||||
panic("invalid state")
|
||||
}
|
||||
ed.col = min(ed.col+n, max(ed.RuneCount()-1, 0))
|
||||
ed.col += n
|
||||
ed.Confine()
|
||||
}
|
||||
|
||||
// key: j
|
||||
@@ -44,7 +46,8 @@ func (ed *Editor) MoveDown(n int) {
|
||||
if ed.mode != ModeCommand {
|
||||
panic("invalid state")
|
||||
}
|
||||
ed.row = min(ed.row+n, max(len(ed.lines)-1, 0))
|
||||
ed.row += n
|
||||
ed.Confine()
|
||||
}
|
||||
|
||||
// key: k
|
||||
@@ -52,7 +55,8 @@ func (ed *Editor) MoveUp(n int) {
|
||||
if ed.mode != ModeCommand {
|
||||
panic("invalid state")
|
||||
}
|
||||
ed.row = max(ed.row-n, 0)
|
||||
ed.row -= n
|
||||
ed.Confine()
|
||||
}
|
||||
|
||||
// key: x
|
||||
@@ -60,11 +64,11 @@ func (ed *Editor) DeleteRune(n int) {
|
||||
if ed.mode != ModeCommand {
|
||||
panic("invalid state")
|
||||
}
|
||||
if len(ed.lines[ed.row]) < 1 {
|
||||
if len(ed.CurrentLine()) < 1 {
|
||||
ed.Ring()
|
||||
return
|
||||
}
|
||||
rs := []rune(ed.lines[ed.row])
|
||||
rs := []rune(ed.CurrentLine())
|
||||
if ed.col < 1 {
|
||||
ed.lines[ed.row] = string(rs[1:])
|
||||
} else {
|
||||
@@ -72,8 +76,5 @@ func (ed *Editor) DeleteRune(n int) {
|
||||
tail := string(rs[ed.col+1:])
|
||||
ed.lines[ed.row] = head + tail
|
||||
}
|
||||
rc := ed.RuneCount()
|
||||
if ed.col >= rc {
|
||||
ed.col = max(rc-1, 0)
|
||||
}
|
||||
ed.Confine()
|
||||
}
|
||||
|
||||
@@ -17,8 +17,9 @@ const (
|
||||
|
||||
type Editor struct {
|
||||
col, row int
|
||||
x, y int
|
||||
vrow int
|
||||
w, h int
|
||||
x, y int
|
||||
lines []string
|
||||
ins *Insert
|
||||
mode Mode
|
||||
@@ -26,15 +27,8 @@ type Editor struct {
|
||||
bell bool
|
||||
}
|
||||
|
||||
func (ed *Editor) Load() {
|
||||
if ed.path == "" {
|
||||
return
|
||||
}
|
||||
_, err := os.Stat(ed.path)
|
||||
if err != nil { // file not exists
|
||||
return
|
||||
}
|
||||
data, err := os.ReadFile(ed.path)
|
||||
func (ed *Editor) Load(path string) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -46,6 +40,7 @@ func (ed *Editor) Load() {
|
||||
data = data[:len(data)-1]
|
||||
}
|
||||
ed.lines = strings.Split(string(data), "\n")
|
||||
ed.path = path
|
||||
}
|
||||
|
||||
func Init(args []string) *Editor {
|
||||
@@ -54,12 +49,15 @@ func Init(args []string) *Editor {
|
||||
path = args[1]
|
||||
}
|
||||
|
||||
w, h := termi.Size()
|
||||
ed := &Editor{
|
||||
col: 0,
|
||||
row: 0,
|
||||
vrow: 0,
|
||||
w: w,
|
||||
h: h,
|
||||
x: 0,
|
||||
y: 0,
|
||||
vrow: 0,
|
||||
lines: make([]string, 1),
|
||||
ins: NewInsert(),
|
||||
mode: ModeCommand,
|
||||
@@ -67,21 +65,24 @@ func Init(args []string) *Editor {
|
||||
bell: false,
|
||||
}
|
||||
|
||||
ed.Load()
|
||||
if path != "" {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil { // file exists
|
||||
ed.Load(path)
|
||||
}
|
||||
}
|
||||
|
||||
termi.Raw()
|
||||
return ed
|
||||
}
|
||||
|
||||
func (ed *Editor) Save() {
|
||||
if ed.path == "" {
|
||||
return
|
||||
}
|
||||
func (ed *Editor) Save(path string) {
|
||||
text := strings.Join(ed.lines, "\n") + "\n"
|
||||
err := os.WriteFile(ed.path, []byte(text), 0644)
|
||||
err := os.WriteFile(path, []byte(text), 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ed.path = path
|
||||
}
|
||||
|
||||
func (ed *Editor) Finish() {
|
||||
@@ -90,16 +91,53 @@ func (ed *Editor) Finish() {
|
||||
termi.Cooked()
|
||||
termi.ShowCursor()
|
||||
|
||||
ed.Save()
|
||||
if ed.path != "" {
|
||||
ed.Save(ed.path)
|
||||
}
|
||||
}
|
||||
|
||||
func (ed *Editor) Line(row int) string {
|
||||
if ed.mode == ModeInsert && row == ed.row {
|
||||
return ed.ins.Line()
|
||||
} else {
|
||||
return ed.lines[row]
|
||||
}
|
||||
}
|
||||
|
||||
func (ed *Editor) CurrentLine() string {
|
||||
return ed.Line(ed.row)
|
||||
}
|
||||
|
||||
func (ed *Editor) RuneCount() int {
|
||||
return utf8.RuneCountInString(ed.lines[ed.row])
|
||||
return utf8.RuneCountInString(ed.CurrentLine())
|
||||
}
|
||||
|
||||
func (ed *Editor) Confine() {
|
||||
if ed.mode != ModeCommand {
|
||||
panic("invalid state")
|
||||
}
|
||||
|
||||
n := len(ed.lines)
|
||||
if ed.row < 0 {
|
||||
ed.row = 0
|
||||
} else if ed.row >= n {
|
||||
ed.row = max(n-1, 0)
|
||||
}
|
||||
|
||||
rc := ed.RuneCount()
|
||||
if ed.col < 0 {
|
||||
ed.col = 0
|
||||
} else if ed.col >= rc {
|
||||
ed.col = max(rc-1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (ed *Editor) InsertRune(r rune) {
|
||||
ed.ins.Write(r)
|
||||
ed.col++
|
||||
if ed.mode != ModeInsert {
|
||||
panic("invalid state")
|
||||
}
|
||||
ed.ins.WriteRune(r)
|
||||
ed.col = ed.ins.Column()
|
||||
}
|
||||
|
||||
func (ed *Editor) Ring() {
|
||||
|
||||
@@ -2,8 +2,6 @@ package editor
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"tea.kareha.org/lab/termi"
|
||||
)
|
||||
|
||||
type Insert struct {
|
||||
@@ -31,11 +29,8 @@ func (ins *Insert) Reset() {
|
||||
}
|
||||
}
|
||||
|
||||
func (ins *Insert) Write(r rune) {
|
||||
ins.body.WriteRune(r)
|
||||
}
|
||||
|
||||
func (ins *Insert) Enter(line string, col int) {
|
||||
func (ins *Insert) Init(line string, col int) {
|
||||
ins.Reset()
|
||||
rs := []rune(line)
|
||||
ins.head = string(rs[:col])
|
||||
if col < len(rs) {
|
||||
@@ -45,6 +40,10 @@ func (ins *Insert) Enter(line string, col int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (ins *Insert) WriteRune(r rune) {
|
||||
ins.body.WriteRune(r)
|
||||
}
|
||||
|
||||
func (ins *Insert) Line() string {
|
||||
return ins.head + ins.body.String() + ins.tail
|
||||
}
|
||||
@@ -60,10 +59,9 @@ func (ins *Insert) Newline() []string {
|
||||
return lines
|
||||
}
|
||||
|
||||
func (ins *Insert) Width() int {
|
||||
func (ins *Insert) Column() int {
|
||||
s := ins.head + ins.body.String()
|
||||
rc := utf8.RuneCountInString(s)
|
||||
return termi.StringWidth(s, rc)
|
||||
return utf8.RuneCountInString(s)
|
||||
}
|
||||
|
||||
func (ins *Insert) Backspace() bool {
|
||||
|
||||
@@ -5,6 +5,9 @@ import (
|
||||
)
|
||||
|
||||
func (ed *Editor) ExitInsert() {
|
||||
if ed.mode != ModeInsert {
|
||||
panic("invalid state")
|
||||
}
|
||||
ed.lines[ed.row] = ed.ins.Line()
|
||||
ed.ins.Reset()
|
||||
ed.mode = ModeCommand
|
||||
@@ -12,6 +15,9 @@ func (ed *Editor) ExitInsert() {
|
||||
}
|
||||
|
||||
func (ed *Editor) InsertNewline() {
|
||||
if ed.mode != ModeInsert {
|
||||
panic("invalid state")
|
||||
}
|
||||
before := make([]string, 0, len(ed.lines)+1)
|
||||
before = append(before, ed.lines[:ed.row]...)
|
||||
var after []string
|
||||
@@ -24,19 +30,24 @@ func (ed *Editor) InsertNewline() {
|
||||
ed.lines = append(append(before, lines...), after...)
|
||||
ed.row++
|
||||
ed.col = 0
|
||||
// row and col are confined automatically
|
||||
}
|
||||
|
||||
func (ed *Editor) DeleteBefore() {
|
||||
func (ed *Editor) Backspace() {
|
||||
if ed.mode != ModeInsert {
|
||||
panic("invalid state")
|
||||
}
|
||||
if !ed.ins.Backspace() {
|
||||
ed.Ring()
|
||||
return
|
||||
}
|
||||
ed.col--
|
||||
// col is confined automatically
|
||||
}
|
||||
|
||||
func (ed *Editor) Main() {
|
||||
for {
|
||||
ed.Repaint()
|
||||
ed.Draw()
|
||||
|
||||
key := termi.ReadKey()
|
||||
switch ed.mode {
|
||||
@@ -83,9 +94,9 @@ func (ed *Editor) Main() {
|
||||
case termi.RuneEnter:
|
||||
ed.InsertNewline()
|
||||
case termi.RuneBackspace:
|
||||
ed.DeleteBefore()
|
||||
ed.Backspace()
|
||||
case termi.RuneDelete:
|
||||
ed.DeleteBefore()
|
||||
ed.Backspace()
|
||||
default:
|
||||
ed.InsertRune(key.Rune)
|
||||
}
|
||||
|
||||
@@ -7,34 +7,26 @@ import (
|
||||
)
|
||||
|
||||
func (ed *Editor) LineHeight(line string) int {
|
||||
w, _ := termi.Size()
|
||||
rc := utf8.RuneCountInString(line)
|
||||
width := termi.StringWidth(line, rc)
|
||||
return 1 + max(width-1, 0)/w
|
||||
return 1 + max(width-1, 0)/ed.w
|
||||
}
|
||||
|
||||
func (ed *Editor) DrawBuffer() {
|
||||
_, h := termi.Size()
|
||||
|
||||
y := 0
|
||||
for i := ed.vrow; i < len(ed.lines); i++ {
|
||||
var line string
|
||||
if ed.mode == ModeInsert && i == ed.row {
|
||||
line = ed.ins.Line()
|
||||
} else {
|
||||
line = ed.lines[i]
|
||||
}
|
||||
line := ed.Line(i)
|
||||
|
||||
termi.MoveCursor(0, y)
|
||||
termi.Draw(line)
|
||||
|
||||
y += ed.LineHeight(line)
|
||||
if y >= h-1 {
|
||||
if y >= ed.h-1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for ; y < h-1; y++ {
|
||||
for ; y < ed.h-1; y++ {
|
||||
termi.MoveCursor(0, y)
|
||||
termi.Draw("~")
|
||||
}
|
||||
@@ -49,8 +41,7 @@ func (ed *Editor) DrawStatus() {
|
||||
m = "i"
|
||||
}
|
||||
|
||||
_, h := termi.Size()
|
||||
termi.MoveCursor(0, h-1)
|
||||
termi.MoveCursor(0, ed.h-1)
|
||||
if ed.bell {
|
||||
termi.EnableInvert()
|
||||
}
|
||||
@@ -62,25 +53,10 @@ func (ed *Editor) DrawStatus() {
|
||||
}
|
||||
|
||||
func (ed *Editor) UpdateCursor() {
|
||||
w, h := termi.Size()
|
||||
|
||||
var dy int
|
||||
switch ed.mode {
|
||||
case ModeCommand:
|
||||
ed.row = min(max(ed.row, 0), max(len(ed.lines)-1, 0))
|
||||
len := ed.RuneCount()
|
||||
ed.col = min(ed.col, max(len-1, 0))
|
||||
|
||||
// XXX approximation
|
||||
width := termi.StringWidth(ed.lines[ed.row], ed.col)
|
||||
ed.x = width % w
|
||||
dy = width / w
|
||||
case ModeInsert:
|
||||
// XXX approximation
|
||||
width := ed.ins.Width()
|
||||
ed.x = width % w
|
||||
dy = width / w
|
||||
}
|
||||
// XXX approximation
|
||||
width := termi.StringWidth(ed.CurrentLine(), ed.col)
|
||||
ed.x = width % ed.w
|
||||
dy := width / ed.w
|
||||
|
||||
if ed.row < ed.vrow {
|
||||
ed.vrow = ed.row
|
||||
@@ -92,7 +68,7 @@ func (ed *Editor) UpdateCursor() {
|
||||
}
|
||||
ed.y = y + dy
|
||||
|
||||
for ed.y >= h-1 {
|
||||
for ed.y >= ed.h-1 {
|
||||
ed.vrow++
|
||||
|
||||
y := 0
|
||||
@@ -104,6 +80,10 @@ func (ed *Editor) UpdateCursor() {
|
||||
}
|
||||
|
||||
func (ed *Editor) Repaint() {
|
||||
w, h := termi.Size()
|
||||
ed.w = w
|
||||
ed.h = h
|
||||
|
||||
termi.HideCursor()
|
||||
|
||||
termi.Clear()
|
||||
@@ -118,3 +98,7 @@ func (ed *Editor) Repaint() {
|
||||
|
||||
termi.ShowCursor()
|
||||
}
|
||||
|
||||
func (ed *Editor) Draw() {
|
||||
ed.Repaint()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user