repl

package module
v0.0.0-...-eedf15c Latest Latest
Warning

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

Go to latest
Published: Dec 4, 2023 License: Unlicense Imports: 9 Imported by: 0

README

go-repl

Lightweight Golang REPL library, inspired by GNU Readline. You provide the Eval function, and go-repl does the rest.

Your REPLs that use this library will enjoy the following features:

  • Session history with reverse-search
    • Ctrl-R to start reverse-search
    • Most edit commands, except the most basic ones, exit the reverse-search mode
    • Use Up/Down to cycle through a filtered list of history entries
  • The input buffer is redrawn when a resize is detected
  • Status bar at bottom with current working dir and other info
  • Truncation of very long inputs (status bar displays info about cursor position)
  • Common edit and movement commands:
    • Right/Left: move cursor one character at a time
    • Ctrl-Right/Left: move cursor one word at a time
    • Up/Down: cycle through history
    • Backspace/Delete: works as expected
    • Shift-Enter: insert newline into buffer without invoking Eval
    • Ctrl-A or Home: move to start of buffer
    • Ctrl-E or End: move to end of buffer
    • Ctrl-W: delete preceding word
    • Ctrl-Q: delete following word
    • Ctrl-C or Esc: ignore current input and reset buffer
    • Ctrl-D: quit REPL
    • Ctrl-U: clear buffer to start
    • Ctrl-K: clear buffer to end
    • Ctrl-L: reset prompt at top and redraw buffer
    • Ctrl-Y: insert previous deletion (from Ctrl-K, Ctrl-U, Ctrl-Q or Ctrl-W)

Notes:

  • Doesn't depend on ncurses
  • Performance hasn't yet been optimized and I haven't yet tested all corner cases exhaustively
  • Might not work in Windows command prompt (keystroke codes could differ, ANSI escape sequences might not be supported, the method that sets terminal to raw mode might not work)
  • No vi edit mode
  • No support for clipboards yet

Usage

Fetch this library with the following command:

$ go get -u github.com/openengineer/go-repl

In order to create your own REPL you have to define a type that implements the Handler interface:

type Handler interface {
  Prompt() string
  Tab(buffer string) string
  Eval(line string) string
}

Here is a complete example (can also be found in ./examples/basic_repl.go):

package main

import (
  "fmt"
  "log"
  "strconv"
  "strings"

  repl "github.com/openengineer/go-repl"
)

var helpMessage = `help              display this message
add <int> <int>   add two numbers
quit              quit this program`

// implements repl.Handler interface
type MyHandler struct {
  r *repl.Repl
}

func main() {
  fmt.Println("Welcome, type \"help\" for more info")

  h := &MyHandler{}
  h.r = repl.NewRepl(h)

  // start the terminal loop
  if err := h.r.Loop(); err != nil {
    log.Fatal(err)
  }
}

func (h *MyHandler) Prompt() string {
  return "> " 
}

func (h *MyHandler) Tab(buffer string) string {
  return "" // do nothing
}

func (h *MyHandler) Eval(line string) string {
  fields := strings.Fields(line)

  if len(fields) == 0 {
    return ""
  } else {
    cmd, args := fields[0], fields[1:]

    switch cmd {
    case "help":
      return helpMessage
    case "add":
      if len(args) != 2 {
        return "\"add\" expects 2 args"
      } else {
        return add(args[0], args[1])
      }
    case "quit":
      h.r.Quit()
      return ""
    default:
      return fmt.Sprintf("unrecognized command \"%s\"", cmd)
    }
  }
}

func add(a_ string, b_ string) string {
  a, err := strconv.Atoi(a_)
  if err != nil {
    return "first arg is not an integer"
  }

  b, err := strconv.Atoi(b_)
  if err != nil {
    return "second arg is not an integer"
  }

  return strconv.Itoa(a + b)
}

Documentation

Overview

Lightweight Golang REPL library, inspired by GNU readline. You provide the Eval function, and go-repl does the rest.

Index

Constants

View Source
const MACHINE_INTERVAL = time.Millisecond

This is a cut-off time for grouping auto-generated escape sequences.

Variables

View Source
var (
	// Period between polls for terminal size changes.
	// 10ms is the default, human reaction times are an order of magnitude slower than this interval,
	// and auto generated escape sequence bytes are an order of magnitude faster than this interval.
	SIZE_POLLING_INTERVAL = 10 * time.Millisecond

	// Used by the package maintainer:
	DEBUG = "" // a non-empty string specifies the destination file for debugging info
)

Functions

This section is empty.

Types

type Handler

type Handler interface {
	Prompt() string
	Eval(buffer string) string
	Tab(buffer string) string
}

Implement this interface in order to use `Repl` with your custom logic.

type Repl

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

func NewRepl

func NewRepl(handler Handler) *Repl

Create a new Repl using your custom Handler.

func (*Repl) Loop

func (r *Repl) Loop() error

Start the REPL loop.

Loop sets the terminal to raw mode, so any further calls to fmt.Print or similar, might not behave as expected and can garble your REPL.

func (*Repl) MakeRaw

func (r *Repl) MakeRaw() error

Explicitely set the terminal back to raw mode after a call to UnmakeRaw.

func (*Repl) Quit

func (r *Repl) Quit()

Exit the REPL program cleanly. Performs the following steps:

  1. cleans the screen
  2. returns the cursor to the appropriate position
  3. unsets terminal raw mode

Important: use this method instead of os.Exit.

func (*Repl) ReadLine

func (r *Repl) ReadLine(echo bool) string

func (*Repl) UnmakeRaw

func (r *Repl) UnmakeRaw()

Unset the raw mode in case you want to run a curses-like command inside your REPL session (e.g. vi or top). Remember to call MakeRaw after the command finishes.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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