DEV Community

Cover image for FTX: Asynchronous File Tree Explorer for Vim and Neovim
Genix
Genix

Posted on

FTX: Asynchronous File Tree Explorer for Vim and Neovim

Abstract

FTX (File Tree eXplorer) is a lightweight, asynchronous file tree explorer built entirely in Vimscript with first-class Git integration. Unlike traditional file browsers that block the editor during operations, FTX leverages Vim's native job API and a custom promise-based async engine to provide non-blocking file tree rendering, real-time Git status updates, and responsive interaction. This article introduces FTX's design philosophy, core features, and the architectural decisions that make it both performant and maintainable—from its Git-inspired cache system to its Go-like concurrency primitives.


The Story Behind FTX

For months, I relied on NERDTree as my file browser. It worked, but something felt heavy. The blocking operations, the occasional lag when navigating large directories—it all added friction to my workflow. So I decided to try Netrw, Vim's built-in file explorer. It was minimal, familiar, and perfectly functional. But after a while, it felt too barebones. I missed having a structured tree view, and more importantly, I desperately missed seeing Git status at a glance.

My workflow is deeply tied to Git. I need to know what's changed, what's staged, what state my branch is in—without running git status in a separate terminal. Even my Bash prompt is heavily configured to show Git info. I wanted that same level of awareness inside my file tree.

One night, while browsing file explorer source code, I stumbled upon ranger.vim. It was strikingly simple—a clean, single-file implementation. That sparked an idea: I could build my own. Something tailored exactly to my needs.

I sketched out ideas, discussed architecture patterns, and made a decision: I was going to build FTX.


What is FTX?

FTX is an asynchronous file tree explorer for Vim 8.0+ and Neovim 0.4+. It's designed around three core principles:

  1. Async by design – All file operations and Git status checks run in the background using Vim's job API. Opening deep directories or refreshing Git status never blocks the editor.

  2. Git-first workflow – Real-time Git status indicators (+ staged, * modified, ? untracked) appear directly in the tree. Branch information, commit tracking (ahead/behind), and stash detection are all visible without leaving Vim.

  3. Zero dependencies – FTX is pure Vimscript with no external dependencies beyond Vim/Neovim itself.

FTX isn't trying to replace project managers or become a Swiss Army knife. It does one thing well: show you your files and exactly what Git thinks about them—quickly, reliably, and without getting in your way.

Project repository: github.com/m-mdy-m/ftx.vim


Core Features

Async Everything

File tree rendering, directory traversal, and Git status parsing all happen asynchronously. FTX uses Vim's +job and +timers features to schedule work without blocking the UI. You can navigate, edit, and work normally while FTX updates in the background.

Git Integration

Every file can display a Git status symbol:

  • + – Staged for commit
  • * – Modified, not staged
  • ? – Untracked by Git
  • - – Deleted
  • ! – Merge conflict
  • – Renamed
  • – Ignored (optional)

The status line shows branch info: [main] ↑2 ↓1 $ tells you you're on main, 2 commits ahead, 1 behind, with an active stash. Press gi for detailed branch information, or gb for Git blame (if enabled).

Multi-File Operations

Mark files with m, then batch open them (mo) or stage them to Git (mg). The marking system makes working with multiple files fast and intuitive.

Flexible Display Modes

FTX works as both a split window (like Netrw) and a project drawer (like NERDTree). Use :FTX . -drawer for fixed-width sidebar mode with smart quit handling and auto-focus restoration.

Customizable

Configure icons, colors, keymaps, Git update intervals, and behavior. FTX provides sensible defaults but gets out of your way if you want something different.

Full feature documentation: doc/README.md

Configuration guide: doc/config.md

Keymaps reference: doc/keymaps.md

Git features: doc/git.md


Architecture Overview

FTX's architecture is built around five interconnected systems: the async engine, cache layer, tree management, Git integration, and rendering pipeline. Each is designed to be modular, testable, and performant.

Async Engine

At the heart of FTX is a custom async engine inspired by JavaScript Promises and Go's concurrency model. Instead of blocking Vim while reading directories or running Git commands, FTX schedules work on a task queue and processes it using timer-based workers.

Key components:

  • Promises (autoload/ftx/async/promise.vim): An ECMAScript-like Promise implementation based on es6-promise. Promises allow chaining async operations with .then() and .catch(), making complex workflows readable and composable.

  • Job API (autoload/ftx/async/job.vim): A unified wrapper around Vim's job_start() and Neovim's jobstart() that returns Promises. This lets FTX run shell commands (like git status) asynchronously and handle their output when ready.

  • Scheduler (autoload/ftx/async/internal/): A Go-inspired goroutine-style scheduler with channels, wait groups, and a worker pool. Tasks are queued and executed by timer-driven workers, scaling up to handle load and shutting down when idle.

This design ensures that no matter how large your repository or how deep your directory tree, FTX remains responsive.

Cache System

FTX's cache is inspired by Git's tree/blob architecture. Instead of naively re-reading directories on every refresh, FTX uses content-based hashing to detect changes and reuse cached results when possible.

How it works:

  • Each file and directory gets a hash based on its modification time and content (for directories, the list of children).
  • Before rebuilding a tree, FTX checks if the hash has changed. If not, it returns the cached result.
  • This dramatically reduces disk I/O and makes navigation feel instant, especially in large projects.

Implementation: autoload/ftx/tree/cache.vim

Tree Management

The tree module (autoload/ftx/tree/tree.vim) handles directory traversal, node expansion, and state tracking. Directories can be lazily loaded—only expanding when the user requests it—which keeps memory usage low and initial render fast.

Node structure:

{
  'path': '/full/path/to/file',
  'name': 'filename',
  'depth': 2,
  'is_dir': 1,
  'is_expanded': 0,
  'children': [...],
  'git_status': '*'
}
Enter fullscreen mode Exit fullscreen mode

Nodes are flattened into a displayable list by tree#flatten(), which recursively collects visible nodes based on expansion state. This flat list is then passed to the renderer.

Git Integration

Git status checking runs asynchronously every few seconds (configurable via g:ftx_git_update_time). FTX spawns a git status --porcelain job, parses the output, and updates an internal cache mapping file paths to status symbols.

Branch tracking (autoload/ftx/git/branch.vim): Parses git status -b to extract branch name, ahead/behind counts, and checks for stashed changes.

Blame support (autoload/ftx/git/blame.vim): If enabled, press gb on a file to see the last 10 commits with author, time, and message in a scrollable popup.

Documentation: doc/git.md

Rendering Pipeline

The renderer (autoload/ftx/renderer/default.vim) transforms tree nodes into display lines with icons, colors, and Git status indicators. It:

  1. Iterates over flattened nodes
  2. Builds display strings (indentation + icons + filename)
  3. Applies syntax highlighting rules
  4. Updates the buffer content in one atomic operation

Syntax groups (FTXDir, FTXGitModified, FTXIconCollapsed, etc.) are dynamically defined based on configuration. Users can override colors and icons by setting g:ftx_colors and g:ftx_icons.

Drawer Mode

Drawer mode (autoload/ftx/internal/drawer/) provides NERDTree-style behavior:

  • Auto-resize: Preserves drawer width across tab switches
  • Auto-restore focus: Returns focus to the previous window when closing other windows
  • Smart quit: Prevents accidentally closing Vim when the drawer is the last window

These behaviors are implemented via autocommands and buffer-local state tracking.


Getting Started

Installation

vim-plug:

Plug 'm-mdy-m/ftx.vim'
Enter fullscreen mode Exit fullscreen mode

Vim 8 packages:

git clone https://github.com/m-mdy-m/ftx.vim ~/.vim/pack/ftx/start/ftx
Enter fullscreen mode Exit fullscreen mode

Neovim packages:

git clone https://github.com/m-mdy-m/ftx.vim ~/.config/nvim/pack/ftx/start/ftx
Enter fullscreen mode Exit fullscreen mode

Basic Usage

Open FTX in current directory:

:FTX .
Enter fullscreen mode Exit fullscreen mode

Toggle FTX:

:FTXToggle
" or press F2
Enter fullscreen mode Exit fullscreen mode

Open as project drawer:

:FTX . -drawer
Enter fullscreen mode Exit fullscreen mode

Auto-open on directory:

vim ~/projects
Enter fullscreen mode Exit fullscreen mode

Common Keymaps

Inside FTX buffer:

Key Action
o, <Enter> Open file / Toggle directory
t Open in tab
s, v Open in split/vsplit
r, R Refresh tree / Git status
I Toggle hidden files
O, C Expand/Collapse all
m, M Toggle mark / Clear marks
mo, mg Open marked / Stage marked
gi, gb Git branch info / Blame
yy, yn Yank path / filename
? Show help
q Close

Full keymap reference: doc/keymaps.md


Configuration

FTX provides extensive configuration options. Here are the essentials:

" Window settings
let g:ftx_width = 30
let g:ftx_position = 'left'  " or 'right'
let g:ftx_show_hidden = 1
let g:ftx_auto_sync = 1      " Sync to current file

" Git settings
let g:ftx_enable_git = 1
let g:ftx_git_update_time = 2000  " Update interval (ms)
let g:ftx_git_blame = 1           " Enable blame feature

" Icons and colors
let g:ftx_enable_icons = 1
let g:ftx_icon_expanded = '▾'
let g:ftx_icon_collapsed = '▸'

" Custom file type icons
let g:ftx_icons = {
      \ 'vim': '',
      \ 'md': '',
      \ 'js': '',
      \ }

" Custom colors
let g:ftx_colors = {
      \ 'vim': 'guifg=#019733 ctermfg=35',
      \ 'py': 'guifg=#3572A5 ctermfg=67',
      \ }
Enter fullscreen mode Exit fullscreen mode

Complete configuration guide: doc/config.md


Under the Hood

For those curious about the implementation details, here's a glimpse into the technical foundation of FTX.

Promise-Based Async

FTX's Promise implementation follows the ECMAScript Promise spec, based on the es6-promise library. Promises wrap asynchronous operations and allow chaining:

ftx#async#fs#readdir('/path')
  .then({entries -> process(entries)})
  .then({result -> cache(result)})
  .catch({err -> handle_error(err)})
Enter fullscreen mode Exit fullscreen mode

This makes complex async workflows (read directory → filter → sort → render) composable and readable.

Goroutine-Style Concurrency

The internal scheduler (autoload/ftx/async/internal/) uses Go-inspired concurrency primitives:

  • Channels (channels.vim): Non-blocking message passing between tasks
  • Wait groups (waiter.vim): Synchronize multiple async operations
  • Worker pool (worker.vim): Dynamically scales workers to process queued tasks

Workers run on Vim timers, executing one task per tick. When the queue is empty, workers shut down. This keeps overhead minimal while providing concurrency when needed.

Git-Like Cache

Inspired by Git's object storage model, FTX computes content hashes for files and directories. A directory's hash depends on its children's names, so moving files invalidates the cache correctly.

Cache keys are path:hash pairs. When checking the cache, FTX recomputes the hash and compares—only re-reading if content has changed. This approach is both efficient and correct.

Modular Design

FTX is split into logical modules:

  • autoload/ftx/async/ – Async engine (promises, jobs, scheduler)
  • autoload/ftx/tree/ – Tree management (build, cache, filter, nodes)
  • autoload/ftx/git/ – Git integration (status, branch, blame)
  • autoload/ftx/renderer/ – Display rendering and syntax
  • autoload/ftx/internal/ – Window/buffer management, drawer mode
  • autoload/ftx/mapping/ – Keymap handlers
  • autoload/ftx/helpers/ – Utilities (path, platform, logging)

Each module has a clear responsibility and minimal coupling. This makes FTX maintainable and testable.

Source code: github.com/m-mdy-m/ftx.vim


Inspiration and Credits

FTX stands on the shoulders of great explorers that came before:

  • netrw: Vim's built-in file browser, the gold standard for simplicity
  • NERDTree: The classic tree explorer, beloved by many
  • fern.vim: Modern async architecture and clean design
  • ranger.vim: Elegant single-file simplicity

FTX aims to carve out its own niche: a fast, Git-aware, no-nonsense file tree that gets out of your way.


Community and Feedback

FTX is designed for real workflows, but it can only improve with feedback from the people who use it.

Repository: github.com/m-mdy-m/ftx.vim

Documentation: :help ftx or doc/ftx.txt

Issues & PRs: github.com/m-mdy-m/ftx.vim/issues

Try it, break it, and let me know what you think.


What's your take on file explorers in Vim?

Top comments (0)