hanchenye-llvm-project/clang-tools-extra/clangd/ProtocolHandlers.cpp

211 lines
7.1 KiB
C++

//===--- ProtocolHandlers.cpp - LSP callbacks -----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ProtocolHandlers.h"
#include "ASTManager.h"
#include "DocumentStore.h"
#include "clang/Format/Format.h"
using namespace clang;
using namespace clangd;
void TextDocumentDidOpenHandler::handleNotification(
llvm::yaml::MappingNode *Params) {
auto DOTDP = DidOpenTextDocumentParams::parse(Params);
if (!DOTDP) {
Output.log("Failed to decode DidOpenTextDocumentParams!\n");
return;
}
Store.addDocument(DOTDP->textDocument.uri.file, DOTDP->textDocument.text);
}
void TextDocumentDidCloseHandler::handleNotification(
llvm::yaml::MappingNode *Params) {
auto DCTDP = DidCloseTextDocumentParams::parse(Params);
if (!DCTDP) {
Output.log("Failed to decode DidCloseTextDocumentParams!\n");
return;
}
Store.removeDocument(DCTDP->textDocument.uri.file);
}
void TextDocumentDidChangeHandler::handleNotification(
llvm::yaml::MappingNode *Params) {
auto DCTDP = DidChangeTextDocumentParams::parse(Params);
if (!DCTDP || DCTDP->contentChanges.size() != 1) {
Output.log("Failed to decode DidChangeTextDocumentParams!\n");
return;
}
// We only support full syncing right now.
Store.addDocument(DCTDP->textDocument.uri.file, DCTDP->contentChanges[0].text);
}
/// Turn a [line, column] pair into an offset in Code.
static size_t positionToOffset(StringRef Code, Position P) {
size_t Offset = 0;
for (int I = 0; I != P.line; ++I) {
// FIXME: \r\n
// FIXME: UTF-8
size_t F = Code.find('\n', Offset);
if (F == StringRef::npos)
return 0; // FIXME: Is this reasonable?
Offset = F + 1;
}
return (Offset == 0 ? 0 : (Offset - 1)) + P.character;
}
/// Turn an offset in Code into a [line, column] pair.
static Position offsetToPosition(StringRef Code, size_t Offset) {
StringRef JustBefore = Code.substr(0, Offset);
// FIXME: \r\n
// FIXME: UTF-8
int Lines = JustBefore.count('\n');
int Cols = JustBefore.size() - JustBefore.rfind('\n') - 1;
return {Lines, Cols};
}
template <typename T>
static std::string replacementsToEdits(StringRef Code, const T &Replacements) {
// Turn the replacements into the format specified by the Language Server
// Protocol. Fuse them into one big JSON array.
std::string Edits;
for (auto &R : Replacements) {
Range ReplacementRange = {
offsetToPosition(Code, R.getOffset()),
offsetToPosition(Code, R.getOffset() + R.getLength())};
TextEdit TE = {ReplacementRange, R.getReplacementText()};
Edits += TextEdit::unparse(TE);
Edits += ',';
}
if (!Edits.empty())
Edits.pop_back();
return Edits;
}
static std::string formatCode(StringRef Code, StringRef Filename,
ArrayRef<tooling::Range> Ranges, StringRef ID) {
// Call clang-format.
// FIXME: Don't ignore style.
format::FormatStyle Style = format::getLLVMStyle();
tooling::Replacements Replacements =
format::reformat(Style, Code, Ranges, Filename);
std::string Edits = replacementsToEdits(Code, Replacements);
return R"({"jsonrpc":"2.0","id":)" + ID.str() +
R"(,"result":[)" + Edits + R"(]})";
}
void TextDocumentRangeFormattingHandler::handleMethod(
llvm::yaml::MappingNode *Params, StringRef ID) {
auto DRFP = DocumentRangeFormattingParams::parse(Params);
if (!DRFP) {
Output.log("Failed to decode DocumentRangeFormattingParams!\n");
return;
}
std::string Code = Store.getDocument(DRFP->textDocument.uri.file);
size_t Begin = positionToOffset(Code, DRFP->range.start);
size_t Len = positionToOffset(Code, DRFP->range.end) - Begin;
writeMessage(formatCode(Code, DRFP->textDocument.uri.file,
{clang::tooling::Range(Begin, Len)}, ID));
}
void TextDocumentOnTypeFormattingHandler::handleMethod(
llvm::yaml::MappingNode *Params, StringRef ID) {
auto DOTFP = DocumentOnTypeFormattingParams::parse(Params);
if (!DOTFP) {
Output.log("Failed to decode DocumentOnTypeFormattingParams!\n");
return;
}
// Look for the previous opening brace from the character position and format
// starting from there.
std::string Code = Store.getDocument(DOTFP->textDocument.uri.file);
size_t CursorPos = positionToOffset(Code, DOTFP->position);
size_t PreviousLBracePos = StringRef(Code).find_last_of('{', CursorPos);
if (PreviousLBracePos == StringRef::npos)
PreviousLBracePos = CursorPos;
size_t Len = 1 + CursorPos - PreviousLBracePos;
writeMessage(formatCode(Code, DOTFP->textDocument.uri.file,
{clang::tooling::Range(PreviousLBracePos, Len)}, ID));
}
void TextDocumentFormattingHandler::handleMethod(
llvm::yaml::MappingNode *Params, StringRef ID) {
auto DFP = DocumentFormattingParams::parse(Params);
if (!DFP) {
Output.log("Failed to decode DocumentFormattingParams!\n");
return;
}
// Format everything.
std::string Code = Store.getDocument(DFP->textDocument.uri.file);
writeMessage(formatCode(Code, DFP->textDocument.uri.file,
{clang::tooling::Range(0, Code.size())}, ID));
}
void CodeActionHandler::handleMethod(llvm::yaml::MappingNode *Params,
StringRef ID) {
auto CAP = CodeActionParams::parse(Params);
if (!CAP) {
Output.log("Failed to decode CodeActionParams!\n");
return;
}
// We provide a code action for each diagnostic at the requested location
// which has FixIts available.
std::string Code = AST.getStore().getDocument(CAP->textDocument.uri.file);
std::string Commands;
for (Diagnostic &D : CAP->context.diagnostics) {
std::vector<clang::tooling::Replacement> Fixes = AST.getFixIts(CAP->textDocument.uri.file, D);
std::string Edits = replacementsToEdits(Code, Fixes);
if (!Edits.empty())
Commands +=
R"({"title":"Apply FixIt ')" + llvm::yaml::escape(D.message) +
R"('", "command": "clangd.applyFix", "arguments": [")" +
llvm::yaml::escape(CAP->textDocument.uri.uri) +
R"(", [)" + Edits +
R"(]]},)";
}
if (!Commands.empty())
Commands.pop_back();
writeMessage(
R"({"jsonrpc":"2.0","id":)" + ID.str() +
R"(, "result": [)" + Commands +
R"(]})");
}
void CompletionHandler::handleMethod(llvm::yaml::MappingNode *Params,
StringRef ID) {
auto TDPP = TextDocumentPositionParams::parse(Params);
if (!TDPP) {
Output.log("Failed to decode TextDocumentPositionParams!\n");
return;
}
auto Items = AST.codeComplete(TDPP->textDocument.uri.file, TDPP->position.line,
TDPP->position.character);
std::string Completions;
for (const auto &Item : Items) {
Completions += CompletionItem::unparse(Item);
Completions += ",";
}
if (!Completions.empty())
Completions.pop_back();
writeMessage(
R"({"jsonrpc":"2.0","id":)" + ID.str() +
R"(,"result":[)" + Completions + R"(]})");
}