Documentation
¶
Overview ¶
Package shellterm is a line-oriented terminal: a line editor (github.com/go-widgets/lineedit: history, Tab completion, Ctrl-R, Ctrl-C, Ctrl-L) in front of a Shell that the caller supplies, drawing into a terminal (Bind connects a toolkit.Terminal).
A Session is the model. Input arrives as the bytes a terminal sends; output goes to the function given to Attach. Keys are handled on the session's own goroutine, never the UI's: a Completer may ask a slow back end, and must not block the event loop that delivers its answer. Everything the session hands the view goes through Options.Post, which runs a function on the UI goroutine (an mvvm queue's Post, or a plain host's equivalent), so the view is only ever touched from there.
It was extracted from go-wasmcontainers/dashboard v0.8.0 (vm/terminal.go) so that the dashboard and wasmdesk share one implementation.
Example ¶
A session in front of a toolkit.Terminal. post runs a function on the UI goroutine; here it runs it at once, as a single-threaded host would.
package main
import (
"context"
"fmt"
"io"
"strings"
"github.com/go-widgets/lineedit"
"github.com/go-widgets/shellterm"
"github.com/go-widgets/toolkit"
)
// echo is a Shell: it prints its line back, and "clear" clears the screen.
type echo struct{}
func (echo) Run(_ context.Context, line string, out io.Writer) (int, bool) {
words, err := lineedit.Split(line)
if err != nil {
fmt.Fprintln(out, "syntax error:", err)
return 2, false
}
if len(words) > 0 && words[0] == "clear" {
return 0, true
}
fmt.Fprintln(out, strings.Join(words, " "))
return 0, false
}
// A session in front of a toolkit.Terminal. post runs a function on the UI
// goroutine; here it runs it at once, as a single-threaded host would.
func main() {
post := func(f func()) { f() }
term := toolkit.NewTerminal(80, 24, 1000)
s := shellterm.New(shellterm.Options{Post: post, Greeting: "echo shell\r\n"})
s.SetShell(echo{}, func(_ context.Context, words []string, _ string) ([]lineedit.Candidate, bool) {
if len(words) == 0 {
return []lineedit.Candidate{{Value: "clear"}}, false
}
return nil, false
})
shellterm.Bind(term, s, func() { /* ask the host to redraw */ })
// The host now draws term and routes its events to it: typed keys
// reach s, and s's output reaches term.
}
Output:
Index ¶
Examples ¶
Constants ¶
const ( // InputQueue bounds the keystrokes waiting for the key goroutine; keys // past it are dropped rather than blocking the UI. InputQueue = 256 // PendingWrites bounds the output kept before the first Attach. PendingWrites = 256 // CompleteTimeout bounds one key's work, a Tab completion included. CompleteTimeout = 5 * time.Second )
Limits on what waits for the view or for the key goroutine.
Variables ¶
This section is empty.
Functions ¶
func Bind ¶
Bind connects a session to a toolkit.Terminal: what the user types goes to the session, a resize tells it the width, and its output and clears go to the terminal, after which invalidate (nil: nothing) asks the host to redraw. A clear empties the screen and the scrollback. Call it on the UI goroutine; it attaches the session.
Types ¶
type Options ¶
type Options struct {
// Post runs f on the UI goroutine, in order. Required: New panics
// without it, since a session that wrote from its own goroutine would
// race the view.
Post func(f func())
// Greeting is written once, at the first Attach, before the prompt.
Greeting string
// OnDone, if set, runs on the UI goroutine after each command, once its
// output and the prompt are written (to refresh what it changed).
OnDone func()
// HistorySize bounds the history (0: 500).
HistorySize int
}
Options configure a Session.
type Session ¶
type Session struct {
// Out receives everything to draw and Clear clears the screen; both
// are set by Attach and called on the UI goroutine.
Out func(string)
Clear func()
// Running says a command runs (only Ctrl-C reaches it). It is set on
// the UI goroutine.
Running *mvvm.Observable[bool]
// contains filtered or unexported fields
}
Session is the terminal's model.
func (*Session) Attach ¶
Attach connects the view and starts the session: greeting, prompt, and the goroutine that handles keys. A second Attach (a new view) replays nothing and greets no more. Called on the UI goroutine. clear may be nil: the escape sequences that clear the screen are written instead.
func (*Session) Input ¶
Input queues what the user typed. It never blocks (keys past a full queue are dropped).
func (*Session) Note ¶
Note shows one line of text above the prompt, the line being edited kept below it; nothing while a command runs or before Attach. The text is written as given (escape sequences included): clean anything untrusted first. Called on the UI goroutine, which it never blocks (see Resize).
type Shell ¶
type Shell interface {
Run(ctx context.Context, line string, out io.Writer) (code int, clear bool)
}
Shell runs one command line, writing its output to out, and returns the exit status and whether the screen should be cleared. It runs on a goroutine of its own; ctx is cancelled by Ctrl-C.