[clangd] (Attempt to) read clang-format file for document formatting

Summary:
Takes into account the clang-format file of the project, if any.
Reverts to LLVM if nothing is found. Replies with an error if any error occured.
For instance, a parse error in the clang-format YAML file.

Reviewers: ilya-biryukov, sammccall, Nebiroth, malaperle, krasimir

Reviewed By: sammccall

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D41031

llvm-svn: 320524
This commit is contained in:
Raoul Wols 2017-12-12 20:25:06 +00:00
parent edcd9dcbc4
commit 212bcf8370
3 changed files with 75 additions and 36 deletions

View File

@ -17,18 +17,29 @@ using namespace clang;
namespace { namespace {
TextEdit replacementToEdit(StringRef Code, const tooling::Replacement &R) {
Range ReplacementRange = {
offsetToPosition(Code, R.getOffset()),
offsetToPosition(Code, R.getOffset() + R.getLength())};
return {ReplacementRange, R.getReplacementText()};
}
std::vector<TextEdit> std::vector<TextEdit>
replacementsToEdits(StringRef Code, replacementsToEdits(StringRef Code,
const std::vector<tooling::Replacement> &Replacements) { const std::vector<tooling::Replacement> &Replacements) {
// Turn the replacements into the format specified by the Language Server // Turn the replacements into the format specified by the Language Server
// Protocol. Fuse them into one big JSON array. // Protocol. Fuse them into one big JSON array.
std::vector<TextEdit> Edits; std::vector<TextEdit> Edits;
for (auto &R : Replacements) { for (const auto &R : Replacements)
Range ReplacementRange = { Edits.push_back(replacementToEdit(Code, R));
offsetToPosition(Code, R.getOffset()), return Edits;
offsetToPosition(Code, R.getOffset() + R.getLength())}; }
Edits.push_back({ReplacementRange, R.getReplacementText()});
} std::vector<TextEdit> replacementsToEdits(StringRef Code,
const tooling::Replacements &Repls) {
std::vector<TextEdit> Edits;
for (const auto &R : Repls)
Edits.push_back(replacementToEdit(Code, R));
return Edits; return Edits;
} }
@ -153,23 +164,36 @@ void ClangdLSPServer::onDocumentOnTypeFormatting(
Ctx C, DocumentOnTypeFormattingParams &Params) { Ctx C, DocumentOnTypeFormattingParams &Params) {
auto File = Params.textDocument.uri.file; auto File = Params.textDocument.uri.file;
std::string Code = Server.getDocument(File); std::string Code = Server.getDocument(File);
C.reply(json::ary( auto ReplacementsOrError = Server.formatOnType(Code, File, Params.position);
replacementsToEdits(Code, Server.formatOnType(File, Params.position)))); if (ReplacementsOrError)
C.reply(json::ary(replacementsToEdits(Code, ReplacementsOrError.get())));
else
C.replyError(ErrorCode::UnknownErrorCode,
llvm::toString(ReplacementsOrError.takeError()));
} }
void ClangdLSPServer::onDocumentRangeFormatting( void ClangdLSPServer::onDocumentRangeFormatting(
Ctx C, DocumentRangeFormattingParams &Params) { Ctx C, DocumentRangeFormattingParams &Params) {
auto File = Params.textDocument.uri.file; auto File = Params.textDocument.uri.file;
std::string Code = Server.getDocument(File); std::string Code = Server.getDocument(File);
C.reply(json::ary( auto ReplacementsOrError = Server.formatRange(Code, File, Params.range);
replacementsToEdits(Code, Server.formatRange(File, Params.range)))); if (ReplacementsOrError)
C.reply(json::ary(replacementsToEdits(Code, ReplacementsOrError.get())));
else
C.replyError(ErrorCode::UnknownErrorCode,
llvm::toString(ReplacementsOrError.takeError()));
} }
void ClangdLSPServer::onDocumentFormatting(Ctx C, void ClangdLSPServer::onDocumentFormatting(Ctx C,
DocumentFormattingParams &Params) { DocumentFormattingParams &Params) {
auto File = Params.textDocument.uri.file; auto File = Params.textDocument.uri.file;
std::string Code = Server.getDocument(File); std::string Code = Server.getDocument(File);
C.reply(json::ary(replacementsToEdits(Code, Server.formatFile(File)))); auto ReplacementsOrError = Server.formatFile(Code, File);
if (ReplacementsOrError)
C.reply(json::ary(replacementsToEdits(Code, ReplacementsOrError.get())));
else
C.replyError(ErrorCode::UnknownErrorCode,
llvm::toString(ReplacementsOrError.takeError()));
} }
void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) { void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) {

View File

@ -38,16 +38,6 @@ private:
std::promise<void> &Promise; std::promise<void> &Promise;
}; };
std::vector<tooling::Replacement> formatCode(StringRef Code, StringRef Filename,
ArrayRef<tooling::Range> Ranges) {
// Call clang-format.
// FIXME: Don't ignore style.
format::FormatStyle Style = format::getLLVMStyle();
auto Result = format::reformat(Style, Code, Ranges, Filename);
return std::vector<tooling::Replacement>(Result.begin(), Result.end());
}
std::string getStandardResourceDir() { std::string getStandardResourceDir() {
static int Dummy; // Just an address in this process. static int Dummy; // Just an address in this process.
return CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy); return CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
@ -331,26 +321,23 @@ ClangdServer::signatureHelp(PathRef File, Position Pos,
return make_tagged(std::move(Result), TaggedFS.Tag); return make_tagged(std::move(Result), TaggedFS.Tag);
} }
std::vector<tooling::Replacement> ClangdServer::formatRange(PathRef File, llvm::Expected<tooling::Replacements>
Range Rng) { ClangdServer::formatRange(StringRef Code, PathRef File, Range Rng) {
std::string Code = getDocument(File);
size_t Begin = positionToOffset(Code, Rng.start); size_t Begin = positionToOffset(Code, Rng.start);
size_t Len = positionToOffset(Code, Rng.end) - Begin; size_t Len = positionToOffset(Code, Rng.end) - Begin;
return formatCode(Code, File, {tooling::Range(Begin, Len)}); return formatCode(Code, File, {tooling::Range(Begin, Len)});
} }
std::vector<tooling::Replacement> ClangdServer::formatFile(PathRef File) { llvm::Expected<tooling::Replacements> ClangdServer::formatFile(StringRef Code,
PathRef File) {
// Format everything. // Format everything.
std::string Code = getDocument(File);
return formatCode(Code, File, {tooling::Range(0, Code.size())}); return formatCode(Code, File, {tooling::Range(0, Code.size())});
} }
std::vector<tooling::Replacement> ClangdServer::formatOnType(PathRef File, llvm::Expected<tooling::Replacements>
Position Pos) { ClangdServer::formatOnType(StringRef Code, PathRef File, Position Pos) {
// Look for the previous opening brace from the character position and // Look for the previous opening brace from the character position and
// format starting from there. // format starting from there.
std::string Code = getDocument(File);
size_t CursorPos = positionToOffset(Code, Pos); size_t CursorPos = positionToOffset(Code, Pos);
size_t PreviousLBracePos = StringRef(Code).find_last_of('{', CursorPos); size_t PreviousLBracePos = StringRef(Code).find_last_of('{', CursorPos);
if (PreviousLBracePos == StringRef::npos) if (PreviousLBracePos == StringRef::npos)
@ -509,6 +496,20 @@ llvm::Optional<Path> ClangdServer::switchSourceHeader(PathRef Path) {
return llvm::None; return llvm::None;
} }
llvm::Expected<tooling::Replacements>
ClangdServer::formatCode(llvm::StringRef Code, PathRef File,
ArrayRef<tooling::Range> Ranges) {
// Call clang-format.
auto TaggedFS = FSProvider.getTaggedFileSystem(File);
auto StyleOrError =
format::getStyle("file", File, "LLVM", Code, TaggedFS.Value.get());
if (!StyleOrError) {
return StyleOrError.takeError();
} else {
return format::reformat(StyleOrError.get(), Code, Ranges, File);
}
}
llvm::Expected<Tagged<std::vector<DocumentHighlight>>> llvm::Expected<Tagged<std::vector<DocumentHighlight>>>
ClangdServer::findDocumentHighlights(PathRef File, Position Pos) { ClangdServer::findDocumentHighlights(PathRef File, Position Pos) {
auto FileContents = DraftMgr.getDraft(File); auto FileContents = DraftMgr.getDraft(File);

View File

@ -289,12 +289,19 @@ public:
llvm::Expected<Tagged<std::vector<DocumentHighlight>>> llvm::Expected<Tagged<std::vector<DocumentHighlight>>>
findDocumentHighlights(PathRef File, Position Pos); findDocumentHighlights(PathRef File, Position Pos);
/// Run formatting for \p Rng inside \p File. /// Run formatting for \p Rng inside \p File with content \p Code.
std::vector<tooling::Replacement> formatRange(PathRef File, Range Rng); llvm::Expected<tooling::Replacements> formatRange(StringRef Code,
/// Run formatting for the whole \p File. PathRef File, Range Rng);
std::vector<tooling::Replacement> formatFile(PathRef File);
/// Run formatting after a character was typed at \p Pos in \p File. /// Run formatting for the whole \p File with content \p Code.
std::vector<tooling::Replacement> formatOnType(PathRef File, Position Pos); llvm::Expected<tooling::Replacements> formatFile(StringRef Code,
PathRef File);
/// Run formatting after a character was typed at \p Pos in \p File with
/// content \p Code.
llvm::Expected<tooling::Replacements>
formatOnType(StringRef Code, PathRef File, Position Pos);
/// Rename all occurrences of the symbol at the \p Pos in \p File to /// Rename all occurrences of the symbol at the \p Pos in \p File to
/// \p NewName. /// \p NewName.
Expected<std::vector<tooling::Replacement>> rename(PathRef File, Position Pos, Expected<std::vector<tooling::Replacement>> rename(PathRef File, Position Pos,
@ -314,6 +321,13 @@ public:
void onFileEvent(const DidChangeWatchedFilesParams &Params); void onFileEvent(const DidChangeWatchedFilesParams &Params);
private: private:
/// FIXME: This stats several files to find a .clang-format file. I/O can be
/// slow. Think of a way to cache this.
llvm::Expected<tooling::Replacements>
formatCode(llvm::StringRef Code, PathRef File,
ArrayRef<tooling::Range> Ranges);
std::future<void> std::future<void>
scheduleReparseAndDiags(PathRef File, VersionedDraft Contents, scheduleReparseAndDiags(PathRef File, VersionedDraft Contents,
std::shared_ptr<CppFile> Resources, std::shared_ptr<CppFile> Resources,