hoshi-lang dev
Yet another programming language
Loading...
Searching...
No Matches
projectIndex.cpp
Go to the documentation of this file.
1//
2// Project Index implementation — module resolution and cross-file parsing
3//
4
5#include "projectIndex.h"
6#include "builtinModule.h"
9#include <share/def.hpp>
10#include <filesystem>
11#include <fstream>
12#include <sstream>
13#include <stdexcept>
14
15namespace fs = std::filesystem;
16
17namespace lsp {
18
20
21void ProjectIndex::setSearchPaths(const std::vector<std::string> &paths) {
22 searchPaths = paths;
23}
24
25void ProjectIndex::addSearchPath(const std::string &path) {
26 searchPaths.push_back(path);
27}
28
29std::string ProjectIndex::resolveModule(const std::string &importPath,
30 const std::string &relativeToFile) {
31 // Special case: "builtin" is the compiler's internal module, embedded in the binary
32 if (importPath == "builtin") {
33 return "builtin";
34 }
35
36 // Build search directory list:
37 // 1. Parent directory of relativeToFile
38 // 2. .tsuki_modules subdirectory of relativeToFile's parent
39 // 3. Global search paths
40 std::vector<std::string> searchDirs;
41
42 if (!relativeToFile.empty()) {
43 fs::path parentDir = fs::path(relativeToFile).parent_path();
44 searchDirs.push_back(parentDir.string());
45 searchDirs.push_back((parentDir / ".tsuki_modules").string());
46 }
47
48 for (auto &sp : searchPaths) {
49 searchDirs.push_back(sp);
50 }
51
52 for (auto &dir : searchDirs) {
53 std::string resolved = tryResolve(dir, importPath);
54 if (!resolved.empty()) return resolved;
55 }
56
57 return ""; // Not found
58}
59
60std::string ProjectIndex::tryResolve(const std::string &searchDir,
61 const std::string &importPath) {
62 fs::path base(searchDir);
63 fs::path target(importPath);
64
65 // Try: searchDir / importPath (as-is)
66 fs::path candidate = base / target;
67 if (fs::exists(candidate) && fs::is_regular_file(candidate)) {
68 std::error_code ec;
69 auto absPath = fs::absolute(candidate, ec);
70 if (!ec) return absPath.string();
71 }
72
73 // Try: searchDir / importPath.hoshi
74 fs::path withExt = base / (target.wstring() + L".hoshi");
75 if (fs::exists(withExt) && fs::is_regular_file(withExt)) {
76 std::error_code ec;
77 auto absPath = fs::absolute(withExt, ec);
78 if (!ec) return absPath.string();
79 }
80
81 // Try: searchDir / importPath / index.hoshi
82 fs::path asDir = candidate / "index.hoshi";
83 if (fs::exists(asDir) && fs::is_regular_file(asDir)) {
84 std::error_code ec;
85 auto absPath = fs::absolute(asDir, ec);
86 if (!ec) return absPath.string();
87 }
88
89 return "";
90}
91
92void ProjectIndex::indexModule(const std::string &absolutePath) {
93 if (absolutePath.empty()) return;
94 if (modules.find(absolutePath) != modules.end()) return; // Already indexed
95
96 std::wstring wContent;
97
98 if (absolutePath == "builtin") {
99 // Use the embedded builtin module content (prebuilt into the compiler)
100 std::string builtinStr(__lsp_builtin_module);
101 wContent = yoi::string2wstring(builtinStr);
102 } else {
103 // Read the file from disk
104 std::ifstream file(absolutePath, std::ios::binary);
105 if (!file.is_open()) return;
106
107 file.seekg(0, std::ios::end);
108 auto size = file.tellg();
109 file.seekg(0, std::ios::beg);
110 std::string content(size, '\0');
111 file.read(&content[0], size);
112 file.close();
113
114 wContent = yoi::string2wstring(content);
115 }
116
117 IndexedModule mod;
118 mod.text = wContent;
119 parseAndIndex(absolutePath, wContent, mod);
120 modules[absolutePath] = std::move(mod);
121}
122
123void ProjectIndex::parseAndIndex(const std::string &absolutePath,
124 const std::wstring &text,
125 IndexedModule &mod) {
126 auto savedFilePath = yoi::__current_file_path;
128
129 try {
130 std::wstringstream stream(text);
131 yoi::lexer lex(std::move(stream));
132 lex.scan();
133 yoi::parse(mod.ast, lex);
134
135 if (mod.ast) {
136 SymbolExtractor extractor;
137 mod.symbols = extractor.extract(mod.ast);
138 }
139 } catch (const std::runtime_error &) {
140 // Parse failed — leave AST as nullptr, symbols empty
141 }
142
143 yoi::set_current_file_path(savedFilePath);
144}
145
146const yoi::vec<Symbol> *ProjectIndex::getModuleSymbols(const std::string &absolutePath) {
147 auto it = modules.find(absolutePath);
148 if (it == modules.end()) return nullptr;
149 return &it->second.symbols;
150}
151
153 for (auto &[path, mod] : modules) {
154 for (auto &sym : mod.symbols) {
155 if (sym.isLocal) continue;
156 // Tag with source module path
157 Symbol tagged = sym;
158 // Store the source file path so we can navigate to it
159 if (tagged.sourceFile.empty()) {
160 tagged.sourceFile = yoi::string2wstring(path);
161 }
162 out.push_back(tagged);
163 }
164 }
165}
166
167const yoi::vec<Symbol> &ProjectIndex::indexAndGet(const std::string &absolutePath) {
168 indexModule(absolutePath);
169 auto it = modules.find(absolutePath);
170 if (it != modules.end()) {
171 return it->second.symbols;
172 }
173 static yoi::vec<Symbol> empty;
174 return empty;
175}
176
177void ProjectIndex::invalidateModule(const std::string &absolutePath) {
178 auto it = modules.find(absolutePath);
179 if (it != modules.end()) {
180 if (it->second.ast) {
181 yoi::finalizeAST(it->second.ast);
182 }
183 modules.erase(it);
184 }
185}
186
187} // namespace lsp
static const char * __lsp_builtin_module
const yoi::vec< Symbol > & indexAndGet(const std::string &absolutePath)
Parse and index a module, then return its symbols.
std::map< std::string, IndexedModule > modules
void invalidateModule(const std::string &absolutePath)
Clear a module from the cache (e.g., when it changes on disk).
const yoi::vec< Symbol > * getModuleSymbols(const std::string &absolutePath)
Get symbols for a specific module.
void indexModule(const std::string &absolutePath)
std::string resolveModule(const std::string &importPath, const std::string &relativeToFile)
void setSearchPaths(const std::vector< std::string > &paths)
void addSearchPath(const std::string &path)
void getAllSymbols(yoi::vec< Symbol > &out)
Collect all symbols from all indexed modules into flat list.
void parseAndIndex(const std::string &absolutePath, const std::wstring &text, IndexedModule &mod)
std::vector< std::string > searchPaths
std::string tryResolve(const std::string &searchDir, const std::string &importPath)
yoi::vec< Symbol > extract(yoi::hoshiModule *module)
token scan()
Definition lexer.cpp:29
std::vector< t > vec
Definition def.hpp:56
std::wstring string2wstring(const std::string &v)
Definition def.cpp:224
thread_local yoi::wstr __current_file_path
Definition def.cpp:15
void set_current_file_path(const std::wstring &path)
Definition def.cpp:123
void finalizeAST(funcTypeSpec *ptr)
Definition ast.cpp:475
void parse(yoi::basicLiterals *&o, yoi::lexer &lex)
Definition parser.cpp:25
yoi::wstr sourceFile