# How 99's Search Operation Differs from Visual Mode: A Technical Deep Dive

> Learn how 99's search operation differs from visual mode. Discover read-only project lookups vs AI-powered buffer modifications. Explore ThePrimeagen/99 technical details.

- Repository: [ThePrimeagen/99](https://github.com/theprimeagen/99)
- Tags: deep-dive
- Published: 2026-02-16

---

**99's search operation performs read-only project-wide lookups that populate the quickfix list, while visual mode performs write-back buffer modifications that replace selected text with AI-generated code.**

ThePrimeagen's `99` Neovim plugin provides two distinct AI-powered operations—search and visual mode—that handle LLM interactions differently. Understanding how 99's search operation differs from visual mode operationally is crucial for developers who want to leverage semantic code search versus AI-assisted code generation effectively.

## Core Operational Differences

### Purpose and Intent

**Search** (`:lua require("99").search()`) is designed for project-wide discovery. It asks the LLM to locate code elsewhere in the codebase using semantic or grep-like queries, returning file locations without modifying the current buffer.

**Visual** (`:lua require("99").visual()`) is designed for inline transformation. It captures the current visual selection, sends it to the LLM with context, and replaces the selected text with the AI-generated response.

### Prompt Engineering

Both operations use different system prompts defined in the prompts module:

- **Search** calls `context._99.prompts.prompts.semantic_search()`, which instructs the model to return results in a strict `file:line:col, notes` format.
- **Visual** calls `context._99.prompts.prompts.visual_selection(range)`, which includes the selected range data and instructs the model to generate replacement text.

### Result Handling and Buffer Interaction

The divergence in operational logic becomes clear when examining how each mode handles the LLM response:

**Search** ([`lua/99/ops/search.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/search.lua)):
- Parses each response line using `parse_line` to extract filename, line number, column, and description
- Builds a quickfix list using `vim.fn.setqflist`
- Opens the quickfix window with `vim.cmd("copen")`
- Performs **zero buffer modifications**—it is strictly read-only

**Visual** ([`lua/99/ops/over-range.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/over-range.lua)):
- Creates persistent marks using `Mark.mark_above_range` and `Mark.mark_point` to survive the asynchronous operation
- Reconstructs the range using `Range.from_marks` after the LLM responds
- Calls `new_range:replace_text()` to **overwrite** the selected text with the AI output
- Modifies the current buffer directly

### User Feedback Mechanisms

**Search** provides minimal feedback—a simple notification on success or failure with no live status indication.

**Visual** implements a sophisticated streaming UI:
- Creates two `RequestStatus` objects (`top_status` and `bottom_status`)
- The observer handles `on_stdout` to update the status bar in real-time as the LLM streams output
- Displays "Implementing" status while processing

## Implementation Deep Dive

### Search Operation Flow

Located in [`lua/99/ops/search.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/search.lua), the search operation follows this sequence:

1. **Prompt Construction**: `make_prompt` merges the `semantic_search` system prompt with any `additional_prompt` options
2. **Request Initialization**: `Request.new(context)` creates the async job with a `clean_up` function that only cancels the LLM request
3. **Observer Pattern**: Uses a simple callback `function(status, response)` that only processes the final status and response
4. **Result Parsing**: `create_search_locations` iterates through response lines, parsing the `file:line:col, notes` format
5. **Quickfix Population**: Calls `vim.fn.setqflist` with the parsed locations and opens the window

### Visual Mode Flow

Located in [`lua/99/ops/over-range.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/over-range.lua), visual mode requires state management across asynchronous boundaries:

1. **Range Capture**: Before sending the request, `Mark.mark_above_range` and `Mark.mark_point` create durable marks at the visual selection boundaries
2. **Prompt Building**: `make_prompt` receives the `visual_selection` prompt with the current range data included
3. **Status Setup**: Initializes two `RequestStatus` instances to display live progress
4. **Streaming Observer**: Uses a structured observer with `on_complete` and `on_stdout` callbacks:
   - `on_stdout` updates the status displays in real-time
   - `on_complete` validates marks still exist, reconstructs the `geo.Range` via `Range.from_marks`, and executes `replace_text`
5. **Comprehensive Cleanup**: The `clean_up` function stops status displays, clears the marks, and cancels the request, ensuring the editor returns to a clean state if aborted

## Practical Usage Examples

### Search (Quickfix List)

To perform a semantic search across your project:

```lua
require("99").search{
  additional_prompt = "Find all usages of `my_func`"
}

```

Behind the scenes in [`lua/99/ops/search.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/search.lua), this builds the prompt, sends the request, and populates the quickfix list with locations in `file:line:col` format.

### Visual (Replace Selected Text)

Map a keybinding to invoke visual mode replacement:

```vim
" In visual mode, press <leader>i to invoke 99's visual AI replace
vnoremap <leader>i :lua require("99").visual()<CR>

```

When executed, [`lua/99/ops/over-range.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/over-range.lua) captures the visual marks, streams AI output to a temporary status bar, and replaces the selected lines with the model's response.

## Summary

- **Search** is a read-only lookup operation that populates Neovim's quickfix list with code locations, using [`lua/99/ops/search.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/search.lua) to parse `file:line:col` formatted responses.
- **Visual** is a write-back transformation that replaces selected text with AI-generated code, using [`lua/99/ops/over-range.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/over-range.lua) to manage marks, stream status updates, and modify buffer content.
- **Search** uses a simple final-response observer, while **visual** implements a streaming observer with `on_stdout` for real-time feedback.
- **Cleanup** differs significantly: search only cancels the request, while visual stops UI components and clears editor marks to prevent state corruption.

## Frequently Asked Questions

### Does search mode modify my current buffer?

No. Search mode is strictly read-only. According to the implementation in [`lua/99/ops/search.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/search.lua), it parses the LLM response into quickfix entries using `parse_line` and displays them via `vim.fn.setqflist` and `vim.cmd("copen")`. Your buffer content remains unchanged.

### Why does visual mode need to create marks?

Visual mode creates durable marks using `Mark.mark_above_range` and `Mark.mark_point` because the operation is asynchronous. The user could move the cursor or change buffers while waiting for the LLM response. As implemented in [`lua/99/ops/over-range.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/over-range.lua), these marks survive the request duration, allowing the code to reconstruct the exact `geo.Range` via `Range.from_marks` and execute `replace_text` accurately.

### Can I see live progress during a search operation?

No. The search operation in [`lua/99/ops/search.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/search.lua) uses a simple observer callback that only handles the final `status` and `response`. In contrast, visual mode implements a streaming observer with `on_stdout` callbacks that update two `RequestStatus` objects in real-time, showing "Implementing" status as the LLM generates output.

### What happens if I cancel a visual mode request?

If you cancel a visual mode request, the `clean_up` function in [`lua/99/ops/over-range.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/over-range.lua) performs three critical actions: it stops the two `RequestStatus` UI displays, clears the durable marks created at the start of the operation, and cancels the underlying LLM request. This ensures the editor returns to a clean state without orphaned UI elements or stale marks.