Add initial version
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/levi
|
||||
7
go.mod
Normal file
7
go.mod
Normal file
@@ -0,0 +1,7 @@
|
||||
module tea.kareha.org/lab/levi
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require golang.org/x/term v0.41.0
|
||||
|
||||
require golang.org/x/sys v0.42.0 // indirect
|
||||
4
go.sum
Normal file
4
go.sum
Normal file
@@ -0,0 +1,4 @@
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||
182
main.go
Normal file
182
main.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func enableRawMode() (*term.State, error) {
|
||||
return term.MakeRaw(int(os.Stdin.Fd()))
|
||||
}
|
||||
|
||||
func disableRawMode(state *term.State) {
|
||||
term.Restore(int(os.Stdin.Fd()), state)
|
||||
}
|
||||
|
||||
func clearScreen() {
|
||||
fmt.Print("\x1b[2J")
|
||||
}
|
||||
|
||||
func goHome() {
|
||||
fmt.Print("\x1b[H")
|
||||
}
|
||||
|
||||
func moveCursor(x, y int) {
|
||||
fmt.Printf("\x1b[%d;%dH", y, x)
|
||||
}
|
||||
|
||||
func hideCursor() {
|
||||
fmt.Print("\x1b[?25l")
|
||||
}
|
||||
|
||||
func showCursor() {
|
||||
fmt.Print("\x1b[?25h")
|
||||
}
|
||||
|
||||
func getScreenSize() (int, int) {
|
||||
w, h, err := term.GetSize(int(os.Stdout.Fd()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return w, h
|
||||
}
|
||||
|
||||
type Screen struct {
|
||||
w, h int
|
||||
}
|
||||
|
||||
func NewScreen() Screen {
|
||||
w, h := getScreenSize()
|
||||
return Screen{
|
||||
w: w,
|
||||
h: h,
|
||||
}
|
||||
}
|
||||
|
||||
func (scr *Screen) Size() (int, int) {
|
||||
return scr.w, scr.h
|
||||
}
|
||||
|
||||
const Esc rune = 0x1b
|
||||
|
||||
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)
|
||||
}
|
||||
size := runeSize(buf[0])
|
||||
if size == -1 {
|
||||
panic("Invalid UTF-8")
|
||||
}
|
||||
full := make([]byte, size)
|
||||
full[0] = buf[0]
|
||||
if size > 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")
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
type Keyboard struct {}
|
||||
|
||||
func NewKeyboard() Keyboard {
|
||||
return Keyboard{}
|
||||
}
|
||||
|
||||
func (kb *Keyboard) ReadRune() rune {
|
||||
return readRune()
|
||||
}
|
||||
|
||||
type Editor struct {
|
||||
scr *Screen
|
||||
kb *Keyboard
|
||||
x, y int
|
||||
line *strings.Builder
|
||||
}
|
||||
|
||||
func NewEditor(scr *Screen, kb *Keyboard) Editor {
|
||||
_, h := scr.Size()
|
||||
return Editor{
|
||||
scr: scr,
|
||||
x: 0,
|
||||
y: h / 2,
|
||||
line: new(strings.Builder),
|
||||
}
|
||||
}
|
||||
|
||||
func (ed *Editor) Screen() *Screen {
|
||||
return ed.scr
|
||||
}
|
||||
|
||||
func (ed *Editor) AddRune(r rune) {
|
||||
ed.line.WriteRune(r)
|
||||
}
|
||||
|
||||
func draw(ed *Editor) {
|
||||
clearScreen()
|
||||
goHome()
|
||||
|
||||
fmt.Print("Hit Esc to Exit")
|
||||
|
||||
moveCursor(ed.x, ed.y)
|
||||
fmt.Print(ed.line.String())
|
||||
}
|
||||
|
||||
func main() {
|
||||
// init
|
||||
oldState, err := enableRawMode()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() {
|
||||
disableRawMode(oldState)
|
||||
showCursor()
|
||||
}()
|
||||
|
||||
// main
|
||||
scr := NewScreen()
|
||||
kb := NewKeyboard()
|
||||
ed := NewEditor(&scr, &kb)
|
||||
for {
|
||||
hideCursor()
|
||||
draw(&ed)
|
||||
showCursor()
|
||||
|
||||
r := kb.ReadRune()
|
||||
if r == Esc {
|
||||
break
|
||||
}
|
||||
ed.AddRune(r)
|
||||
}
|
||||
|
||||
// cleanup
|
||||
clearScreen()
|
||||
goHome()
|
||||
}
|
||||
Reference in New Issue
Block a user