ghostline

package module
v0.0.5 Latest Latest
Warning

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

Go to latest
Published: Dec 18, 2025 License: MIT Imports: 11 Imported by: 0

README

./ghostline 👻

Minimal readline API with ghost text suggestions

features

  • Ghost text suggestions (dimmed inline completions)
  • Fuzzy matching ("gco""git checkout")
  • Dropdown hints showing match count and navigation
  • Tab to accept suggestions, Up/Down to cycle matches
  • Ctrl+Right to accept next word from ghost text
  • Command history with arrow keys
  • Multiline editing (Ctrl+J)
  • Emacs-style keybindings

install

go get github.com/termisquad/x/ghostline@latest

usage

package main

import (
    "fmt"
    "github.com/termisquad/x/ghostline"
)

func main() {
    suggestions := []string{"help", "history", "exit"}
    input := ghostline.NewInput(suggestions, nil, nil)

    for {
        line, err := input.Readline(">>> ")

        if err == ghostline.ErrInterrupted {
            continue
        }

        if err == ghostline.ErrEOF {
            break
        }

        if err != nil {
            break
        }

        input.AddHistory(line)
        fmt.Println("You typed:", line)
    }
}

keybindings

Key Action
Tab Accept suggestion
↑ ↓ Cycle suggestions / History
Ctrl+→ Accept next word from ghost
Ctrl+← Move to previous word
Enter Submit
Ctrl+J New line
Ctrl+C Interrupt
Ctrl+D EOF (exit)
← → Move cursor
Ctrl+A Beginning of line
Ctrl+E End of line
Ctrl+K Kill to end of line
Ctrl+U Kill to beginning
Ctrl+W Delete word
Delete Delete char

example

go run ./examples/main.go

license

MIT

Documentation

Overview

Package ghostline provides interactive readline functionality with "ghost text" suggestions. Displays completion suggestions inline as dimmed text that users can accept with Tab. Supports standard line editing with backspace and handles terminal raw mode automatically.

Ghost text suggestions appear after the cursor as the user types, showing potential completions based on prefix matching against a configurable list of suggestions. The terminal is placed in raw mode during input to capture individual keystrokes.

Example:

suggestions := []string{"help", "hello", "history", "exit"}
input := ghostline.NewInput(suggestions, nil, nil)
line, err := input.Readline("$ ")
if err != nil {
	// handle ErrInterrupted, ErrEOF, or other errors
}
fmt.Println("You entered:", line)

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInterrupted is returned when the user presses Ctrl+C.
	ErrInterrupted = errors.New("interrupted")

	// ErrEOF is returned when the user presses Ctrl+D on an empty line.
	ErrEOF = errors.New("eof")
)

Readline errors for distinguishing abort types.

Functions

This section is empty.

Types

type History

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

History stores command history with up/down arrow navigation. Preserves the user's in-progress input when navigating through entries.

func NewHistory

func NewHistory() *History

NewHistory creates an empty history.

func (*History) Add

func (h *History) Add(entry string)

Add appends an entry to history after trimming whitespace. Ignores empty strings and consecutive duplicates.

func (*History) Len

func (h *History) Len() int

Len returns the number of stored history entries.

func (*History) Next

func (h *History) Next() (string, bool)

Next navigates to a newer history entry or restores the original input. Returns the entry and true, or the saved input and true when reaching the end.

func (*History) Previous

func (h *History) Previous(current string) (string, bool)

Previous navigates to an older history entry. Saves the current input on first call for later restoration. Returns the entry and true, or the current input and false if at oldest.

func (*History) Reset

func (h *History) Reset(current string)

Reset prepares history for a new readline session. Stores the current input for restoration after navigation.

type Input

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

Input manages interactive line input with ghost text suggestions. Handles raw terminal mode, keyboard input, and inline suggestion rendering. Create instances using NewInput rather than constructing directly.

The zero value is not usable; always use NewInput to create Input instances.

Input is not safe for concurrent use. Each Input instance must be used by a single goroutine only. If you need to read from multiple inputs concurrently, create a separate Input instance per goroutine.

func NewInput

func NewInput(suggestions []string, in io.Reader, out io.Writer) *Input

NewInput creates an Input configured with the given suggestions and I/O streams. Returns a ready-to-use Input for interactive line reading with ghost text.

Parameters:

  • suggestions: list of strings to use for prefix-based completion
  • in: input source for reading keystrokes (nil defaults to os.Stdin)
  • out: output destination for rendering (nil defaults to os.Stdout)

Example:

// Using defaults for standard terminal interaction
input := ghostline.NewInput([]string{"commit", "checkout", "cherry-pick"}, nil, nil)

// Using custom streams for testing
input := ghostline.NewInput(suggestions, mockReader, mockWriter)

func (*Input) AddHistory

func (i *Input) AddHistory(line string)

AddHistory adds a line to the command history. Empty lines and duplicates of the last entry are ignored.

func (*Input) Readline

func (i *Input) Readline(prompt string) (string, error)

Readline reads a line of input with interactive ghost text suggestions. Returns the entered text and nil on success, or an error if aborted. Places the terminal in raw mode for the duration of input.

Parameters:

  • prompt: string displayed before the input area (e.g., "$ " or "> ")

Keyboard controls:

  • Tab: accept the current ghost text suggestion
  • Enter: submit the current input
  • Backspace/Delete: remove the last character
  • Ctrl+C: abort input (returns ErrInterrupted)
  • Ctrl+D: abort input when buffer is empty (returns ErrEOF)

Example:

input := ghostline.NewInput([]string{"help", "history"}, nil, nil)
for {
	line, err := input.Readline(">>> ")

	if err == ghostline.ErrInterrupted {
		fmt.Println("^C")
		continue
	}

	if err == ghostline.ErrEOF {
		fmt.Println("Goodbye!")
		break
	}

	if err != nil {
		fmt.Println("Error:", err)
		break
	}

	fmt.Println("Command:", line)
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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