This commit is contained in:
305
vendor/github.com/aymanbagabas/go-osc52/v2/osc52.go
generated
vendored
Normal file
305
vendor/github.com/aymanbagabas/go-osc52/v2/osc52.go
generated
vendored
Normal file
@ -0,0 +1,305 @@
|
||||
// OSC52 is a terminal escape sequence that allows copying text to the clipboard.
|
||||
//
|
||||
// The sequence consists of the following:
|
||||
//
|
||||
// OSC 52 ; Pc ; Pd BEL
|
||||
//
|
||||
// Pc is the clipboard choice:
|
||||
//
|
||||
// c: clipboard
|
||||
// p: primary
|
||||
// q: secondary (not supported)
|
||||
// s: select (not supported)
|
||||
// 0-7: cut-buffers (not supported)
|
||||
//
|
||||
// Pd is the data to copy to the clipboard. This string should be encoded in
|
||||
// base64 (RFC-4648).
|
||||
//
|
||||
// If Pd is "?", the terminal replies to the host with the current contents of
|
||||
// the clipboard.
|
||||
//
|
||||
// If Pd is neither a base64 string nor "?", the terminal clears the clipboard.
|
||||
//
|
||||
// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
|
||||
// where Ps = 52 => Manipulate Selection Data.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// // copy "hello world" to the system clipboard
|
||||
// fmt.Fprint(os.Stderr, osc52.New("hello world"))
|
||||
//
|
||||
// // copy "hello world" to the primary Clipboard
|
||||
// fmt.Fprint(os.Stderr, osc52.New("hello world").Primary())
|
||||
//
|
||||
// // limit the size of the string to copy 10 bytes
|
||||
// fmt.Fprint(os.Stderr, osc52.New("0123456789").Limit(10))
|
||||
//
|
||||
// // escape the OSC52 sequence for screen using DCS sequences
|
||||
// fmt.Fprint(os.Stderr, osc52.New("hello world").Screen())
|
||||
//
|
||||
// // escape the OSC52 sequence for Tmux
|
||||
// fmt.Fprint(os.Stderr, osc52.New("hello world").Tmux())
|
||||
//
|
||||
// // query the system Clipboard
|
||||
// fmt.Fprint(os.Stderr, osc52.Query())
|
||||
//
|
||||
// // query the primary clipboard
|
||||
// fmt.Fprint(os.Stderr, osc52.Query().Primary())
|
||||
//
|
||||
// // clear the system Clipboard
|
||||
// fmt.Fprint(os.Stderr, osc52.Clear())
|
||||
//
|
||||
// // clear the primary Clipboard
|
||||
// fmt.Fprint(os.Stderr, osc52.Clear().Primary())
|
||||
package osc52
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Clipboard is the clipboard buffer to use.
|
||||
type Clipboard rune
|
||||
|
||||
const (
|
||||
// SystemClipboard is the system clipboard buffer.
|
||||
SystemClipboard Clipboard = 'c'
|
||||
// PrimaryClipboard is the primary clipboard buffer (X11).
|
||||
PrimaryClipboard = 'p'
|
||||
)
|
||||
|
||||
// Mode is the mode to use for the OSC52 sequence.
|
||||
type Mode uint
|
||||
|
||||
const (
|
||||
// DefaultMode is the default OSC52 sequence mode.
|
||||
DefaultMode Mode = iota
|
||||
// ScreenMode escapes the OSC52 sequence for screen using DCS sequences.
|
||||
ScreenMode
|
||||
// TmuxMode escapes the OSC52 sequence for tmux. Not needed if tmux
|
||||
// clipboard is set to `set-clipboard on`
|
||||
TmuxMode
|
||||
)
|
||||
|
||||
// Operation is the OSC52 operation.
|
||||
type Operation uint
|
||||
|
||||
const (
|
||||
// SetOperation is the copy operation.
|
||||
SetOperation Operation = iota
|
||||
// QueryOperation is the query operation.
|
||||
QueryOperation
|
||||
// ClearOperation is the clear operation.
|
||||
ClearOperation
|
||||
)
|
||||
|
||||
// Sequence is the OSC52 sequence.
|
||||
type Sequence struct {
|
||||
str string
|
||||
limit int
|
||||
op Operation
|
||||
mode Mode
|
||||
clipboard Clipboard
|
||||
}
|
||||
|
||||
var _ fmt.Stringer = Sequence{}
|
||||
|
||||
var _ io.WriterTo = Sequence{}
|
||||
|
||||
// String returns the OSC52 sequence.
|
||||
func (s Sequence) String() string {
|
||||
var seq strings.Builder
|
||||
// mode escape sequences start
|
||||
seq.WriteString(s.seqStart())
|
||||
// actual OSC52 sequence start
|
||||
seq.WriteString(fmt.Sprintf("\x1b]52;%c;", s.clipboard))
|
||||
switch s.op {
|
||||
case SetOperation:
|
||||
str := s.str
|
||||
if s.limit > 0 && len(str) > s.limit {
|
||||
return ""
|
||||
}
|
||||
b64 := base64.StdEncoding.EncodeToString([]byte(str))
|
||||
switch s.mode {
|
||||
case ScreenMode:
|
||||
// Screen doesn't support OSC52 but will pass the contents of a DCS
|
||||
// sequence to the outer terminal unchanged.
|
||||
//
|
||||
// Here, we split the encoded string into 76 bytes chunks and then
|
||||
// join the chunks with <end-dsc><start-dsc> sequences. Finally,
|
||||
// wrap the whole thing in
|
||||
// <start-dsc><start-osc52><joined-chunks><end-osc52><end-dsc>.
|
||||
// s := strings.SplitN(b64, "", 76)
|
||||
s := make([]string, 0, len(b64)/76+1)
|
||||
for i := 0; i < len(b64); i += 76 {
|
||||
end := i + 76
|
||||
if end > len(b64) {
|
||||
end = len(b64)
|
||||
}
|
||||
s = append(s, b64[i:end])
|
||||
}
|
||||
seq.WriteString(strings.Join(s, "\x1b\\\x1bP"))
|
||||
default:
|
||||
seq.WriteString(b64)
|
||||
}
|
||||
case QueryOperation:
|
||||
// OSC52 queries the clipboard using "?"
|
||||
seq.WriteString("?")
|
||||
case ClearOperation:
|
||||
// OSC52 clears the clipboard if the data is neither a base64 string nor "?"
|
||||
// we're using "!" as a default
|
||||
seq.WriteString("!")
|
||||
}
|
||||
// actual OSC52 sequence end
|
||||
seq.WriteString("\x07")
|
||||
// mode escape end
|
||||
seq.WriteString(s.seqEnd())
|
||||
return seq.String()
|
||||
}
|
||||
|
||||
// WriteTo writes the OSC52 sequence to the writer.
|
||||
func (s Sequence) WriteTo(out io.Writer) (int64, error) {
|
||||
n, err := out.Write([]byte(s.String()))
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// Mode sets the mode for the OSC52 sequence.
|
||||
func (s Sequence) Mode(m Mode) Sequence {
|
||||
s.mode = m
|
||||
return s
|
||||
}
|
||||
|
||||
// Tmux sets the mode to TmuxMode.
|
||||
// Used to escape the OSC52 sequence for `tmux`.
|
||||
//
|
||||
// Note: this is not needed if tmux clipboard is set to `set-clipboard on`. If
|
||||
// TmuxMode is used, tmux must have `allow-passthrough on` set.
|
||||
//
|
||||
// This is a syntactic sugar for s.Mode(TmuxMode).
|
||||
func (s Sequence) Tmux() Sequence {
|
||||
return s.Mode(TmuxMode)
|
||||
}
|
||||
|
||||
// Screen sets the mode to ScreenMode.
|
||||
// Used to escape the OSC52 sequence for `screen`.
|
||||
//
|
||||
// This is a syntactic sugar for s.Mode(ScreenMode).
|
||||
func (s Sequence) Screen() Sequence {
|
||||
return s.Mode(ScreenMode)
|
||||
}
|
||||
|
||||
// Clipboard sets the clipboard buffer for the OSC52 sequence.
|
||||
func (s Sequence) Clipboard(c Clipboard) Sequence {
|
||||
s.clipboard = c
|
||||
return s
|
||||
}
|
||||
|
||||
// Primary sets the clipboard buffer to PrimaryClipboard.
|
||||
// This is the X11 primary clipboard.
|
||||
//
|
||||
// This is a syntactic sugar for s.Clipboard(PrimaryClipboard).
|
||||
func (s Sequence) Primary() Sequence {
|
||||
return s.Clipboard(PrimaryClipboard)
|
||||
}
|
||||
|
||||
// Limit sets the limit for the OSC52 sequence.
|
||||
// The default limit is 0 (no limit).
|
||||
//
|
||||
// Strings longer than the limit get ignored. Settting the limit to 0 or a
|
||||
// negative value disables the limit. Each terminal defines its own escapse
|
||||
// sequence limit.
|
||||
func (s Sequence) Limit(l int) Sequence {
|
||||
if l < 0 {
|
||||
s.limit = 0
|
||||
} else {
|
||||
s.limit = l
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Operation sets the operation for the OSC52 sequence.
|
||||
// The default operation is SetOperation.
|
||||
func (s Sequence) Operation(o Operation) Sequence {
|
||||
s.op = o
|
||||
return s
|
||||
}
|
||||
|
||||
// Clear sets the operation to ClearOperation.
|
||||
// This clears the clipboard.
|
||||
//
|
||||
// This is a syntactic sugar for s.Operation(ClearOperation).
|
||||
func (s Sequence) Clear() Sequence {
|
||||
return s.Operation(ClearOperation)
|
||||
}
|
||||
|
||||
// Query sets the operation to QueryOperation.
|
||||
// This queries the clipboard contents.
|
||||
//
|
||||
// This is a syntactic sugar for s.Operation(QueryOperation).
|
||||
func (s Sequence) Query() Sequence {
|
||||
return s.Operation(QueryOperation)
|
||||
}
|
||||
|
||||
// SetString sets the string for the OSC52 sequence. Strings are joined with a
|
||||
// space character.
|
||||
func (s Sequence) SetString(strs ...string) Sequence {
|
||||
s.str = strings.Join(strs, " ")
|
||||
return s
|
||||
}
|
||||
|
||||
// New creates a new OSC52 sequence with the given string(s). Strings are
|
||||
// joined with a space character.
|
||||
func New(strs ...string) Sequence {
|
||||
s := Sequence{
|
||||
str: strings.Join(strs, " "),
|
||||
limit: 0,
|
||||
mode: DefaultMode,
|
||||
clipboard: SystemClipboard,
|
||||
op: SetOperation,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Query creates a new OSC52 sequence with the QueryOperation.
|
||||
// This returns a new OSC52 sequence to query the clipboard contents.
|
||||
//
|
||||
// This is a syntactic sugar for New().Query().
|
||||
func Query() Sequence {
|
||||
return New().Query()
|
||||
}
|
||||
|
||||
// Clear creates a new OSC52 sequence with the ClearOperation.
|
||||
// This returns a new OSC52 sequence to clear the clipboard.
|
||||
//
|
||||
// This is a syntactic sugar for New().Clear().
|
||||
func Clear() Sequence {
|
||||
return New().Clear()
|
||||
}
|
||||
|
||||
func (s Sequence) seqStart() string {
|
||||
switch s.mode {
|
||||
case TmuxMode:
|
||||
// Write the start of a tmux escape sequence.
|
||||
return "\x1bPtmux;\x1b"
|
||||
case ScreenMode:
|
||||
// Write the start of a DCS sequence.
|
||||
return "\x1bP"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (s Sequence) seqEnd() string {
|
||||
switch s.mode {
|
||||
case TmuxMode:
|
||||
// Terminate the tmux escape sequence.
|
||||
return "\x1b\\"
|
||||
case ScreenMode:
|
||||
// Write the end of a DCS sequence.
|
||||
return "\x1b\x5c"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user