Refactor
This commit is contained in:
@@ -2,9 +2,7 @@ package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
@@ -25,7 +23,7 @@ func Raw() {
|
||||
|
||||
func Cooked() {
|
||||
if state == nil {
|
||||
panic("state is nil")
|
||||
panic("invalid state")
|
||||
}
|
||||
term.Restore(int(os.Stdin.Fd()), state)
|
||||
}
|
||||
@@ -53,47 +51,7 @@ func ShowCursor() {
|
||||
func Size() (int, int) {
|
||||
w, h, err := term.GetSize(int(os.Stdout.Fd()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return 80, 24
|
||||
}
|
||||
return w, h
|
||||
}
|
||||
|
||||
func runeSize(b byte) int {
|
||||
switch {
|
||||
case b&0x80 == 0:
|
||||
return 1
|
||||
case b&0xe0 == 0xc0:
|
||||
return 2
|
||||
case b&0xf0 == 0xe0:
|
||||
return 3
|
||||
case b&0xf8 == 0xf0:
|
||||
return 4
|
||||
default:
|
||||
return -1 // invalid
|
||||
}
|
||||
}
|
||||
|
||||
func ReadRune() rune {
|
||||
buf := make([]byte, 1)
|
||||
_, err := io.ReadFull(os.Stdin, buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
expected := runeSize(buf[0])
|
||||
if expected == -1 {
|
||||
panic("Invalid UTF-8 head")
|
||||
}
|
||||
full := make([]byte, expected)
|
||||
full[0] = buf[0]
|
||||
if expected > 1 {
|
||||
_, err := io.ReadFull(os.Stdin, full[1:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
r, size := utf8.DecodeRune(full)
|
||||
if r == utf8.RuneError && size == 1 {
|
||||
panic("Invalid UTF-8 body")
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
95
internal/console/input.go
Normal file
95
internal/console/input.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package console
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Key int
|
||||
|
||||
const (
|
||||
KeyNormal = iota
|
||||
KeyUp
|
||||
KeyDown
|
||||
KeyRight
|
||||
KeyLeft
|
||||
)
|
||||
|
||||
const RuneEscape rune = 0x1b
|
||||
const RuneEnter rune = '\r'
|
||||
const RuneBackspace rune = '\b'
|
||||
const RuneDelete rune = 0x7f
|
||||
|
||||
var buf []rune = make([]rune, 0)
|
||||
|
||||
func runeSize(b byte) int {
|
||||
switch {
|
||||
case b&0x80 == 0:
|
||||
return 1
|
||||
case b&0xe0 == 0xc0:
|
||||
return 2
|
||||
case b&0xf0 == 0xe0:
|
||||
return 3
|
||||
case b&0xf8 == 0xf0:
|
||||
return 4
|
||||
default:
|
||||
return -1 // invalid
|
||||
}
|
||||
}
|
||||
|
||||
func readRune() rune {
|
||||
buf := make([]byte, 1)
|
||||
_, err := io.ReadFull(os.Stdin, buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
expected := runeSize(buf[0])
|
||||
if expected == -1 {
|
||||
panic("Invalid UTF-8 head")
|
||||
}
|
||||
full := make([]byte, expected)
|
||||
full[0] = buf[0]
|
||||
if expected > 1 {
|
||||
_, err := io.ReadFull(os.Stdin, full[1:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
r, size := utf8.DecodeRune(full)
|
||||
if r == utf8.RuneError && size == 1 {
|
||||
panic("Invalid UTF-8 body")
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func ReadKey() (Key, rune) {
|
||||
if len(buf) > 0 {
|
||||
r := buf[0]
|
||||
buf = buf[1:]
|
||||
return KeyNormal, r
|
||||
}
|
||||
r := readRune()
|
||||
if r != RuneEscape {
|
||||
return KeyNormal, r
|
||||
}
|
||||
r2 := readRune()
|
||||
if r2 != '[' {
|
||||
buf = append(buf, r2)
|
||||
return KeyNormal, r
|
||||
}
|
||||
r3 := readRune()
|
||||
switch r3 {
|
||||
case 'A':
|
||||
return KeyUp, 0
|
||||
case 'B':
|
||||
return KeyDown, 0
|
||||
case 'C':
|
||||
return KeyRight, 0
|
||||
case 'D':
|
||||
return KeyLeft, 0
|
||||
}
|
||||
buf = append(buf, r2)
|
||||
buf = append(buf, r3)
|
||||
return KeyNormal, r
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package util
|
||||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"tea.kareha.org/lab/levi/internal/console"
|
||||
"tea.kareha.org/lab/levi/internal/util"
|
||||
)
|
||||
|
||||
type mode int
|
||||
@@ -18,8 +17,6 @@ const (
|
||||
)
|
||||
|
||||
type Editor struct {
|
||||
scr *screen
|
||||
kb *keyboard
|
||||
col, row int
|
||||
x, y int
|
||||
vrow int
|
||||
@@ -55,12 +52,7 @@ func Init(args []string) *Editor {
|
||||
|
||||
console.Raw()
|
||||
|
||||
scr := newScreen()
|
||||
kb := newKeyboard()
|
||||
|
||||
return &Editor{
|
||||
scr: &scr,
|
||||
kb: &kb,
|
||||
col: 0,
|
||||
row: 0,
|
||||
x: 0,
|
||||
@@ -95,14 +87,14 @@ func (ed *Editor) runeCount() int {
|
||||
}
|
||||
|
||||
func (ed *Editor) lineHeight(line string) int {
|
||||
w, _ := ed.scr.size()
|
||||
w, _ := console.Size()
|
||||
rc := utf8.RuneCountInString(line)
|
||||
width := util.StringWidth(line, rc)
|
||||
width := console.StringWidth(line, rc)
|
||||
return 1 + max(width-1, 0)/w
|
||||
}
|
||||
|
||||
func (ed *Editor) drawBuffer() {
|
||||
_, h := ed.scr.size()
|
||||
_, h := console.Size()
|
||||
|
||||
y := 0
|
||||
for i := ed.vrow; i < len(ed.lines); i++ {
|
||||
@@ -114,7 +106,7 @@ func (ed *Editor) drawBuffer() {
|
||||
}
|
||||
|
||||
console.MoveCursor(0, y)
|
||||
util.Print(line)
|
||||
console.Print(line)
|
||||
|
||||
y += ed.lineHeight(line)
|
||||
if y >= h-1 {
|
||||
@@ -124,24 +116,24 @@ func (ed *Editor) drawBuffer() {
|
||||
|
||||
for ; y < h-1; y++ {
|
||||
console.MoveCursor(0, y)
|
||||
util.Print("~")
|
||||
console.Print("~")
|
||||
}
|
||||
}
|
||||
|
||||
func (ed *Editor) drawStatus() {
|
||||
_, h := ed.scr.size()
|
||||
_, h := console.Size()
|
||||
|
||||
console.MoveCursor(0, h-1)
|
||||
switch ed.mode {
|
||||
case modeCommand:
|
||||
util.Print("c")
|
||||
console.Print("c")
|
||||
case modeInsert:
|
||||
util.Print("i")
|
||||
console.Print("i")
|
||||
}
|
||||
}
|
||||
|
||||
func (ed *Editor) updateCursor() {
|
||||
w, h := ed.scr.size()
|
||||
w, h := console.Size()
|
||||
|
||||
var dy int
|
||||
switch ed.mode {
|
||||
@@ -151,12 +143,12 @@ func (ed *Editor) updateCursor() {
|
||||
ed.col = min(ed.col, max(len-1, 0))
|
||||
|
||||
// XXX approximation
|
||||
width := util.StringWidth(ed.lines[ed.row], ed.col)
|
||||
width := console.StringWidth(ed.lines[ed.row], ed.col)
|
||||
ed.x = width % w
|
||||
dy = width / w
|
||||
case modeInsert:
|
||||
// XXX approximation
|
||||
width := util.StringWidth(ed.head+ed.insert.String(), ed.col)
|
||||
width := console.StringWidth(ed.head+ed.insert.String(), ed.col)
|
||||
ed.x = width % w
|
||||
dy = width / w
|
||||
}
|
||||
@@ -249,11 +241,11 @@ func (ed *Editor) Main() {
|
||||
for {
|
||||
ed.repaint()
|
||||
|
||||
k, r := ed.kb.readKey()
|
||||
k, r := console.ReadKey()
|
||||
switch ed.mode {
|
||||
case modeCommand:
|
||||
switch k {
|
||||
case keyNormal:
|
||||
case console.KeyNormal:
|
||||
switch r {
|
||||
case 'q':
|
||||
return
|
||||
@@ -272,42 +264,42 @@ func (ed *Editor) Main() {
|
||||
case 'x':
|
||||
ed.deleteRune(1)
|
||||
}
|
||||
case keyUp:
|
||||
case console.KeyUp:
|
||||
ed.moveUp(1)
|
||||
case keyDown:
|
||||
case console.KeyDown:
|
||||
ed.moveDown(1)
|
||||
case keyRight:
|
||||
case console.KeyRight:
|
||||
ed.moveRight(1)
|
||||
case keyLeft:
|
||||
case console.KeyLeft:
|
||||
ed.moveLeft(1)
|
||||
default:
|
||||
// TODO ring
|
||||
}
|
||||
case modeInsert:
|
||||
switch k {
|
||||
case keyNormal:
|
||||
case console.KeyNormal:
|
||||
switch r {
|
||||
case runeEscape:
|
||||
case console.RuneEscape:
|
||||
ed.exitInsert()
|
||||
case runeEnter:
|
||||
case console.RuneEnter:
|
||||
ed.insertNewline()
|
||||
case runeBackspace:
|
||||
case console.RuneBackspace:
|
||||
ed.deleteBefore()
|
||||
case runeDelete:
|
||||
case console.RuneDelete:
|
||||
ed.deleteBefore()
|
||||
default:
|
||||
ed.insertRune(r)
|
||||
}
|
||||
case keyUp:
|
||||
case console.KeyUp:
|
||||
ed.exitInsert()
|
||||
ed.moveUp(1)
|
||||
case keyDown:
|
||||
case console.KeyDown:
|
||||
ed.exitInsert()
|
||||
ed.moveDown(1)
|
||||
case keyRight:
|
||||
case console.KeyRight:
|
||||
ed.exitInsert()
|
||||
ed.moveRight(1)
|
||||
case keyLeft:
|
||||
case console.KeyLeft:
|
||||
ed.exitInsert()
|
||||
ed.moveLeft(1)
|
||||
default:
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
package editor
|
||||
|
||||
import (
|
||||
"tea.kareha.org/lab/levi/internal/console"
|
||||
)
|
||||
|
||||
const runeEscape rune = 0x1b
|
||||
const runeEnter rune = '\r'
|
||||
const runeBackspace rune = '\b'
|
||||
const runeDelete rune = 0x7f
|
||||
|
||||
type key int
|
||||
|
||||
const (
|
||||
keyNormal = iota
|
||||
keyUp
|
||||
keyDown
|
||||
keyRight
|
||||
keyLeft
|
||||
)
|
||||
|
||||
type keyboard struct {
|
||||
buf []rune
|
||||
}
|
||||
|
||||
func newKeyboard() keyboard {
|
||||
return keyboard{
|
||||
buf: make([]rune, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (kb *keyboard) readKey() (key, rune) {
|
||||
if len(kb.buf) > 0 {
|
||||
r := kb.buf[0]
|
||||
kb.buf = kb.buf[1:]
|
||||
return keyNormal, r
|
||||
}
|
||||
r := console.ReadRune()
|
||||
if r != runeEscape {
|
||||
return keyNormal, r
|
||||
}
|
||||
r2 := console.ReadRune()
|
||||
if r2 != '[' {
|
||||
kb.buf = append(kb.buf, r2)
|
||||
return keyNormal, r
|
||||
}
|
||||
r3 := console.ReadRune()
|
||||
switch r3 {
|
||||
case 'A':
|
||||
return keyUp, 0
|
||||
case 'B':
|
||||
return keyDown, 0
|
||||
case 'C':
|
||||
return keyRight, 0
|
||||
case 'D':
|
||||
return keyLeft, 0
|
||||
}
|
||||
kb.buf = append(kb.buf, r2)
|
||||
kb.buf = append(kb.buf, r3)
|
||||
return keyNormal, r
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package editor
|
||||
|
||||
import (
|
||||
"tea.kareha.org/lab/levi/internal/console"
|
||||
)
|
||||
|
||||
type screen struct{}
|
||||
|
||||
func newScreen() screen {
|
||||
return screen{}
|
||||
}
|
||||
|
||||
func (scr *screen) size() (int, int) {
|
||||
return console.Size()
|
||||
}
|
||||
Reference in New Issue
Block a user