forked from OSchip/llvm-project
141 lines
4.8 KiB
C++
141 lines
4.8 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 "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, DOTDP->textDocument.text);
|
|
}
|
|
|
|
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, 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};
|
|
}
|
|
|
|
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();
|
|
// On windows FileManager doesn't like file://. Just strip it, clang-format
|
|
// doesn't need it.
|
|
Filename.consume_front("file://");
|
|
tooling::Replacements Replacements =
|
|
format::reformat(Style, Code, Ranges, Filename);
|
|
|
|
// Now 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 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);
|
|
|
|
size_t Begin = positionToOffset(Code, DRFP->range.start);
|
|
size_t Len = positionToOffset(Code, DRFP->range.end) - Begin;
|
|
|
|
writeMessage(formatCode(Code, DRFP->textDocument.uri,
|
|
{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);
|
|
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,
|
|
{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);
|
|
writeMessage(formatCode(Code, DFP->textDocument.uri,
|
|
{clang::tooling::Range(0, Code.size())}, ID));
|
|
}
|