[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:
parent
edcd9dcbc4
commit
212bcf8370
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue