tree-sitter test

This commit is contained in:
csh
2025-08-29 16:51:24 +08:00
parent aad4917fc0
commit 4d79b8d6ba
44 changed files with 180634 additions and 1841 deletions
+4 -4
View File
@@ -19,9 +19,9 @@ namespace lsp::core
spdlog::debug("RequestScheduler set in dispatcher");
}
void RequestDispatcher::SetDocumentService(services::DocumentService* document_manager)
void RequestDispatcher::SetSeviceContainer(services::ServiceContainer* service_container)
{
document_service_ = document_manager;
service_container_ = service_container;
spdlog::debug("DocumentService is set in dispatcher");
}
@@ -52,7 +52,7 @@ namespace lsp::core
std::string RequestDispatcher::Dispatch(const protocol::RequestMessage& request)
{
providers::ExecutionContext context(scheduler_, context_lifecycle_callback_, document_service_);
providers::ExecutionContext context(context_lifecycle_callback_, *scheduler_, *service_container_);
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
auto it = providers_.find(request.method);
@@ -76,7 +76,7 @@ namespace lsp::core
void RequestDispatcher::Dispatch(const protocol::NotificationMessage& notification)
{
providers::ExecutionContext context(scheduler_, context_lifecycle_callback_, document_service_);
providers::ExecutionContext context(context_lifecycle_callback_, *scheduler_, *service_container_);
std::shared_lock<std::shared_mutex> lock(notification_providers_mutex_);
// 先尝试精确匹配
+3 -2
View File
@@ -4,6 +4,7 @@
#include <shared_mutex>
#include "../protocol/protocol.hpp"
#include "../provider/base/provider_interface.hpp"
#include "../services/service_container.hpp"
namespace lsp::core
{
@@ -15,7 +16,7 @@ namespace lsp::core
~RequestDispatcher() = default;
void SetRequestScheduler(scheduler::RequestScheduler* scheduler);
void SetDocumentService(services::DocumentService* document_manager);
void SetSeviceContainer(services::ServiceContainer* service_container);
void RegisterRequestProvider(std::shared_ptr<providers::IRequestProvider> provider);
void RegisterNotificationProvider(std::shared_ptr<providers::INotificationProvider> provider);
@@ -52,6 +53,6 @@ namespace lsp::core
// 服务引用
scheduler::RequestScheduler* scheduler_ = nullptr;
services::DocumentService* document_service_ = nullptr;
services::ServiceContainer* service_container_ = nullptr;
};
}
+3 -2
View File
@@ -10,6 +10,7 @@
#include "../protocol/transform/facade.hpp"
#include "../scheduler/request_scheduler.hpp"
#include "../services/document.hpp"
#include "../services/document.hpp"
#include "./server.hpp"
namespace lsp::core
@@ -300,8 +301,8 @@ namespace lsp::core
{
spdlog::debug("Initializing extension services...");
document_service_ = std::make_unique<services::DocumentService>();
dispatcher_.SetDocumentService(document_service_.get());
// service_container_.RegisterService(std::shared_ptr<services::DocumentService>());
dispatcher_.SetSeviceContainer(&service_container_);
spdlog::debug("Extension services initialized");
}
+1 -4
View File
@@ -50,12 +50,9 @@ namespace lsp::core
void SendStateError(const protocol::RequestMessage& request);
private:
// 核心组件-必需的,生命周期和LspServer一致
RequestDispatcher dispatcher_;
scheduler::RequestScheduler scheduler_;
// 可选/扩展组件 -- 所以用智能指针
std::unique_ptr<services::DocumentService> document_service_;
services::ServiceContainer service_container_;
std::atomic<bool> is_initialized_ = false;
std::atomic<bool> is_shutting_down_ = false;
@@ -180,11 +180,6 @@ namespace lsp::protocol
ServerInfo serverInfo;
};
enum class InitializeErrorCodes
{
kUnknownProtocolVersion = 1
};
struct InitializeError
{
boolean retry;
@@ -201,8 +196,6 @@ namespace lsp::protocol
std::optional<integer> processId;
std::optional<ClientInfo> clientInfo;
std::optional<string> locale;
std::optional<string> rootPath;
std::optional<DocumentUri> rootUri;
std::optional<LSPAny> initializationOptions;
std::optional<ClientCapabilities> capabilities;
TraceValue trace;
@@ -86,15 +86,7 @@ namespace lsp::protocol
struct Hover
{
struct MarkedStringObject
{
std::string language;
std::string value;
};
using MarkedString = std::variant<std::string, MarkedStringObject>;
std::variant<MarkedString, std::vector<MarkedString>, MarkupContent> contents;
MarkupContent contents;
std::optional<Range> range;
};
@@ -67,7 +67,6 @@ namespace lsp::protocol
struct TextDocumentContentChangeEvent
{
Range range;
uinteger rangeLength;
string text;
};
@@ -47,22 +47,11 @@ namespace lsp::protocol
std::optional<string> detail;
SymbolKind kind;
std::optional<std::vector<SymbolTag>> tags;
std::optional<boolean> deprecated;
Range range;
Range selectionRange;
std::optional<std::vector<DocumentSymbol>> children;
};
struct SymbolInformation
{
string name;
SymbolKind kind;
std::optional<std::vector<SymbolTag>> tags;
std::optional<boolean> deprecated;
Location location;
std::optional<string> containerName;
};
struct DocumentSymbolClientCapabilities
{
struct SymbolKinds
-2
View File
@@ -103,8 +103,6 @@ namespace glz
&T::processId,
&T::clientInfo,
&T::locale,
&T::rootPath,
&T::rootUri,
&T::initializationOptions,
&T::capabilities,
&T::trace,
@@ -1,9 +1,10 @@
#pragma once
#include <memory>
#include <string>
#include <spdlog/spdlog.h>
#include "../../protocol/protocol.hpp"
#include "../../scheduler/request_scheduler.hpp"
#include "../../services/document.hpp"
#include "../../services/service_container.hpp"
namespace lsp::providers
{
@@ -21,12 +22,16 @@ namespace lsp::providers
class ExecutionContext
{
public:
ExecutionContext(scheduler::RequestScheduler* scheduler, LifecycleCallback lifecycle_callback, services::DocumentService* document_manager = nullptr) :
scheduler_(scheduler), lifecycle_callback_(lifecycle_callback), document_service_(document_manager) {}
ExecutionContext(LifecycleCallback lifecycle_callback, scheduler::RequestScheduler& scheduler, services::ServiceContainer& container) :
lifecycle_callback_(lifecycle_callback), scheduler_(scheduler), service_container_(container) {}
scheduler::RequestScheduler* GetScheduler() const { return scheduler_; }
scheduler::RequestScheduler& GetScheduler() const { return scheduler_; }
services::DocumentService* GetDocumentService() const { return document_service_; }
template<typename T>
T& GetService() const
{
return service_container_.Get<T>();
}
void TriggerLifecycleEvent(ServerLifecycleEvent event) const
{
@@ -35,9 +40,9 @@ namespace lsp::providers
}
private:
scheduler::RequestScheduler* scheduler_;
LifecycleCallback lifecycle_callback_;
services::DocumentService* document_service_;
scheduler::RequestScheduler& scheduler_;
services::ServiceContainer& service_container_;
};
// LSP请求提供者接口基类
@@ -4,6 +4,7 @@
#include "../initialized/initialized_provider.hpp"
#include "../text_document/did_open_provider.hpp"
#include "../text_document/did_change_provider.hpp"
#include "../text_document/did_close_provider.hpp"
#include "../text_document/completion_provider.hpp"
#include "../trace/set_trace_provider.hpp"
#include "../shutdown/shutdown_provider.hpp"
@@ -21,6 +22,7 @@ namespace lsp::providers
RegisterProvider<initialized::InitializedProvider>(dispatcher);
RegisterProvider<text_document::DidOpenProvider>(dispatcher);
RegisterProvider<text_document::DidChangeProvider>(dispatcher);
RegisterProvider<text_document::DidCloseProvider>(dispatcher);
RegisterProvider<text_document::CompletionProvider>(dispatcher);
RegisterProvider<set_trace::SetTraceProvider>(dispatcher);
RegisterProvider<shutdown::ShutdownProvider>(dispatcher);
@@ -20,11 +20,10 @@ namespace lsp::providers::cancel_request
std::string id_to_cancel = transform::debug::GetIdString(params.id);
spdlog::info("Processing cancel request for ID: {}", id_to_cancel);
if (auto* scheduler = context.GetScheduler())
{
bool cancelled = scheduler->Cancel(id_to_cancel);
spdlog::info("Cancel request {} result: {}", id_to_cancel, cancelled ? "success" : "not found");
}
auto& scheduler = context.GetScheduler();
bool cancelled = scheduler.Cancel(id_to_cancel);
spdlog::info("Cancel request {} result: {}", id_to_cancel, cancelled ? "success" : "not found");
}
catch (const std::exception& e)
{
@@ -1,5 +1,8 @@
#include <spdlog/spdlog.h>
#include "./did_change_provider.hpp"
#include "../../protocol/protocol.hpp"
#include "../../protocol/transform/facade.hpp"
#include "../../services/document.hpp"
namespace lsp::providers::text_document
{
@@ -16,8 +19,12 @@ namespace lsp::providers::text_document
void DidChangeProvider::HandleNotification(const protocol::NotificationMessage& notification, ExecutionContext& context)
{
static_cast<void>(context);
spdlog::debug("DidChangeProvider: Providing response for method {}", notification.method);
protocol::DidChangeTextDocumentParams did_change_text_document_params = transform::As<protocol::DidChangeTextDocumentParams>(notification.params.value());
services::DocumentService& document_service = context.GetService<services::DocumentService>();
document_service.UpdateDocument(did_change_text_document_params);
}
}
@@ -0,0 +1,30 @@
#include <spdlog/spdlog.h>
#include "./did_close_provider.hpp"
#include "../../protocol/protocol.hpp"
#include "../../protocol/transform/facade.hpp"
#include "../../services/document.hpp"
namespace lsp::providers::text_document
{
std::string DidCloseProvider::GetMethod() const
{
return "textDocument/didClose";
}
std::string DidCloseProvider::GetProviderName() const
{
return "DidCloseProvider";
}
void DidCloseProvider::HandleNotification(const protocol::NotificationMessage& notification, ExecutionContext& context)
{
spdlog::debug("DidCloseProvider: Providing response for method {}", notification.method);
protocol::DidCloseTextDocumentParams did_close_text_document_params = transform::As<protocol::DidCloseTextDocumentParams>(notification.params.value());
services::DocumentService& document_service = context.GetService<services::DocumentService>();
document_service.CloseDocument(did_close_text_document_params);
}
}
@@ -0,0 +1,14 @@
#pragma once
#include "../base/provider_interface.hpp"
namespace lsp::providers::text_document
{
class DidCloseProvider : public INotificationProvider
{
public:
DidCloseProvider() = default;
std::string GetMethod() const override;
std::string GetProviderName() const override;
void HandleNotification(const protocol::NotificationMessage& notification, ExecutionContext& context) override;
};
}
@@ -1,5 +1,7 @@
#include <spdlog/spdlog.h>
#include "./did_open_provider.hpp"
#include "../../services/document.hpp"
#include "../../protocol/transform/facade.hpp"
namespace lsp::providers::text_document
{
@@ -15,8 +17,23 @@ namespace lsp::providers::text_document
void DidOpenProvider::HandleNotification(const protocol::NotificationMessage& notification, ExecutionContext& context)
{
static_cast<void>(context);
spdlog::debug("DidOpenProvider: Providing response for method {}", notification.method);
protocol::DidOpenTextDocumentParams did_open_text_document_params = transform::As<protocol::DidOpenTextDocumentParams>(notification.params.value());
services::DocumentService& document_service = context.GetService<services::DocumentService>();
document_service.OpenDocument(did_open_text_document_params);
/*
if (auto* symbolService = context.TryGetService<SymbolService>()) {
symbolService->parseDocument(uri, content);
}
// 3. 触发诊断
if (auto* diagnosticService = context.TryGetService<DiagnosticService>()) {
diagnosticService->diagnose(uri);
}
*/
}
}
+94 -803
View File
@@ -1,821 +1,112 @@
#include <algorithm>
#include "./document.hpp"
namespace lsp::services
{
// ===== Document 实现 =====
Document::Document(const protocol::TextDocumentItem& item) :
item_(item), last_modified_time_(std::chrono::system_clock::now())
void DocumentService::OpenDocument(const protocol::DidOpenTextDocumentParams& params)
{
UpdateInternalState();
spdlog::trace("Created document: {} (version {}, {} bytes)", item_.uri, item_.version, item_.text.length());
std::unique_lock<std::shared_mutex> lock(mutex_);
documents_[params.textDocument.uri] = params.textDocument;
}
void Document::SetContent(int32_t newVersion, const std::string& newText)
void DocumentService::UpdateDocument(const protocol::DidChangeTextDocumentParams& params)
{
item_.version = newVersion;
item_.text = newText;
is_dirty_ = true;
last_modified_time_ = std::chrono::system_clock::now();
UpdateInternalState();
spdlog::trace("Document {} updated to version {} ({} bytes)", item_.uri, item_.version, item_.text.length());
}
void Document::ApplyContentChange(protocol::integer version, const std::vector<protocol::TextDocumentContentChangeEvent>& changes)
{
// 应用所有变更
for (const auto& change : changes)
std::unique_lock<std::shared_mutex> lock(mutex_);
auto it = documents_.find(params.textDocument.uri);
if (it != documents_.end())
{
ApplyContentChange(change);
}
item_.version = version;
is_dirty_ = true;
last_modified_time_ = std::chrono::system_clock::now();
UpdateInternalState();
spdlog::trace("Document {} updated to version {} with {} changes", item_.uri, item_.version, changes.size());
}
void Document::ApplyContentChange(const protocol::TextDocumentContentChangeEvent& change)
{
// 增量更新
size_t startOffset = PositionToOffset(change.range.start);
size_t endOffset = PositionToOffset(change.range.end);
// 替换指定范围
item_.text = item_.text.substr(0, startOffset) + change.text + item_.text.substr(endOffset);
}
size_t Document::PositionToOffset(const protocol::Position& position) const
{
if (position.line >= lines_.size())
{
return item_.text.length();
}
size_t offset = line_offsets_[position.line];
// 根据编码计算字符偏移
if (encoding_ == protocol::PositionEncodingKindLiterals::UTF8)
{
// 直接使用字节偏移
offset += std::min(static_cast<size_t>(position.character), lines_[position.line].length());
}
else
{
// UTF-16 或 UTF-32
offset += CharacterToByteOffset(lines_[position.line], position.character);
}
return offset;
}
protocol::Position Document::OffsetToPosition(size_t offset) const
{
protocol::Position pos;
pos.line = 0;
pos.character = 0;
// 二分查找行号
auto it = std::upper_bound(line_offsets_.begin(), line_offsets_.end(), offset);
if (it != line_offsets_.begin())
{
--it;
pos.line = static_cast<int32_t>(std::distance(line_offsets_.begin(), it));
size_t lineOffset = *it;
size_t byteOffset = offset - lineOffset;
// 根据编码计算字符位置
if (encoding_ == protocol::PositionEncodingKindLiterals::UTF8)
for (const auto& change : params.contentChanges)
{
pos.character = static_cast<int32_t>(byteOffset);
if (IsFullDocumentUpdate(change, it->second.text))
it->second.text = change.text;
else
ApplyIncrementalChange(it->second.text, change);
// 更新文本
}
if (params.textDocument.version.has_value())
it->second.version = params.textDocument.version.value();
}
}
void DocumentService::CloseDocument(const protocol::DidCloseTextDocumentParams& params)
{
std::unique_lock<std::shared_mutex> lock(mutex_);
documents_.erase(params.textDocument.uri);
}
void DocumentService::ApplyIncrementalChange(protocol::string& content, const protocol::TextDocumentContentChangeEvent& change)
{
protocol::uinteger start_offset = PositionToOffset(content, change.range.start);
protocol::uinteger end_offset = PositionToOffset(content, change.range.end);
if (start_offset > content.length() || end_offset > content.length() || start_offset > end_offset)
spdlog::error("Invalid range for text edit: start={}, end={}, length={}", start_offset, end_offset, content.length());
else
content.replace(start_offset, end_offset - start_offset, change.text);
}
bool DocumentService::IsFullDocumentUpdate(const protocol::TextDocumentContentChangeEvent& change, const protocol::string& current_content)
{
if (change.range.start.line == 0 && change.range.start.character == 0)
return true;
if (change.range.start.line == 0 && change.range.start.character == 0 && change.range.end.line == 0 && change.range.end.character == 0)
return true;
return false;
}
protocol::uinteger DocumentService::PositionToOffset(const protocol::string& content, const protocol::Position& position)
{
protocol::uinteger offset = 0;
protocol::uinteger current_line = 0;
// 找到目标行
while (offset < content.length() && current_line < position.line)
{
if (content[offset] == '\n')
current_line++;
offset++;
}
if (offset >= content.length())
return content.length();
protocol::uinteger current_char = 0;
while (offset < content.length() && current_char < position.character)
{
if (content[offset] == '\n')
break;
// UTF-8字符边界检测
unsigned char ch = static_cast<unsigned char>(content[offset]);
if ((ch & 0x80) == 0)
{
// ASCII (1 byte)
offset += 1;
}
else if ((ch & 0xE0) == 0xC0)
{
// 2-byte UTF-8
offset += 2;
}
else if ((ch & 0xF0) == 0xE0)
{
// 3-byte UTF-8
offset += 3;
}
else if ((ch & 0xF8) == 0xF0)
{
// 4-byte UTF-8
offset += 4;
}
else
{
pos.character = ByteOffsetToCharacter(lines_[pos.line], byteOffset);
// 错误的UTF-8序列,跳过一个字节
offset += 1;
}
current_char++;
}
return pos;
}
std::string Document::GetTextInRange(const protocol::Range& range) const
{
size_t start = PositionToOffset(range.start);
size_t end = PositionToOffset(range.end);
if (start >= item_.text.length())
{
return "";
}
end = std::min(end, item_.text.length());
return item_.text.substr(start, end - start);
}
std::optional<char> Document::GetCharAt(const protocol::Position& position) const
{
if (position.line >= lines_.size())
{
return std::nullopt;
}
const std::string& line = lines_[position.line];
size_t byteOffset = CharacterToByteOffset(line, position.character);
if (byteOffset >= line.length())
{
return std::nullopt;
}
return line[byteOffset];
}
std::string Document::GetLine(size_t lineNumber) const
{
if (lineNumber < lines_.size())
{
return lines_[lineNumber];
}
return "";
}
std::string Document::GetLineAt(const protocol::Position& position) const
{
return GetLine(position.line);
}
std::string Document::GetWordAt(const protocol::Position& position) const
{
if (position.line >= lines_.size())
{
return "";
}
const std::string& line = lines_[position.line];
size_t bytePos = CharacterToByteOffset(line, position.character);
// 找到单词边界
size_t start = bytePos;
while (start > 0 && IsWordChar(line[start - 1]))
{
--start;
}
size_t end = bytePos;
while (end < line.length() && IsWordChar(line[end]))
{
++end;
}
return line.substr(start, end - start);
}
protocol::Range Document::GetWordRangeAt(const protocol::Position& position) const
{
if (position.line >= lines_.size())
{
return protocol::Range{ position, position };
}
const std::string& line = lines_[position.line];
size_t bytePos = CharacterToByteOffset(line, position.character);
// 找到单词边界
size_t start = bytePos;
while (start > 0 && IsWordChar(line[start - 1]))
{
--start;
}
size_t end = bytePos;
while (end < line.length() && IsWordChar(line[end]))
{
++end;
}
protocol::Range range;
range.start.line = position.line;
range.start.character = ByteOffsetToCharacter(line, start);
range.end.line = position.line;
range.end.character = ByteOffsetToCharacter(line, end);
return range;
}
void Document::UpdateInternalState()
{
UpdateLines();
UpdateLineOffsets();
}
void Document::UpdateLines()
{
lines_.clear();
size_t start = 0;
for (size_t i = 0; i < item_.text.length(); ++i)
{
if (item_.text[i] == '\n')
{
lines_.push_back(item_.text.substr(start, i - start));
start = i + 1;
}
else if (item_.text[i] == '\r')
{
if (i + 1 < item_.text.length() && item_.text[i + 1] == '\n')
{
lines_.push_back(item_.text.substr(start, i - start));
start = i + 2;
++i; // Skip \n
}
else
{
lines_.push_back(item_.text.substr(start, i - start));
start = i + 1;
}
}
}
// 添加最后一行
if (start <= item_.text.length())
{
lines_.push_back(item_.text.substr(start));
}
}
void Document::UpdateLineOffsets()
{
line_offsets_.clear();
line_offsets_.reserve(lines_.size() + 1);
size_t offset = 0;
line_offsets_.push_back(0);
for (size_t i = 0; i < item_.text.length(); ++i)
{
if (item_.text[i] == '\n')
{
line_offsets_.push_back(i + 1);
}
else if (item_.text[i] == '\r')
{
if (i + 1 < item_.text.length() && item_.text[i + 1] == '\n')
{
line_offsets_.push_back(i + 2);
++i;
}
else
{
line_offsets_.push_back(i + 1);
}
}
}
}
bool Document::IsWordChar(char c) const
{
return std::isalnum(static_cast<unsigned char>(c)) || c == '_' || c == '$';
}
size_t Document::CharacterToByteOffset(const std::string& line, int32_t character) const
{
if (encoding_ == protocol::PositionEncodingKindLiterals::UTF8)
{
return std::min(static_cast<size_t>(character), line.length());
}
// UTF-16 编码:需要正确计算
size_t byteOffset = 0;
int32_t charCount = 0;
while (byteOffset < line.length() && charCount < character)
{
unsigned char c = line[byteOffset];
if (encoding_ == protocol::PositionEncodingKindLiterals::UTF16)
{
// UTF-16: 计算代码单元
if ((c & 0x80) == 0)
{
// ASCII
byteOffset += 1;
charCount += 1;
}
else if ((c & 0xE0) == 0xC0)
{
// 2字节UTF-8 -> 1个UTF-16单元
byteOffset += 2;
charCount += 1;
}
else if ((c & 0xF0) == 0xE0)
{
// 3字节UTF-8 -> 1个UTF-16单元
byteOffset += 3;
charCount += 1;
}
else if ((c & 0xF8) == 0xF0)
{
// 4字节UTF-8 -> 2个UTF-16单元(代理对)
byteOffset += 4;
charCount += 2;
}
else
{
byteOffset += 1; // 错误情况
}
}
else // UTF32
{
// UTF-32: 每个Unicode代码点算一个
if ((c & 0x80) == 0)
{
byteOffset += 1;
}
else if ((c & 0xE0) == 0xC0)
{
byteOffset += 2;
}
else if ((c & 0xF0) == 0xE0)
{
byteOffset += 3;
}
else if ((c & 0xF8) == 0xF0)
{
byteOffset += 4;
}
else
{
byteOffset += 1;
}
charCount += 1;
}
}
return byteOffset;
}
int32_t Document::ByteOffsetToCharacter(const std::string& line, size_t byteOffset) const
{
if (encoding_ == protocol::PositionEncodingKindLiterals::UTF8)
{
return static_cast<int32_t>(byteOffset);
}
int32_t charCount = 0;
size_t pos = 0;
while (pos < byteOffset && pos < line.length())
{
unsigned char c = line[pos];
if (encoding_ == protocol::PositionEncodingKindLiterals::UTF16)
{
if ((c & 0x80) == 0)
{
pos += 1;
charCount += 1;
}
else if ((c & 0xE0) == 0xC0)
{
pos += 2;
charCount += 1;
}
else if ((c & 0xF0) == 0xE0)
{
pos += 3;
charCount += 1;
}
else if ((c & 0xF8) == 0xF0)
{
pos += 4;
charCount += 2; // 代理对
}
else
{
pos += 1;
charCount += 1;
}
}
else // UTF32
{
if ((c & 0x80) == 0)
{
pos += 1;
}
else if ((c & 0xE0) == 0xC0)
{
pos += 2;
}
else if ((c & 0xF0) == 0xE0)
{
pos += 3;
}
else if ((c & 0xF8) == 0xF0)
{
pos += 4;
}
else
{
pos += 1;
}
charCount += 1;
}
}
return charCount;
}
// ===== DocumentManager 实现 =====
void DocumentManager::DidOpenTextDocument(const protocol::DidOpenTextDocumentParams& params)
{
std::unique_lock lock(mutex_);
// 检查文档大小
if (config_.max_document_size > 0 &&
params.textDocument.text.length() > config_.max_document_size)
{
spdlog::error("Document {} exceeds maximum size ({} > {})",
params.textDocument.uri,
params.textDocument.text.length(),
config_.max_document_size);
return;
}
// 创建新文档
auto doc = std::make_shared<Document>(params.textDocument);
doc->SetEncoding(config_.default_encoding);
documents_[params.textDocument.uri] = doc;
spdlog::info("Opened document: {} (version {}, {} bytes, language: {})",
params.textDocument.uri,
params.textDocument.version,
params.textDocument.text.length(),
params.textDocument.languageId);
}
void DocumentManager::DidChangeTextDocument(const protocol::DidChangeTextDocumentParams& params)
{
std::unique_lock lock(mutex_);
auto it = documents_.find(params.textDocument.uri);
if (it == documents_.end())
{
spdlog::error("Attempt to change non-existent document: {}",
params.textDocument.uri);
return;
}
auto& doc = it->second;
// 版本检查
if (params.textDocument.version)
{
protocol::integer expectedVersion = params.textDocument.version;
if (expectedVersion <= doc->GetVersion())
{
spdlog::warn("Ignoring stale change for {}: version {} <= current {}",
params.textDocument.uri,
expectedVersion,
doc->GetVersion());
return;
}
}
// 应用变更
if (params.contentChanges.empty())
{
spdlog::warn("Empty content changes for document: {}", params.textDocument.uri);
return;
}
// 检查是全文还是增量
if (params.contentChanges.size() == 1)
{
// 全文更新
// doc->SetContent(params.textDocument.version(doc->GetVersion() + 1), params.contentChanges[0].text);
}
else
{
// 增量更新
// doc->ApplyContentChanges( params.textDocument.version(doc->GetVersion() + 1), params.contentChanges);
}
spdlog::debug("Changed document: {} to version {} ({} changes)", params.textDocument.uri, doc->GetVersion(), params.contentChanges.size());
}
void DocumentManager::DidCloseTextDocument(const protocol::DidCloseTextDocumentParams& params)
{
std::unique_lock lock(mutex_);
auto it = documents_.find(params.textDocument.uri);
if (it == documents_.end())
{
spdlog::warn("Attempt to close non-existent document: {}",
params.textDocument.uri);
return;
}
documents_.erase(it);
spdlog::info("Closed document: {}", params.textDocument.uri);
}
void DocumentManager::DidSaveTextDocument(const protocol::DidSaveTextDocumentParams& params)
{
std::shared_lock lock(mutex_);
auto it = documents_.find(params.textDocument.uri);
if (it == documents_.end())
{
spdlog::warn("Attempt to save non-existent document: {}",
params.textDocument.uri);
return;
}
it->second->SetDirty(false);
// 如果保存通知包含文本,可以验证同步状态
if (params.text.has_value())
{
if (params.text.value() != it->second->GetText())
{
spdlog::error("Document content mismatch on save for: {}", params.textDocument.uri);
}
}
spdlog::info("Saved document: {}", params.textDocument.uri);
}
std::shared_ptr<Document> DocumentManager::GetDocument(const std::string& uri) const
{
std::shared_lock lock(mutex_);
auto it = documents_.find(uri);
if (it != documents_.end())
{
return it->second;
}
return nullptr;
}
std::vector<std::string> DocumentManager::GetAllUris() const
{
std::shared_lock lock(mutex_);
std::vector<std::string> uris;
uris.reserve(documents_.size());
for (const auto& [uri, doc] : documents_)
{
uris.push_back(uri);
}
return uris;
}
std::vector<std::shared_ptr<Document>> DocumentManager::GetAllDocuments() const
{
std::shared_lock lock(mutex_);
std::vector<std::shared_ptr<Document>> docs;
docs.reserve(documents_.size());
for (const auto& [uri, doc] : documents_)
{
docs.push_back(doc);
}
return docs;
}
std::vector<std::shared_ptr<Document>> DocumentManager::GetDocumentsByLanguage(
const std::string& languageId) const
{
std::shared_lock lock(mutex_);
std::vector<std::shared_ptr<Document>> docs;
for (const auto& [uri, doc] : documents_)
{
if (doc->GetLanguageId() == languageId)
{
docs.push_back(doc);
}
}
return docs;
}
bool DocumentManager::HasDocument(const std::string& uri) const
{
std::shared_lock lock(mutex_);
return documents_.find(uri) != documents_.end();
}
size_t DocumentManager::GetDocumentCount() const
{
std::shared_lock lock(mutex_);
return documents_.size();
}
std::vector<std::string> DocumentManager::GetDirtyDocuments() const
{
std::shared_lock lock(mutex_);
std::vector<std::string> dirtyUris;
for (const auto& [uri, doc] : documents_)
{
if (doc->IsDirty())
{
dirtyUris.push_back(uri);
}
}
return dirtyUris;
}
std::string DocumentManager::ResolveUri(const std::string& uri) const
{
// 如果已经是绝对URI,直接返回
if (utils::IsFileUri(uri))
{
return uri;
}
// 尝试相对于工作区文件夹解析
for (const auto& folder : workspace_folders_)
{
std::string folderPath = utils::UriToPath(folder.uri);
std::string resolvedPath = folderPath + "/" + uri;
// 检查文件是否存在(这里简化处理)
return utils::PathToUri(resolvedPath);
}
return uri;
}
// ===== 工具函数实现 =====
namespace utils
{
std::string NormalizeUri(const std::string& uri)
{
std::string normalized = uri;
// 确保使用正斜杠
std::replace(normalized.begin(), normalized.end(), '\\', '/');
// 移除重复的斜杠
auto newEnd = std::unique(normalized.begin(), normalized.end(), [](char a, char b) { return a == '/' && b == '/'; });
normalized.erase(newEnd, normalized.end());
return normalized;
}
std::string UriToPath(const std::string& uri)
{
if (uri.substr(0, 7) == "file://")
{
std::string path = uri.substr(7);
// Windows路径处理
#ifdef _WIN32
if (path.length() >= 3 && path[0] == '/' &&
std::isalpha(path[1]) && path[2] == ':')
{
path = path.substr(1);
}
#endif
return path;
}
return uri;
}
std::string PathToUri(const std::string& path)
{
std::string uri = "file://";
#ifdef _WIN32
// Windows路径
if (path.length() >= 2 && std::isalpha(path[0]) && path[1] == ':')
{
uri += "/";
}
#endif
uri += path;
return NormalizeUri(uri);
}
bool IsFileUri(const std::string& uri)
{
return uri.substr(0, 7) == "file://";
}
protocol::TextEdit CreateReplace(const protocol::Range& range, const std::string& newText)
{
protocol::TextEdit edit;
edit.range = range;
edit.newText = newText;
return edit;
}
protocol::TextEdit CreateInsert(const protocol::Position& position, const std::string& text)
{
return CreateReplace(protocol::Range{ position, position }, text);
}
protocol::TextEdit CreateDelete(const protocol::Range& range)
{
return CreateReplace(range, "");
}
bool IsPositionInRange(const protocol::Position& position, const protocol::Range& range)
{
if (position.line < range.start.line || position.line > range.end.line)
{
return false;
}
if (position.line == range.start.line && position.character < range.start.character)
{
return false;
}
if (position.line == range.end.line && position.character >= range.end.character)
{
return false;
}
return true;
}
bool IsRangeOverlapping(const protocol::Range& a, const protocol::Range& b)
{
return !(a.end.line < b.start.line ||
(a.end.line == b.start.line && a.end.character <= b.start.character) ||
b.end.line < a.start.line ||
(b.end.line == a.start.line && b.end.character <= a.start.character));
}
protocol::Range ExtendRange(const protocol::Range& range, int32_t lines)
{
protocol::Range extended = range;
extended.start.line = std::max(static_cast<std::int32_t>(0), static_cast<std::int32_t>(extended.start.line - lines));
extended.end.line += lines;
return extended;
}
std::string ApplyTextEdits(const std::string& text,
const std::vector<protocol::TextEdit>& edits)
{
if (edits.empty())
{
return text;
}
// 排序编辑(从后向前,避免偏移问题)
std::vector<protocol::TextEdit> sortedEdits = edits;
std::sort(sortedEdits.begin(), sortedEdits.end(), [](const protocol::TextEdit& a, const protocol::TextEdit& b) {
if (a.range.start.line != b.range.start.line)
{
return a.range.start.line > b.range.start.line;
}
return a.range.start.character > b.range.start.character;
});
// 创建临时文档来应用编辑
protocol::TextDocumentItem tempItem;
tempItem.uri = "temp://";
tempItem.languageId = "";
tempItem.version = 0;
tempItem.text = text;
Document tempDoc(tempItem);
std::string result = text;
for (const auto& edit : sortedEdits)
{
size_t start = tempDoc.PositionToOffset(edit.range.start);
size_t end = tempDoc.PositionToOffset(edit.range.end);
result = result.substr(0, start) + edit.newText + result.substr(end);
// 更新临时文档
tempDoc.SetContent(0, result);
}
return result;
}
return std::min(offset, static_cast<protocol::uinteger>(content.length()));
}
}
+12 -219
View File
@@ -1,238 +1,31 @@
#pragma once
#include <string>
#include <vector>
#include <optional>
#include <chrono>
#include <unordered_map>
#include <shared_mutex>
#include <spdlog/spdlog.h>
#include "../protocol/protocol.hpp"
namespace lsp::services
{
class Document
class DocumentService
{
public:
Document(const protocol::TextDocumentItem& item);
DocumentService() = default;
~DocumentService() = default;
const protocol::DocumentUri& GetUri() const { return item_.uri; }
const protocol::string& GetLanguageId() const { return item_.languageId; }
protocol::integer GetVersion() const { return item_.version; }
const protocol::string& GetText() const { return item_.text; }
const protocol::TextDocumentItem& GetItem() const { return item_; }
void OpenDocument(const protocol::DidOpenTextDocumentParams& params);
void UpdateDocument(const protocol::DidChangeTextDocumentParams& params);
void CloseDocument(const protocol::DidCloseTextDocumentParams& params);
protocol::TextDocumentIdentifier GetIdentifier() const
{
return protocol::TextDocumentIdentifier{item_.uri};
}
protocol::VersionedTextDocumentIdentifier GetVersionedIdentifier() const
{
protocol::VersionedTextDocumentIdentifier id;
id.uri = item_.uri;
id.version = item_.version;
return id;
}
// ===== 位置和范围操作 =====
size_t PositionToOffset(const protocol::Position& position) const;
protocol::Position OffsetToPosition(size_t offset) const;
std::string GetTextInRange(const protocol::Range& range) const;
// 内容更新
void SetContent(protocol::integer version, const protocol::string& new_text);
void ApplyContentChange(protocol::integer version, const std::vector<protocol::TextDocumentContentChangeEvent>& changes);
// 获取指定位置的字符
std::optional<char> GetCharAt(const protocol::Position& position) const;
// ===== 行操作 =====
const std::vector<std::string>& GetLines() const { return lines_; }
size_t GetLineCount() const { return lines_.size(); }
std::string GetLine(size_t lineNumber) const;
std::string GetLineAt(const protocol::Position& position) const;
// ===== 单词和符号操作 =====
std::string GetWordAt(const protocol::Position& position) const;
protocol::Range GetWordRangeAt(const protocol::Position& position) const;
// ===== 实用方法 =====
// 创建一个Location
protocol::Location CreateLocation(const protocol::Range& range) const
{
return protocol::Location{item_.uri, range};
}
// 创建一个TextDocumentPositionParams
protocol::TextDocumentPositionParams CreatePositionParams(const protocol::Position& position) const
{
protocol::TextDocumentPositionParams params;
params.textDocument = GetIdentifier();
params.position = position;
return params;
}
// ===== 元数据 =====
// 文档是否被修改(相对于上次保存)
bool IsDirty() const { return is_dirty_; }
void SetDirty(bool dirty) { is_dirty_ = dirty; }
// 最后修改时间
std::chrono::system_clock::time_point GetLastModified() const { return last_modified_time_; }
void SetEncoding(protocol::PositionEncodingKind encoding) { encoding_ = encoding; }
protocol::PositionEncodingKind GetEncoding() const { return encoding_; }
std::optional<protocol::string> GetContent(const protocol::string& uri) const;
private:
// 更新内部缓存
void UpdateInternalState();
void UpdateLines();
void UpdateLineOffsets();
void ApplyIncrementalChange(protocol::string& content, const protocol::TextDocumentContentChangeEvent& change);
// 辅助方法
bool IsWordChar(char c) const;
size_t CharacterToByteOffset(const std::string& line, std::int32_t character) const;
std::int32_t ByteOffsetToCharacter(const std::string& line, size_t byteOffset) const;
// 应用单个内容变更
void ApplyContentChange(const protocol::TextDocumentContentChangeEvent& change);
private:
protocol::TextDocumentItem item_;
// 缓存行的信息
std::vector<std::string> lines_;
std::vector<size_t> line_offsets_;
bool is_dirty_ = false;
std::chrono::system_clock::time_point last_modified_time_;
protocol::PositionEncodingKind encoding_ = protocol::PositionEncodingKindLiterals::UTF16;
};
/**
* 文档管理器 - 使用protocol类型作为接口
*/
class DocumentManager
{
public:
DocumentManager() = default;
~DocumentManager() = default;
// 禁止拷贝
DocumentManager(const DocumentManager&) = delete;
DocumentManager& operator=(const DocumentManager&) = delete;
// ===== 文档生命周期管理 - 直接使用protocol类型 =====
// 处理 textDocument/didOpen
void DidOpenTextDocument(const protocol::DidOpenTextDocumentParams& params);
// 处理 textDocument/didChange
void DidChangeTextDocument(const protocol::DidChangeTextDocumentParams& params);
// 处理 textDocument/didClose
void DidCloseTextDocument(const protocol::DidCloseTextDocumentParams& params);
// 处理 textDocument/didSave
void DidSaveTextDocument(const protocol::DidSaveTextDocumentParams& params);
// ===== 文档访问 - 支持多种查询方式 =====
// 通过URI获取
std::shared_ptr<Document> GetDocument(const std::string& uri) const;
// 通过标识符获取
std::shared_ptr<Document> GetDocument(const protocol::TextDocumentIdentifier& identifier) const
{
return GetDocument(identifier.uri);
}
// 通过版本化标识符获取
std::shared_ptr<Document> GetDocument(const protocol::VersionedTextDocumentIdentifier& identifier) const
{
auto doc = GetDocument(identifier.uri);
if (doc && identifier.version && doc->GetVersion() != identifier.version)
{
spdlog::warn("Version mismatch for {}: expected {}, got {}", identifier.uri, identifier.version, doc->GetVersion());
}
return doc;
}
// 通过TextDocumentPositionParams获取
std::shared_ptr<Document> GetDocument(const protocol::TextDocumentPositionParams& params) const
{
return GetDocument(params.textDocument);
}
// ===== 批量操作 =====
std::vector<std::string> GetAllUris() const;
std::vector<std::shared_ptr<Document>> GetAllDocuments() const;
std::vector<std::shared_ptr<Document>> GetDocumentsByLanguage(const std::string& languageId) const;
// ===== 查询 =====
bool HasDocument(const std::string& uri) const;
bool IsDocumentOpen(const protocol::TextDocumentIdentifier& identifier) const
{
return HasDocument(identifier.uri);
}
size_t GetDocumentCount() const;
// ===== 诊断支持 =====
// 获取需要诊断的文档(已修改的)
std::vector<std::string> GetDirtyDocuments() const;
// ===== 工作区支持 =====
// 设置工作区文件夹(用于相对路径解析)
void SetWorkspaceFolders(const std::vector<protocol::WorkspaceFolder>& folders)
{
workspace_folders_ = folders;
}
const std::vector<protocol::WorkspaceFolder>& GetWorkspaceFolders() const
{
return workspace_folders_;
}
// 解析相对URI
std::string ResolveUri(const std::string& uri) const;
// ===== 配置 =====
struct Configuration {
size_t max_document_size = 10 * 1024 * 1024; // 10MB
bool validate_utf8 = true;
protocol::PositionEncodingKind default_encoding = protocol::PositionEncodingKindLiterals::UTF16;
};
void SetConfiguration(const Configuration& config) { config_ = config; }
const Configuration& GetConfiguration() const { return config_; }
static bool IsFullDocumentUpdate(const protocol::TextDocumentContentChangeEvent& change, const protocol::string& current_content);
static protocol::uinteger PositionToOffset(const protocol::string& content, const protocol::Position& position);
private:
mutable std::shared_mutex mutex_;
std::unordered_map<std::string, std::shared_ptr<Document>> documents_;
std::vector<protocol::WorkspaceFolder> workspace_folders_;
Configuration config_;
std::unordered_map<protocol::string, protocol::TextDocumentItem> documents_;
};
// ===== 工具函数 =====
namespace utils
{
// URI处理
std::string NormalizeUri(const std::string& uri);
std::string UriToPath(const std::string& uri);
std::string PathToUri(const std::string& path);
bool IsFileUri(const std::string& uri);
// 创建TextEdit
protocol::TextEdit CreateReplace(const protocol::Range& range, const std::string& newText);
protocol::TextEdit CreateInsert(const protocol::Position& position, const std::string& text);
protocol::TextEdit CreateDelete(const protocol::Range& range);
// 范围操作
bool IsPositionInRange(const protocol::Position& position, const protocol::Range& range);
bool IsRangeOverlapping(const protocol::Range& a, const protocol::Range& b);
protocol::Range ExtendRange(const protocol::Range& range, int32_t lines);
// 应用编辑
std::string ApplyTextEdits(const std::string& text, const std::vector<protocol::TextEdit>& edits);
}
}
@@ -0,0 +1,43 @@
#pragma once
#include <any>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <stdexcept>
#include <unordered_map>
#include <typeindex>
#include <spdlog/spdlog.h>
namespace lsp::services
{
class ServiceContainer
{
public:
// 注册服务
template<typename T>
void RegisterService(std::shared_ptr<T> service)
{
std::unique_lock<std::shared_mutex> lock(mutex_);
if (!service)
throw std::invalid_argument("Cannot register null service");
services_[std::type_index(typeid(T))] = service;
spdlog::info("Registered service '{}' ", std::type_index(typeid(T)).name());
}
// 获取服务
template<typename T>
T& Get() const
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = services_.find(std::type_index(typeid(T)));
if (it != services_.end())
return *std::any_cast<std::shared_ptr<T>>(it->second);
throw std::runtime_error(std::string("Service not found:") + typeid(T).name());
}
private:
mutable std::shared_mutex mutex_;
std::unordered_map<std::type_index, std::any> services_;
};
}
+186
View File
@@ -0,0 +1,186 @@
#include <sstream>
#include <regex>
#include <algorithm>
#include "symbol.hpp"
namespace lsp::services::symbol
{
void SymbolService::UpdateDocument(const std::string& uri, const std::string& content)
{
// 从URI提取文件名作为unit名称
std::string unit_name = uri.substr(uri.find_last_of("/\\") + 1);
if (unit_name.find(".tsf") != std::string::npos)
{
unit_name = unit_name.substr(0, unit_name.find(".tsf"));
}
// 解析符号
auto symbols = ParseTsfUnit(content, unit_name);
// 更新文档符号
document_symbols_[uri] = symbols;
// 更新unit导出符号(只包含public符号)
std::vector<UnitSymbol> public_symbols;
std::copy_if(symbols.begin(), symbols.end(), std::back_inserter(public_symbols), [](const UnitSymbol& s) { return s.is_public; });
if (!public_symbols.empty())
{
unit_exports_[unit_name] = public_symbols;
}
}
std::vector<protocol::DocumentSymbol> SymbolService::GetDocumentSymbols(const std::string& uri) const
{
std::vector<protocol::DocumentSymbol> result;
auto it = document_symbols_.find(uri);
if (it != document_symbols_.end())
{
for (const auto& info : it->second)
{
result.push_back(info.symbol);
}
}
return result;
}
std::vector<UnitSymbol> SymbolService::GetCompletionSymbols(const std::string& uri) const
{
std::vector<UnitSymbol> result;
// TODO: 这里应该解析uses语句,确定哪些unit被导入
// 现在简单返回所有public符号
for (const auto& [unit_name, symbols] : unit_exports_)
{
result.insert(result.end(), symbols.begin(), symbols.end());
}
return result;
}
void SymbolService::RemoveDocument(const std::string& uri)
{
document_symbols_.erase(uri);
}
std::vector<UnitSymbol> SymbolService::ParseTsfUnit(const std::string& content, const std::string& unit_name)
{
std::vector<UnitSymbol> symbols;
std::istringstream stream(content);
std::string line;
protocol::uinteger line_number = 0;
bool in_interface = false;
bool in_implementation = false;
while (std::getline(stream, line))
{
std::string trimmed_line = trim(line);
// 跟踪section
if (trimmed_line == "interface")
{
in_interface = true;
in_implementation = false;
}
else if (trimmed_line == "implementation")
{
in_interface = false;
in_implementation = true;
}
// 解析符号
if (!trimmed_line.empty() && (in_interface || in_implementation))
{
// 解析常量
if (trimmed_line.find("const ") == 0)
{
std::regex constRegex(R"(const\s+(\w+)\s*=\s*(.+);?)");
std::smatch match;
if (std::regex_search(trimmed_line, match, constRegex))
{
UnitSymbol info;
info.symbol.name = match[1];
info.symbol.detail = "= " + std::string(match[2]);
info.symbol.kind = protocol::SymbolKind::kConstant;
info.symbol.range = { { line_number, 0 }, { line_number, static_cast<protocol::uinteger>(line.length()) } };
info.symbol.selectionRange = { { line_number, static_cast<protocol::uinteger>(line.find(match[1])) },
{ line_number, static_cast<protocol::uinteger>(line.find(match[1]) + match[1].length()) } };
info.signature = trimmed_line;
info.unit_name = unit_name;
info.is_public = in_interface;
symbols.push_back(info);
}
}
// 解析函数
else if (trimmed_line.find("function ") == 0)
{
std::regex funcRegex(R"(function\s+(\w+)\s*\(([^)]*)\)\s*(?::\s*(\w+))?;?)");
std::smatch match;
if (std::regex_search(trimmed_line, match, funcRegex))
{
UnitSymbol info;
info.symbol.name = match[1];
info.symbol.kind = protocol::SymbolKind::kFunction;
// 构建函数签名
std::string params = match[2];
std::string returnType = match[3];
info.symbol.detail = "(" + params + ")";
if (!returnType.empty())
{
info.symbol.detail = info.symbol.detail.value() + " : " + returnType;
}
info.symbol.range = { { line_number, 0 }, { line_number, static_cast<protocol::uinteger>(line.length()) } };
info.symbol.selectionRange = { { line_number, static_cast<protocol::uinteger>(line.find(match[1])) },
{ line_number, static_cast<protocol::uinteger>(line.find(match[1]) + match[1].length()) } };
info.signature = trimmed_line;
info.unit_name = unit_name;
info.is_public = in_interface;
symbols.push_back(info);
}
}
// 解析类型定义
else if (trimmed_line.find("type ") == 0)
{
std::regex typeRegex(R"(type\s+(\w+)\s*=\s*class)");
std::smatch match;
if (std::regex_search(trimmed_line, match, typeRegex))
{
UnitSymbol info;
info.symbol.name = match[1];
info.symbol.kind = protocol::SymbolKind::kClass;
info.symbol.detail = "class";
info.symbol.range = { { line_number, 0 }, { line_number, static_cast<protocol::uinteger>(line.length()) } };
info.symbol.selectionRange = { { line_number, static_cast<protocol::uinteger>(line.find(match[1])) },
{ line_number, static_cast<protocol::uinteger>(line.find(match[1]) + match[1].length()) } };
info.signature = trimmed_line;
info.unit_name = unit_name;
info.is_public = in_interface;
// TODO: 解析类成员作为children
symbols.push_back(info);
}
}
}
line_number++;
}
return symbols;
}
std::string SymbolService::trim(const std::string& str)
{
size_t first = str.find_first_not_of(" \t\r\n");
if (first == std::string::npos)
return "";
size_t last = str.find_last_not_of(" \t\r\n");
return str.substr(first, (last - first + 1));
}
} // namespace lsp::services
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include <unordered_map>
#include <vector>
#include <string>
#include "../protocol/protocol.hpp"
namespace lsp::services::symbol
{
struct UnitSymbol
{
protocol::DocumentSymbol symbol;
std::string signature;
std::string unit_name;
bool is_public;
};
class SymbolService
{
public:
void UpdateDocument(const std::string& uri, const std::string& content);
void RemoveDocument(const std::string& uri);
std::vector<protocol::DocumentSymbol> GetDocumentSymbols(const std::string& uri) const;
std::vector<UnitSymbol> GetCompletionSymbols(const std::string& uri) const;
private:
std::vector<UnitSymbol> ParseTsfUnit(const std::string& content, const std::string& unit_name);
protocol::SymbolKind GetSymbolKind(const std::string& line);
std::string trim(const std::string& str);
private:
std::unordered_map<std::string, std::vector<UnitSymbol>> document_symbols_;
std::unordered_map<std::string, std::vector<UnitSymbol>> unit_exports_;
};
}