#pragma once // Step 675: Extract minimal context slices from source files. #include "WorkspaceFileIndex.h" #include #include #include #include #include struct SliceRequest { std::string filePath; std::string symbolName; int lineHint = 0; int headLines = 40; }; struct ContextSlice { std::string filePath; std::string symbolName; int lineStart = 0; int lineEnd = 0; std::string content; bool found = false; }; class ContextSliceAssembler { public: static std::vector assemble(const std::vector& requests, const WorkspaceFileIndex& index) { std::vector out; out.reserve(requests.size()); for (const auto& req : requests) { ContextSlice slice; slice.filePath = req.filePath; slice.symbolName = req.symbolName; std::vector lines; if (!readFileLines(req.filePath, &lines)) { slice.found = false; out.push_back(std::move(slice)); continue; } if (!req.symbolName.empty()) { auto symbols = index.findInFile(normalizePath(req.filePath)); if (symbols.empty()) { namespace fs = std::filesystem; fs::path p(req.filePath); symbols = index.findInFile(normalizePath(p.filename().generic_string())); } if (symbols.empty()) { namespace fs = std::filesystem; fs::path p(req.filePath); if (p.is_absolute()) { std::error_code ec; const std::string rel = normalizePath(p.lexically_relative(fs::current_path(ec)).generic_string()); if (!rel.empty() && rel != p.generic_string()) symbols = index.findInFile(rel); if (symbols.empty()) symbols = index.findInFile(normalizePath(p.filename().generic_string())); } } const auto it = std::find_if(symbols.begin(), symbols.end(), [&](const SymbolLocation& s) { return s.symbolName == req.symbolName; }); if (it != symbols.end()) { int lineStart = clampLine(it->lineStart, static_cast(lines.size())); int lineEnd = findDeclarationEnd(lines, lineStart); fillSlice(lines, lineStart, lineEnd, true, &slice); out.push_back(std::move(slice)); continue; } const int fallbackHead = req.headLines > 0 ? req.headLines : 40; fillSlice(lines, 1, std::min(fallbackHead, static_cast(lines.size())), false, &slice); out.push_back(std::move(slice)); continue; } if (req.lineHint > 0) { const int start = clampLine(req.lineHint, static_cast(lines.size())); const int span = req.headLines > 0 ? req.headLines : 1; const int end = std::min(static_cast(lines.size()), start + span - 1); fillSlice(lines, start, end, true, &slice); out.push_back(std::move(slice)); continue; } const int head = req.headLines > 0 ? req.headLines : 40; fillSlice(lines, 1, std::min(head, static_cast(lines.size())), false, &slice); out.push_back(std::move(slice)); } return out; } private: static std::string normalizePath(const std::string& path) { std::string out = path; for (char& c : out) { if (c == '\\') c = '/'; } return out; } static bool readFileLines(const std::string& filePath, std::vector* out) { if (!out) return false; out->clear(); namespace fs = std::filesystem; fs::path p(filePath); if (!fs::exists(p)) { p = fs::current_path() / p; if (!fs::exists(p)) return false; } std::ifstream in(p); if (!in.good()) return false; std::string line; while (std::getline(in, line)) { out->push_back(line); } return true; } static int clampLine(int line, int maxLine) { if (maxLine <= 0) return 0; if (line < 1) return 1; if (line > maxLine) return maxLine; return line; } static int findDeclarationEnd(const std::vector& lines, int lineStart) { if (lineStart <= 0 || lineStart > static_cast(lines.size())) return lineStart; int braceDepth = 0; bool seenBrace = false; for (int i = lineStart; i <= static_cast(lines.size()); ++i) { const std::string& line = lines[static_cast(i - 1)]; for (char c : line) { if (c == '{') { ++braceDepth; seenBrace = true; } else if (c == '}') { if (braceDepth > 0) --braceDepth; if (seenBrace && braceDepth == 0) return i; } } if (!seenBrace && line.find(';') != std::string::npos) return i; } return static_cast(lines.size()); } static void fillSlice(const std::vector& lines, int lineStart, int lineEnd, bool found, ContextSlice* out) { if (!out) return; out->lineStart = lineStart; out->lineEnd = lineEnd; out->found = found; out->content.clear(); if (lineStart <= 0 || lineEnd <= 0 || lineStart > lineEnd) return; for (int i = lineStart; i <= lineEnd && i <= static_cast(lines.size()); ++i) { out->content += lines[static_cast(i - 1)]; if (i < lineEnd) out->content += "\n"; } } };