How Window Capture Functions Work in ThePrimeagen/99 for Prompt Input
ThePrimeagen/99 implements prompt input through centered floating windows that use Neovim autocmds for BufWriteCmd and WinClosed to capture or cancel user text, delivering results via callbacks when users press <CR> or q.
ThePrimeagen/99 is a Neovim plugin designed for AI-assisted development workflows that require dynamic user interaction. When the plugin needs to collect free-form text input—such as search queries, tutorial names, or visual mode ranges—it relies on specialized window capture functions that create temporary, focused interfaces. These functions manage the complete lifecycle from window geometry to input validation, all implemented in the lua/99/window/init.lua module.
Overview of the Window Capture Architecture
The window capture system follows a six-stage pipeline that transforms a simple callback into an interactive floating interface. Each stage handles specific responsibilities, from calculating screen geometry to processing lifecycle events.
The flow begins with create_centered_window calculating editor dimensions to position the window in the exact center of the screen. Next, create_floating_window instantiates a non-file buffer with acwrite type and displays it with a formatted title "99 ". The system then configures buffer options through set_defaul_win_options, disabling swapfiles and setting the filetype to 99.
During the active phase, highlight_rules_found establishes autocmds on InsertLeave and TextChanged events to scan for rule keywords and highlight them with the Search group. Finally, lifecycle autocmds for BufWriteCmd and WinClosed handle acceptance and cancellation, while a q keymap provides immediate exit functionality.
Core Window Creation and Configuration
Centered Window Geometry
The create_centered_window function in lua/99/window/init.lua (line 105) calculates the editor's current dimensions using vim.api.nvim_list_uis() and constructs a configuration table that positions the floating window at the visual center. This ensures the prompt appears exactly where the user expects it, regardless of screen size or split layout.
Floating Buffer Setup
The create_floating_window function (line 140) creates a scratch buffer using vim.api.nvim_create_buf(false, true) with acwrite type, meaning it acts like a file buffer that triggers BufWriteCmd events but never writes to disk. The window opens with a formatted border title displaying "99 " to indicate the prompt's purpose.
Buffer Options and Filetype
The set_defaul_win_options function (line 70) configures the prompt buffer with security and usability settings. It sets the filetype to 99, disables the swapfile to prevent disk I/O for temporary input, and establishes other window-local options that isolate the prompt from the user's regular editing environment.
Input Handling and Lifecycle Management
Accepting Input with BufWriteCmd
When the user completes their input and presses <CR> or executes :w, the BufWriteCmd autocmd (lines 82-92 in lua/99/window/init.lua) triggers the acceptance callback. This handler collects all buffer lines using vim.api.nvim_buf_get_lines, concatenates them with newline characters, clears active popups via M.clear_active_popups, and invokes the supplied callback with ok = true and the resulting text.
Cancelling with WinClosed and Keymaps
The system provides multiple escape routes for users who decide not to proceed. The WinClosed autocmd (lines 106-115) monitors the specific window ID and triggers when the floating window closes for any reason, calling opts.cb(false, "") to signal cancellation. Additionally, a buffer-local normal mode mapping binds q to immediately clear popups and abort with ok = false, providing intuitive vim-style exit behavior.
Real-Time Rule Highlighting
The highlight_rules_found function establishes intelligent text analysis during user input. It creates autocmds for InsertLeave and TextChanged events that scan buffer content using Agents.by_name to identify rule keywords. When matches are found, the function applies the Search highlight group to those terms, giving users immediate visual feedback when they reference available rules or commands within their input text.
Integration with the 99 Plugin
The capture_prompt Wrapper
While lua/99/window/init.lua contains the low-level window logic, most operations interact with the higher-level capture_prompt function defined in lua/99/init.lua (lines 286-302). This wrapper simplifies the API by accepting a callback, prompt name, context object, and options, then forwarding these to Window.capture_input with appropriate defaults. The wrapper also handles the on_load hook that triggers Extensions.setup_buffer to configure LSP or Treesitter support for the prompt buffer.
Operation Integration
The window capture system serves as the input foundation for various 99 operations. For example, lua/99/ops/search.lua invokes capture_prompt when building search queries, while tutorial selection and visual mode range specification use the same underlying mechanism. This unified approach ensures consistent UX across all interactive features, with each operation supplying its own callback to process the captured text according to specific business logic.
Practical Usage Examples
Direct Window Capture Usage
For plugin developers extending 99, the Window.capture_input API provides direct access to the prompt system:
local win = require("99.window")
win.capture_input("Custom Query", {
cb = function(ok, text)
if ok then
vim.notify("Received: " .. text)
else
vim.notify("Cancelled", vim.log.levels.WARN)
end
end,
rules = require("99.agents").default_rules,
})
This creates a centered floating window titled "99 Custom Query" with rule highlighting enabled.
High-Level Operation Example
When implementing new 99 operations, use the capture_prompt wrapper for consistency:
local _99 = require("99")
local function my_operation(context)
_99.capture_prompt(function(ok, input)
if ok then
-- Process the input
print("Processing:", input)
end
end, "My Operation", context, {
additional_prompt = nil,
})
end
The wrapper automatically handles window cleanup and context preservation.
Rule Highlighting Configuration
To enable real-time keyword highlighting in custom prompts:
local win = require("99.window")
win.capture_input("Task Entry", {
cb = function(ok, text) end,
rules = {
custom = {
{ name = "URGENT" },
{ name = "REVIEW" },
{ name = "NOTE" },
}
},
})
Occurrences of "URGENT", "REVIEW", or "NOTE" will highlight automatically as the user types using the Search highlight group.
Summary
- ThePrimeagen/99 implements prompt input through a specialized window capture system located in
lua/99/window/init.luathat creates centered floating buffers withacwritetype. - The
capture_inputfunction orchestrates window geometry, buffer configuration, and lifecycle management through Neovim'sBufWriteCmdandWinClosedautocmds. - User input is accepted via
<CR>or:wcommands, which trigger callbacks withok = trueand concatenated buffer text, while pressingqor closing the window signals cancellation withok = false. - Real-time rule highlighting scans buffer content during
InsertLeaveandTextChangedevents, applying theSearchhighlight group to matched keywords viaAgents.by_name. - The
capture_promptwrapper inlua/99/init.luaprovides a high-level API that integrates the window system with 99's operation framework, supporting optionalon_loadhooks for LSP and Treesitter configuration.
Frequently Asked Questions
How does ThePrimeagen/99 handle user cancellation in window capture functions?
When a user decides to abort a prompt, the system provides multiple exit paths that all invoke the callback with ok = false. The WinClosed autocmd monitors the floating window ID and triggers if the window closes for any reason, while a buffer-local normal mode mapping binds q to immediately call M.clear_active_popups() and abort. Both mechanisms ensure the calling operation receives an empty string and a false status, allowing graceful handling of cancelled inputs.
What is the difference between capture_input and capture_prompt in the 99 plugin?
capture_input is the low-level API defined in lua/99/window/init.lua that directly manages window creation, buffer configuration with acwrite type, and autocmd registration. It accepts a name string and an options table containing the callback, rules, and optional on_load hook. In contrast, capture_prompt is a higher-level wrapper in lua/99/init.lua (lines 286-302) that simplifies the interface for 99 operations by accepting context objects and automatically wiring the callback to the plugin's request pipeline. Most operations use capture_prompt, while extensions and custom integrations may call capture_input directly.
How does real-time rule highlighting work in the prompt window?
The highlight_rules_found function establishes intelligent text analysis during user input by creating autocmds for InsertLeave and TextChanged events. When these events fire, the system scans buffer content using Agents.by_name to identify rule keywords. When matches are found, the function applies the Search highlight group to those terms, giving users immediate visual feedback when they reference available rules or commands within their input text. This highlighting occurs entirely within the floating buffer and does not modify the underlying text.
Can I customize the window appearance or behavior when using capture_input?
While the core geometry and lifecycle management are handled internally to ensure consistency, you can customize several aspects through the options table passed to capture_input. The rules parameter allows you to enable and configure real-time keyword highlighting by passing a table of rule definitions that Agents.by_name will recognize. Additionally, the on_load callback lets you execute custom setup code after the window appears, such as configuring LSP support or Treesitter highlighting for the prompt buffer via Extensions.setup_buffer. However, fundamental window properties like the centered positioning and acwrite buffer type are fixed to ensure reliable behavior across the plugin.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →