# How the Range Operation Applies AI Suggestions to Code in 99

> Discover how the range operation in 99 leverages AI to suggest code improvements. Send code regions to an LLM for automatic replacement with intelligent suggestions.

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

---

**The range operation (over-range) in 99 selects a region of code in Neovim, sends it to an LLM with surrounding context and a user prompt, and automatically replaces the selected text with the AI-generated suggestion.**

The **99** plugin by ThePrimeagen is an AI coding assistant for Neovim that enables visual-AI workflows. At its core lies the **range operation** (`99.ops.over-range`), which bridges the gap between visual selections and LLM-powered code transformations. This operation captures your highlighted code, preserves positional anchors using Neovim extmarks, and orchestrates the entire request lifecycle from prompt construction to text replacement.

## High-Level Flow of the Over-Range Operation

The range operation executes through a coordinated pipeline across multiple modules:

1. **Prepare marks** – Creates two extmarks: one above the selection start (`Mark.mark_above_range`) and one at the selection end (`Mark.mark_point`) to survive buffer modifications.
2. **Build the request** – Instantiates a `Request` object with the current `RequestContext`.
3. **Assemble the prompt** – `make_prompt` composes the system prompt (`prompts.visual_selection`) and injects the user-provided `additional_prompt`, parsing any completion tokens.
4. **Show AI status** – Attaches `RequestStatus` spinners to the marks, displaying "Implementing …" virtual text during streaming.
5. **Send the request** – `Request:start` writes the prompt to a temporary file and invokes the provider (`OpenCodeProvider`) as a background process.
6. **Handle completion** – On finish, the callback verifies mark validity, builds a new `Range` from the marks, splits the LLM response into lines, inserts a blank line to preserve line numbering, and calls `Range:replace_text` to overwrite the original selection.
7. **Clean-up** – Removes marks, stops spinners, kills the background process, and deletes temporary files.

## Deep Dive into the Implementation

### Entry Point and Mark Preparation

The operation begins in [`lua/99/ops/over-range.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/over-range.lua) with the `over_range` function:

```lua
local function over_range(context, range, opts)
  opts = opts or {}
  local logger = context.logger:set_area("visual")
  local request = Request.new(context)

  -- Create marks surrounding the visual selection
  local top_mark    = Mark.mark_above_range(range)
  local bottom_mark = Mark.mark_point(range.buffer, range.end_)
  context.marks.top_mark    = top_mark
  context.marks.bottom_mark = bottom_mark
  …

```

The function receives the `RequestContext` (containing logger, buffer, cwd), the `Range` derived from the visual selection, and optional user options. The marks are created via [`lua/99/ops/marks.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/marks.lua) to ensure positional stability during the asynchronous LLM call.

### Prompt Construction

The prompt assembly happens in [`lua/99/ops/make-prompt.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/make-prompt.lua):

```lua
local system_cmd = context._99.prompts.prompts.visual_selection(range)
local prompt, refs = make_prompt(context, system_cmd, opts)
request:add_prompt_content(prompt)
context:add_references(refs)

```

The `visual_selection(range)` function generates a system prompt containing the file path, language, and the text inside the selected range. Then `make_prompt` merges this with the user-supplied `additional_prompt` (e.g., "Refactor this to use async/await"), parses any `@completion` directives for context references, and returns the final prompt plus reference snippets.

### Real-Time Feedback with Status Spinners

While the LLM processes the request, the user sees live feedback via [`lua/99/ops/request_status.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/request_status.lua):

```lua
local top_status = RequestStatus.new(250, context._99.ai_stdout_rows or 1,
                                    "Implementing", top_mark)
local bottom_status = RequestStatus.new(250, 1, "Implementing", bottom_mark)
local clean_up = make_clean_up(function()
  top_status:stop()
  bottom_status:stop()
  context:clear_marks()
  request:cancel()
end)

```

Two `RequestStatus` objects attach to the extmarks, displaying animated spinners (e.g., `⠋⠙⠹⠸⠼⠴`) with "Implementing" labels. The `clean_up` closure ensures these stop regardless of success or failure.

### Applying the AI Suggestion

When the provider finishes, the callback in [`over-range.lua`](https://github.com/ThePrimeagen/99/blob/main/over-range.lua) handles the replacement using [`lua/99/geo.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/geo.lua):

```lua
top_status:start()
bottom_status:start()
request:start(make_observer(clean_up, {
  on_complete = function(status, response)
    if status == "success" then
      -- Apply AI suggestion
      local new_range = Range.from_marks(top_mark, bottom_mark)
      local lines = vim.split(response, "\n")
      table.insert(lines, 1, "")   -- keep original line number
      new_range:replace_text(lines)
    end
  end,
  on_stdout = function(line)   -- stream AI output to the spinner
    if display_ai_status then top_status:push(line) end
  end,
}))

```

The `Range.from_marks` reconstructs the selection boundaries from the extmarks. The response is split into lines, prepended with an empty line to compensate for `mark_above_range` placing the top mark one line above the selection, and finally `Range:replace_text` overwrites the buffer content.

### Resource Cleanup

The [`lua/99/ops/clean-up.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/clean-up.lua) module provides `make_clean_up`, which composes multiple cleanup functions into a single closure. This guarantees that extmarks are deleted, spinners halt, background LLM processes terminate, and temporary prompt files are removed—even if the user cancels the operation or an error occurs.

## Practical Usage Example

To use the range operation in your workflow:

```vim
" 1. Visually select the code you want the AI to rewrite
"    (e.g., a function block)

" 2. Call the over-range operation via the plugin's command:
:lua require("99").ops.over_range(
      require("99").context_from_current_buffer(),
      require("99").geo.Range.from_visual_selection(),
      { additional_prompt = "Refactor this to use async/await." }
    )

```

**What you’ll see:**

- A virtual-text spinner appears just above your selection displaying "Implementing …"
- The LLM streams its output line-by-line into the status indicator
- Upon completion, your selected code block is silently replaced with the AI-generated suggestion
- All marks and temporary files are automatically cleaned up

## Summary

- **The range operation** (`over-range`) is the core mechanism in 99 for applying AI suggestions to specific code regions in Neovim.
- **Extmarks** (`mark_above_range` and `mark_point`) serve as durable anchors that survive asynchronous LLM calls, ensuring precise text replacement.
- **The prompt pipeline** (`make_prompt`, `visual_selection`) merges system context, selected code, and user instructions into a structured LLM request.
- **Real-time feedback** via `RequestStatus` spinners provides visual confirmation during streaming generation.
- **Automatic cleanup** guarantees resource disposal regardless of success or cancellation, preventing extmark leakage and orphaned processes.

## Frequently Asked Questions

### How does the range operation handle line number changes during AI generation?

The operation inserts a blank line at the beginning of the LLM response (`table.insert(lines, 1, "")`) to compensate for the top extmark being placed one line above the selection via `mark_above_range`. This ensures that when `Range:replace_text` executes, the replacement aligns perfectly with the original visual selection boundaries.

### What happens if I cancel the AI request mid-generation?

The `make_clean_up` closure in [`lua/99/ops/clean-up.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/clean-up.lua) ensures deterministic teardown. It stops the `RequestStatus` spinners, clears the extmarks via `context:clear_marks()`, and cancels the running request with `request:cancel()`. This prevents UI artifacts and kills the background LLM process even if the operation is interrupted.

### Can I customize the system prompt used for visual selections?

Yes. The system prompt is generated by `context._99.prompts.prompts.visual_selection(range)` in [`lua/99/ops/make-prompt.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/ops/make-prompt.lua). You can modify the prompt templates in your 99 configuration to change how file paths, language detection, and surrounding context are presented to the LLM before your `additional_prompt` is injected.

### Which LLM providers work with the range operation?

By default, 99 uses the `OpenCodeProvider` defined in [`lua/99/providers.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/providers.lua), which executes the LLM as a background process. The `Request` abstraction in [`lua/99/request/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/request/init.lua) is provider-agnostic, allowing you to configure alternative providers that implement the same interface for streaming responses and process management.