yams/plugins/symbol_extractor_treesitter/grammar_loader.h

165 lines
6.5 KiB
C++

#pragma once
#include <unordered_map>
#include <tl/expected.hpp>
#include <filesystem>
#include <span>
#include <string>
#include <string_view>
#include <vector>
// Forward declaration
struct TSLanguage;
namespace yams::plugins::treesitter {
struct GrammarLoadError {
enum Type { NOT_FOUND, LOAD_FAILED, INVALID_SYMBOL };
Type type;
std::string message;
};
/**
* @brief Manages Tree-sitter grammar discovery and loading
*
* Follows XDG Base Directory Specification for grammar paths:
* - XDG_DATA_HOME/yams/grammars (primary)
* - ~/.local/share/yams/grammars (fallback)
* - /usr/local/share/yams/grammars
* - /usr/share/yams/grammars
*
* Plus environment variable overrides and config paths.
*/
class GrammarLoader {
public:
struct GrammarHandle {
void* library_handle = nullptr;
TSLanguage* language = nullptr;
GrammarHandle() = default;
GrammarHandle(void* handle, TSLanguage* lang) noexcept;
GrammarHandle(const GrammarHandle&) = delete;
GrammarHandle& operator=(const GrammarHandle&) = delete;
GrammarHandle(GrammarHandle&& other) noexcept;
GrammarHandle& operator=(GrammarHandle&& other) noexcept;
~GrammarHandle();
};
GrammarLoader();
/**
* @brief Add custom grammar path override from plugin config
*/
void addGrammarPath(std::string_view language, std::string_view path);
/**
* @brief Load grammar library for a language
* @return Language factory function or error
*/
tl::expected<GrammarHandle, GrammarLoadError> loadGrammar(std::string_view language);
/**
* @brief Get all search paths that will be checked
*/
std::vector<std::filesystem::path> getGrammarSearchPaths() const;
/**
* @brief Check if grammar is available (without loading)
*/
bool grammarExists(std::string_view language) const;
private:
struct GrammarSpec {
std::string_view key;
std::string_view env_var;
std::string_view symbol;
std::string_view default_so;
};
static constexpr GrammarSpec kSpecs[] = {
{"c", "YAMS_TS_C_LIB", "tree_sitter_c", "libtree-sitter-c.so"},
{"cpp", "YAMS_TS_CPP_LIB", "tree_sitter_cpp", "libtree-sitter-cpp.so"},
{"c++", "YAMS_TS_CPP_LIB", "tree_sitter_cpp", "libtree-sitter-cpp.so"},
{"python", "YAMS_TS_PY_LIB", "tree_sitter_python", "libtree-sitter-python.so"},
{"go", "YAMS_TS_GO_LIB", "tree_sitter_go", "libtree-sitter-go.so"},
{"rust", "YAMS_TS_RUST_LIB", "tree_sitter_rust", "libtree-sitter-rust.so"},
{"js", "YAMS_TS_JS_LIB", "tree_sitter_javascript", "libtree-sitter-javascript.so"},
{"javascript", "YAMS_TS_JS_LIB", "tree_sitter_javascript", "libtree-sitter-javascript.so"},
{"ts", "YAMS_TS_TS_LIB", "tree_sitter_typescript", "libtree-sitter-typescript.so"},
{"typescript", "YAMS_TS_TS_LIB", "tree_sitter_typescript", "libtree-sitter-typescript.so"},
{"java", "YAMS_TS_JAVA_LIB", "tree_sitter_java", "libtree-sitter-java.so"},
{"csharp", "YAMS_TS_CSHARP_LIB", "tree_sitter_c_sharp", "libtree-sitter-c-sharp.so"},
{"c#", "YAMS_TS_CSHARP_LIB", "tree_sitter_c_sharp", "libtree-sitter-c-sharp.so"},
{"cs", "YAMS_TS_CSHARP_LIB", "tree_sitter_c_sharp", "libtree-sitter-c-sharp.so"},
{"php", "YAMS_TS_PHP_LIB", "tree_sitter_php", "libtree-sitter-php.so"},
{"kotlin", "YAMS_TS_KOTLIN_LIB", "tree_sitter_kotlin", "libtree-sitter-kotlin.so"},
{"kt", "YAMS_TS_KOTLIN_LIB", "tree_sitter_kotlin", "libtree-sitter-kotlin.so"},
{"perl", "YAMS_TS_PERL_LIB", "tree_sitter_perl", "libtree-sitter-perl.so"},
{"pl", "YAMS_TS_PERL_LIB", "tree_sitter_perl", "libtree-sitter-perl.so"},
{"r", "YAMS_TS_R_LIB", "tree_sitter_r", "libtree-sitter-r.so"},
{"sql", "YAMS_TS_SQL_LIB", "tree_sitter_sql", "libtree-sitter-sql.so"},
{"sol", "YAMS_TS_SOL_LIB", "tree_sitter_solidity", "libtree-sitter-solidity.so"},
{"solidity", "YAMS_TS_SOL_LIB", "tree_sitter_solidity", "libtree-sitter-solidity.so"},
{"dart", "YAMS_TS_DART_LIB", "tree_sitter_dart", "libtree-sitter-dart.so"},
{"flutter", "YAMS_TS_DART_LIB", "tree_sitter_dart", "libtree-sitter-dart.so"},
{"p4", "YAMS_TS_P4_LIB", "tree_sitter_p4", "libtree-sitter-p4.so"},
{"p4_16", "YAMS_TS_P4_LIB", "tree_sitter_p4", "libtree-sitter-p4.so"},
{"p4lang", "YAMS_TS_P4_LIB", "tree_sitter_p4", "libtree-sitter-p4.so"},
{"zig", "YAMS_TS_ZIG_LIB", "tree_sitter_zig", "libtree-sitter-zig.so"},
{"swift", "YAMS_TS_SWIFT_LIB", "tree_sitter_swift", "libtree-sitter-swift.so"},
};
const GrammarSpec* findSpec(std::string_view language) const;
std::vector<std::string> getLibraryCandidates(std::string_view language) const;
std::unordered_map<std::string, std::string> grammar_paths_;
mutable std::vector<std::filesystem::path> cached_search_paths_;
mutable bool search_paths_cached_ = false;
};
/**
* @brief Automatically download and build missing grammars
*/
class GrammarDownloader {
public:
/**
* @brief Download and build grammar for a language
* @return Path to built library or error
*/
static tl::expected<std::filesystem::path, std::string>
downloadGrammar(std::string_view language);
/**
* @brief Check if download tools are available (git, gcc)
*/
static bool canAutoDownload();
private:
static constexpr struct GrammarRepo {
std::string_view language;
std::string_view repo;
} kGrammarRepos[] = {
{"c", "tree-sitter/tree-sitter-c"},
{"cpp", "tree-sitter/tree-sitter-cpp"},
{"python", "tree-sitter/tree-sitter-python"},
{"rust", "tree-sitter/tree-sitter-rust"},
{"go", "tree-sitter/tree-sitter-go"},
{"javascript", "tree-sitter/tree-sitter-javascript"},
{"typescript", "tree-sitter/tree-sitter-typescript"},
{"java", "tree-sitter/tree-sitter-java"},
{"csharp", "tree-sitter/tree-sitter-c-sharp"},
{"php", "tree-sitter/tree-sitter-php"},
{"kotlin", "fwcd/tree-sitter-kotlin"},
{"perl", "tree-sitter-perl/tree-sitter-perl"},
{"r", "r-lib/tree-sitter-r"},
{"sql", "DerekStride/tree-sitter-sql"},
{"solidity", "JoranHonig/tree-sitter-solidity"},
{"dart", "UserNobody14/tree-sitter-dart"},
{"p4", "prona-p4-learning-platform/tree-sitter-p4"},
{"zig", "maxxnino/tree-sitter-zig"},
{"swift", "alex-pinkus/tree-sitter-swift"},
};
};
} // namespace yams::plugins::treesitter