tree-sitter test
This commit is contained in:
@@ -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_);
|
||||
// 先尝试精确匹配
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
@@ -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_;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user