822 lines
24 KiB
C++
822 lines
24 KiB
C++
#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())
|
||
{
|
||
UpdateInternalState();
|
||
spdlog::trace("Created document: {} (version {}, {} bytes)", item_.uri, item_.version, item_.text.length());
|
||
}
|
||
|
||
void Document::SetContent(int32_t newVersion, const std::string& newText)
|
||
{
|
||
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)
|
||
{
|
||
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)
|
||
{
|
||
pos.character = static_cast<int32_t>(byteOffset);
|
||
}
|
||
else
|
||
{
|
||
pos.character = ByteOffsetToCharacter(lines_[pos.line], byteOffset);
|
||
}
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|