Skip to content

Commit

Permalink
feat(v2): wip
Browse files Browse the repository at this point in the history
  • Loading branch information
aymanbagabas committed Oct 17, 2024
1 parent 3b4d040 commit dee8c40
Show file tree
Hide file tree
Showing 12 changed files with 333 additions and 0 deletions.
24 changes: 24 additions & 0 deletions vt/c0.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package vt

import "github.com/charmbracelet/x/ansi"

// handleControl handles a control character.
func (t *Terminal) handleControl(r rune) {
switch r {
case ansi.BEL: // BEL - Bell
if t.Bell != nil {
t.Bell()
}
case ansi.BS: // BS - Backspace
if t.scr.cur.Pos.X > 0 {
t.scr.cur.Pos.X--
}
case ansi.HT: // HT - Horizontal Tab
case ansi.LF: // LF - Line Feed
if t.scr.cur.Pos.Y < t.scr.Height()-1 {
t.scr.cur.Pos.Y++
}
case ansi.CR: // CR - Carriage Return
t.scr.cur.Pos.X = 0
}
}
15 changes: 15 additions & 0 deletions vt/csi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package vt

// handleCsi handles a CSI escape sequences.
func (t *Terminal) handleCsi(seq []byte) {
params := t.parser.Params[:t.parser.ParamsLen]
cmd := t.parser.Cmd
switch cmd {
case 'm': // SGR - Select Graphic Rendition
t.handleSgr(params)
}
}

// handleSgr handles SGR escape sequences.
func (t *Terminal) handleSgr(params []int) {
}
13 changes: 13 additions & 0 deletions vt/cursor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package vt

import (
"image"
)

// Cursor represents a cursor in a terminal.
type Cursor struct {
Pen Style
Pos image.Point
Style int
Visible bool
}
5 changes: 5 additions & 0 deletions vt/dcs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package vt

// handleDcs handles a DCS escape sequence.
func (t *Terminal) handleDcs(seq []byte) {
}
12 changes: 12 additions & 0 deletions vt/esc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package vt

// handleEsc handles an escape sequence.
func (t *Terminal) handleEsc(seq []byte) {
cmd := t.parser.Cmd
switch cmd {
case '7': // DECSC - Save Cursor
t.scr.SaveCursor()
case '8': // DECRC - Restore Cursor
t.scr.RestoreCursor()
}
}
12 changes: 12 additions & 0 deletions vt/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module github.com/charmbracelet/x/vt

go 1.23.1

require github.com/charmbracelet/x/cellbuf v0.0.0-20241014013228-327c48faaf2e

require (
github.com/charmbracelet/x/ansi v0.3.2 // indirect
github.com/charmbracelet/x/wcwidth v0.0.0-20241011142426-46044092ad91 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/text v0.19.0 // indirect
)
14 changes: 14 additions & 0 deletions vt/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
github.com/charmbracelet/x/ansi v0.3.2 h1:wsEwgAN+C9U06l9dCVMX0/L3x7ptvY1qmjMwyfE6USY=
github.com/charmbracelet/x/ansi v0.3.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/cellbuf v0.0.0-20241011142426-46044092ad91 h1:3iIITXJhYqbKQKIDhApjMY0r7bkQ3ZFUAgu9ImcuPWw=
github.com/charmbracelet/x/cellbuf v0.0.0-20241011142426-46044092ad91/go.mod h1:YIkHDHri4mY/TRfsZtYryGkAABZx12otLDkC0lju7HQ=
github.com/charmbracelet/x/cellbuf v0.0.0-20241014013228-327c48faaf2e h1:uCN5BuqaOSDlAfxLPmNmAK7jXzNXgKnC/irIEKXQD7Y=
github.com/charmbracelet/x/cellbuf v0.0.0-20241014013228-327c48faaf2e/go.mod h1:mFpvlGowTd0Fiv4TdoKyGZaZdigSUHtBJralbADonwE=
github.com/charmbracelet/x/wcwidth v0.0.0-20240910151828-580711411937 h1:uedm0silIB0T2oq5B7DmzIFK5uuzE11JGRGlLg0qd6w=
github.com/charmbracelet/x/wcwidth v0.0.0-20240910151828-580711411937/go.mod h1:4aLP2na+IIQOyb2ZxmwiJmcVN8cVfDk6T91kjSMOWRI=
github.com/charmbracelet/x/wcwidth v0.0.0-20241011142426-46044092ad91 h1:D5OO0lVavz7A+Swdhp62F9gbkibxmz9B2hZ/jVdMPf0=
github.com/charmbracelet/x/wcwidth v0.0.0-20241011142426-46044092ad91/go.mod h1:Ey8PFmYwH+/td9bpiEx07Fdx9ZVkxfIjWXxBluxF4Nw=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
17 changes: 17 additions & 0 deletions vt/osc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package vt

// handleOsc handles an OSC escape sequence.
func (t *Terminal) handleOsc([]byte) {
cmd := t.parser.Cmd
switch cmd {
case 0: // Set window title and icon name
name := string(t.parser.Data[:t.parser.DataLen])
t.iconName, t.title = name, name
case 1: // Set icon name
name := string(t.parser.Data[:t.parser.DataLen])
t.iconName = name
case 2: // Set window title
name := string(t.parser.Data[:t.parser.DataLen])
t.title = name
}
}
81 changes: 81 additions & 0 deletions vt/screen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package vt

import "github.com/charmbracelet/x/cellbuf"

// Screen represents a virtual terminal screen.
type Screen struct {
// The buffer of the screen.
buf cellbuf.Buffer
// The cur of the screen.
cur, saved Cursor
}

var _ cellbuf.Grid = &Screen{}

// NewScreen creates a new screen.
func NewScreen(w, h int) *Screen {
s := new(Screen)
s.buf.Resize(w, h)
return s
}

// At implements cellbuf.Grid.
func (s *Screen) At(x int, y int) (cellbuf.Cell, error) {
return s.buf.At(x, y)
}

// Height implements cellbuf.Grid.
func (s *Screen) Height() int {
return s.buf.Height()
}

// Resize implements cellbuf.Grid.
func (s *Screen) Resize(width int, height int) {
s.buf.Resize(width, height)
}

// Set implements cellbuf.Grid.
func (s *Screen) Set(x int, y int, c cellbuf.Cell) bool {
return s.buf.Set(x, y, c)
}

// Width implements cellbuf.Grid.
func (s *Screen) Width() int {
return s.buf.Width()
}

// Pos returns the cursor position.
func (s *Screen) Pos() (int, int) {
return s.cur.Pos.X, s.cur.Pos.Y
}

// Cursor returns the cursor.
func (s *Screen) Cursor() Cursor {
return s.cur
}

// SaveCursor saves the cursor.
func (s *Screen) SaveCursor() {
s.saved = s.cur
}

// RestoreCursor restores the cursor.
func (s *Screen) RestoreCursor() {
s.cur = s.saved
}

// ShowCursor shows the cursor.
func (s *Screen) ShowCursor() {
s.cur.Visible = true
}

// HideCursor hides the cursor.
func (s *Screen) HideCursor() {
s.cur.Visible = false
}

// moveCursor moves the cursor.
func (s *Screen) moveCursor(x, y int) {
s.cur.Pos.X = x
s.cur.Pos.Y = y
}
132 changes: 132 additions & 0 deletions vt/terminal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package vt

import (
"bytes"
"unicode"
"unicode/utf8"

"github.com/charmbracelet/x/ansi"
"github.com/charmbracelet/x/ansi/parser"
"github.com/charmbracelet/x/cellbuf"
)

type (
// Style represents a style.
Style = cellbuf.Style

// Link represents a hyperlink.
Link = cellbuf.Link
)

// Terminal represents a virtual terminal.
type Terminal struct {
// The input buffer of the terminal.
buf bytes.Buffer

// The current focused screen.
scr *Screen

// Both main and alt screens.
scrs [2]Screen

// The ANSI parser to use.
parser *ansi.Parser

// The terminal's icon name and title.
iconName, title string

// Bell handler. When set, this function is called when a bell character is
// received.
Bell func()
}

// NewTerminal creates a new terminal.
func NewTerminal(w, h int) *Terminal {
t := new(Terminal)
t.scrs[0] = *NewScreen(w, h)
t.scrs[1] = *NewScreen(w, h)
t.scr = &t.scrs[0]
t.parser = ansi.NewParser(parser.MaxParamsSize, 1024*4) // 4MB data buffer
return t
}

// At returns the cell at the given position.
func (t *Terminal) At(x int, y int) (cellbuf.Cell, error) {
return t.scr.At(x, y)
}

// Height returns the height of the terminal.
func (t *Terminal) Height() int {
return t.scr.Height()
}

// Width returns the width of the terminal.
func (t *Terminal) Width() int {
return t.scr.Width()
}

// Resize resizes the terminal.
func (t *Terminal) Resize(width int, height int) {
t.scrs[0].Resize(width, height)
t.scrs[1].Resize(width, height)
}

// Read reads data from the terminal input buffer.
func (t *Terminal) Read(p []byte) (n int, err error) {
return t.buf.Read(p)
}

// Write writes data to the terminal output buffer.
func (t *Terminal) Write(p []byte) (n int, err error) {
var state byte
for len(p) > 0 {
seq, width, m, newState := ansi.DecodeSequence(p, state, t.parser)
r, rw := utf8.DecodeRune(seq)

switch {
case ansi.HasCsiPrefix(seq):
t.handleCsi(seq)
case ansi.HasOscPrefix(seq):
t.handleOsc(seq)
case ansi.HasDcsPrefix(seq):
t.handleDcs(seq)
case ansi.HasEscPrefix(seq):
t.handleEsc(seq)
case unicode.IsControl(r):
t.handleControl(r)
default:
t.handleUtf8(seq, width, r, rw)
}

state = newState
p = p[m:]
n += m
}

return
}

// Cursor returns the cursor.
func (t *Terminal) Cursor() Cursor {
return t.scr.Cursor()
}

// Pos returns the cursor position.
func (t *Terminal) Pos() (int, int) {
return t.scr.Pos()
}

// Title returns the terminal's title.
func (t *Terminal) Title() string {
return t.title
}

// IconName returns the terminal's icon name.
func (t *Terminal) IconName() string {
return t.iconName
}

// String returns the terminal's content as a string.
func (t *Terminal) String() string {
return cellbuf.Render(t.scr)
}
5 changes: 5 additions & 0 deletions vt/utf8.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package vt

// handleUtf8 handles a UTF-8 characters.
func (t *Terminal) handleUtf8(seq []byte, width int, r rune, rw int) {
}
3 changes: 3 additions & 0 deletions vt/vt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// vt is a virtual terminal emulator that can be used to render text-based user
// interfaces.
package vt

0 comments on commit dee8c40

Please sign in to comment.