Step 100: memory strategy suggestions

This commit is contained in:
Bill
2026-02-09 10:36:06 -07:00
parent b987d45939
commit 865cb3efa6
5 changed files with 173 additions and 1 deletions

View File

@@ -27,6 +27,7 @@ struct CodeEditorOptions {
const std::vector<int>* warningLines = nullptr;
const std::vector<struct DiagnosticRange>* diagnostics = nullptr;
const std::vector<struct AnnotationMarker>* annotations = nullptr;
const std::vector<struct SuggestionMarker>* suggestions = nullptr;
};
struct CodeEditorResult {
@@ -41,6 +42,8 @@ struct CodeEditorResult {
float minimapWidth = 0.0f;
float minimapViewportStart = 0.0f;
float minimapViewportEnd = 0.0f;
bool suggestionClicked = false;
struct SuggestionMarker clickedSuggestion;
};
struct DiagnosticRange {
@@ -58,6 +61,16 @@ struct AnnotationMarker {
std::string message;
};
struct SuggestionMarker {
int line = 0;
double confidence = 0.0;
std::string label;
std::string reason;
std::string annotationType;
std::string strategy;
std::string nodeId;
};
struct FoldRegion {
int startLine = 0;
int endLine = 0;
@@ -275,6 +288,29 @@ public:
}
}
// Suggestion marker (lightbulb)
if (options.suggestions) {
SuggestionMarker suggestion;
if (suggestionAtLine(*options.suggestions, ln, suggestion)) {
ImVec2 center(origin.x + 20.0f, y + lineHeight * 0.5f);
ImU32 color = IM_COL32(240, 200, 40, 255);
drawList->AddCircleFilled(center, 3.0f, color);
ImVec2 a(center.x - 4.0f, center.y - 4.0f);
ImVec2 b(center.x + 4.0f, center.y + 4.0f);
if (ImGui::IsMouseHoveringRect(a, b)) {
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
result.suggestionClicked = true;
result.clickedSuggestion = suggestion;
}
ImGui::BeginTooltip();
ImGui::Text("%s (%.2f)", suggestion.label.c_str(), suggestion.confidence);
ImGui::Separator();
ImGui::TextUnformatted(suggestion.reason.c_str());
ImGui::EndTooltip();
}
}
}
// Fold indicator
const FoldRegion* fold = findFoldAtLine(ln);
if (fold) {
@@ -575,6 +611,18 @@ private:
return false;
}
static bool suggestionAtLine(const std::vector<SuggestionMarker>& markers,
int line,
SuggestionMarker& out) {
for (const auto& m : markers) {
if (m.line == line) {
out = m;
return true;
}
}
return false;
}
static std::string diagnosticMessageAtLine(const std::vector<DiagnosticRange>& diags, int line) {
for (const auto& d : diags) {
if (line >= d.startLine && line <= d.endLine) return d.message;

View File

@@ -30,6 +30,7 @@
#include "SettingsManager.h"
#include "LayoutManager.h"
#include "ASTMutationAPI.h"
#include "MemoryStrategyInference.h"
#include "ast/Generator.h"
#include "ast/Annotation.h"
@@ -110,6 +111,9 @@ struct EditorState {
SettingsManager settings;
bool showLspSettings = false;
ASTMutationAPI mutator;
std::vector<MemoryStrategyInference::Suggestion> suggestions;
bool showSuggestionPopup = false;
MemoryStrategyInference::Suggestion suggestionPopup;
BufferState* active() { return activeBuffer; }
@@ -616,6 +620,15 @@ static ASTNode* findAnnotationTarget(ASTNode* node, int line) {
return best;
}
static ASTNode* findNodeById(ASTNode* node, const std::string& id) {
if (!node) return nullptr;
if (node->id == id) return node;
for (auto* child : node->allChildren()) {
if (auto* found = findNodeById(child, id)) return found;
}
return nullptr;
}
static void collectAnnotationMarkers(const ASTNode* node, std::vector<AnnotationMarker>& out) {
if (!node) return;
if (node->hasSpan()) {
@@ -1217,6 +1230,7 @@ int main(int, char**) {
std::vector<int> warningLines;
std::vector<DiagnosticRange> diagRanges;
std::vector<AnnotationMarker> annoMarkers;
std::vector<SuggestionMarker> suggestionMarkers;
std::string activeUri = EditorState::toFileUri(buf->path);
if (state.lsp) {
auto lspDiags = state.lsp->getDiagnosticsForUri(activeUri);
@@ -1248,7 +1262,24 @@ int main(int, char**) {
}
if (state.active()) {
Module* ast = state.active()->sync.getAST();
if (ast) collectAnnotationMarkers(ast, annoMarkers);
if (ast) {
collectAnnotationMarkers(ast, annoMarkers);
for (const auto& s : state.suggestions) {
if (s.confidence <= 0.5) continue;
ASTNode* target = findNodeById(ast, s.nodeId);
if (!target || !target->hasSpan()) continue;
SuggestionMarker sm;
sm.line = target->spanStartLine;
sm.confidence = s.confidence;
sm.reason = s.reason;
sm.annotationType = s.annotationType;
sm.strategy = s.strategy;
sm.nodeId = s.nodeId;
sm.label = "@" + s.annotationType.substr(0, s.annotationType.find("Annotation")) +
"(" + s.strategy + ")";
suggestionMarkers.push_back(std::move(sm));
}
}
}
CodeEditorOptions opts;
@@ -1264,6 +1295,7 @@ int main(int, char**) {
opts.warningLines = &warningLines;
opts.diagnostics = &diagRanges;
opts.annotations = &annoMarkers;
opts.suggestions = &suggestionMarkers;
CodeEditorResult res = buf->widget.render("##editor",
buf->editBuf, buf->highlights, opts, avail, monoFont);
@@ -1290,6 +1322,16 @@ int main(int, char**) {
state.analysisLastChange = ImGui::GetTime();
}
if (res.suggestionClicked) {
state.suggestionPopup.annotationType = res.clickedSuggestion.annotationType;
state.suggestionPopup.strategy = res.clickedSuggestion.strategy;
state.suggestionPopup.reason = res.clickedSuggestion.reason;
state.suggestionPopup.confidence = res.clickedSuggestion.confidence;
state.suggestionPopup.nodeId = res.clickedSuggestion.nodeId;
state.showSuggestionPopup = true;
ImGui::OpenPopup("SuggestionPopup");
}
double now = ImGui::GetTime();
if (state.completionPending && (now - state.completionLastChange) > 0.2) {
if (state.lsp && state.active() && state.active()->path.rfind("(untitled", 0) != 0) {
@@ -1314,6 +1356,13 @@ int main(int, char**) {
} else {
state.whetstoneDiagnostics.clear();
}
Module* ast = state.active()->sync.getAST();
if (ast) {
MemoryStrategyInference inf;
state.suggestions = inf.inferAnnotations(ast);
} else {
state.suggestions.clear();
}
}
state.analysisPending = false;
}
@@ -1372,6 +1421,43 @@ int main(int, char**) {
}
}
if (state.showSuggestionPopup) {
if (ImGui::BeginPopupModal("SuggestionPopup", &state.showSuggestionPopup, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("%s(%s)", state.suggestionPopup.annotationType.c_str(),
state.suggestionPopup.strategy.c_str());
ImGui::Separator();
ImGui::TextWrapped("%s", state.suggestionPopup.reason.c_str());
ImGui::Text("Confidence: %.2f", state.suggestionPopup.confidence);
if (ImGui::Button("Apply")) {
Module* ast = state.active() ? state.active()->sync.getAST() : nullptr;
if (ast) {
ASTNode* target = findNodeById(ast, state.suggestionPopup.nodeId);
if (target) {
auto* anno = createAnnotationNode(state.suggestionPopup.annotationType,
state.suggestionPopup.strategy);
if (anno) {
if (target->hasSpan()) {
anno->setSpan(target->spanStartLine, target->spanStartCol,
target->spanEndLine, target->spanEndCol);
}
state.mutator.setRoot(ast);
auto res2 = state.mutator.insertNode(target->id, "annotations", anno);
if (!res2.error.empty()) state.outputLog += res2.error + "\n";
}
}
}
state.showSuggestionPopup = false;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
state.showSuggestionPopup = false;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
if (state.lsp && state.active()) {
if (ImGui::IsWindowHovered()) {
ImVec2 mouse = ImGui::GetMousePos();