Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -469,10 +469,10 @@ Reply("Fix applied."); ApplyEdit(std::move(WE)); }; - Server->applyTweak(Params.tweakArgs->file.file(), - Params.tweakArgs->selection, Params.tweakArgs->tweakID, - Bind(Action, std::move(Reply), Params.tweakArgs->file, - std::move(*Code))); + Server->applyTweak( + *Code, Params.tweakArgs->file.file(), Params.tweakArgs->selection, + Params.tweakArgs->tweakID, + Bind(Action, std::move(Reply), Params.tweakArgs->file, *Code)); } else { // We should not get here because ExecuteCommandParams would not have // parsed in the first place and this handler should not be called. But if Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -23,6 +23,7 @@ #include "index/FileIndex.h" #include "index/Index.h" #include "refactor/Tweak.h" +#include "clang/Format/Format.h" #include "clang/Tooling/CompilationDatabase.h" #include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/FunctionExtras.h" @@ -223,7 +224,7 @@ Callback> CB); /// Apply the code tweak with a specified \p ID. - void applyTweak(PathRef File, Range Sel, StringRef ID, + void applyTweak(StringRef Code, PathRef File, Range Sel, StringRef ID, Callback CB); /// Only for testing purposes. @@ -257,12 +258,15 @@ blockUntilIdleForTest(llvm::Optional TimeoutSeconds = 10); 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 formatCode(llvm::StringRef Code, PathRef File, ArrayRef Ranges); + /// 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 getFormatStyle(llvm::StringRef Code, + PathRef File); + tooling::CompileCommand getCompileCommand(PathRef File); const GlobalCompilationDatabase &CDB; Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -10,6 +10,7 @@ #include "ClangdUnit.h" #include "CodeComplete.h" #include "FindSymbols.h" +#include "Format.h" #include "Headers.h" #include "SourceCode.h" #include "Trace.h" @@ -358,9 +359,14 @@ Bind(Action, std::move(CB), File.str())); } -void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID, +void ClangdServer::applyTweak(StringRef Code, PathRef File, Range Sel, + StringRef TweakID, Callback CB) { + auto Style = getFormatStyle(Code, File); + if (!Style) + return; auto Action = [Sel](decltype(CB) CB, std::string File, std::string TweakID, + format::FormatStyle Style, Expected InpAST) { if (!InpAST) return CB(InpAST.takeError()); @@ -370,12 +376,15 @@ auto A = prepareTweak(TweakID, *Selection); if (!A) return CB(A.takeError()); - // FIXME: run formatter on top of resulting replacements. - return CB((*A)->apply(*Selection)); + auto ResultReplacements = (*A)->apply(*Selection); + if (!ResultReplacements) + return CB(ResultReplacements.takeError()); + return CB( + cleanupAndFormat(InpAST->Inputs.Contents, *ResultReplacements, Style)); }; WorkScheduler.runWithAST( "ApplyTweak", File, - Bind(Action, std::move(CB), File.str(), TweakID.str())); + Bind(Action, std::move(CB), File.str(), TweakID.str(), *Style)); } void ClangdServer::dumpAST(PathRef File, @@ -471,9 +480,7 @@ ClangdServer::formatCode(llvm::StringRef Code, PathRef File, llvm::ArrayRef Ranges) { // Call clang-format. - auto FS = FSProvider.getFileSystem(); - auto Style = format::getStyle(format::DefaultFormatStyle, File, - format::DefaultFallbackStyle, Code, FS.get()); + auto Style = getFormatStyle(Code, File); if (!Style) return Style.takeError(); @@ -489,6 +496,13 @@ File)); } +llvm::Expected +ClangdServer::getFormatStyle(llvm::StringRef Code, PathRef File) { + return format::getStyle(format::DefaultFormatStyle, File, + format::DefaultFallbackStyle, Code, + FSProvider.getFileSystem().get()); +} + void ClangdServer::findDocumentHighlights( PathRef File, Position Pos, Callback> CB) { auto Action = [Pos](Callback> CB, Index: clangd/Format.h =================================================================== --- /dev/null +++ clangd/Format.h @@ -0,0 +1,28 @@ +//===--- Format.h ------------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Format/Format.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace clangd { + +// Cleanup and format the given replacements. +inline llvm::Expected +cleanupAndFormat(StringRef Code, const tooling::Replacements &Replaces, + const format::FormatStyle &Style) { + auto CleanReplaces = cleanupAroundReplacements(Code, Replaces, Style); + if (!CleanReplaces) + return CleanReplaces; + return formatReplacements(Code, std::move(*CleanReplaces), Style); +} + +} // namespace clangd +} // namespace clang \ No newline at end of file Index: unittests/clangd/TweakTests.cpp =================================================================== --- unittests/clangd/TweakTests.cpp +++ unittests/clangd/TweakTests.cpp @@ -9,6 +9,7 @@ #include "Annotations.h" #include "SourceCode.h" +#include "Format.h" #include "TestTU.h" #include "refactor/Tweak.h" #include "clang/AST/Expr.h" @@ -77,7 +78,8 @@ void checkNotAvailable(StringRef ID, llvm::StringRef Input) { return checkAvailable(ID, Input, /*Available=*/false); } -llvm::Expected apply(StringRef ID, llvm::StringRef Input) { +llvm::Expected apply(StringRef ID, llvm::StringRef Input, + bool Format) { Annotations Code(Input); Range SelectionRng; if (Code.points().size() != 0) { @@ -102,12 +104,19 @@ auto Replacements = (*T)->apply(S); if (!Replacements) return Replacements.takeError(); + if (Format) { + Replacements = cleanupAndFormat( + Code.code(), *Replacements, + clang::format::getGoogleStyle(::clang::format::FormatStyle::LK_Cpp)); + if (!Replacements) + return Replacements.takeError(); + } return applyAllReplacements(Code.code(), *Replacements); } void checkTransform(llvm::StringRef ID, llvm::StringRef Input, - llvm::StringRef Output) { - EXPECT_THAT_EXPECTED(apply(ID, Input), HasValue(Output)) + llvm::StringRef Output, bool Format = false) { + EXPECT_THAT_EXPECTED(apply(ID, Input, Format), HasValue(Output)) << "action id is" << ID; } @@ -150,6 +159,22 @@ )cpp"; checkTransform(ID, Input, Output); + Input = R"cpp( + void test() { + ^if () { return 100; } else { continue; } + } + )cpp"; + Output = R"cpp( + void test() { + if () { + continue; + } else { + return 100; + } + } + )cpp"; + checkTransform(ID, Input, Output, /*Format=*/true); + // Available in subexpressions of the condition. checkAvailable(ID, R"cpp( void test() {