How the Marks System Tracks AI-Assisted Edits in ThePrimeagen/99

The marks system uses Neovim extmarks to create persistent top and bottom markers around visual selections, validating their positions before inserting AI-generated responses and automatically cleaning them up after the request completes.

ThePrimeagen's 99 repository is a Neovim plugin that streamlines AI-assisted coding workflows. At its core, the marks system for tracking AI-assisted edits ensures that when you send a visual selection to an AI model, the plugin remembers exactly where that text lives—even if you edit other parts of the buffer while waiting for the response.

Core Architecture of the Marks System

The system is built on three interconnected components that wrap Neovim's native extmarks API.

The Mark Class (ops/marks.lua)

The lua/99/ops/marks.lua file defines the _99.Mark class, which abstracts extmark creation and management. Key methods include:

  • Mark.mark_above_range(range) – Places a mark one line above the start of the visual selection (lines 15-38)
  • Mark.mark_point(buf, point) – Creates a mark at an exact coordinate (lines 56-68)
  • Mark.mark_range(range) – Returns both start and end marks for a range (lines 40-47)
  • Mark:is_valid() – Verifies the underlying extmark still exists (lines 49-54)
  • Mark:delete() – Removes the extmark (lines 49-52)

Request Context Management (request-context.lua)

The lua/99/request-context.lua file maintains per-request state through the RequestContext class. It stores marks in a dedicated marks table (line 13) and provides RequestContext:clear_marks() (lines 49-53), which iterates through stored marks and calls :delete() on each to prevent ghost extmarks from persisting after the AI request completes.

Visual Selection Workflow (ops/over-range.lua)

The lua/99/ops/over-range.lua file orchestrates the actual AI-assisted editing workflow. It bridges the Mark class and RequestContext by:

  1. Creating the top and bottom marks at lines 22-26
  2. Storing them on the context's marks table
  3. Registering cleanup logic at lines 43-48 that calls context:clear_marks() when the request finishes, is cancelled, or errors

How Marks Track AI-Assisted Edits Step by Step

When you trigger an AI edit over a visual selection, the marks system executes the following workflow:

  1. Mark Creation: ops/over-range.lua creates two extmarks:

    • A top mark positioned one line above your selection via Mark.mark_above_range(range)
    • A bottom mark at the exact end of your selection via Mark.mark_point(range.buffer, range.end_)
  2. Persistence: These marks survive buffer edits because they are native Neovim extmarks. If you type elsewhere in the file while the AI generates a response, the marks automatically adjust their positions to stay anchored to the original text.

  3. Validation: Before inserting the AI response, the plugin checks top_mark:is_valid() and bottom_mark:is_valid(). If either mark is invalid (meaning the original text was deleted), the plugin aborts with a fatal error to prevent corruption.

  4. Range Reconstruction: Valid marks are converted back into a Range object via Range.from_marks(top_mark, bottom_mark), which determines exactly where to insert the AI-generated lines.

  5. Cleanup: Regardless of success or failure, RequestContext:clear_marks() deletes both extmarks, ensuring no tracking artifacts remain in the buffer.

Practical Code Examples

Creating a Simple Point Mark

local Mark = require("99.ops.marks")
local geo = require("99.geo")
local Point = geo.Point

local buf = vim.api.nvim_get_current_buf()
local point = Point:from_1_based(3, 5)  -- line 3, column 5 (1-based)
local mark = Mark.mark_point(buf, point)

-- Retrieve position
print(vim.inspect(Point.from_mark(mark)))  -- → {row = 3, col = 5}

-- Clean up
mark:delete()

Implementation reference: Mark.mark_point (lines 56-68) and Mark:delete (lines 49-52) in lua/99/ops/marks.lua.

Marking a Full Range

local Mark = require("99.ops.marks")
local geo = require("99.geo")
local Point = geo.Point
local Range = geo.Range

local buf = vim.api.nvim_get_current_buf()
local start = Point:from_1_based(2, 3)
local finish = Point:from_1_based(4, 10)
local range = Range:new(buf, start, finish)

local mark_start, mark_end = Mark.mark_range(range)

assert(Point.from_mark(mark_start) == start)
assert(Point.from_mark(mark_end) == finish)

-- Clean-up
mark_start:delete()
mark_end:delete()

Implementation reference: Mark.mark_range (lines 40-47) in lua/99/ops/marks.lua.

Using Marks in Visual Selection Requests

local over_range = require("99.ops.over-range")
local RequestContext = require("99.request-context")

local ctx = RequestContext.from_current_buffer(_99_state, 1)  -- state & xid
local visual_range = geo.Range.from_visual_selection()

over_range(ctx, visual_range)  -- creates top/bottom marks, runs AI request,
                               -- and automatically clears marks when done

Key lines: Mark creation (lines 22-26) and cleanup registration (lines 43-48) in lua/99/ops/over-range.lua.

Summary

  • The marks system for tracking AI-assisted edits wraps Neovim extmarks to create persistent anchors around visual selections.
  • Two marks are created for each request: a top mark above the selection and a bottom mark at the exact end point.
  • Marks automatically adjust to buffer edits but are validated with is_valid() before AI responses are inserted.
  • The RequestContext class manages mark lifecycle through clear_marks(), ensuring no extmarks leak after requests complete.
  • All mark operations are implemented in lua/99/ops/marks.lua and consumed by lua/99/ops/over-range.lua and lua/99/request-context.lua.

Frequently Asked Questions

How do marks survive if I edit other parts of the buffer while waiting for the AI?

Neovim's native extmarks automatically track text movements. When you create a mark via Mark.mark_point or Mark.mark_above_range, the underlying extmark binds to the text at that position. If you insert or delete lines above the marked region, Neovim shifts the extmark's position accordingly, ensuring the AI response inserts at the correct location when it arrives.

What happens if I delete the text that was originally selected?

Before inserting any AI-generated content, the plugin validates both marks using Mark:is_valid() (lines 49-54 in lua/99/ops/marks.lua). If you deleted the original visual selection, the underlying extmarks become invalid, is_valid() returns false, and the plugin aborts the operation with a fatal error to prevent corrupting your buffer with misplaced text.

How are marks cleaned up after the AI request finishes?

Every request creates a RequestContext that stores marks in a dedicated table (line 13 in lua/99/request-context.lua). When the request completes, errors out, or is cancelled, the over_range function triggers context:clear_marks() (lines 49-53), which iterates through all stored marks and calls mark:delete() on each. This ensures no "ghost" extmarks remain in the buffer after the AI interaction ends.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →