diff --git a/PROGRESS.md b/PROGRESS.md index ad562ab..48c38cb 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -382,7 +382,7 @@ vcpkg's imgui 1.91.9 removed the `sdl2-binding` feature (only `sdl3-binding` exi ## What's Next -Sprint 5 in progress. Step 133 (import statement generation) done. Next: Step 134 (available primitives registry). +Sprint 5 in progress. Step 134 (available primitives registry) done. Next: Step 135 (library-aware completion). --- @@ -479,3 +479,4 @@ Sprint 5 in progress. Step 133 (import statement generation) done. Next: Step 13 | 2026-02-09 | Codex | Step 131: Stub-based library indexing (pyi/d.ts/headers/lib.rs) with workspace scan fallback. 8/8 tests pass. | | 2026-02-09 | Codex | Step 132: Library symbol browser panel with filtering, doc detail display, and insert template helpers. 5/5 tests pass. | | 2026-02-09 | Codex | Step 133: Import statement generation + unused import warnings across Python/JS/Rust/Go/Elisp with auto-insert on library symbol use. 7/7 tests pass. | +| 2026-02-09 | Codex | Step 134: PrimitivesRegistry aggregating builtins, imports, and in-scope symbols. 6/6 tests pass. | diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index ce59c28..da717a5 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -750,6 +750,10 @@ add_executable(step133_test tests/step133_test.cpp) target_include_directories(step133_test PRIVATE src) target_link_libraries(step133_test PRIVATE nlohmann_json::nlohmann_json) +add_executable(step134_test tests/step134_test.cpp) +target_include_directories(step134_test PRIVATE src) +target_link_libraries(step134_test PRIVATE nlohmann_json::nlohmann_json) + find_package(SDL2 CONFIG REQUIRED) find_package(OpenGL REQUIRED) find_package(glad CONFIG REQUIRED) diff --git a/editor/src/EditorState.h b/editor/src/EditorState.h index 9597b75..4412f04 100644 --- a/editor/src/EditorState.h +++ b/editor/src/EditorState.h @@ -47,6 +47,7 @@ #include "LibraryIndexer.h" #include "LibraryBrowserPanel.h" #include "ImportManager.h" +#include "PrimitivesRegistry.h" #include "IncrementalOptimizer.h" #include "ast/Serialization.h" #include "ast/Generator.h" @@ -180,6 +181,7 @@ struct EditorState { }; std::vector libraryIndexRequests; LibraryIndexData libraryIndex; + PrimitivesRegistry primitives; // Custom editor widget state bool showWhitespace = false; @@ -437,6 +439,8 @@ struct EditorState { activeBuffer = state.get(); bufferStates[path] = std::move(state); active()->orchestratorDirty = true; + primitives.setRoot(activeAST()); + primitives.setLanguage(active()->language); recordUndoSnapshot(); if (path.rfind("(untitled", 0) != 0) watcher.watch(path); if (lsp && path.rfind("(untitled", 0) != 0) { @@ -525,6 +529,8 @@ struct EditorState { buffers.switchToBuffer(path); activeBuffer = bufferStates[path].get(); if (active()) active()->orchestratorDirty = true; + primitives.setRoot(activeAST()); + primitives.setLanguage(active()->language); symbolsPending = true; symbolsLastChange = ImGui::GetTime(); if (lsp) lsp->clearDocumentSymbols(); @@ -1236,6 +1242,7 @@ struct EditorState { std::string prevLang = active()->language; active()->language = lang; active()->mode.setLanguage(lang); + primitives.setLanguage(lang); if (active()->generatedLanguage == prevLang) { active()->generatedLanguage = lang; active()->generatedMode.setLanguage(lang); @@ -1324,6 +1331,8 @@ struct EditorState { active()->sync.syncNow(); active()->incrementalOptimizer.setRoot(active()->sync.getAST()); } + primitives.setRoot(activeAST()); + primitives.setLanguage(active()->language); active()->orchestratorDirty = true; active()->highlightsDirty = true; active()->generatedHighlightsDirty = true; diff --git a/editor/src/PrimitivesRegistry.h b/editor/src/PrimitivesRegistry.h new file mode 100644 index 0000000..a70c769 --- /dev/null +++ b/editor/src/PrimitivesRegistry.h @@ -0,0 +1,133 @@ +#pragma once +#include "ContextAPI.h" +#include "ast/ExternalModule.h" +#include "ast/TypeSignature.h" +#include "ast/Module.h" +#include +#include +#include +#include +#include + +struct PrimitiveSymbol { + std::string name; + std::string kind; // function, type, constant, variable + std::string source; // builtin, import, user + std::string library; +}; + +class PrimitivesRegistry { +public: + void setRoot(Module* root) { root_ = root; } + void setLanguage(const std::string& lang) { language_ = lang; } + + std::vector getAvailableFunctions(const std::string& nodeId = "") { + return collect(nodeId, "function"); + } + + std::vector getAvailableTypes(const std::string& nodeId = "") { + return collect(nodeId, "type"); + } + + std::vector getAvailableConstants(const std::string& nodeId = "") { + return collect(nodeId, "constant"); + } + +private: + std::vector collect(const std::string& nodeId, + const std::string& filterKind) { + std::vector out; + std::unordered_set seen; + + // Builtins + for (const auto& sym : builtinsForLanguage(language_)) { + if (!filterKind.empty() && sym.kind != filterKind) continue; + if (seen.insert(sym.name).second) out.push_back(sym); + } + + // Imported libraries + if (root_) { + for (auto* modNode : root_->getChildren("externalModules")) { + auto* ext = static_cast(modNode); + for (auto* sigNode : ext->getChildren("signatures")) { + auto* sig = static_cast(sigNode); + PrimitiveSymbol ps; + ps.name = sig->name; + ps.source = "import"; + ps.library = ext->name; + ps.kind = classifySymbol(ps.name); + if (!filterKind.empty() && ps.kind != filterKind) continue; + if (seen.insert(ps.name).second) out.push_back(ps); + } + } + } + + // User-defined (ContextAPI) + if (root_ && !nodeId.empty()) { + ContextAPI ctx; + ctx.setRoot(root_); + auto symbols = ctx.getInScopeSymbols(nodeId); + for (const auto& sym : symbols) { + PrimitiveSymbol ps; + ps.name = sym.name; + ps.source = "user"; + ps.library.clear(); + ps.kind = sym.kind == "variable" ? "constant" : sym.kind; + if (!filterKind.empty() && ps.kind != filterKind) continue; + if (seen.insert(ps.name).second) out.push_back(ps); + } + } + + return out; + } + + static std::string classifySymbol(const std::string& name) { + if (!name.empty() && std::isupper((unsigned char)name[0])) return "type"; + if (std::all_of(name.begin(), name.end(), + [](unsigned char c) { return std::isupper(c) || c == '_' || std::isdigit(c); })) { + return "constant"; + } + return "function"; + } + + static std::vector builtinsForLanguage(const std::string& lang) { + std::vector out; + auto add = [&](const std::string& name, const std::string& kind) { + out.push_back({name, kind, "builtin", ""}); + }; + if (lang == "python") { + add("print", "function"); + add("len", "function"); + add("list", "type"); + add("dict", "type"); + add("True", "constant"); + add("False", "constant"); + } else if (lang == "javascript" || lang == "typescript") { + add("console.log", "function"); + add("Array", "type"); + add("Promise", "type"); + add("undefined", "constant"); + } else if (lang == "cpp") { + add("std::vector", "type"); + add("std::string", "type"); + add("printf", "function"); + add("nullptr", "constant"); + } else if (lang == "rust") { + add("println!", "function"); + add("Vec", "type"); + add("Option", "type"); + } else if (lang == "go") { + add("fmt.Println", "function"); + add("string", "type"); + add("nil", "constant"); + } else if (lang == "elisp") { + add("message", "function"); + add("t", "constant"); + add("nil", "constant"); + } + return out; + } + + Module* root_ = nullptr; + std::string language_ = "python"; +}; diff --git a/editor/tests/step134_test.cpp b/editor/tests/step134_test.cpp new file mode 100644 index 0000000..392a140 --- /dev/null +++ b/editor/tests/step134_test.cpp @@ -0,0 +1,57 @@ +// Step 134 TDD Test: PrimitivesRegistry aggregation +#include "PrimitivesRegistry.h" +#include "ast/Function.h" +#include "ast/Variable.h" +#include + +static void expect(bool cond, const std::string& name, int& passed, int& failed) { + if (cond) { + std::cout << "Test " << (passed + failed + 1) << " PASS: " << name << "\n"; + ++passed; + } else { + std::cout << "Test " << (passed + failed + 1) << " FAIL: " << name << "\n"; + ++failed; + } +} + +static bool containsName(const std::vector& list, const std::string& name) { + for (const auto& s : list) { + if (s.name == name) return true; + } + return false; +} + +int main() { + int passed = 0; + int failed = 0; + + Module mod("m1", "Module", "python"); + auto* fn = new Function("fn1", "user_fn"); + auto* var = new Variable("var1", "USER_CONST"); + mod.addChild("functions", fn); + mod.addChild("variables", var); + + auto* ext = new ExternalModule("ext_numpy", "numpy", "python", "1.0"); + ext->addChild("signatures", new TypeSignature("sig_1", "numpy.array")); + ext->addChild("signatures", new TypeSignature("sig_2", "Matrix")); + mod.addChild("externalModules", ext); + + PrimitivesRegistry reg; + reg.setRoot(&mod); + reg.setLanguage("python"); + + auto funcs = reg.getAvailableFunctions("fn1"); + expect(containsName(funcs, "print"), "builtin function present", passed, failed); + expect(containsName(funcs, "numpy.array"), "import function present", passed, failed); + expect(containsName(funcs, "user_fn"), "user function present", passed, failed); + + auto types = reg.getAvailableTypes("fn1"); + expect(containsName(types, "Matrix"), "import type present", passed, failed); + expect(containsName(types, "list"), "builtin type present", passed, failed); + + auto constants = reg.getAvailableConstants("fn1"); + expect(containsName(constants, "USER_CONST"), "user constant present", passed, failed); + + std::cout << "\n=== Step 134 Results: " << passed << " passed, " << failed << " failed ===\n"; + return failed == 0 ? 0 : 1; +} diff --git a/sprint5_plan.md b/sprint5_plan.md index df36b33..c90beb0 100644 --- a/sprint5_plan.md +++ b/sprint5_plan.md @@ -101,7 +101,7 @@ are a convenience, not a 100% constraint — users can always write any code they want, but the system surfaces library functions first and nudges toward what's already available. -- [ ] **Step 134: Available primitives registry** +- [x] **Step 134: Available primitives registry** Create `PrimitivesRegistry.h` that aggregates all available symbols: - Built-in language primitives (Python builtins, C++ std library, etc.) - Imported library symbols (from `ExternalModule` AST nodes)