-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3b4d040
commit dee8c40
Showing
12 changed files
with
333 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |