
gittech. site
for different kinds of informations and explorations.
A lightweight Vim like editor for TUIs using bubble tea
VimTea - Vim-like Text Editor for TUIs
VimTea is a lightweight, Vim-inspired text editor for the terminal, built with Go and the Bubble Tea TUI framework. It provides a modular, extensible foundation for building Vim-like text editors in your terminal applications.
Features
- Multiple editing modes (Normal, Insert, Visual, Command)
- Vim-like keybindings and commands
- Line numbers (absolute and relative)
- Count-based movement commands (e.g.
5j
,10k
) - Undo/redo functionality
- Visual mode selection (character and line-wise)
- Command mode
- Clipboard operations (yank, delete, paste)
- Word operations
- Extensible architecture
- Custom key bindings
- Customizable highlighting
Installation
go get github.com/kujtimiihoxha/vimtea
Code Structure
The codebase has been organized into modular components:
- model.go: Main editor model and public interfaces
- buffer.go: Text buffer with undo/redo operations
- cursor.go: Cursor and text range operations
- bindings.go: Key binding registry
- commands.go: Command implementations
- view.go: Rendering functions
- highlight.go: Syntax highlighting
- styles.go: UI style definitions
Usage
Basic Usage
package main
import (
"log"
tea "github.com/charmbracelet/bubbletea"
"github.com/kujtimiihoxha/vimtea"
)
func main() {
// Create a new editor with default options
editor := vimtea.NewEditor(vimtea.WithFullScreen())
// Run the editor
p := tea.NewProgram(editor)
if _, err := p.Run(); err != nil {
log.Fatal(err)
}
}
Load Content
package main
import (
"log"
tea "github.com/charmbracelet/bubbletea"
"github.com/kujtimiihoxha/vimtea"
)
func main() {
content := `This is a sample file
with multiple lines
for testing the editor`
}
// Create editor with content
editor := vimtea.NewEditor(
vimtea.WithContent(content),
vimtea.WithFileName("example.txt"),
vimtea.WithFullScreen(),
)
p := tea.NewProgram(editor)
if _, err := p.Run(); err != nil {
log.Fatal(err)
}
}
Custom Key Bindings
package main
import (
"log"
tea "github.com/charmbracelet/bubbletea"
"github.com/kujtimiihoxha/vimtea"
)
func main() {
// Create editor
editor := vimtea.NewEditor(vimtea.WithFullScreen())
// Add custom binding
editor.AddBinding(vimtea.KeyBinding{
Key: "ctrl+s",
Mode: vimtea.ModeNormal,
Description: "Save file",
Handler: func(b vimtea.Buffer) tea.Cmd {
return vimtea.SetStatusMsg("File saved!")
},
})
p := tea.NewProgram(editor)
if _, err := p.Run(); err != nil {
log.Fatal(err)
}
}
Custom Commands
package main
import (
"log"
tea "github.com/charmbracelet/bubbletea"
"github.com/kujtimiihoxha/vimtea"
)
func main() {
// Create editor
editor := vimtea.NewEditor(vimtea.WithFullScreen())
// Add custom command
editor.AddCommand("mysave", func(b vimtea.Buffer, args []string) tea.Cmd {
return vimtea.SetStatusMsg("Custom save executed!")
})
p := tea.NewProgram(editor)
if _, err := p.Run(); err != nil {
log.Fatal(err)
}
}
Custom Styling
package main
import (
"log"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/kujtimiihoxha/vimtea"
)
func main() {
// Custom styles
lineNumberStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#888888")).
Background(lipgloss.Color("#222222")).
PaddingRight(1)
currentLineStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("white")).
Background(lipgloss.Color("#444444")).
Bold(true).
PaddingRight(1)
cursorStyle := lipgloss.NewStyle().
Background(lipgloss.Color("#CC8800")).
Foreground(lipgloss.Color("black"))
// Create editor with custom styles
editor := vimtea.NewEditor(
vimtea.WithLineNumberStyle(lineNumberStyle),
vimtea.WithCurrentLineNumberStyle(currentLineStyle),
vimtea.WithCursorStyle(cursorStyle),
vimtea.WithRelativeNumbers(true),
vimtea.WithFullScreen(),
)
p := tea.NewProgram(editor)
if _, err := p.Run(); err != nil {
log.Fatal(err)
}
}
Default Key Bindings
Normal Mode
h
,j
,k
,l
: Basic movement (left, down, up, right)- Number prefixes:
5j
,10k
: Move multiple lines at once w
: Move to next word startb
: Move to previous word start0
: Move to start of line^
: Move to first non-whitespace character in line$
: Move to end of linegg
: Move to start of documentG
: Move to end of documenti
: Enter insert modea
: Append after cursorA
: Append at end of lineI
: Insert at start of linev
: Enter visual modeV
: Enter visual line mode:
: Enter command modex
: Delete character at cursordd
: Delete lineD
: Delete from cursor to end of lineyy
: Yank (copy) linep
: Paste after cursorP
: Paste before cursoru
: Undoctrl+r
: Redoo
: Open line below and enter insert modeO
: Open line above and enter insert modediw
: Delete inner wordyiw
: Yank inner wordciw
: Change inner wordzr
: Toggle relative line numbersq
: Quit
Insert Mode
esc
: Return to normal mode- Arrow keys: Navigate
- Regular typing inserts text
Visual Mode
esc
: Return to normal modeh
,j
,k
,l
: Expand selectiony
: Yank selectiond
,x
: Delete selectionp
: Replace selection with yanked text
Command Mode
esc
: Cancel commandenter
: Execute command
Extending VimTea
VimTea is designed to be easily extendable. You can:
- Add custom key bindings with
editor.AddBinding()
- Create new commands with
editor.AddCommand()
- Modify the rendering style with custom style options
- Access buffer operations directly via the Buffer interface
- Create custom views by implementing the View interface
- Customize the editor appearance with style options (WithTextStyle, WithLineNumberStyle, WithCurrentLineNumberStyle, etc.)
Contributing
Contributions are welcome! Here's how you can contribute:
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
Please make sure to update tests as appropriate and follow the existing code style.
Development Workflow
- Clone the repository
- Install dependencies:
go mod download
- Make your changes
- Format code:
go fmt ./...
- Verify imports:
goimports -w .
- Run the example:
cd example && go run main.go
- Create tests for your changes
Architecture
VimTea follows a modular architecture centered around these core components:
- Editor: The main interface that integrates all components
- Buffer: Manages text content with undo/redo operations
- Cursor: Handles positioning and selection
- Bindings: Registers and manages key bindings
- Commands: Implements editor commands (like Vim ex commands)
- View: Renders the editor to the terminal
These components follow clean separation of concerns, making it easier to:
- Add new features
- Test individual components
- Understand the codebase
- Customize functionality
License
This project is licensed under the MIT License - see the LICENSE file for details.