goeditor

package module
v0.4.10 Latest Latest
Warning

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

Go to latest
Published: Apr 5, 2026 License: MIT Imports: 17 Imported by: 4

README

GoEditor

A feature-rich, Vim-inspired text editor library for Go, built on Bubble Tea.

Features

  • Multiple editing modes: Normal, Insert, Visual, Visual Line, and Command modes
  • Vim-style keybindings: Navigate and edit text efficiently with familiar Vim commands
  • Unicode support: Full support for international characters and emojis
  • Undo/Redo: Navigate through your editing history
  • Search functionality: Find text within your document
  • Clipboard integration: Copy, cut, and paste with system clipboard support
  • Line wrapping: Automatic word-wrap for long lines
  • Custom Themes: Customizable color schemes and styles with Lip Gloss
  • Line numbers: Optional absolute or relative line numbering
  • Syntax highlighting: Automatic syntax highlighting for various languages (Go, Python, Markdown, etc.)
  • Customizable word highlighting: Highlight specific words with custom styles
  • Status line: Shows current mode, cursor position, and file status
  • Responsive: Adapts to terminal size changes
  • Cursor modes: Blinking or steady cursor with mode-specific styling
  • Focus/Blur: Programmatic focus management
  • Placeholder text: Display helpful text when the buffer is empty
  • Completion menu: Pluggable autocompletion with a scrollable, keyboard-navigable suggestion menu

Installation

go get github.com/ionut-t/goeditor

Quick Start

package main

import (
    "log"

    tea "charm.land/bubbletea/v2"
    "github.com/ionut-t/goeditor"
)

func main() {
    m := goeditor.New(80, 24)
    m.SetContent("Hello, World!\nWelcome to GoEditor.")
    m.Focus()

    p := tea.NewProgram(m)

    if _, err := p.Run(); err != nil {
        log.Fatal(err)
    }
}

Configuration

// Disable Vim mode for a simpler editing experience
m.DisableVimMode(true)

// Show relative line numbers
m.ShowRelativeLineNumbers(true)

// Hide line numbers entirely
m.HideLineNumbers(true)

// Set placeholder text
m.SetPlaceholder("Start typing...")

// Set language for syntax highlighting
m.SetLanguage("go", "catppuccin-mocha")

// Highlight specific words
highlights := map[string]lipgloss.Style{
    "TODO":  lipgloss.NewStyle().Foreground(lipgloss.Color("214")).Bold(true),
    "FIXME": lipgloss.NewStyle().Foreground(lipgloss.Color("196")).Bold(true),
    "NOTE":  lipgloss.NewStyle().Foreground(lipgloss.Color("33")).Bold(true),
}
m.SetHighlightedWords(highlights)

// Set cursor to blink
m.SetCursorMode(goeditor.CursorBlink)

// Custom theme
theme := goeditor.Theme{
    NormalModeStyle: lipgloss.NewStyle().Background(lipgloss.Color("22")),
    InsertModeStyle: lipgloss.NewStyle().Background(lipgloss.Color("28")),
    SelectionStyle:  lipgloss.NewStyle().Background(lipgloss.Color("240")),
    // ... customize other styles
}
m.WithTheme(theme)

Vim Keybindings

Normal Mode
  • Movement: h, j, k, l or arrow keys
  • Word movement: w (forward), b (backward), e (end of word)
  • Line movement: 0 (start), $ (end), ^ (first non-blank)
  • Document movement: g (first line), G (last line)
  • Editing: x (delete char), dd (delete line), D (delete to end of line)
  • Mode switching: i (insert), v (visual), V (visual line), : (command)
  • Undo/Redo: u (undo), U (redo)
  • Copy/Paste: y (yank), p (paste)
Insert Mode
  • Type normally to insert text
  • Esc to return to Normal mode
  • Backspace to delete characters
  • Arrow keys for navigation
  • Ctrl+Space to manually trigger autocompletion
  • Ctrl+N / Ctrl+P to navigate suggestions
  • Ctrl+Y to accept the selected suggestion
  • Ctrl+E / Esc to dismiss the completion menu
Visual Mode
  • Select text character by character
  • d or x to delete selection
  • y to copy selection
  • Esc to cancel selection
Command Mode
  • :w - Save file
  • :q - Quit
  • :wq - Save and quit
  • :q! - Force quit without saving
  • :set rnu - Enable relative line numbers
  • :set nornu - Disable relative line numbers

API Reference

Editor Model Methods
// Content Management
SetContent(content string)
SetBytes(content []byte)
GetCurrentContent() string
GetSavedContent() string
HasChanges() bool
IsEmpty() bool

// Mode Control
SetNormalMode()
SetInsertMode()
SetVisualMode()
SetCommandMode()
DisableVimMode(disable bool)

// Display Options
HideLineNumbers(hide bool)
ShowRelativeLineNumbers(show bool)
ShowTildeIndicator(show bool)
HideStatusLine(hide bool)

// Cursor Control
SetCursorPosition(row, col int) error
SetCursorPositionEnd() error
SetCursorMode(mode CursorMode)

// Styling
WithTheme(theme Theme)
SetHighlightedWords(words map[string]lipgloss.Style)
SetPlaceholder(placeholder string)

// Focus Management
Focus()
Blur()
IsFocused() bool

// Completion
SetCompletions(completions []core.Completion, context core.CompletionContext)
WithCompletionAutoTrigger(enabled bool)
WithCompletionDebounce(duration time.Duration)
SetCompletionMenuMaxVisibleItems(max int)
Handling Editor Events

Handle events in your Bubble Tea Update method:

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case goeditor.SaveMsg:
        content := msg.Content
        // Save to file...

    case goeditor.QuitMsg:
        return m, tea.Quit

    case goeditor.YankMsg:
        return m, m.editor.DispatchMessage(fmt.Sprintf("%d bytes yanked", len(msg.Content)), 3*time.Second)

    case goeditor.DeleteMsg:
        return m, m.editor.DispatchMessage(fmt.Sprintf("%d bytes deleted", len(msg.Content)), 3*time.Second)

    case goeditor.ErrorMsg:
        return m, m.editor.DispatchError(msg.Error, 3*time.Second)
    }
}

Autocompletion

GoEditor does not fetch completions itself — it dispatches a CompletionRequestMsg when the user triggers completion and expects the host application to respond with SetCompletions.

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {

    case goeditor.CompletionRequestMsg:
        ctx := msg.Context
        return m, func() tea.Msg {
            completions := fetchCompletions(ctx) // your completion source
            return goeditor.CompletionResponseMsg{
                Completions: completions,
                Context:     ctx,
            }
        }

    case goeditor.CompletionResponseMsg:
        m.editor.SetCompletions(msg.Completions, msg.Context)
    }
}

Enable auto-trigger (fires on every keystroke in Insert mode):

m.WithCompletionAutoTrigger(true)
m.WithCompletionDebounce(150 * time.Millisecond) // optional debounce

Core Package

The core package contains the editor engine with no UI dependencies and can be used independently:

import "github.com/ionut-t/goeditor/core"

// Implement core.Clipboard interface
type clipboardImpl struct{}

func (c *clipboardImpl) Write(text string) error { ... }
func (c *clipboardImpl) Read() (string, error)   { ... }

ed := core.New(&clipboardImpl{})
ed.SetContent([]byte("Hello, World!"))
ed.HandleKey(core.KeyEvent{Rune: 'i'})

content := ed.GetBuffer().GetCurrentContent()

Examples

See examples/basic and examples/completion.

Acknowledgements

  • Bubble Tea: A powerful TUI framework for Go.
  • Chroma: A general purpose syntax highlighter in pure Go.
  • Lip Gloss: Style definitions for nice terminal layouts.
  • atotto/clipboard: A cross-platform clipboard package for Go.

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CompletionDebounceMsg

type CompletionDebounceMsg struct {
	TriggerChar string
	Timestamp   time.Time
}

type CompletionRequestMsg

type CompletionRequestMsg struct {
	Context core.CompletionContext
}

type CompletionResponseMsg

type CompletionResponseMsg struct {
	Completions []core.Completion
	Context     core.CompletionContext
}

type CursorMode

type CursorMode int
const (
	CursorSteady CursorMode = iota
	CursorBlink
)

type DeleteFileMsg

type DeleteFileMsg struct{}

type DeleteMsg

type DeleteMsg struct {
	Content string
}

type ErrorMsg

type ErrorMsg struct {
	ID    core.ErrorId
	Error error
}

type Model

type Model struct {
	StatusLineFunc func() string
	// contains filtered or unexported fields
}

func New

func New(width, height int) Model

func (*Model) Blur

func (m *Model) Blur()

Blur sets the editor to unfocused state.

func (m *Model) CursorBlink() tea.Cmd

CursorBlink is the main command for the blinking cursor effect (toggling visibility)

func (*Model) DisableCommandMode

func (m *Model) DisableCommandMode(disable bool)

DisableCommandMode allows disabling command mode in the core. This will disable the command mode functionality, meaning the editor will not respond to command mode keybindings.

func (*Model) DisableInsertMode

func (m *Model) DisableInsertMode(disable bool)

DisableInsertMode allows disabling insert mode in the core. This will disable the insert mode functionality, meaning the editor will not respond to insert mode keybindings and will prevent text modifications.

func (*Model) DisableSearchMode

func (m *Model) DisableSearchMode(disable bool)

func (*Model) DisableVimMode

func (m *Model) DisableVimMode(disable bool)

DisableVimMode allows disabling Vim mode in the core. This will disable all Vim-specific features and revert to a simpler text editor mode. If Vim mode is disabled, the editor will not respond to Vim keybindings.

func (*Model) DisableVisualLineMode

func (m *Model) DisableVisualLineMode(disable bool)

DisableVisualLineMode allows disabling visual line mode in the core. This will disable the visual line mode functionality, meaning the editor will not respond to visual line mode keybindings.

func (*Model) DisableVisualMode

func (m *Model) DisableVisualMode(disable bool)

DisableVisualMode allows disabling visual mode in the core. This will disable the visual mode functionality, meaning the editor will not respond to visual mode keybindings.

func (*Model) DispatchError

func (m *Model) DispatchError(err error, duration time.Duration) tea.Cmd

DispatchError allows setting an error to be displayed in the command line for a specified duration.

func (*Model) DispatchMessage

func (m *Model) DispatchMessage(message string, duration time.Duration) tea.Cmd

DispatchMessage allows setting a message to be displayed in the command line for a specified duration.

func (*Model) Focus

func (m *Model) Focus()

Focus sets the editor to focused state.

func (*Model) GetCurrentContent

func (m *Model) GetCurrentContent() string

GetCurrentContent returns the current content of the editor buffer. This content may not be saved yet, as it reflects the current state of the core.

func (Model) GetCursorPosition

func (m Model) GetCursorPosition() core.Position

GetCursorPosition returns the current cursor position in the core.

func (*Model) GetEditor

func (m *Model) GetEditor() core.Editor

GetEditor returns the underlying editor instance

func (*Model) GetSavedContent

func (m *Model) GetSavedContent() string

GetSavedContent returns the saved content of the editor buffer This content is what was last saved to disk, and may not reflect the current state of the core. It is useful for operations that require the last saved state, such as saving to a file.

func (*Model) HasChanges

func (m *Model) HasChanges() bool

HasChanges checks if the editor has unsaved changes

func (*Model) HideLineNumbers

func (m *Model) HideLineNumbers(hide bool)

HideLineNumbers controls whether to show line numbers in the viewport.

func (*Model) HideStatusLine

func (m *Model) HideStatusLine(hide bool)

HideStatusLine controls whether to show the status line at the bottom of the viewport. If Vim mode is disabled, this will not have any effect.

func (Model) Init

func (m Model) Init() tea.Cmd

func (*Model) IsCommandMode

func (m *Model) IsCommandMode() bool

IsCommandMode returns whether the editor is in command mode.

func (*Model) IsEmpty

func (m *Model) IsEmpty() bool

IsEmpty checks if the editor buffer is empty.

func (*Model) IsFocused

func (m *Model) IsFocused() bool

IsFocused returns whether the editor is currently focused.

func (*Model) IsInsertMode

func (m *Model) IsInsertMode() bool

IsInsertMode returns whether the editor is in insert mode.

func (*Model) IsNormalMode

func (m *Model) IsNormalMode() bool

IsNormalMode returns whether the editor is in normal mode.

func (*Model) IsSearchMode

func (m *Model) IsSearchMode() bool

IsSearchMode returns whether the editor is in search mode.

func (*Model) IsVisualLineMode

func (m *Model) IsVisualLineMode() bool

IsVisualLineMode returns whether the editor is in visual line mode.

func (*Model) IsVisualMode

func (m *Model) IsVisualMode() bool

IsVisualMode returns whether the editor is in visual mode.

func (*Model) SetBytes

func (m *Model) SetBytes(content []byte)

SetBytes sets the content of the core.

func (*Model) SetCommandMode

func (m *Model) SetCommandMode()

SetCommandMode sets the editor to command mode.

func (*Model) SetCompletionMenuMaxVisibleItems added in v0.4.10

func (m *Model) SetCompletionMenuMaxVisibleItems(max int)

SetCompletionMenuMaxVisibleItems sets the maximum number of visible items in the completion menu.

func (Model) SetCompletions added in v0.4.10

func (m Model) SetCompletions(completions []core.Completion, context core.CompletionContext)

SetCompletions sends completion results to the editor to be displayed in the completion menu.

func (*Model) SetContent

func (m *Model) SetContent(content string)

SetContent sets the content of the editor from a string.

func (*Model) SetCursorMode

func (m *Model) SetCursorMode(mode CursorMode)

SetCursorMode sets the cursor mode for the core. It can be either CursorSteady or CursorBlink.

Warning: Enabling CursorBlink may have performance implications.

func (*Model) SetCursorPosition

func (m *Model) SetCursorPosition(row, col int) error

SetCursorPosition sets the cursor position in the core.

func (*Model) SetCursorPositionEnd

func (m *Model) SetCursorPositionEnd() error

SetCursorPositionEnd sets the cursor position to the end of the editor buffer.

func (*Model) SetExtraHighlightedContextLines

func (m *Model) SetExtraHighlightedContextLines(lines uint16)

SetExtraHighlightedContextLines sets the number of extra lines to tokenise around the visible viewport. This is crucial for Markdown where code blocks need context (the opening ```) to highlight correctly.

PERFORMANCE TRADE-OFF: - Higher values: Better syntax highlighting for large code blocks, but slower scrolling - Lower values: Faster scrolling, but code blocks may lose highlighting when scrolling

Recommended values: - Small files (<5000 lines): 100-200 (default is 100) - Large files (5000-20000 lines): 50-100 - Very large files (>20000 lines): 20-50

When scrolling, if the new range overlaps with cached range, no re-tokenisation occurs (fast). When scrolling beyond cached range, the entire new range gets re-tokenised (slow if value is high).

func (*Model) SetExtraWordChars added in v0.4.3

func (m *Model) SetExtraWordChars(chars ...rune)

SetExtraWordChars allows specifying additional characters to be considered part of words for cursor movement and selection. By default, the editor considers alphanumeric characters and underscores as part of words. This method allows to include additional characters (e.g., hyphens, dots).

func (*Model) SetHighlightedWords

func (m *Model) SetHighlightedWords(words map[string]lipgloss.Style)

SetHighlightedWords allows setting highlighted words in the core. These words will be styled with the provided lipgloss styles. This is useful for highlighting specific keywords or phrases in the text.

func (*Model) SetInsertMode

func (m *Model) SetInsertMode()

SetInsertMode sets the editor to insert mode.

func (*Model) SetLanguage

func (m *Model) SetLanguage(language string, theme string)

SetLanguage sets the programming language for syntax highlighting.

If the language is empty, syntax highlighting will be disabled.

The theme parameter allows specifying a Chroma theme for the syntax highlighter. For a full list of available themes, see: https://github.com/alecthomas/chroma/blob/master/styles

func (*Model) SetMaxHistory

func (m *Model) SetMaxHistory(max uint32)

SetMaxHistory sets the maximum number of history entries for undo/redo. This allows controlling how many undo steps are kept in memory. If set to 0, no history will be kept. The default value is 1000. If the number of history entries exceeds this limit, the oldest entries will be removed. This is useful for managing memory usage in the core.

func (*Model) SetNormalMode

func (m *Model) SetNormalMode()

SetNormalMode sets the editor to normal mode.

func (*Model) SetPlaceholder

func (m *Model) SetPlaceholder(placeholder string)

SetPlaceholder sets the placeholder text for the core.

func (*Model) SetSize

func (m *Model) SetSize(width, height int)

func (*Model) SetVisualLineMode

func (m *Model) SetVisualLineMode()

SetVisualLineMode sets the editor to visual line mode.

func (*Model) SetVisualMode

func (m *Model) SetVisualMode()

SetVisualMode sets the editor to visual mode.

func (*Model) ShowRelativeLineNumbers

func (m *Model) ShowRelativeLineNumbers(show bool)

ShowLineNumbers controls whether to show relative line numbers in the viewport. If Vim mode is disabled, this will not have any effect. If line numbers are hidden, this will not have any effect.

func (*Model) ShowTildeIndicator

func (m *Model) ShowTildeIndicator(show bool)

ShowTildeIndicator controls whether to show the tilde indicator in the viewport. If line numbers are hidden, this will not have any effect.

func (Model) Update

func (m Model) Update(msg tea.Msg) (Model, tea.Cmd)

func (Model) View

func (m Model) View() string

func (*Model) WithCompletionAutoTrigger added in v0.4.10

func (m *Model) WithCompletionAutoTrigger(enabled bool)

WithCompletionAutoTrigger enables or disables auto-trigger completions.

func (*Model) WithCompletionDebounce

func (m *Model) WithCompletionDebounce(duration time.Duration)

WithCompletionDebounce sets the debounce time for auto-trigger completions.

func (*Model) WithSearchInputCursorMode

func (m *Model) WithSearchInputCursorMode(mode cursor.Mode)

WithSearchInputCursorMode allows setting the cursor mode for the search input. Default is CursorStatic.

func (*Model) WithSearchOptions

func (m *Model) WithSearchOptions(options core.SearchOptions)

WithSearchOptions allows setting custom search options for the core.

func (*Model) WithSyntaxHighlighter

func (m *Model) WithSyntaxHighlighter(highlighter *highlighter.Highlighter)

WithSyntaxHighlighter allows setting a custom syntax highlighter.

func (*Model) WithTheme

func (m *Model) WithTheme(theme Theme)

WithTheme allows setting a custom theme for the core.

type PasteMsg

type PasteMsg struct {
	Content string
}

type QuitMsg

type QuitMsg struct{}

type RedoMsg

type RedoMsg struct {
	ContentBefore string
}

type RelativeNumbersChangeMsg

type RelativeNumbersChangeMsg struct {
	Enabled bool
}

type RenameMsg

type RenameMsg struct {
	FileName string
}

type SaveMsg

type SaveMsg struct {
	Path    *string
	Content string
}

type SearchResultsMsg

type SearchResultsMsg struct {
	Positions []core.Position
}

type Theme

type Theme struct {
	NormalModeStyle        lipgloss.Style
	InsertModeStyle        lipgloss.Style
	VisualModeStyle        lipgloss.Style
	CommandModeStyle       lipgloss.Style
	SearchModeStyle        lipgloss.Style
	StatusLineStyle        lipgloss.Style
	CommandLineStyle       lipgloss.Style
	MessageStyle           lipgloss.Style
	LineNumberStyle        lipgloss.Style
	CurrentLineNumberStyle lipgloss.Style
	CurrentLineStyle       lipgloss.Style
	SelectionStyle         lipgloss.Style
	ErrorStyle             lipgloss.Style
	HighlightYankStyle     lipgloss.Style
	PlaceholderStyle       lipgloss.Style

	SearchHighlightStyle   lipgloss.Style
	SearchInputPromptStyle lipgloss.Style
	SearchInputTextStyle   lipgloss.Style
	SearchInputCursorStyle lipgloss.Style

	CompletionMenuItemStyle         lipgloss.Style
	CompletionMenuSelectedItemStyle lipgloss.Style
	CompletionMenuBorderStyle       lipgloss.Style
	CompletionMenuLabelStyle        lipgloss.Style
	CompletionMenuTypeStyle         lipgloss.Style
}

func DefaultTheme

func DefaultTheme(isDark bool) Theme

DefaultTheme creates a theme with adaptive colors based on terminal background.

type UndoMsg

type UndoMsg struct {
	ContentBefore string
}

type VisualLineInfo

type VisualLineInfo struct {
	Content         string
	LogicalRow      int
	LogicalStartCol int
	IsFirstSegment  bool
}

type YankMsg

type YankMsg struct {
	Content string
}

Directories

Path Synopsis
editor/visual_line_mode.go
editor/visual_line_mode.go
examples
basic command
completion command

Jump to

Keyboard shortcuts

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