🔒 TouchQuill is in closed testing.Want to try it? Get in touch →
Lens

History of a function, not just files

A plain blame answers "who changed line 40 of file X". Lens answers the question you actually have: what happened to this particular function, even when the file was renamed along the way and the code moved to another module. That includes Unreal Blueprints, not just code. Below, in detail: what it tracks, in which languages, how it matches and where its limits are. No promises beyond what ships.

What you get

One question, a full answer

You ask about a function by name. You get its whole life story, stitched across every rename and every move between files. Purely cosmetic changes (formatting, comments, variable names) don't clutter the result.

tq lens blame --symbol calculate_damage

CREATED        ch-6f9  rev:1   chris    "first version"
MODIFIED       ch-a12  rev:3   marta    "damage buff"
MOVED+RENAMED  ch-c73  rev:5   chris    (from compute_damage in Weapon.rs)  [~86%]

The number in brackets, for example [~86%], is the match confidence. When the name and code match exactly, it's 100%. When a function was renamed and rewritten at once, Lens shows how sure it is that it's still the same thing.

Scope

What it tracks, precisely

Lens recognizes and tracks code symbols, that is, the named elements of a program. Eight kinds specifically:

Functions and methods

Free functions and methods inside classes. A method carries a qualified name, for example Enemy::TakeDamage, so two different methods with the same name don't get confused.

Types

Classes, structs, enums, interfaces and traits (in Rust). Tracked the same way as functions, across renames and moves.

Modules

Units that group code (module, namespace, package), as far as the language expresses them.

Blueprint graphs

Unreal Blueprints (.uasset): EventGraph, functions, macros and the ConstructionScript. "Who changed Interact in BP_Door?" works without a single line of code.

What Lens does not track: variables, class fields, constants or macros. That's deliberate. Lens works at the level of functions and types, because those are what have a meaningful "history" worth following.
Languages

A real parser where it can

For most languages Lens uses a full syntax parser (tree-sitter), so it understands the structure of the code rather than guessing from text. A few languages without a ready parser are handled by a simpler fallback.

MethodLanguagesAccuracy
Full parser (tree-sitter)Rust, C, C++, C#, Python, JavaScript, TypeScript, Go, Java, Luahigh
UE Blueprints (native).uasset / .umap files — Blueprint graphs: EventGraph, functions, macros, ConstructionScriptgraphs
Fallback extractor (heuristic)Verse (UEFN), GDScript, ActionScript, HLSL / GLSL / USF / USH shadersapproximate

The fallback extractor reads code line by line and recognizes declarations from indentation and keywords. It works, but it's sensitive to unusual formatting, one-line blocks or mixing tabs with spaces. File extensions can also be mapped by hand in a .tqlens.toml file (for example, treat .inc as C++).

Resilience

What survives a refactor

The heart of Lens: it recognizes that a symbol is still the same symbol, even after sizeable changes. It detects:

SituationWhat Lens does
Renaming a file, or moving a function to a different fileKeeps tracking it, regardless of the path.
Renaming the function itselfRecognizes it by code structure, with a confidence score.
Renaming and rewriting the body at onceMatches fuzzily, shows the confidence (for example [~72%]).
Swapping the names of two functionsResolves it by structure, not by name.
Extracting part into a new function, or inlining it backDetects it as a split or a merge.
Copying a functionMarks it as a copy of the still-living original.

Compares structure, not text

Lens reduces code to a normalized form: variable names become anonymous placeholders, formatting and comments are dropped. So renaming variables inside a function doesn't lose its identity. It even holds up against a cascade of renamed called functions.

Won't force a guess

When two functions match almost equally well, Lens doesn't quietly pick one. It flags the result as ambiguous and shows the candidates. You resolve it yourself with tq lens link or one click in Studio, and your decision beats the heuristic.

Stitches moves across commits

When a function disappears in one commit and reappears in another within 7 days (the window is configurable), Lens glues it into a single move instead of showing a separate "deleted" and "created".

A real example

A function that survived a refactor

A damage method in an Unreal project: it starts in one file, gets moved to a separate module along the way, and is renamed. A plain blame would lose the trail at each of those steps. Lens glues it into a single life story:

tq lens trace --symbol ApplyDamage

CREATED   rev:1   Source/Combat/Enemy.cpp          TakeDamage
MODIFIED  rev:4   Source/Combat/Enemy.cpp          (damage balancing)
MOVED     rev:7   Source/Combat/DamageSystem.cpp   (from Enemy.cpp)            [~100%]
RENAMED   rev:9   Source/Combat/DamageSystem.cpp   TakeDamage → ApplyDamage    [~91%]

The move between modules is recognized by an exact fingerprint (100%), while a rename together with a small body edit gets a confidence score (91%). When you later extract part of that method into its own function, Lens shows it as a split:

tq lens blame --symbol UpdateCooldowns

CREATED   rev:12  Source/Combat/DamageSystem.cpp   (extracted from ApplyDamage)  [~78%]
Unreal C++. Lens treats Unreal code as first-class. It tracks your classes and methods across renames and moves between modules, including code wrapped in Unreal's macros (UCLASS, UFUNCTION, UPROPERTY, GENERATED_BODY). A symbol's history lives where the logic does, in your methods, no matter which file or module you move them to.
Commands

How to use it

CommandWhat it does
tq lens blame --symbol <name>History of a symbol. Takes the full name or just the tail (TakeDamage finds Enemy::TakeDamage).
tq lens trace --symbol <name>The full life story across every name the symbol carried along the way.
tq lens symbols <file>List of symbols in a file at the current revision.
tq lens link <newer> <older>Manually stitch two histories when the heuristic hesitated. unlink undoes it.
tq lens reindex / indexRebuild the index, or pull in pending revisions.

Every command accepts --json, so Lens plugs easily into scripts and tools.

In the editor

Lens in VS Code

VS Code extension. Above every function and class it shows a Lens: name label (CodeLens). A click opens the symbol's history. The same works from the context menu on the symbol under the cursor, with the result in a panel. Underneath it's tq lens blame --json, so the editor and the terminal show exactly the same thing. It's the only ready plugin today; full Visual Studio and JetBrains Rider are planned (see "Roadmap" below).
Performance

Only what's new gets computed

Parse results are cached under the file's content fingerprint. Because storage addresses files by their content, a file that's unchanged, or identical to another, isn't parsed a second time.

Indexing 64 filesTime
first time (cold cache)135 ms
again (warm cache)19 ms
Binary formats

Blueprint and assets: what's there, what isn't

Lens understands text. Binary files, like Blueprint or .uasset, are a different story, and here it's worth being honest.

✓ There: a plugin mechanism

Lens has a working plugin host (WASM) together with a toolkit (SDK) for writing your own symbol extractor for any format, binary included. Such an extractor computes its own fingerprint, and Lens tracks those symbols just like text ones: across renames and moves.

≈ Blueprint: on the roadmap

Lens doesn't read Blueprints or .uasset files yet. Blueprint support is on our development roadmap, and it will be built on exactly this plugin mechanism. For now Lens tracks symbols in text code.

Roadmap

What's on the way

We keep an honest line between what works today and what's still planned. Near the top of the list:

Lens for Blueprints

Tracking symbols right inside Blueprint graphs and .uasset files, built on the existing plugin mechanism. The history of a Blueprint node or function, just like for text code.

IDE plugins

Alongside the existing VS Code extension, we're planning plugins for full Visual Studio and JetBrains Rider, so a symbol's history is at hand where you actually write game code.

Limits

Where Lens ends

So there are no surprises, here are the things Lens doesn't do, or does with caveats:

Want to see Lens on your own repo? Get in touch. More on the day-to-day is in the documentation.