tsl-devkit/lsp-server/src/services/document.cpp

822 lines
24 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}
}
}