class NoirAIContext::SourceReader

Overview

Reads source files once (cached per path) and extracts the contextual snippets the augmentor attaches to AIContext entries: a fixed-radius window around a line, or a heuristic "route scope" that walks a handler block to its end across brace / Python-indent / Ruby-end styles.

Defined in:

ai_context/source_reader.cr

Constant Summary

MAX_LEAD_DECORATOR_LINES = 4

Maximum number of decorator / annotation lines to capture before path_info.line. Lets negative-protection markers (@csrf_exempt, @PreAuthorize, @CrossOrigin) reach the source-scan even when the analyzer sets path_line to the function def rather than the decorator above it.

MAX_ROUTE_SCOPE_LINES = 12
MAX_SNIPPET_CHARS = 240

Constructors

Instance Method Summary

Constructor Detail

def self.new #

[View source]

Instance Method Detail

def lines_for(path : String | Nil) : Array(String) #

Read-only view of a file's lines. The result is the cached array itself, not a copy — callers must not mutate it.

It used to .dup, which copied every line of the file on each call. add_query_parameter_binding_validator calls this for every endpoint × code_path unconditionally, so a 5000-line controller scanned for 200 endpoints paid 200 full-array copies for data nobody writes to. No caller mutates the result.


[View source]
def route_scope_snippet_for(path : String | Nil, line : Int32 | Nil, max_lines : Int32 = MAX_ROUTE_SCOPE_LINES, max_chars : Int32 = MAX_SNIPPET_CHARS) : String | Nil #

max_lines / max_chars default to the limits every caller used before they were parameterized. A caller that needs to see further into the handler — sensitive_response has to find a response emitter and a credential key in the same scope, and they are routinely more than 12 lines apart — raises them. The block-boundary walk below is unchanged either way, so a wider window still stops at the end of this handler rather than bleeding into the next one.


[View source]
def snippet_for(path : String | Nil, line : Int32 | Nil, radius : Int32) : String | Nil #

[View source]