LSP: add resource operations to the protocol implementation

This allows the language server to request file creation, renaming, or
deletion.

Fixes: QTCREATORBUG-29542
Change-Id: I31ab3c0b36f87d3b797b54ff4261cab85a322e2c
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2023-08-28 15:00:08 +02:00
parent c2ba218583
commit ac5ab71db8
9 changed files with 364 additions and 28 deletions

View File

@@ -539,24 +539,50 @@ Utils::SearchResultItems generateReplaceItems(const WorkspaceEdit &edits,
bool limitToProjects,
const DocumentUri::PathMapper &pathMapper)
{
Utils::SearchResultItems items;
auto convertEdits = [](const QList<TextEdit> &edits) {
return Utils::transform(edits, [](const TextEdit &edit) {
return ItemData{SymbolSupport::convertRange(edit.range()), QVariant(edit)};
});
};
QMap<Utils::FilePath, QList<ItemData>> rangesInDocument;
auto documentChanges = edits.documentChanges().value_or(QList<TextDocumentEdit>());
auto documentChanges = edits.documentChanges().value_or(QList<DocumentChange>());
if (!documentChanges.isEmpty()) {
for (const TextDocumentEdit &documentChange : std::as_const(documentChanges)) {
rangesInDocument[documentChange.textDocument().uri().toFilePath(pathMapper)]
= convertEdits(documentChange.edits());
for (const DocumentChange &documentChange : std::as_const(documentChanges)) {
if (std::holds_alternative<TextDocumentEdit>(documentChange)) {
const TextDocumentEdit edit = std::get<TextDocumentEdit>(documentChange);
rangesInDocument[edit.textDocument().uri().toFilePath(pathMapper)] = convertEdits(
edit.edits());
} else {
Utils::SearchResultItem item;
if (std::holds_alternative<LanguageServerProtocol::CreateFile>(documentChange)) {
auto op = std::get<LanguageServerProtocol::CreateFile>(documentChange);
item.setLineText(op.message(pathMapper));
item.setFilePath(op.uri().toFilePath(pathMapper));
item.setUserData(QVariant(op));
} else if (std::holds_alternative<RenameFile>(documentChange)) {
auto op = std::get<RenameFile>(documentChange);
item.setLineText(op.message(pathMapper));
item.setFilePath(op.oldUri().toFilePath(pathMapper));
item.setUserData(QVariant(op));
} else if (std::holds_alternative<LanguageServerProtocol::DeleteFile>(documentChange)) {
auto op = std::get<LanguageServerProtocol::DeleteFile>(documentChange);
item.setLineText(op.message(pathMapper));
item.setFilePath(op.uri().toFilePath(pathMapper));
item.setUserData(QVariant(op));
}
items << item;
}
}
} else {
auto changes = edits.changes().value_or(WorkspaceEdit::Changes());
for (auto it = changes.begin(), end = changes.end(); it != end; ++it)
rangesInDocument[it.key().toFilePath(pathMapper)] = convertEdits(it.value());
}
return generateSearchResultItems(rangesInDocument, search, limitToProjects);
items += generateSearchResultItems(rangesInDocument, search, limitToProjects);
return items;
}
Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams &positionParams,
@@ -659,15 +685,25 @@ void SymbolSupport::applyRename(const Utils::SearchResultItems &checkedItems,
{
QSet<Utils::FilePath> affectedNonOpenFilePaths;
QMap<Utils::FilePath, QList<TextEdit>> editsForDocuments;
QList<DocumentChange> changes;
for (const Utils::SearchResultItem &item : checkedItems) {
const auto filePath = Utils::FilePath::fromUserInput(item.path().value(0));
if (!m_client->documentForFilePath(filePath))
affectedNonOpenFilePaths << filePath;
TextEdit edit(item.userData().toJsonObject());
if (edit.isValid())
const QJsonObject jsonObject = item.userData().toJsonObject();
if (const TextEdit edit(jsonObject); edit.isValid())
editsForDocuments[filePath] << edit;
else if (const LanguageServerProtocol::CreateFile createFile(jsonObject); createFile.isValid())
changes << createFile;
else if (const RenameFile renameFile(jsonObject); renameFile.isValid())
changes << renameFile;
else if (const LanguageServerProtocol::DeleteFile deleteFile(jsonObject); deleteFile.isValid())
changes << deleteFile;
}
for (const DocumentChange &change : changes)
applyDocumentChange(m_client, change);
for (auto it = editsForDocuments.begin(), end = editsForDocuments.end(); it != end; ++it)
applyTextEdits(m_client, it.key(), it.value());