Skip to content

nyaosorg/go-readline-ny

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Go Reference Go Report Card Wiki License

The New Yet another Readline for Go (go-readline-ny)

go-readline-ny is a one-line input library for CUI applications written in Go. It is designed to be extensible to meet diverse needs and has been used in the command-line shell NYAGOS for a long time.

  • Emacs-like key-bindings
  • Input history
  • Word completion (file names, command names, or any names in given array)
  • Syntax highlighting
  • Supported platforms: Windows and Linux
  • Full Unicode (UTF8) support, including:
    • Surrogate-pair
    • Emojis (via clipboard)
    • Zero-width joiner (via clipboard)
    • Variation selectors (via clipboard pasted with Ctrl-Y)
  • Add-Ons:

Zero-Width-Joiner sample on Windows-Terminal

examples

package main

import (
    "context"
    "fmt"

    "github.com/nyaosorg/go-readline-ny"
)

func main() {
    var editor readline.Editor
    text, err := editor.ReadLine(context.Background())
    if err != nil {
        fmt.Printf("ERR=%s\n", err.Error())
    } else {
        fmt.Printf("TEXT=%s\n", text)
    }
}

If the target platform includes Windows, you have to import and use go-colorable like example2.go .

This is a sample demonstrating prompt change, syntax highlighting, filename completion, history browsing, and access to the operating system clipboard.

package main

import (
    "context"
    "fmt"
    "io"
    "os"
    "os/exec"
    "regexp"
    "strings"

    "github.com/atotto/clipboard"
    "github.com/mattn/go-colorable"

    "github.com/nyaosorg/go-readline-ny"
    "github.com/nyaosorg/go-readline-ny/completion"
    "github.com/nyaosorg/go-readline-ny/keys"
    "github.com/nyaosorg/go-readline-ny/simplehistory"
)

type OSClipboard struct{}

func (OSClipboard) Read() (string, error) {
    return clipboard.ReadAll()
}

func (OSClipboard) Write(s string) error {
    return clipboard.WriteAll(s)
}

func main() {
    history := simplehistory.New()

    editor := &readline.Editor{
        PromptWriter: func(w io.Writer) (int, error) {
            return io.WriteString(w, "\x1B[36;22m$ ") // print `$ ` with cyan
        },
        Writer:  colorable.NewColorableStdout(),
        History: history,
        Highlight: []readline.Highlight{
            {Pattern: regexp.MustCompile("&"), Sequence: "\x1B[33;49;22m"},
            {Pattern: regexp.MustCompile(`"[^"]*"`), Sequence: "\x1B[35;49;22m"},
            {Pattern: regexp.MustCompile(`%[^%]*%`), Sequence: "\x1B[36;49;1m"},
            {Pattern: regexp.MustCompile("\u3000"), Sequence: "\x1B[37;41;22m"},
        },
        HistoryCycling: true,
        PredictColor:   [...]string{"\x1B[3;22;34m", "\x1B[23;39m"},
        ResetColor:     "\x1B[0m",
        DefaultColor:   "\x1B[33;49;1m",

        Clipboard: OSClipboard{},
    }

    editor.BindKey(keys.CtrlI, &completion.CmdCompletionOrList2{
        // Characters listed here are excluded from completion.
        Delimiter: "&|><;",
        // Enclose candidates with these characters when they contain spaces
        Enclosure: `"'`,
        // String to append when only one candidate remains
        Postfix: " ",
        // Function for listing candidates
        Candidates: completion.PathComplete,
    })
    // If you do not want to list files with double-tab-key,
    // use `CmdCompletion2` instead of `CmdCompletionOrList2`

    fmt.Println("Tiny Shell. Type Ctrl-D to quit.")
    for {
        text, err := editor.ReadLine(context.Background())

        if err != nil {
            fmt.Printf("ERR=%s\n", err.Error())
            return
        }

        fields := strings.Fields(text)
        if len(fields) <= 0 {
            continue
        }
        cmd := exec.Command(fields[0], fields[1:]...)
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        cmd.Stdin = os.Stdin

        cmd.Run()

        history.Add(text)
    }
}
package main

import (
    "context"
    "fmt"
    "io"
    "os"

    "github.com/nyaosorg/go-readline-ny"
    "github.com/nyaosorg/go-readline-ny/completion"
    "github.com/nyaosorg/go-readline-ny/keys"
)

func mains() error {
    var editor readline.Editor

    editor.PromptWriter = func(w io.Writer) (int, error) {
        return io.WriteString(w, "menu> ")
    }
    candidates := []string{"list", "say", "pewpew", "help", "exit", "Space Command"}

    // If you do not want to list files with double-tab-key,
    // use `CmdCompletion2` instead of `CmdCompletionOrList2`

    editor.BindKey(keys.CtrlI, &completion.CmdCompletionOrList2{
        // Characters listed here are excluded from completion.
        Delimiter: "&|><;",
        // Enclose candidates with these characters when they contain spaces
        Enclosure: `"'`,
        // String to append when only one candidate remains
        Postfix: " ",
        // Function for listing candidates
        Candidates: func(field []string) (forComp []string, forList []string) {
            if len(field) <= 1 {
                return candidates, candidates
            }
            return nil, nil
        },
    })
    ctx := context.Background()
    for {
        line, err := editor.ReadLine(ctx)
        if err != nil {
            return err
        }
        fmt.Printf("TEXT=%#v\n", line)
    }
    return nil
}

func main() {
    if err := mains(); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

Acknowledgements

Release notes

License

MIT License