♻️ 重构
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
#include "bootstrap.hpp"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "../utils/args_parser.hpp"
|
||||
|
||||
namespace lsp::manager::bootstrap
|
||||
{
|
||||
void InitializeManagerHub(
|
||||
ManagerHub& hub,
|
||||
scheduler::AsyncExecutor& async_executor,
|
||||
const std::vector<std::string>& system_lib_paths)
|
||||
{
|
||||
spdlog::info("Initializing manager hub...");
|
||||
|
||||
for (const auto& path : system_lib_paths)
|
||||
{
|
||||
std::string task_name = fmt::format("Load system library: {}", path);
|
||||
|
||||
async_executor.Submit(
|
||||
task_name,
|
||||
[&hub, path]() -> std::optional<std::string> {
|
||||
try
|
||||
{
|
||||
hub.symbols().LoadSystemLibrary(path);
|
||||
return fmt::format("Loaded system library: {}", path);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
spdlog::error("Failed to load system library {}: {}", path, e.what());
|
||||
throw;
|
||||
}
|
||||
},
|
||||
[path](const std::optional<std::string>& result, bool cancelled) {
|
||||
if (cancelled)
|
||||
spdlog::info("System library load task cancelled: {}", path);
|
||||
else if (result)
|
||||
spdlog::info("{}", *result);
|
||||
});
|
||||
}
|
||||
spdlog::info("Manager hub initialized, system library loading in background");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "../manager/manager_hub.hpp"
|
||||
#include "../scheduler/async_executor.hpp"
|
||||
|
||||
namespace lsp::manager::bootstrap
|
||||
{
|
||||
void InitializeManagerHub(
|
||||
ManagerHub& hub,
|
||||
scheduler::AsyncExecutor& async_executor,
|
||||
const std::vector<std::string>& system_lib_paths);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "text_document.hpp"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "../../utils/text_coordinates.hpp"
|
||||
|
||||
namespace lsp::manager::detail
|
||||
{
|
||||
TextDocument::TextDocument(const protocol::TextDocumentItem& item) : item_(item) {}
|
||||
|
||||
void TextDocument::ApplyChange(const protocol::TextDocumentContentChangeEvent& change)
|
||||
{
|
||||
if (IsFullDocumentUpdate(change, item_.text))
|
||||
{
|
||||
item_.text = change.text;
|
||||
spdlog::debug("Full Document update for: {}", item_.uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
protocol::uinteger start_offset = utils::text_coordinates::ToOffset(change.range.start, item_.text);
|
||||
protocol::uinteger end_offset = utils::text_coordinates::ToOffset(change.range.end, item_.text);
|
||||
item_.text.replace(start_offset, end_offset - start_offset, change.text);
|
||||
spdlog::debug("Incremental update for: {}", item_.uri);
|
||||
}
|
||||
}
|
||||
|
||||
void TextDocument::SetVersion(protocol::integer version)
|
||||
{
|
||||
item_.version = version;
|
||||
}
|
||||
|
||||
const protocol::string& TextDocument::GetContent() const
|
||||
{
|
||||
return item_.text;
|
||||
}
|
||||
|
||||
const protocol::DocumentUri& TextDocument::GetUri() const
|
||||
{
|
||||
return item_.uri;
|
||||
}
|
||||
|
||||
protocol::integer TextDocument::GetVersion() const
|
||||
{
|
||||
return item_.version;
|
||||
}
|
||||
|
||||
bool TextDocument::IsFullDocumentUpdate(const protocol::TextDocumentContentChangeEvent& change, const protocol::string& current_content)
|
||||
{
|
||||
if (change.range.start.line == 0 && change.range.start.character == 0)
|
||||
{
|
||||
protocol::uinteger line_count = 0;
|
||||
for (size_t i = 0; i < current_content.length(); ++i)
|
||||
{
|
||||
if (current_content[i] == '\n')
|
||||
line_count++;
|
||||
}
|
||||
if (change.range.end.line >= line_count)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "../../protocol/protocol.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <tree_sitter/api.h>
|
||||
}
|
||||
|
||||
namespace lsp::manager::detail
|
||||
{
|
||||
class TextDocument
|
||||
{
|
||||
public:
|
||||
explicit TextDocument(const protocol::TextDocumentItem& item);
|
||||
|
||||
void ApplyChange(const protocol::TextDocumentContentChangeEvent& change);
|
||||
void SetVersion(protocol::integer version);
|
||||
|
||||
const protocol::string& GetContent() const;
|
||||
const protocol::DocumentUri& GetUri() const;
|
||||
protocol::integer GetVersion() const;
|
||||
|
||||
private:
|
||||
static bool IsFullDocumentUpdate(const protocol::TextDocumentContentChangeEvent& change, const protocol::string& current_content);
|
||||
|
||||
protocol::TextDocumentItem item_;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "document.hpp"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "./events.hpp"
|
||||
|
||||
namespace lsp::manager
|
||||
{
|
||||
Document::Document(EventBus& event_bus) : event_bus_(event_bus) {}
|
||||
|
||||
Document::~Document() = default;
|
||||
|
||||
void Document::OpenDocument(const protocol::DidOpenTextDocumentParams& params)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
documents_.emplace(params.textDocument.uri, detail::TextDocument(params.textDocument));
|
||||
}
|
||||
|
||||
event_bus_.Publish(events::DocumentOpened{ params });
|
||||
spdlog::debug("Document opened: {}", params.textDocument.uri);
|
||||
}
|
||||
|
||||
void Document::UpdateDocument(const protocol::DidChangeTextDocumentParams& params)
|
||||
{
|
||||
protocol::string content;
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
auto it = documents_.find(params.textDocument.uri);
|
||||
if (it == documents_.end())
|
||||
{
|
||||
spdlog::warn("Attempting to update non-existent document: {}", params.textDocument.uri);
|
||||
return;
|
||||
}
|
||||
for (const auto& change : params.contentChanges)
|
||||
it->second.ApplyChange(change);
|
||||
if (params.textDocument.version)
|
||||
it->second.SetVersion(params.textDocument.version);
|
||||
content = it->second.GetContent();
|
||||
}
|
||||
|
||||
event_bus_.Publish(events::DocumentChanged{
|
||||
.uri = params.textDocument.uri,
|
||||
.version = params.textDocument.version,
|
||||
.changes = params.contentChanges,
|
||||
.content = std::move(content) });
|
||||
|
||||
spdlog::debug("Document updated: {}", params.textDocument.uri);
|
||||
}
|
||||
|
||||
void Document::CloseDocument(const protocol::DidCloseTextDocumentParams& params)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
documents_.erase(params.textDocument.uri);
|
||||
}
|
||||
event_bus_.Publish(events::DocumentClosed{ params });
|
||||
spdlog::debug("Document closed: {}", params.textDocument.uri);
|
||||
}
|
||||
|
||||
std::optional<protocol::string> Document::GetContent(const protocol::DocumentUri& uri) const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
auto it = documents_.find(uri);
|
||||
if (it != documents_.end())
|
||||
return it->second.GetContent();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<protocol::integer> Document::GetVersion(const protocol::DocumentUri& uri) const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
auto it = documents_.find(uri);
|
||||
if (it != documents_.end())
|
||||
return it->second.GetVersion();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<protocol::DocumentUri> Document::GetAllDocumentUris() const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
std::vector<protocol::DocumentUri> uris;
|
||||
uris.reserve(documents_.size());
|
||||
for (const auto& [uri, _] : documents_)
|
||||
uris.push_back(uri);
|
||||
return uris;
|
||||
}
|
||||
|
||||
void Document::Clear()
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
documents_.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <shared_mutex>
|
||||
#include "./event_bus.hpp"
|
||||
#include "./detail/text_document.hpp"
|
||||
#include "../protocol/protocol.hpp"
|
||||
|
||||
namespace lsp::manager
|
||||
{
|
||||
class Document
|
||||
{
|
||||
public:
|
||||
explicit Document(EventBus& event_bus);
|
||||
~Document();
|
||||
|
||||
void OpenDocument(const protocol::DidOpenTextDocumentParams& params);
|
||||
void UpdateDocument(const protocol::DidChangeTextDocumentParams& params);
|
||||
void CloseDocument(const protocol::DidCloseTextDocumentParams& params);
|
||||
|
||||
std::optional<protocol::string> GetContent(const protocol::DocumentUri& uri) const;
|
||||
std::optional<protocol::integer> GetVersion(const protocol::DocumentUri& uri) const;
|
||||
std::vector<protocol::DocumentUri> GetAllDocumentUris() const;
|
||||
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
mutable std::shared_mutex mutex_;
|
||||
std::unordered_map<protocol::DocumentUri, detail::TextDocument> documents_;
|
||||
EventBus& event_bus_;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace lsp::manager
|
||||
{
|
||||
class EventBus
|
||||
{
|
||||
public:
|
||||
template<typename EventType>
|
||||
using Handler = std::function<void(const EventType&)>;
|
||||
|
||||
template<typename EventType>
|
||||
void Subscribe(Handler<EventType> handler)
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
handlers_[std::type_index(typeid(EventType))].push_back(
|
||||
[handler](const void* event) {
|
||||
handler(*static_cast<const EventType*>(event));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename EventType>
|
||||
void Publish(const EventType& event) const
|
||||
{
|
||||
std::vector<std::function<void(const void*)>> local_handlers;
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
auto it = handlers_.find(std::type_index(typeid(EventType)));
|
||||
if (it != handlers_.end())
|
||||
local_handlers = it->second;
|
||||
}
|
||||
for (const auto& handler : local_handlers)
|
||||
handler(&event);
|
||||
}
|
||||
|
||||
template<typename EventType>
|
||||
void Unsubscribe(std::type_index type)
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
handlers_.erase(type);
|
||||
}
|
||||
|
||||
size_t GetSubscriberCount() const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
size_t count = 0;
|
||||
for (const auto& [type, handlers] : handlers_)
|
||||
count += handlers.size();
|
||||
return count;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::shared_mutex mutex_;
|
||||
std::unordered_map<std::type_index, std::vector<std::function<void(const void*)>>> handlers_;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "../protocol/protocol.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <tree_sitter/api.h>
|
||||
}
|
||||
|
||||
namespace lsp::manager::events
|
||||
{
|
||||
using DocumentOpened = protocol::DidOpenTextDocumentParams;
|
||||
using DocumentClosed = protocol::DidCloseTextDocumentParams;
|
||||
|
||||
struct DocumentChanged
|
||||
{
|
||||
protocol::DocumentUri uri;
|
||||
protocol::integer version;
|
||||
std::vector<protocol::TextDocumentContentChangeEvent> changes;
|
||||
protocol::string content;
|
||||
};
|
||||
|
||||
struct DocumentParsed
|
||||
{
|
||||
protocol::TextDocumentItem item;
|
||||
TSTree* tree;
|
||||
};
|
||||
|
||||
struct DocumentReparsed
|
||||
{
|
||||
protocol::TextDocumentItem item;
|
||||
TSTree* tree;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "manager_hub.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "../utils/text_coordinates.hpp"
|
||||
|
||||
namespace lsp::manager
|
||||
{
|
||||
ManagerHub::ManagerHub() : event_bus_(),
|
||||
documents_(event_bus_),
|
||||
parser_(event_bus_),
|
||||
symbols_(event_bus_)
|
||||
{
|
||||
}
|
||||
|
||||
ManagerHub::~ManagerHub() = default;
|
||||
|
||||
void ManagerHub::Initialize()
|
||||
{
|
||||
documents_.Clear();
|
||||
parser_.Clear();
|
||||
}
|
||||
|
||||
void ManagerHub::Shutdown()
|
||||
{
|
||||
documents_.Clear();
|
||||
parser_.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "../manager/event_bus.hpp"
|
||||
#include "../manager/document.hpp"
|
||||
#include "../manager/parser.hpp"
|
||||
#include "../manager/symbol.hpp"
|
||||
#include "../protocol/protocol.hpp"
|
||||
|
||||
namespace lsp::manager
|
||||
{
|
||||
class ManagerHub
|
||||
{
|
||||
public:
|
||||
ManagerHub();
|
||||
~ManagerHub();
|
||||
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
Document& documents() { return documents_; }
|
||||
Parser& parser() { return parser_; }
|
||||
Symbol& symbols() { return symbols_; }
|
||||
|
||||
private:
|
||||
EventBus event_bus_;
|
||||
|
||||
Document documents_;
|
||||
Parser parser_;
|
||||
Symbol symbols_;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
#include "parser.hpp"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "../utils/text_coordinates.hpp"
|
||||
|
||||
namespace lsp::manager
|
||||
{
|
||||
TreeSitter::TreeSitter()
|
||||
{
|
||||
parser_ = ts_parser_new();
|
||||
if (!parser_)
|
||||
throw std::runtime_error("Failed to create tree-sitter parser");
|
||||
}
|
||||
|
||||
TreeSitter::~TreeSitter()
|
||||
{
|
||||
if (parser_)
|
||||
ts_parser_delete(parser_);
|
||||
}
|
||||
|
||||
TreeSitter::TreeSitter(TreeSitter&& other) noexcept : parser_(other.parser_)
|
||||
{
|
||||
other.parser_ = nullptr;
|
||||
}
|
||||
|
||||
bool TreeSitter::SetLanguage(const TSLanguage* language)
|
||||
{
|
||||
if (!parser_)
|
||||
return false;
|
||||
return ts_parser_set_language(parser_, language);
|
||||
}
|
||||
|
||||
TSTree* TreeSitter::Parse(const char* content, size_t length, TSTree* old_tree)
|
||||
{
|
||||
if (!parser_)
|
||||
return nullptr;
|
||||
return ts_parser_parse_string(parser_, old_tree, content, length);
|
||||
}
|
||||
|
||||
TSParser* TreeSitter::GetRawParser() const
|
||||
{
|
||||
return parser_;
|
||||
}
|
||||
|
||||
SyntaxTree::SyntaxTree(TSTree* tree) : tree_(tree, ts_tree_delete) {}
|
||||
|
||||
SyntaxTree::~SyntaxTree() = default;
|
||||
|
||||
TSTree* SyntaxTree::Get() const
|
||||
{
|
||||
return tree_.get();
|
||||
}
|
||||
|
||||
void SyntaxTree::ApplyEdit(
|
||||
const protocol::TextDocumentContentChangeEvent& change,
|
||||
const protocol::string& content)
|
||||
{
|
||||
if (!tree_)
|
||||
return;
|
||||
|
||||
protocol::uinteger start_offset = utils::text_coordinates::ToOffset(change.range.start, content);
|
||||
protocol::uinteger end_offset = utils::text_coordinates::ToOffset(change.range.end, content);
|
||||
|
||||
TSInputEdit edit{};
|
||||
edit.start_byte = start_offset;
|
||||
edit.old_end_byte = end_offset;
|
||||
edit.new_end_byte = start_offset + change.text.length();
|
||||
edit.start_point = utils::text_coordinates::ToPoint(change.range.start);
|
||||
edit.old_end_point = utils::text_coordinates::ToPoint(change.range.end);
|
||||
edit.new_end_point = utils::text_coordinates::CalculateEndPoint(change.text, edit.start_point);
|
||||
|
||||
ts_tree_edit(tree_.get(), &edit);
|
||||
}
|
||||
|
||||
TSNode SyntaxTree::GetRootNode() const
|
||||
{
|
||||
return tree_ ? ts_tree_root_node(tree_.get()) : TSNode{};
|
||||
}
|
||||
|
||||
Parser::Parser(EventBus& event_bus) : event_bus_(event_bus)
|
||||
{
|
||||
if (parser_.SetLanguage(tree_sitter_tsf()))
|
||||
spdlog::info("Set tree-sitter-tsf successfully");
|
||||
else
|
||||
spdlog::error("Failed to set tree-sitter language");
|
||||
|
||||
event_bus_.Subscribe<events::DocumentOpened>(
|
||||
[this](const auto& e) { OnDocumentOpened(e); });
|
||||
event_bus_.Subscribe<events::DocumentChanged>(
|
||||
[this](const auto& e) { OnDocumentChanged(e); });
|
||||
event_bus_.Subscribe<events::DocumentClosed>(
|
||||
[this](const auto& e) { OnDocumentClosed(e); });
|
||||
}
|
||||
|
||||
Parser::~Parser() = default;
|
||||
|
||||
TSParser* Parser::GetRawParser() const
|
||||
{
|
||||
return parser_.GetRawParser();
|
||||
}
|
||||
|
||||
TSTree* Parser::GetTree(const protocol::DocumentUri& uri) const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
auto it = trees_.find(uri);
|
||||
if (it != trees_.end() && it->second)
|
||||
return it->second->Get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Parser::Clear()
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
trees_.clear();
|
||||
}
|
||||
|
||||
void Parser::OnDocumentOpened(const events::DocumentOpened& event)
|
||||
{
|
||||
TSTree* tree = parser_.Parse(
|
||||
event.textDocument.text.c_str(),
|
||||
event.textDocument.text.length());
|
||||
|
||||
if (tree)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
trees_[event.textDocument.uri] = std::make_unique<SyntaxTree>(tree);
|
||||
}
|
||||
|
||||
event_bus_.Publish(events::DocumentParsed{
|
||||
.item = event.textDocument,
|
||||
.tree = tree });
|
||||
|
||||
spdlog::debug("Successfully parsed document: {}", event.textDocument.uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::error("Failed to parse document: {}", event.textDocument.uri);
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::OnDocumentChanged(const events::DocumentChanged& event)
|
||||
{
|
||||
TSTree* old_tree = nullptr;
|
||||
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
auto it = trees_.find(event.uri);
|
||||
if (it != trees_.end() && it->second)
|
||||
{
|
||||
for (const auto& change : event.changes)
|
||||
it->second->ApplyEdit(change, event.content);
|
||||
old_tree = it->second->Get();
|
||||
}
|
||||
}
|
||||
|
||||
TSTree* tree = parser_.Parse(
|
||||
event.content.c_str(),
|
||||
event.content.length(),
|
||||
old_tree);
|
||||
|
||||
if (tree)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
trees_[event.uri] = std::make_unique<SyntaxTree>(tree);
|
||||
}
|
||||
|
||||
event_bus_.Publish(events::DocumentReparsed{
|
||||
.item{
|
||||
.uri = event.uri,
|
||||
.languageId = "",
|
||||
.version = event.version,
|
||||
.text = event.content },
|
||||
.tree = tree });
|
||||
|
||||
spdlog::debug("Document reparsed successfully: {}", event.uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::error("Failed to reparse document: {}", event.uri);
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::OnDocumentClosed(const events::DocumentClosed& event)
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
trees_.erase(event.textDocument.uri);
|
||||
spdlog::debug("Removed syntax tree for: {}", event.textDocument.uri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include "./events.hpp"
|
||||
#include "./event_bus.hpp"
|
||||
#include "../protocol/protocol.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <tree_sitter/api.h>
|
||||
}
|
||||
|
||||
extern "C" const TSLanguage* tree_sitter_tsf(void);
|
||||
|
||||
namespace lsp::manager
|
||||
{
|
||||
class TreeSitter
|
||||
{
|
||||
public:
|
||||
TreeSitter();
|
||||
TreeSitter(const TreeSitter&) = delete;
|
||||
TreeSitter& operator=(const TreeSitter&) = delete;
|
||||
TreeSitter(TreeSitter&& other) noexcept;
|
||||
~TreeSitter();
|
||||
|
||||
bool SetLanguage(const TSLanguage* language);
|
||||
TSTree* Parse(const char* content, size_t length, TSTree* old_tree = nullptr);
|
||||
TSParser* GetRawParser() const;
|
||||
|
||||
private:
|
||||
TSParser* parser_;
|
||||
};
|
||||
|
||||
class SyntaxTree
|
||||
{
|
||||
public:
|
||||
explicit SyntaxTree(TSTree* tree);
|
||||
~SyntaxTree();
|
||||
|
||||
void ApplyEdit(const protocol::TextDocumentContentChangeEvent& change, const protocol::string& content);
|
||||
TSTree* Get() const;
|
||||
TSNode GetRootNode() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<TSTree, void (*)(TSTree*)> tree_;
|
||||
};
|
||||
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
explicit Parser(EventBus& event_bus);
|
||||
~Parser();
|
||||
|
||||
TSTree* GetTree(const protocol::DocumentUri& uri) const;
|
||||
TSParser* GetRawParser() const;
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
void OnDocumentOpened(const events::DocumentOpened& event);
|
||||
void OnDocumentChanged(const events::DocumentChanged& event);
|
||||
void OnDocumentClosed(const events::DocumentClosed& event);
|
||||
|
||||
TreeSitter parser_;
|
||||
std::unordered_map<protocol::DocumentUri, std::unique_ptr<SyntaxTree>> trees_;
|
||||
EventBus& event_bus_;
|
||||
mutable std::shared_mutex mutex_;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
#include "symbol.hpp"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <cctype>
|
||||
#include "../utils/string.hpp"
|
||||
#include "../language/ast/deserializer.hpp"
|
||||
#include "../language/symbol/builder.hpp"
|
||||
|
||||
namespace lsp::manager
|
||||
{
|
||||
extern "C" const TSLanguage* tree_sitter_tsf(void);
|
||||
namespace
|
||||
{
|
||||
std::string PathToUri(const std::filesystem::path& path)
|
||||
{
|
||||
auto absolute = std::filesystem::absolute(path).generic_string();
|
||||
#ifdef _WIN32
|
||||
std::replace(absolute.begin(), absolute.end(), '\\', '/');
|
||||
#endif
|
||||
if (!absolute.starts_with("/"))
|
||||
absolute = "/" + absolute;
|
||||
return "file://" + absolute;
|
||||
}
|
||||
|
||||
std::string UriToPath(const std::string& uri)
|
||||
{
|
||||
std::string path = uri;
|
||||
if (path.starts_with("file://"))
|
||||
path = path.substr(7);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!path.empty() && path[0] == '/')
|
||||
path = path.substr(1);
|
||||
std::replace(path.begin(), path.end(), '/', '\\');
|
||||
#endif
|
||||
|
||||
// Percent-decoding
|
||||
std::string decoded;
|
||||
decoded.reserve(path.size());
|
||||
for (size_t i = 0; i < path.size(); ++i)
|
||||
{
|
||||
if (path[i] == '%' && i + 2 < path.size())
|
||||
{
|
||||
std::string hex = path.substr(i + 1, 2);
|
||||
char ch = static_cast<char>(std::stoi(hex, nullptr, 16));
|
||||
decoded.push_back(ch);
|
||||
i += 2;
|
||||
}
|
||||
else if (path[i] == '+')
|
||||
{
|
||||
decoded.push_back(' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
decoded.push_back(path[i]);
|
||||
}
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
bool IsTsfFile(const std::filesystem::path& path)
|
||||
{
|
||||
if (!path.has_extension())
|
||||
return false;
|
||||
std::string ext = path.extension().string();
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
|
||||
return ext == ".tsf" || ext == ".tsl";
|
||||
}
|
||||
|
||||
std::unique_ptr<language::symbol::SymbolTable> BuildSymbolTableFromFile(
|
||||
const std::filesystem::path& file_path)
|
||||
{
|
||||
if (!IsTsfFile(file_path))
|
||||
return nullptr;
|
||||
|
||||
std::ifstream file(file_path, std::ios::binary);
|
||||
if (!file.is_open())
|
||||
{
|
||||
spdlog::warn("Failed to open symbol file: {}", file_path.string());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
|
||||
auto parser_deleter = [](TSParser* parser) {
|
||||
if (parser)
|
||||
ts_parser_delete(parser);
|
||||
};
|
||||
std::unique_ptr<TSParser, decltype(parser_deleter)> parser(ts_parser_new(), parser_deleter);
|
||||
if (!parser || !ts_parser_set_language(parser.get(), tree_sitter_tsf()))
|
||||
{
|
||||
spdlog::error("Failed to create tree-sitter parser for file: {}", file_path.string());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TSTree* tree_handle = ts_parser_parse_string(parser.get(), nullptr, content.c_str(), content.length());
|
||||
if (!tree_handle)
|
||||
{
|
||||
spdlog::warn("tree-sitter failed to parse file: {}", file_path.string());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto tree_deleter = [](TSTree* tree) {
|
||||
if (tree)
|
||||
ts_tree_delete(tree);
|
||||
};
|
||||
std::unique_ptr<TSTree, decltype(tree_deleter)> tree(tree_handle, tree_deleter);
|
||||
|
||||
language::ast::Deserializer deserializer;
|
||||
auto ast_result = deserializer.Parse(ts_tree_root_node(tree.get()), content);
|
||||
if (!ast_result.root)
|
||||
{
|
||||
spdlog::warn("Failed to deserialize AST for file: {}", file_path.string());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto symbol_table = std::make_unique<language::symbol::SymbolTable>();
|
||||
try
|
||||
{
|
||||
language::symbol::Builder builder(*symbol_table);
|
||||
builder.Build(*ast_result.root);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
spdlog::error("Exception building symbol table for {}: {}", file_path.string(), e.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return symbol_table;
|
||||
}
|
||||
|
||||
bool HasMatchingTopLevelSymbol(const language::symbol::SymbolTable& table, const std::string& stem)
|
||||
{
|
||||
for (const auto& wrapper : table.all_definitions())
|
||||
{
|
||||
const auto& symbol = wrapper.get();
|
||||
switch (symbol.kind())
|
||||
{
|
||||
case protocol::SymbolKind::Function:
|
||||
case protocol::SymbolKind::Class:
|
||||
case protocol::SymbolKind::Module:
|
||||
if (utils::IEquals(symbol.name(), stem))
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Symbol::Symbol(EventBus& event_bus) : event_bus_(event_bus)
|
||||
{
|
||||
event_bus_.Subscribe<events::DocumentParsed>(
|
||||
[this](const auto& e) { OnDocumentParsed(e); });
|
||||
|
||||
event_bus_.Subscribe<events::DocumentReparsed>(
|
||||
[this](const auto& e) { OnDocumentReparsed(e); });
|
||||
|
||||
event_bus_.Subscribe<events::DocumentClosed>(
|
||||
[this](const auto& e) { OnDocumentClosed(e); });
|
||||
}
|
||||
|
||||
Symbol::~Symbol() = default;
|
||||
|
||||
void Symbol::LoadSystemLibrary(const std::string& lib_path)
|
||||
{
|
||||
spdlog::info("Loading system library from: {}", lib_path);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
if (!std::filesystem::exists(lib_path))
|
||||
{
|
||||
spdlog::warn("System library path does not exist: {}", lib_path);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t loaded = 0;
|
||||
size_t failed = 0;
|
||||
|
||||
std::unordered_map<std::string, StoredSymbolEntry> new_symbols;
|
||||
auto options = std::filesystem::directory_options::follow_directory_symlink |
|
||||
std::filesystem::directory_options::skip_permission_denied;
|
||||
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(lib_path, options))
|
||||
{
|
||||
if (!entry.is_regular_file())
|
||||
continue;
|
||||
|
||||
auto table = BuildSymbolTableFromFile(entry.path());
|
||||
if (!table)
|
||||
{
|
||||
++failed;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto stem = entry.path().stem().string();
|
||||
if (!HasMatchingTopLevelSymbol(*table, stem))
|
||||
{
|
||||
spdlog::warn("Skipping system file {}: top-level symbol does not match file name", entry.path().string());
|
||||
++failed;
|
||||
continue;
|
||||
}
|
||||
|
||||
StoredSymbolEntry stored;
|
||||
stored.symbol_table = std::move(table);
|
||||
stored.semantic_model = std::make_unique<language::semantic::SemanticModel>(*stored.symbol_table);
|
||||
|
||||
new_symbols[PathToUri(entry.path())] = std::move(stored);
|
||||
++loaded;
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
system_symbols_ = std::move(new_symbols);
|
||||
RebuildIndex();
|
||||
}
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - start)
|
||||
.count();
|
||||
|
||||
spdlog::info("System library loaded: {} files, {} failed, {}ms",
|
||||
loaded,
|
||||
failed,
|
||||
duration);
|
||||
}
|
||||
|
||||
void Symbol::LoadWorkspace(const protocol::DocumentUri& workspace_uri)
|
||||
{
|
||||
auto workspace_path = UriToPath(workspace_uri);
|
||||
spdlog::info("Loading workspace from: {}", workspace_path);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
if (!std::filesystem::exists(workspace_path))
|
||||
{
|
||||
spdlog::warn("Workspace path does not exist: {}", workspace_path);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t loaded = 0;
|
||||
size_t failed = 0;
|
||||
|
||||
std::unordered_map<std::string, StoredSymbolEntry> new_symbols;
|
||||
auto options = std::filesystem::directory_options::follow_directory_symlink |
|
||||
std::filesystem::directory_options::skip_permission_denied;
|
||||
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(workspace_path, options))
|
||||
{
|
||||
if (!entry.is_regular_file())
|
||||
continue;
|
||||
if (!IsTsfFile(entry.path()))
|
||||
continue;
|
||||
|
||||
auto table = BuildSymbolTableFromFile(entry.path());
|
||||
if (!table)
|
||||
{
|
||||
++failed;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto stem = entry.path().stem().string();
|
||||
if (!HasMatchingTopLevelSymbol(*table, stem))
|
||||
{
|
||||
spdlog::warn("Skipping system file {}: top-level symbol does not match file name", entry.path().string());
|
||||
++failed;
|
||||
continue;
|
||||
}
|
||||
|
||||
StoredSymbolEntry stored;
|
||||
stored.symbol_table = std::move(table);
|
||||
stored.semantic_model = std::make_unique<language::semantic::SemanticModel>(*stored.symbol_table);
|
||||
|
||||
new_symbols[PathToUri(entry.path())] = std::move(stored);
|
||||
++loaded;
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
workspace_symbols_ = std::move(new_symbols);
|
||||
RebuildIndex();
|
||||
}
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - start)
|
||||
.count();
|
||||
|
||||
spdlog::info("Workspace loaded: {} files, {} failed, {}ms",
|
||||
loaded,
|
||||
failed,
|
||||
duration);
|
||||
}
|
||||
|
||||
const language::symbol::SymbolTable* Symbol::GetSymbolTable(
|
||||
const protocol::DocumentUri& uri) const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
|
||||
if (auto it = editing_symbols_.find(uri); it != editing_symbols_.end())
|
||||
{
|
||||
return it->second.symbol_table.get();
|
||||
}
|
||||
|
||||
if (auto it = workspace_symbols_.find(uri); it != workspace_symbols_.end())
|
||||
{
|
||||
return it->second.symbol_table.get();
|
||||
}
|
||||
|
||||
if (auto it = system_symbols_.find(uri); it != system_symbols_.end())
|
||||
{
|
||||
return it->second.symbol_table.get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const language::semantic::SemanticModel* Symbol::GetSemanticModel(
|
||||
const protocol::DocumentUri& uri) const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
|
||||
if (auto it = editing_symbols_.find(uri); it != editing_symbols_.end())
|
||||
{
|
||||
return it->second.semantic_model.get();
|
||||
}
|
||||
|
||||
if (auto it = workspace_symbols_.find(uri); it != workspace_symbols_.end())
|
||||
{
|
||||
return it->second.semantic_model.get();
|
||||
}
|
||||
|
||||
if (auto it = system_symbols_.find(uri); it != system_symbols_.end())
|
||||
{
|
||||
return it->second.semantic_model.get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<const language::symbol::SymbolTable*> Symbol::GetWorkspaceSymbolTables() const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
std::vector<const language::symbol::SymbolTable*> result;
|
||||
result.reserve(workspace_symbols_.size());
|
||||
for (const auto& [uri, entry] : workspace_symbols_)
|
||||
{
|
||||
(void)uri;
|
||||
result.push_back(entry.symbol_table.get());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<const language::symbol::SymbolTable*> Symbol::GetSystemSymbolTables() const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
std::vector<const language::symbol::SymbolTable*> result;
|
||||
result.reserve(system_symbols_.size());
|
||||
for (const auto& [uri, entry] : system_symbols_)
|
||||
{
|
||||
(void)uri;
|
||||
result.push_back(entry.symbol_table.get());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Symbol::IndexedSymbol> Symbol::QueryIndexedSymbols(protocol::SymbolKind kind, std::optional<SymbolSource> source) const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
std::vector<IndexedSymbol> result;
|
||||
for (const auto& [_, symbols] : index_by_name_)
|
||||
{
|
||||
for (const auto& item : symbols)
|
||||
{
|
||||
if (item.kind != kind)
|
||||
continue;
|
||||
if (source.has_value() && item.source != *source)
|
||||
continue;
|
||||
result.push_back(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Symbol::OnDocumentParsed(const events::DocumentParsed& event)
|
||||
{
|
||||
if (!event.tree)
|
||||
{
|
||||
spdlog::warn("Received null tree for document: {}", event.item.uri);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
DocumentAnalysis analysis;
|
||||
analysis.uri = event.item.uri;
|
||||
analysis.version = event.item.version;
|
||||
|
||||
analysis.deserializer = std::make_unique<language::ast::Deserializer>();
|
||||
auto ast_result = analysis.deserializer->Parse(
|
||||
ts_tree_root_node(event.tree),
|
||||
event.item.text);
|
||||
|
||||
if (!ast_result.IsSuccess())
|
||||
{
|
||||
spdlog::error("Failed to deserialize AST for: {}", event.item.uri);
|
||||
return;
|
||||
}
|
||||
|
||||
analysis.ast = std::move(ast_result.root);
|
||||
|
||||
analysis.symbol_table = std::make_unique<language::symbol::SymbolTable>();
|
||||
|
||||
language::symbol::Builder builder(*analysis.symbol_table);
|
||||
builder.Build(*analysis.ast);
|
||||
|
||||
analysis.semantic_model = std::make_unique<language::semantic::SemanticModel>(*analysis.symbol_table);
|
||||
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
editing_symbols_[event.item.uri] = std::move(analysis);
|
||||
RebuildIndex();
|
||||
}
|
||||
|
||||
spdlog::debug("Document parsed and symbols built: {}", event.item.uri);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
spdlog::error("Exception building symbols for {}: {}",
|
||||
event.item.uri,
|
||||
e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void Symbol::OnDocumentReparsed(const events::DocumentReparsed& event)
|
||||
{
|
||||
OnDocumentParsed(events::DocumentParsed{
|
||||
.item = event.item,
|
||||
.tree = event.tree });
|
||||
}
|
||||
|
||||
void Symbol::OnDocumentClosed(const events::DocumentClosed& event)
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
editing_symbols_.erase(event.textDocument.uri);
|
||||
RebuildIndex();
|
||||
spdlog::debug("Document closed and symbols removed: {}",
|
||||
event.textDocument.uri);
|
||||
}
|
||||
|
||||
void Symbol::RebuildIndex()
|
||||
{
|
||||
index_by_name_.clear();
|
||||
|
||||
auto add_container = [this](const auto& container, SymbolSource source) {
|
||||
for (const auto& [uri, entry] : container)
|
||||
{
|
||||
if (entry.symbol_table)
|
||||
AddTableToIndex(*entry.symbol_table, uri, source);
|
||||
}
|
||||
};
|
||||
|
||||
add_container(system_symbols_, SymbolSource::kSystem);
|
||||
add_container(workspace_symbols_, SymbolSource::kWorkspace);
|
||||
for (const auto& [uri, analysis] : editing_symbols_)
|
||||
{
|
||||
if (analysis.symbol_table)
|
||||
AddTableToIndex(*analysis.symbol_table, uri, SymbolSource::kEditing);
|
||||
}
|
||||
}
|
||||
|
||||
bool Symbol::IsTopLevelSymbol(const language::symbol::SymbolTable& table, language::symbol::SymbolId id) const
|
||||
{
|
||||
const auto& scopes = table.scopes().all_scopes();
|
||||
auto global_id = table.scopes().global_scope();
|
||||
auto it = scopes.find(global_id);
|
||||
if (it == scopes.end())
|
||||
return false;
|
||||
|
||||
const auto& symbols = it->second.symbols;
|
||||
for (const auto& [_, ids] : symbols)
|
||||
{
|
||||
if (std::find(ids.begin(), ids.end(), id) != ids.end())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Symbol::AddTableToIndex(const language::symbol::SymbolTable& table, const protocol::DocumentUri& uri, SymbolSource source)
|
||||
{
|
||||
for (const auto& wrapper : table.all_definitions())
|
||||
{
|
||||
const auto& symbol = wrapper.get();
|
||||
if (symbol.kind() != protocol::SymbolKind::Function &&
|
||||
symbol.kind() != protocol::SymbolKind::Class &&
|
||||
symbol.kind() != protocol::SymbolKind::Module)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsTopLevelSymbol(table, symbol.id()))
|
||||
continue;
|
||||
|
||||
IndexedSymbol item{
|
||||
.uri = uri,
|
||||
.name = symbol.name(),
|
||||
.kind = symbol.kind(),
|
||||
.source = source,
|
||||
.id = symbol.id() };
|
||||
|
||||
auto key = utils::ToLower(symbol.name());
|
||||
index_by_name_[key].push_back(std::move(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <shared_mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "../protocol/protocol.hpp"
|
||||
#include "../language/symbol/table.hpp"
|
||||
#include "../language/ast/deserializer.hpp"
|
||||
#include "../language/semantic/semantic_model.hpp"
|
||||
#include "./event_bus.hpp"
|
||||
#include "./events.hpp"
|
||||
#include "../utils/string.hpp"
|
||||
|
||||
namespace lsp::manager
|
||||
{
|
||||
class Symbol
|
||||
{
|
||||
public:
|
||||
enum class SymbolSource
|
||||
{
|
||||
kEditing,
|
||||
kWorkspace,
|
||||
kSystem
|
||||
};
|
||||
|
||||
struct IndexedSymbol
|
||||
{
|
||||
protocol::DocumentUri uri;
|
||||
std::string name;
|
||||
protocol::SymbolKind kind;
|
||||
SymbolSource source;
|
||||
language::symbol::SymbolId id;
|
||||
};
|
||||
|
||||
explicit Symbol(EventBus& event_bus);
|
||||
~Symbol();
|
||||
|
||||
void LoadSystemLibrary(const std::string& lib_path);
|
||||
void LoadWorkspace(const protocol::DocumentUri& workspace_uri);
|
||||
|
||||
const language::symbol::SymbolTable* GetSymbolTable(const protocol::DocumentUri& uri) const;
|
||||
const language::semantic::SemanticModel* GetSemanticModel(const protocol::DocumentUri& uri) const;
|
||||
|
||||
std::vector<const language::symbol::SymbolTable*> GetWorkspaceSymbolTables() const;
|
||||
std::vector<const language::symbol::SymbolTable*> GetSystemSymbolTables() const;
|
||||
std::vector<IndexedSymbol> QueryIndexedSymbols(protocol::SymbolKind kind, std::optional<SymbolSource> source = std::nullopt) const;
|
||||
|
||||
private:
|
||||
void OnDocumentParsed(const events::DocumentParsed& event);
|
||||
void OnDocumentReparsed(const events::DocumentReparsed& event);
|
||||
void OnDocumentClosed(const events::DocumentClosed& event);
|
||||
|
||||
struct DocumentAnalysis
|
||||
{
|
||||
protocol::DocumentUri uri;
|
||||
protocol::integer version;
|
||||
|
||||
std::unique_ptr<language::ast::Deserializer> deserializer;
|
||||
std::unique_ptr<language::ast::Program> ast;
|
||||
std::unique_ptr<language::symbol::SymbolTable> symbol_table;
|
||||
std::unique_ptr<language::semantic::SemanticModel> semantic_model;
|
||||
};
|
||||
|
||||
struct StoredSymbolEntry
|
||||
{
|
||||
std::unique_ptr<language::symbol::SymbolTable> symbol_table;
|
||||
std::unique_ptr<language::semantic::SemanticModel> semantic_model;
|
||||
};
|
||||
|
||||
void RebuildIndex();
|
||||
void AddTableToIndex(const language::symbol::SymbolTable& table, const protocol::DocumentUri& uri, SymbolSource source);
|
||||
bool IsTopLevelSymbol(const language::symbol::SymbolTable& table, language::symbol::SymbolId id) const;
|
||||
|
||||
std::unordered_map<std::string, StoredSymbolEntry> system_symbols_;
|
||||
std::unordered_map<std::string, StoredSymbolEntry> workspace_symbols_;
|
||||
std::unordered_map<protocol::DocumentUri, DocumentAnalysis> editing_symbols_;
|
||||
std::unordered_map<std::string, std::vector<IndexedSymbol>, utils::IHasher, utils::IEqualTo> index_by_name_;
|
||||
|
||||
EventBus& event_bus_;
|
||||
mutable std::shared_mutex mutex_;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user