terminal

package module
Version: v0.12.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 17, 2018 License: BSD-3-Clause Imports: 10 Imported by: 6

README

Terminal

This is a highly modified mini-fork of https://github.com/golang/crypto to create a more standalone terminal reader that gives more power to the app, adds more recognized key sequences, and leaves the output to the app.

See the godoc documentation for complete API docs.

Features

  • Completely standalone key / line reader:
    • Unlike the Go ssh/terminal package, this is pretty simple (no inclusion of all those other crypto libraries)
    • Unlike many all-in-one terminal packages (like termbox), this uses io.Reader instead of forcing raw terminal access, so you can listen to an SSH socket, read from a raw terminal, or convert any arbitrary stream of raw bytes to keystrokes
    • Unlike just about every key-reader I found, you're not tied to a specific output approach; this terminal package is designed to be output-agnostic.
  • Parses a wide variety of keys, tested in Windows and Linux, over ssh connections and local terminals
  • Handles unknown sequences without user getting "stuck" (after accidentally hitting Alt+left-square-bracket, for instance)
  • OnKeypress callback for handling more than just autocomplete-style situations
  • AfterKeypress callback for firing off events after the built-in processing has already occurred

Readers

This package contains multiple ways to gather input from a user:

terminal.Reader

terminal.Reader is very similar to the ssh terminal in Go's crypto package except that it doesn't do any output. It's useful for gathering input from a user in an asynchronous way while still having niceties like a command history and special key handling (e.g., CTRL+U deletes everything from the beginning of the line to the cursor). Specific needs can be addressed by wrapping this type with another type, such as the AbsPrompt.

Internally uses KeyReader for parsing keys from the io.Reader.

Have a look at the simple reader example to get an idea how to use this type in an application which draws random output while prompting the user for input and printing their keystrokes to the screen.

Note that the example has some special handling for a few keys to demonstrate (and verify correctness of) some key interception functionality.

terminal.Prompt

terminal.Prompt is the closest thing to the terminal which exists in the crypto package. It will draw a prompt and wait for input, handling arrow keys and other special keys to reposition the cursor, fetch history, etc. This should be used in cases where the crypto terminal would normally be used, but more complex handling is necessary, such as the on/after keypress handlers.

terminal.AbsPrompt

terminal.AbsPrompt offers simple output layer on top of a terminal.Reader for cases where a prompt should be displayed at a fixed location in the terminal. It is tied to a given io.Writer and can be asked to draw changes to the input since it was last drawn, or redraw itself fully, including all repositioning ANSI codes. Since drawing is done on command, there's no need to synchronize writes with other output.

Internally uses KeyReader for parsing keys from the io.Reader.

Have a look at the absprompt example to get an idea how this type can simplify getting input from a user compared to building your code on top of the simpler Reader type.

As mentioned in "features", this package isn't coupled to a particular output approach. Check out the goterm example to see how you can use a AbsPrompt with goterm - or any output package which doesn't force its input layer on you.

terminal.DT

terminal.DT is for a very simple, no-frills terminal. It has no support for special keys other than backspace, and is meant to just gather printable keys from a user who may not have ANSI support. It writes directly to the given io.Writer with no synchronizing, as it is assumed that if you wanted complex output to happen, you wouldn't use this.

Internally uses KeyReader for parsing keys from the io.Reader.

Have a loot at the "dumb" example to see how a DT can be used for an extremely simple interface.

terminal.KeyReader

terminal.KeyReader lets you read keys as they're typed, giving extremely low-level control (for a terminal reader, anyway). The optional Force variable can be set to true if you need immediate key parsing despite the oddities that can bring. See the ParseKey documentation for an in-depth explanation of this.

In normal mode (Force is false), special keys like Escape and Alt-left-bracket will not be properly parsed until another key is pressed due to limitations discussed in the ParseKey documentation and the Caveats section below. However, users won't get "stuck", as the parser will just force-parse sequences if more than 250ms separates one read from the next.

Take a look at the keyreport example to get an idea how to build a raw key parser using KeyReader. You can also run it directly (go run example/keyreport.go) to see what sequence of bytes a given key (or key combination) spits out. Note that this has special handling for Ctrl+C (exit program) and Ctrl+F (toggle "forced" parse mode).

Caveats

Terminals suck

Please note that different terminals implement different key sequences in hilariously different ways. What's in this package may or may not actually handle your real-world use-case. Terminals are simply not the right medium for getting raw keys in any kind of consistent and guaranteed way. As an example, the key sequence for "Alt+F" is the same as hitting "Escape" and then "F" immediately after. The left arrow is the same as hitting alt+[+D. Try it on a command line! In linux, at least, you can fake quite a lot of special keys because the console is so ... weird.

io.Reader is limited

Go doesn't provide an easy mechanism for reading from an io.Reader in a "pollable" way. It's already impossible to tell if alt+[ is really alt+[ or the beginning of a longer sequence. With no way to poll the io.Reader, this package has to make a guess. I tried using goroutines and channels to try to determine when it had been long enough to force the parse, but that had its own problems, the worst of which was you couldn't cancel a read that was just sitting waiting. Which meant users would have to press at least one extra key before the app could stop listening - or else the app had to force-close an io.ReadCloser, which isn't how you want to handle something like an ssh connection that's meant to be persistent.

In "forced" parse mode, alt+[ will work just fine, but a left arrow can get parsed as "alt+[" followed by "D" if the reader doesn't see the D at precisely the same moment as the "alt+[". But in normal mode, a user who hits alt-[ by mistake, and tries typing numbers can find themselves "stuck" for a moment until the reader sees that enough time has passed since their mistaken "alt+[" keystroke and the "real" keys. Or until they hit 8 bytes' worth of keys, at which point the key reader starts making assumptions that are likely incorrect.

Low-level reading of the keyboard would solve this problem, but this package is meant to be as portable as possible, and able to parse input from ANYTHING readable. Not just a local console, but also SSH, telnet, etc. It may even be valuable to read keystrokes captured in a file (though I suspect that would break things in even more hilarious ways).

Limited testing
  • Tested in Windows: cmd and PowerShell, Putty ssh into Ubuntu server
  • Tested in Linux: Konsole in Ubuntu VM, tmux on Debian and Ubuntu, and a raw GUI-less debian VM in VMWare

Windows terminals (cmd and PowerShell) have very limited support for anything beyond ASCII as far as I can tell. Putty is a lot better. If you plan to write an application that needs to support even simple sequences like arrow keys, you should host it on a Linux system and have users ssh in. Shipping a Windows binary won't work with built-in tools.

If you can test out the keyreport tool in other OSes, that would be super helpful.

Therefore....

If you use this package for any kind of application, just make sure you understand the limitations. Parsing of keys is, in many cases, done just to be able to throw away absurd user input (like Meta+Ctrl+7) rather than end up making wrong guesses (my Linux terminal thinks certain Meta combos should print a list of local servers followed by the ASCII parts of the sequence).

So while you may not be able to count on specific key sequences, this package might help you gather useful input while ignoring (many) completely absurd sequences.

Documentation

Overview

Package terminal provides support functions for dealing with terminals, as commonly found on UNIX systems.

This is a completely standalone key / line reader. All types that read data allow anything conforming to io.Reader. All types that write data allow anything conforming to io.Writer.

Putting a terminal into raw mode is the most common requirement, and can be seen in the example.

Example

Example of a very basic use of the Prompt type, which is probably the simplest type available

package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/Nerdmaster/terminal"
)

func main() {
	// Put terminal in raw mode; this is almost always going to be required for
	// local terminals, but not necessary when connecting to an ssh terminal
	oldState, err := terminal.MakeRaw(int(os.Stdin.Fd()))
	if err != nil {
		panic(err)
	}
	defer terminal.Restore(0, oldState)

	// This is a simple example, so we just use Stdin and Stdout; but any reader
	// and writer will work.  Note that the prompt can contain ANSI colors.
	var p = terminal.NewPrompt(os.Stdin, os.Stdout, "\x1b[34;1mCommand\x1b[0m: ")

	// This is a simple key interceptor; on CTRL-r we forcibly change the scroll to 20
	p.OnKeypress = func(e *terminal.KeyEvent) {
		if e.Key == terminal.KeyCtrlR {
			p.Scroller.ScrollOffset = 20
		}
	}

	// Make the input scroll at 40 characters, maxing out at a 120-char string
	p.Scroller.InputWidth = 40
	p.Scroller.MaxLineLength = 120

	// Loop forever until we get an error (typically EOF from user pressing
	// CTRL+D) or the "quit" command is entered.  We echo each command unless the
	// user turns echoing off.
	var echo = true
	for {
		// Just for fun we can demonstrate that the cursor never touches anything
		// outside out 40-character input.  First, we print padding for the prompt
		// ("Command: ").  Then 40 spaces, a bar, and \r to return the cursor to
		// the beginning of the line.
		fmt.Printf("         ")
		fmt.Printf(strings.Repeat(" ", 40))
		fmt.Printf("|\r")
		var cmd, err = p.ReadLine()
		if err != nil {
			fmt.Printf("%s\r\n", err)
			break
		}

		if strings.ToLower(cmd) == "quit" {
			fmt.Print("Quitter!\r\n")
			break
		}

		if strings.ToLower(cmd) == "echo on" {
			echo = true
		}
		if strings.ToLower(cmd) == "echo off" {
			echo = false
		}

		if echo {
			fmt.Printf("%#v\r\n", cmd)
		}
	}
}
Output:

Index

Examples

Constants

View Source
const (
	KeyCtrlA = 1 + iota
	KeyCtrlB
	KeyCtrlC
	KeyCtrlD
	KeyCtrlE
	KeyCtrlF
	KeyCtrlG
	KeyCtrlH
	KeyCtrlI
	KeyCtrlJ
	KeyCtrlK
	KeyCtrlL
	KeyCtrlM
	KeyCtrlN
	KeyCtrlO
	KeyCtrlP
	KeyCtrlQ
	KeyCtrlR
	KeyCtrlS
	KeyCtrlT
	KeyCtrlU
	KeyCtrlV
	KeyCtrlW
	KeyCtrlX
	KeyCtrlY
	KeyCtrlZ
	KeyEscape
	KeyLeftBracket  = '['
	KeyRightBracket = ']'
	KeyEnter        = '\r'
	KeyBackspace    = 127
	KeyUnknown      = 0xd800 + iota
	KeyUp
	KeyDown
	KeyLeft
	KeyRight
	KeyHome
	KeyEnd
	KeyPasteStart
	KeyPasteEnd
	KeyInsert
	KeyDelete
	KeyPgUp
	KeyPgDn
	KeyPause
	KeyF1
	KeyF2
	KeyF3
	KeyF4
	KeyF5
	KeyF6
	KeyF7
	KeyF8
	KeyF9
	KeyF10
	KeyF11
	KeyF12
)

Giant list of key constants. Everything above KeyUnknown matches an actual ASCII key value. After that, we have various pseudo-keys in order to represent complex byte sequences that correspond to keys like Page up, Right arrow, etc.

View Source
const (
	ModNone KeyModifier = 0
	ModAlt              = 1
	ModMeta             = 2
)

KeyModifier values. We don't include Shift in here because terminals don't include shift for a great deal of keys that can exist; e.g., there is no "SHIFT + PgUp". Similarly, CTRL doesn't make sense as a modifier in terminals. CTRL+A is just ASCII character 1, whereas there is no CTRL+1, and CTRL+Up is its own totally separate sequence from Up. So CTRL keys are just defined on an as-needed basis.

View Source
const DefaultMaxLineLength = 4096

DefaultMaxLineLength is the default MaxLineLength for a Reader; once a line reaches this length, the Reader no longer accepts input which would increase the line

View Source
const ScrollBy = 10

ScrollBy is the default value a scroller scrolls by when the cursor would otherwise be outside the input area

Variables

View Source
var CRLF = []byte("\r\n")

CRLF is the byte sequence all terminals use for moving to the beginning of the next line

View Source
var ErrPasteIndicator = pasteIndicatorError{}

ErrPasteIndicator may be returned from ReadLine as the error, in addition to valid line data. It indicates that bracketed paste mode is enabled and that the returned line consists only of pasted data. Programs may wish to interpret pasted data more literally than typed data.

Functions

func GetSize

func GetSize(fd int) (width, height int, err error)

GetSize returns the dimensions of the given terminal.

func IsTerminal

func IsTerminal(fd int) bool

IsTerminal returns true if the given file descriptor is a terminal.

func ReadPassword

func ReadPassword(fd int) ([]byte, error)

ReadPassword reads a line of input from a terminal without local echo. This is commonly used for inputting passwords and other sensitive data. The slice returned does not include the \n.

func Restore

func Restore(fd int, state *State) error

Restore restores the terminal connected to the given file descriptor to a previous state.

func VisualLength added in v0.10.0

func VisualLength(s string) int

VisualLength returns the number of visible glyphs in a string. This can be useful for getting the length of a string which has ANSI color sequences, but it doesn't count "wide" glyphs differently than others, and it won't handle ANSI cursor commands; e.g., it ignores "\x1b[D" rather than knowing that the cursor position moved to the left.

Types

type AbsPrompt added in v0.10.0

type AbsPrompt struct {
	*Reader

	Out io.Writer
	// contains filtered or unexported fields
}

AbsPrompt is a wrapper around a Reader which will write a prompt, wait for a user's input, and return it. It will print whatever needs to be printed on demand to an io.Writer, using ANSI to ensure the cursor is always at the right screen location, allowing the AbsPrompt to be used concurrently with other screen writing. AbsPrompt stores the Reader's prior state in order to avoid unnecessary writes.

func NewAbsPrompt added in v0.10.0

func NewAbsPrompt(r io.Reader, w io.Writer, p string) *AbsPrompt

NewAbsPrompt returns an AbsPrompt which will read lines from r, write its prompt and current line to w, and use p as the prompt string.

func (*AbsPrompt) NeedWrite added in v0.10.0

func (p *AbsPrompt) NeedWrite() bool

NeedWrite returns true if there are any pending changes to the line or cursor position

func (*AbsPrompt) PrintCursorMovement added in v0.10.0

func (p *AbsPrompt) PrintCursorMovement()

PrintCursorMovement sends the ANSI escape sequence for moving the cursor

func (*AbsPrompt) PrintLine added in v0.10.0

func (p *AbsPrompt) PrintLine()

PrintLine gets the current line and prints it to the screen just after the prompt location

func (*AbsPrompt) PrintPrompt added in v0.10.0

func (p *AbsPrompt) PrintPrompt()

PrintPrompt moves to the x/y coordinates of the prompt and prints the prompt string

func (*AbsPrompt) ReadLine added in v0.10.0

func (p *AbsPrompt) ReadLine() (string, error)

ReadLine delegates to the reader's ReadLine function

func (*AbsPrompt) SetLocation added in v0.10.0

func (p *AbsPrompt) SetLocation(x, y int)

SetLocation changes the internal x and y coordinates. If this is called while a ReadLine is in progress, you won't be happy.

func (*AbsPrompt) SetPrompt added in v0.10.0

func (p *AbsPrompt) SetPrompt(s string)

SetPrompt changes the current prompt. This shouldn't be called while a ReadLine is in progress.

func (*AbsPrompt) WriteAll added in v0.10.0

func (p *AbsPrompt) WriteAll()

WriteAll forces a write of the entire prompt

func (*AbsPrompt) WriteChanges added in v0.10.0

func (p *AbsPrompt) WriteChanges()

WriteChanges attempts to only write to the console when something has changed (line text or the cursor position). It will also print the prompt if that hasn't yet been printed.

func (*AbsPrompt) WriteChangesNoCursor added in v0.10.0

func (p *AbsPrompt) WriteChangesNoCursor()

WriteChangesNoCursor prints prompt and line if necessary, but doesn't reposition the cursor in order to allow a frequently-updating app to write the cursor change where it makes sense, regardless of changes to the user's input.

type DT

type DT struct {
	sync.RWMutex

	// Echo is on by default; set it to false for things like password prompts
	Echo bool
	// contains filtered or unexported fields
}

DT contains the state for running a *very* basic terminal which operates effectively like an old-school telnet connection: no ANSI, no special keys, no history preservation, etc. This isn't actually useful in any way I can see, but it's a decent example of lower-level key reading.

func Dumb

func Dumb(r io.Reader, w io.Writer) *DT

Dumb runs a dumb terminal reader on the given io.Reader. If the terminal is local, it must first have been put into raw mode.

func (*DT) Line

func (dt *DT) Line() string

Line returns the current input line as a string; this can be useful for interrupting a user's input for critical messages and then re-entering the previous prompt and text. I don't know if doing this is accessible, but I've seen apps that do it.

func (*DT) ReadLine

func (dt *DT) ReadLine() (line string, err error)

ReadLine returns a line of input from the terminal

type KeyEvent

type KeyEvent struct {
	Keypress
	Line                  *Line
	IgnoreDefaultHandlers bool
}

KeyEvent is used for OnKeypress handlers to get the key and modify handler state when the custom handler needs default handlers to be bypassed

type KeyModifier

type KeyModifier int

KeyModifier tells us what modifiers were pressed at the same time as a normal key, such as CTRL, Alt, Meta, etc.

func ParseKey

func ParseKey(b []byte, force bool) (r rune, rl int, mod KeyModifier)

ParseKey tries to parse a key sequence from b. If successful, it returns the key, the length in bytes of that key, and the modifier, if any. Otherwise it returns utf8.RuneError, 0, and an undefined mod which should be ignored.

ParseKey is the function on which all terminal's types rely, and offers the lowest-level parsing in this package. Even though it's the base function used by all types, the way it handles sequences is complex enough that it is generally best to use one of the key/line parsers rather than calling this directly.

When used by the various types, "force" defaults to being false. This means that we assume users are not typically typing key sequence prefixes like the Escape key or Alt-left-bracket. This eases parsing in most typical cases, but it means when a user *does* press one of these keys, the Parser treats it as if nothing was pressed at all (returning utf8.RuneError and a length of 0). It's then up to the caller to decide to either drop the bytes or append to them with more data.

When one of the readers (KeyReader, Reader, Prompter) gets this kind of "empty" response, it will hold onto the bytes and try to append to them next time it reads. It is basically assuming the sequence is incomplete. If the next read doesn't happen soon enough, the reader will decide the sequence was in fact complete, and then return the raw Escape or Alt+[, but this doesn't happen until after the subsequent read, which means effectively a one-key "lag".

This means applications really can't rely on getting raw Escape keys or alt-left-bracket. And in some cases, this is not acceptable. Hence, the "force" flag.

If "force" is true, ParseKey will return immediately, even if the sequence is nonsensical. This makes it a lot easier to get very special keys, but when listening on a network there is probably a small chance a key sequence could be broken up over multiple reads, and the result will be essentially "corrupt" keys. As an example, if the left-arrow is sent, it's normally three bytes: Escape followed by left-bracket followed by uppercase "D". If the Escape is read separately from the rest of the sequence, the overall result will be an Escape key followed by a left-bracket followed by a "D". If the Escape and left-bracket are read separately from the "D", the result will be Alt-left-bracket followed by "D". The weird variations can get worse with longer key sequences.

Additionally, if user input is serialized to a file or something, just as a raw stream of bytes, the read operation won't read more than 256 at a time. This will undoubtedly lead to all kinds of broken "keys" being parsed.

The tl;dr is that terminals kind of suck at complex key parsing, so make sure you go into it with your eyes wide open.

func (KeyModifier) String

func (m KeyModifier) String() string

type KeyReader

type KeyReader struct {

	// If ForceParse is true, the reader won't wait for certain sequences to
	// finish, which allows for things like ESC or Alt-left-bracket to be
	// detected properly
	ForceParse bool
	// contains filtered or unexported fields
}

KeyReader is the low-level type for reading raw keypresses from a given io stream, usually stdin or an ssh socket. Stores raw bytes in a buffer so that if many keys are read at once, they can still be parsed individually.

func NewKeyReader

func NewKeyReader(r io.Reader) *KeyReader

NewKeyReader returns a simple KeyReader set to read from r

func (*KeyReader) ReadKeypress

func (r *KeyReader) ReadKeypress() (Keypress, error)

ReadKeypress reads the next key sequence, returning a Keypress object and possibly an error if the input stream can't be read for some reason. This will block if the buffer has no more data, which would obviously require a direct Read call on the underlying io.Reader.

type Keypress

type Keypress struct {
	Key      rune
	Modifier KeyModifier
	Size     int
	Raw      []byte
}

Keypress contains the data which made up a key: our internal KeyXXX constant and the bytes which were parsed to get said constant. If the raw bytes need to be held for any reason, they should be copied, not stored as-is, since what's in here is a simple slice into the raw buffer.

type Line added in v0.12.0

type Line struct {
	Text []rune
	Pos  int
}

Line manages a very encapsulated version of a terminal line's state

func (*Line) AddKeyToLine added in v0.12.0

func (l *Line) AddKeyToLine(key rune)

AddKeyToLine inserts the given key at the current position in the current line.

func (*Line) Clear added in v0.12.0

func (l *Line) Clear()

Clear erases the input line

func (*Line) CountToLeftWord added in v0.12.0

func (l *Line) CountToLeftWord() int

CountToLeftWord returns then number of characters from the cursor to the start of the previous word

func (*Line) CountToRightWord added in v0.12.0

func (l *Line) CountToRightWord() int

CountToRightWord returns then number of characters from the cursor to the start of the next word

func (*Line) DeleteLine added in v0.12.0

func (l *Line) DeleteLine()

DeleteLine removes all runes after the cursor position

func (*Line) DeleteRuneUnderCursor added in v0.12.0

func (l *Line) DeleteRuneUnderCursor()

DeleteRuneUnderCursor erases the character under the current position

func (*Line) DeleteToBeginningOfLine added in v0.12.0

func (l *Line) DeleteToBeginningOfLine()

DeleteToBeginningOfLine removes everything behind the cursor

func (*Line) EraseNPreviousChars added in v0.12.0

func (l *Line) EraseNPreviousChars(n int)

EraseNPreviousChars deletes n characters from l.Text and updates l.Pos

func (*Line) MoveEnd added in v0.12.0

func (l *Line) MoveEnd()

MoveEnd puts the cursor at the end of the line

func (*Line) MoveHome added in v0.12.0

func (l *Line) MoveHome()

MoveHome moves the cursor to the beginning of the line

func (*Line) MoveLeft added in v0.12.0

func (l *Line) MoveLeft()

MoveLeft moves pos one rune left

func (*Line) MoveRight added in v0.12.0

func (l *Line) MoveRight()

MoveRight moves pos one rune right

func (*Line) MoveToLeftWord added in v0.12.0

func (l *Line) MoveToLeftWord()

MoveToLeftWord moves pos to the first rune of the word to the left

func (*Line) MoveToRightWord added in v0.12.0

func (l *Line) MoveToRightWord()

MoveToRightWord moves pos to the first rune of the word to the right

func (*Line) Set added in v0.12.0

func (l *Line) Set(t []rune, p int)

Set overwrites Text and Pos with t and p, respectively

func (*Line) Split added in v0.12.0

func (l *Line) Split() (string, string)

Split returns everything to the left of the cursor and everything at and to the right of the cursor as two strings

func (*Line) String added in v0.12.0

func (l *Line) String() string

String just returns l.Text's runes as a single string

type Prompt added in v0.10.0

type Prompt struct {
	*Reader

	Out io.Writer

	// AfterKeypress shadows the Reader variable of the same name to allow custom
	// keypress listeners even though Prompt has to listen in order to write output
	AfterKeypress func(event *KeyEvent)

	// Scroller processes the pending output to figure out if scrolling is
	// necessary and what should be printed if so
	Scroller *Scroller
	// contains filtered or unexported fields
}

A Prompt is a wrapper around a Reader which will write a prompt, wait for a user's input, and return it. It will print whatever needs to be printed on demand to an io.Writer. The Prompt stores the Reader's prior state in order to avoid unnecessary writes.

func NewPrompt added in v0.10.0

func NewPrompt(r io.Reader, w io.Writer, p string) *Prompt

NewPrompt returns a prompt which will read lines from r, write its prompt and current line to w, and use p as the prompt string.

func (*Prompt) ReadLine added in v0.10.0

func (p *Prompt) ReadLine() (string, error)

ReadLine delegates to the reader's ReadLine function

func (*Prompt) SetPrompt added in v0.10.0

func (p *Prompt) SetPrompt(s string)

SetPrompt changes the current prompt

type Reader

type Reader struct {
	// OnKeypress, if non-null, is called for each keypress with the key and
	// input line sent in
	OnKeypress func(event *KeyEvent)

	// AfterKeypress, if non-nil, is called after each keypress has been
	// processed.  event should be considered read-only, as any changes will be
	// ignored since the key has already been processed.
	AfterKeypress func(event *KeyEvent)

	// NoHistory is on when we don't want to preserve history, such as when a
	// password is being entered
	NoHistory bool

	// MaxLineLength tells us when to stop accepting input (other than things
	// like allowing up/down/left/right and other control keys)
	MaxLineLength int
	// contains filtered or unexported fields
}

Reader contains the state for running a VT100 terminal that is capable of reading lines of input. It is similar to the golang crypto/ssh/terminal package except that it doesn't write, leaving that to the caller. The idea is to store what the user is typing, and where the cursor should be, while letting something else decide what to draw and where on the screen to draw it. This separation enables more complex applications where there's other real-time data being rendered at the same time as the input line.

func NewReader

func NewReader(r io.Reader) *Reader

NewReader runs a terminal reader on the given io.Reader. If the Reader is a local terminal, that terminal must first have been put into raw mode.

func (*Reader) LinePos

func (r *Reader) LinePos() (string, int)

LinePos returns the current input line and cursor position

func (*Reader) Pos

func (r *Reader) Pos() int

Pos returns the position of the cursor

func (*Reader) ReadLine

func (r *Reader) ReadLine() (line string, err error)

ReadLine returns a line of input from the terminal.

func (*Reader) ReadPassword

func (r *Reader) ReadPassword() (line string, err error)

ReadPassword temporarily reads a password without saving to history

type Scroller added in v0.12.0

type Scroller struct {
	// InputWidth should be set to the terminal width or smaller.  If this is
	// equal to or larger than MaxWidth, no scrolling will occur
	InputWidth int

	// MaxLineLength should be set to the maximum number of runes to allow in the
	// scrolling input.  This should be set to the underlying Reader's
	// MaxLineLength or less, otherwise the Reader will block further input.
	MaxLineLength int

	// ScrollOffset is set to the number of characters which are "off-screen" to
	// the left of the input area; the input line displays just the characters
	// which are after this offset.  This should typically not be adjusted
	// manually, but it may make sense to allow scrolling the input via a
	// keyboard shortcut that doesn't alter the line or cursor position.
	ScrollOffset int

	// LeftOverflow and RightOverflow are used to signify that the input is
	// scrolling left or right.  They both default to the UTF ellipsis character,
	// but can be overridden as needed.  If set to ”, no overflow character will
	// be displayed when scrolling
	LeftOverflow, RightOverflow rune

	// ScrollBy is the number of runes we "shift" when the cursor would otherwise
	// leave the printable area; defaults to the ScrollBy package constant
	ScrollBy int
	// contains filtered or unexported fields
}

A Scroller is a Line filter for taking the internal Line's state and giving an output widget what should be drawn to the screen

func NewScroller added in v0.12.0

func NewScroller() *Scroller

NewScroller creates a simple scroller instance with no limits. MaxLineLength and InputWidth must be set before any of the scrolling logic will kick in. Whatever uses this is responsible for setting InputWidth and MaxLineLength to appropriate values.

func (*Scroller) Filter added in v0.12.0

func (s *Scroller) Filter(l *Line) ([]rune, int)

Filter looks at the Input's line and our scroll properties to figure out if we should scroll, and what should be drawn in the input area

func (*Scroller) Reset added in v0.12.0

func (s *Scroller) Reset()

Reset is called when a new line is being evaluated

type State

type State struct {
	// contains filtered or unexported fields
}

State contains the state of a terminal.

func GetState

func GetState(fd int) (*State, error)

GetState returns the current state of a terminal which may be useful to restore the terminal after a signal.

func MakeRaw

func MakeRaw(fd int) (*State, error)

MakeRaw put the terminal connected to the given file descriptor into raw mode and returns the previous state of the terminal so that it can be restored.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL