Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -353,8 +353,11 @@ // Replacement with offset UINT_MAX and length 0 will be treated as include // insertion. tooling::Replacement R(File, /*Offset=*/UINT_MAX, 0, "#include " + ToInclude); - return format::cleanupAroundReplacements(Code, tooling::Replacements(R), - *Style); + auto Replaces = format::cleanupAroundReplacements( + Code, tooling::Replacements(R), *Style); + if (!Replaces) + return Replaces; + return formatReplacements(Code, *Replaces, *Style); } llvm::Optional ClangdServer::getDocument(PathRef File) { @@ -465,13 +468,21 @@ ArrayRef Ranges) { // Call clang-format. auto TaggedFS = FSProvider.getTaggedFileSystem(File); - auto StyleOrError = + auto Style = format::getStyle("file", File, "LLVM", Code, TaggedFS.Value.get()); - if (!StyleOrError) { - return StyleOrError.takeError(); - } else { - return format::reformat(StyleOrError.get(), Code, Ranges, File); - } + if (!Style) + return Style.takeError(); + + tooling::Replacements IncludeReplaces = + format::sortIncludes(*Style, Code, Ranges, File); + auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces); + if (!Changed) + return Changed.takeError(); + + return IncludeReplaces.merge(format::reformat( + Style.get(), *Changed, + tooling::calculateRangesAfterReplacements(IncludeReplaces, Ranges), + File)); } void ClangdServer::findDocumentHighlights( Index: unittests/clangd/ClangdTests.cpp =================================================================== --- unittests/clangd/ClangdTests.cpp +++ unittests/clangd/ClangdTests.cpp @@ -945,6 +945,7 @@ auto FooCpp = testPath("foo.cpp"); const auto Code = R"cpp( +#include "z.h" #include "x.h" void f() {} @@ -952,15 +953,18 @@ FS.Files[FooCpp] = Code; runAddDocument(Server, FooCpp, Code); - auto Inserted = [&](llvm::StringRef Original, llvm::StringRef Preferred, - llvm::StringRef Expected) { + auto ChangedCode = [&](llvm::StringRef Original, llvm::StringRef Preferred) { auto Replaces = Server.insertInclude( FooCpp, Code, Original, Preferred.empty() ? Original : Preferred); EXPECT_TRUE(static_cast(Replaces)); auto Changed = tooling::applyAllReplacements(Code, *Replaces); EXPECT_TRUE(static_cast(Changed)); - return llvm::StringRef(*Changed).contains( - (llvm::Twine("#include ") + Expected + "").str()); + return *Changed; + }; + auto Inserted = [&](llvm::StringRef Original, llvm::StringRef Preferred, + llvm::StringRef Expected) { + return llvm::StringRef(ChangedCode(Original, Preferred)) + .contains((llvm::Twine("#include ") + Expected + "").str()); }; EXPECT_TRUE(Inserted("\"y.h\"", /*Preferred=*/"","\"y.h\"")); @@ -976,6 +980,45 @@ /*Preferred=*/"", "")); EXPECT_TRUE(Inserted(OriginalHeader, PreferredHeader, "\"Y.h\"")); EXPECT_TRUE(Inserted("", PreferredHeader, "\"Y.h\"")); + + // Check that includes are sorted. + const auto Expected = R"cpp( +#include "x.h" +#include "y.h" +#include "z.h" + +void f() {} +)cpp"; + EXPECT_EQ(Expected, ChangedCode("\"y.h\"", /*Preferred=*/"")); +} + +TEST_F(ClangdVFSTest, FormatCode) { + MockFSProvider FS; + ErrorCheckingDiagConsumer DiagConsumer; + MockCompilationDatabase CDB; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + auto Path = testPath("foo.cpp"); + std::string Code = R"cpp( +#include "y.h" +#include "x.h" + +void f( ) {} +)cpp"; + std::string Expected = R"cpp( +#include "x.h" +#include "y.h" + +void f() {} +)cpp"; + FS.Files[Path] = Code; + runAddDocument(Server, Path, Code); + + auto Replaces = Server.formatFile(Code, Path); + EXPECT_TRUE(static_cast(Replaces)); + auto Changed = tooling::applyAllReplacements(Code, *Replaces); + EXPECT_TRUE(static_cast(Changed)); + EXPECT_EQ(Expected, *Changed); } } // namespace