Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions clipboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,23 @@ var clipboardName = map[string]string{
}

//nolint:mnd
func handleClipboard(p *ansi.Parser) (string, error) {
func handleClipboard(p *ansi.Parser) (seqInfo, error) {
parts := bytes.Split(p.Data(), []byte{';'})
if len(parts) != 3 {
// Invalid, ignore
return "", errInvalid
return seqNoMnemonic(""), errInvalid
}

if string(parts[2]) == "?" {
return fmt.Sprintf("Request %q clipboard", clipboardName[string(parts[1])]), nil
return seqNoMnemonic(fmt.Sprintf("Request %q clipboard", clipboardName[string(parts[1])])), nil
}

b64, err := base64.StdEncoding.DecodeString(string(parts[2]))
if err != nil {
// Invalid, ignore
//nolint:wrapcheck
return "", err
return seqNoMnemonic(""), err
}

return fmt.Sprintf("Set clipboard %q to %q", clipboardName[string(parts[1])], b64), nil
return seqNoMnemonic(fmt.Sprintf("Set clipboard %q to %q", clipboardName[string(parts[1])], b64)), nil
}
12 changes: 6 additions & 6 deletions color.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import (
)

//nolint:mnd
func handleTerminalColor(p *ansi.Parser) (string, error) {
func handleTerminalColor(p *ansi.Parser) (seqInfo, error) {
parts := bytes.Split(p.Data(), []byte{';'})
if len(parts) != 2 {
// Invalid, ignore
return "", errInvalid
return seqNoMnemonic(""), errInvalid
}

arg := string(parts[1])
Expand All @@ -33,15 +33,15 @@ func handleTerminalColor(p *ansi.Parser) (string, error) {
if arg == "?" {
buf += " to " + arg
}
return buf, nil
return seqNoMnemonic(buf), nil
}

//nolint:mnd
func handleResetTerminalColor(p *ansi.Parser) (string, error) {
func handleResetTerminalColor(p *ansi.Parser) (seqInfo, error) {
parts := bytes.Split(p.Data(), []byte{';'})
if len(parts) != 1 {
// Invalid, ignore
return "", errInvalid
return seqNoMnemonic(""), errInvalid
}
var buf string
switch p.Command() {
Expand All @@ -52,5 +52,5 @@ func handleResetTerminalColor(p *ansi.Parser) (string, error) {
case 112:
buf += "Reset cursor color"
}
return buf, nil
return seqNoMnemonic(buf), nil
}
57 changes: 42 additions & 15 deletions cursor.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

//nolint:mnd
func handleCursor(p *ansi.Parser) (string, error) {
func handleCursor(p *ansi.Parser) (seqInfo, error) {
count := 1
if n, ok := p.Param(0, 1); ok && n > 0 {
count = n
Expand All @@ -18,20 +18,38 @@ func handleCursor(p *ansi.Parser) (string, error) {
switch cmd.Final() {
case 'A':
// CUU - Cursor Up
return fmt.Sprintf("Cursor up %d", default1(count)), nil
return seqInfo{
"CUU",
fmt.Sprintf("Cursor up %d", default1(count)),
}, nil
case 'B':
// CUD - Cursor Down
return fmt.Sprintf("Cursor down %d", default1(count)), nil
return seqInfo{
"CUD",
fmt.Sprintf("Cursor down %d", default1(count)),
}, nil
case 'C':
// CUF - Cursor Forward
return fmt.Sprintf("Cursor right %d", default1(count)), nil
return seqInfo{
"CUF",
fmt.Sprintf("Cursor right %d", default1(count)),
}, nil
case 'D':
// CUB - Cursor Back
return fmt.Sprintf("Cursor left %d", default1(count)), nil
return seqInfo{
"CUB",
fmt.Sprintf("Cursor left %d", default1(count)),
}, nil
case 'E':
return fmt.Sprintf("Cursor next line %d", default1(count)), nil
return seqInfo{
"CNL",
fmt.Sprintf("Cursor next line %d", default1(count)),
}, nil
case 'F':
return fmt.Sprintf("Cursor previous line %d", default1(count)), nil
return seqInfo{
"CPL",
fmt.Sprintf("Cursor previous line %d", default1(count)),
}, nil
case 'H':
row, col := 1, 1
if n, ok := p.Param(0, 1); ok && n > 0 {
Expand All @@ -40,23 +58,32 @@ func handleCursor(p *ansi.Parser) (string, error) {
if n, ok := p.Param(1, 1); ok && n > 0 {
col = n
}
return fmt.Sprintf("Set cursor position row=%[1]d col=%[2]d", row, col), nil
return seqInfo{
"CUP",
fmt.Sprintf("Set cursor position row=%[1]d col=%[2]d", row, col),
}, nil
case 'n':
if count != 6 {
return "", errInvalid
return seqNoMnemonic(""), errInvalid
}
if isPrivate {
return "Request extended cursor position", nil
return seqInfo{
"DECXCPR",
"Request extended cursor position",
}, nil
}
return "Request cursor position", nil
return seqInfo{"CPR", "Request cursor position"}, nil
case 's':
return "Save cursor position", nil
return seqInfo{"SCOSC", "Save cursor position"}, nil
case 'u':
return "Restore cursor position", nil
return seqInfo{"SCORC", "Restore cursor position"}, nil
case 'q':
return fmt.Sprintf("Set cursor style %s", descCursorStyle(count)), nil
return seqInfo{
"DECSCUSR",
fmt.Sprintf("Set cursor style %s", descCursorStyle(count)),
}, nil
}
return "", errUnhandled
return seqNoMnemonic(""), errUnhandled
}

//nolint:mnd
Expand Down
8 changes: 4 additions & 4 deletions cwd.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ import (
)

//nolint:mnd
func handleWorkingDirectoryURL(p *ansi.Parser) (string, error) {
func handleWorkingDirectoryURL(p *ansi.Parser) (seqInfo, error) {
parts := bytes.Split(p.Data(), []byte{';'})
if len(parts) != 2 {
// Invalid, ignore
return "", errInvalid
return seqNoMnemonic(""), errInvalid
}

u, err := url.ParseRequestURI(string(parts[1]))

if err != nil || u.Scheme != "file" {
// Should be a file URL.
return "", errInvalid
return seqNoMnemonic(""), errInvalid
}

return fmt.Sprintf("Set working directory to %s (on %s)", u.Path, u.Host), nil
return seqNoMnemonic(fmt.Sprintf("Set working directory to %s (on %s)", u.Path, u.Host)), nil
}
26 changes: 17 additions & 9 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ package main

import (
"errors"
"fmt"

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

var csiHandlers = map[int]handlerFn{
'm': handleSgr,
'c': printf("Request primary device attributes"),
'c': printWithMnemonic("DA1", "Request primary device attributes"),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unrelated but the description for DA1 should just be:

Suggested change
'c': printWithMnemonic("DA1", "Request primary device attributes"),
'c': printWithMnemonic("DA1", "Primary device attributes"),

When there is no parameters \x1b[c, it means a request for the terminal to send its primary device attributes which then responds with something like \x1b[64;1;2;3;4c, the same sequence command but with paramters.


// kitty
'u' | '?'<<markerShift: handleKitty,
Expand Down Expand Up @@ -73,24 +72,33 @@ var dcsHandlers = map[int]handlerFn{
}

var escHandler = map[int]handlerFn{
'7': printf("Save cursor"),
'8': printf("Restore cursor"),
'7': printWithMnemonic("DECSC", "Save cursor"),
'8': printWithMnemonic("DECRC", "Restore cursor"),

// C0/7-bit ASCII variant of ST.
// C1/8-bit extended ASCII variant handled as Ctrl.
'\\': printf("String terminator"),
'\\': printWithMnemonic("ST", "String terminator"),
}

var (
errUnhandled = errors.New("TODO: unhandled sequence")
errInvalid = errors.New("invalid sequence")
)

type handlerFn = func(*ansi.Parser) (string, error)
type seqInfo struct {
mnemonic string
explanation string
}

func seqNoMnemonic(explanation string) seqInfo {
return seqInfo{"", explanation}
}

type handlerFn = func(*ansi.Parser) (seqInfo, error)

func printf(format string, v ...any) handlerFn { //nolint:unparam
return func(*ansi.Parser) (string, error) {
return fmt.Sprintf(format, v...), nil
func printWithMnemonic(mnemonic string, explanation string) handlerFn { //nolint:unparam
return func(*ansi.Parser) (seqInfo, error) {
return seqInfo{mnemonic, explanation}, nil
}
}

Expand Down
6 changes: 3 additions & 3 deletions hyperlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import (
)

//nolint:mnd
func handleHyperlink(p *ansi.Parser) (string, error) {
func handleHyperlink(p *ansi.Parser) (seqInfo, error) {
parts := bytes.Split(p.Data(), []byte{';'})
if len(parts) != 3 {
// Invalid, ignore
return "", errInvalid
return seqNoMnemonic(""), errInvalid
}

opts := bytes.Split(parts[1], []byte{':'})
Expand All @@ -25,5 +25,5 @@ func handleHyperlink(p *ansi.Parser) (string, error) {
}

buf += fmt.Sprintf(" to %q", parts[2])
return buf, nil
return seqNoMnemonic(buf), nil
}
14 changes: 7 additions & 7 deletions kitty.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

//nolint:mnd
func handleKitty(p *ansi.Parser) (string, error) {
func handleKitty(p *ansi.Parser) (seqInfo, error) {
flagDesc := func(flag int) string {
var r []string
if flag&1 != 0 {
Expand Down Expand Up @@ -49,18 +49,18 @@ func handleKitty(p *ansi.Parser) (string, error) {
cmd := ansi.Cmd(p.Command())
switch cmd.Prefix() {
case '?':
return "Request Kitty keyboard", nil
return seqNoMnemonic("Request Kitty keyboard"), nil
case '>':
if first == 0 {
return "Disable Kitty keyboard", nil
return seqNoMnemonic("Disable Kitty keyboard"), nil
}
return fmt.Sprintf("Push %q Kitty keyboard flag", flagDesc(first)), nil
return seqNoMnemonic(fmt.Sprintf("Push %q Kitty keyboard flag", flagDesc(first))), nil
case '<':
return fmt.Sprintf("Pop %d Kitty keyboard flags", first), nil
return seqNoMnemonic(fmt.Sprintf("Pop %d Kitty keyboard flags", first)), nil
case '=':
if n, ok := p.Param(1, 0); ok {
return fmt.Sprintf("Set %q Kitty keyboard flags to %q", flagDesc(first), modeDesc(n)), nil
return seqNoMnemonic(fmt.Sprintf("Set %q Kitty keyboard flags to %q", flagDesc(first), modeDesc(n))), nil
}
}
return "", errUnhandled
return seqNoMnemonic(""), errUnhandled
}
19 changes: 10 additions & 9 deletions line.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,31 @@ import (
)

//nolint:mnd
func handleLine(p *ansi.Parser) (string, error) {
func handleLine(p *ansi.Parser) (seqInfo, error) {
var count int
if n, ok := p.Param(0, 0); ok {
count = n
}

switch p.Command() {
case 'K':
mnemonic := "EL"
switch count {
case 0:
return "Erase line right", nil
return seqInfo{mnemonic, "Erase line right"}, nil
case 1:
return "Erase line left", nil
return seqInfo{mnemonic, "Erase line left"}, nil
case 2:
return "Erase entire line", nil
return seqInfo{mnemonic, "Erase entire line"}, nil
}
case 'L':
return fmt.Sprintf("Insert %d blank lines", default1(count)), nil
return seqInfo{"IL", fmt.Sprintf("Insert %d blank lines", default1(count))}, nil
case 'M':
return fmt.Sprintf("Delete %d lines", default1(count)), nil
return seqInfo{"DL", fmt.Sprintf("Delete %d lines", default1(count))}, nil
case 'S':
return fmt.Sprintf("Scroll up %d lines", default1(count)), nil
return seqInfo{"SU", fmt.Sprintf("Scroll up %d lines", default1(count))}, nil
case 'T':
return fmt.Sprintf("Scroll down %d lines", default1(count)), nil
return seqInfo{"SD", fmt.Sprintf("Scroll down %d lines", default1(count))}, nil
}
return "", errUnhandled
return seqNoMnemonic(""), errUnhandled
}
Loading