Now we can load them in via JSON files at launch time. They are embded in the final exe though...
██████╗ ██╗███╗ ███╗
██╔════╝ ██║████╗ ████║
██║ ███╗██║██╔████╔██║
██║ ██║██║██║╚██╔╝██║
╚██████╔╝██║██║ ╚═╝ ██║
╚═════╝ ╚═╝╚═╝ ╚═╝
Go + Vim = Gim
A blazingly fast, terminal-based text editor with vim-style keybindings
Features • Installation • Keybindings • Architecture • Roadmap
✨ What is Gim?
Gim is a modern, lightweight text editor written in Go that brings the power of vim-style modal editing to your terminal. Built on the excellent Charm stack, it combines the speed of Go with the elegance of functional UI patterns.
┌──────────────────────────────────────────────────────────────┐
│ Why Gim? │
├──────────────────────────────────────────────────────────────┤
│ 🚀 Fast - Native Go performance │
│ 🎨 Beautiful - Lipgloss-powered terminal styling │
│ 🧠 Vim-like - Modal editing for efficiency │
│ 🧪 Well-tested - Comprehensive integration test suite │
│ 📦 Single binary - No dependencies, just run │
└──────────────────────────────────────────────────────────────┘
Trade Offs
Undo Tree vs. Undo Stack
While the undo tree method that vim uses is powerful, I rarely find myself using it. A stack terminal-based approach is more natural to "non-vim" users and much simpler to implement. Implementing a feature similar to Vims undo tree would many times longer than a simple stack.
Vim-like Replace vs. Custom Replace
The way that vim's replace mode is implemented is quite complex, keeping track of the previous line backspace can only delete newly replaced characters. This is a complex feature, one that I rarely use, and even find a bit un-intuitive. Implementing replace mode in a way where all actions function the same as insert mode (other than the actual character typing) allows for a much simpler implementation, as well as a more intuitive user experience.
Replace mode implements and replaces (no pun intended) the last inserted keys of insert mode. Due to the infrequent use of replace mode, and the '.' action for insert mode, this felt like a natural trade off.
TODO List
- Ops like change, and substitute and such should add to paste reg
- Delete op should also add to paste reg
- Gap buffer implementation (this shouldn't be TOO hard)
- Alternate buffer handling and implementation
- Scroll in X direction
🎯 Features
🎭 Six Editor Modes
| Mode | Key | Description |
|---|---|---|
| Normal | Esc |
Navigate and command - your home base |
| Insert | i/a/o |
Type and create |
| Visual | v |
Select characters |
| Visual Line | V |
Select whole lines |
| Visual Block | Ctrl+V |
Select rectangular regions |
| Command | : |
Execute commands (coming soon) |
⌨️ Full Motion System
gg
↑
b ←── · ──→ w/e
↓
G
0 ──────── $
│ _ │
└──┴───────┘
line
- Character motions:
hjkl- The classics - Word motions:
web- Jump by word boundaries - Line motions:
0$_- Start, end, first non-whitespace - File motions:
ggG- Top and bottom of file
🔧 Powerful Operators
| Operator | Action | Examples |
|---|---|---|
d |
Delete | dd dw d$ dG |
x |
Delete char | x 3x |
Operators compose with motions and counts!
3 d 2w
↓ ↓ ↓
count op motion
= Delete 6 words (3 × 2)
🎪 Visual Mode Magic
Select text visually, then operate on it:
┌─────────────────────────────────┐
│ Hello, [world! How] are you? │ ← v + motion + d
│ Hello, are you? │ ← Result
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ [Line one ] │
│ [Line two ] │ ← V + j + d
│ [Line three ] │
│ │ ← All lines deleted
└─────────────────────────────────┘
🔢 Count Everything
Prefix any command with a number:
| Input | Action |
|---|---|
5j |
Move down 5 lines |
3dd |
Delete 3 lines |
10i=Esc |
Insert ========== |
2d3w |
Delete 6 words |
🪟 Smart Scrolling
- Automatic viewport following
scrollOffmargin keeps context visible- Smooth cursor tracking
📦 Installation
From Source
# Clone the repository
git clone https://git.gophernest.net/azpect/TextEditor.git
cd TextEditor
# Build
go build -o gim ./cmd/gim
# Run
./gim [filename]
Quick Run
go run ./cmd/gim [filename]
⌨️ Keybindings
Normal Mode
🧭 Navigation
| Key | Action |
|---|---|
h |
Move left |
j |
Move down |
k |
Move up |
l |
Move right |
w |
Next word start |
e |
Next word end |
b |
Previous word start |
0 |
Line start |
$ |
Line end |
_ |
First non-whitespace |
gg |
File start |
G |
File end |
✏️ Entering Insert Mode
| Key | Action |
|---|---|
i |
Insert before cursor |
a |
Insert after cursor |
I |
Insert at line start |
A |
Insert at line end |
o |
Open line below |
O |
Open line above |
🗑️ Deletion
| Key | Action |
|---|---|
x |
Delete character |
dd |
Delete line |
d{motion} |
Delete to motion |
D |
Delete to end of line |
👁️ Visual Mode
| Key | Action |
|---|---|
v |
Character visual |
V |
Line visual |
Ctrl+V |
Block visual |
Insert Mode
| Key | Action |
|---|---|
Esc |
Return to normal |
Backspace |
Delete previous char |
Delete |
Delete next char |
Ctrl+W |
Delete previous word |
Tab |
Insert tab (2 spaces) |
Enter |
New line |
↑↓←→ |
Navigate |
Visual Mode
All normal mode navigation keys work, plus:
| Key | Action |
|---|---|
d |
Delete selection |
Esc |
Return to normal |
🏗️ Architecture
Gim follows a clean, modular architecture inspired by the Elm Architecture (Model-Update-View):
┌─────────────────────────────────────────────────────────────┐
│ cmd/gim/ │
│ Entry Point │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ internal/editor/ │
│ Model • Update • View • Style │
└─────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ internal/input/ │ │ internal/motion/│ │internal/operator│
│ Handler │ │ h j k l w e │ │ d (delete) │
│ Keymap │ │ b 0 $ gg G │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└───────────────────┴────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ internal/action/ │
│ Action • Motion • Operator • Position │
│ (Core Interfaces) │
└─────────────────────────────────────────────────────────────┘
Key Design Patterns
- 🔌 Interface-based: Actions/motions use a
Modelinterface, enabling clean testing - 🎭 State Machine: Input handler manages mode transitions and pending operations
- 📐 Motion Types: Linewise vs Charwise motions for proper operator behavior
- 🔄 Composition: Operators compose with motions (e.g.,
d+w= delete word)
🧪 Testing
Gim has comprehensive integration tests using the teatest library:
# Run all tests
go test ./...
# Run with verbose output
go test -v ./internal/editor/
# Run specific test
go test -v -run TestDeleteOperator ./internal/editor/
Test Coverage Areas:
- ✅ Basic motions (hjkl)
- ✅ Jump motions (gg, G, 0, $, _)
- ✅ Word motions (w, e, b)
- ✅ Delete operator with all motions
- ✅ Insert mode (all entry points, recording, replay)
- ✅ Visual mode (all three types)
- ✅ Scrolling behavior
🗺️ Roadmap
✅ Implemented
- Modal editing (Normal, Insert, Visual, Visual Line, Visual Block)
- Basic motions (hjkl, word motions, line motions)
- Delete operator with motion composition
- Count prefixes for all commands
- Insert mode with recording/replay
- Visual selection with operators
- Smart scrolling with scrollOff
- Relative line numbers
🚧 In Progress
- Command mode (
:commands) - File I/O (save/load)
🔮 Future
- Operators:
y(yank),c(change),p(paste) - Motions:
f/F/t/T(find char),//?(search),{/}(paragraph) - Features:
- Undo/redo system
- Multiple buffers/tabs
- Syntax highlighting
- Configuration file
- Clipboard integration
- Macros (
qrecording) - Marks (
mand') - Registers (
") - Text objects (
iw,a", etc.) - Plugins system
🎨 Built With
- BubbleTea - The fun, functional TUI framework
- Lipgloss - Style definitions for terminal apps
- Go - The language that makes it all possible
🤝 Contributing
Contributions are welcome! Whether it's:
- 🐛 Bug fixes
- ✨ New features
- 📖 Documentation improvements
- 🧪 Test coverage
Feel free to open issues and pull requests!
📜 License
This project is open source. See LICENSE for details.
Made with 💜 and lots of ☕
"Because sometimes you just want to build your own vim"