diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -66,6 +66,7 @@ FindTarget.cpp FileDistance.cpp Format.cpp + FormatProvider.cpp FS.cpp FuzzyMatch.cpp GlobalCompilationDatabase.cpp diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -12,6 +12,7 @@ #include "../clang-tidy/ClangTidyOptions.h" #include "CodeComplete.h" #include "ConfigProvider.h" +#include "FormatProvider.h" #include "GlobalCompilationDatabase.h" #include "Hover.h" #include "Protocol.h" @@ -132,6 +133,8 @@ /// checks will be disabled. TidyProviderRef ClangTidyProvider; + FormatProviderRef ClangFormatProvider; + /// Clangd's workspace root. Relevant for "workspace" operations not bound /// to a particular file. /// FIXME: If not set, should use the current working directory. @@ -377,6 +380,9 @@ // When set, provides clang-tidy options for a specific file. TidyProviderRef ClangTidyProvider; + // When set, provides clang-format options for a specific file. + FormatProviderRef ClangFormatProvider; + // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex) llvm::StringMap> CachedCompletionFuzzyFindRequestByFile; diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -12,6 +12,7 @@ #include "DumpAST.h" #include "FindSymbols.h" #include "Format.h" +#include "FormatProvider.h" #include "HeaderSourceSwitch.h" #include "Headers.h" #include "ParsedAST.h" @@ -123,6 +124,7 @@ Opts.AsyncThreadsCount = 4; // Consistent! Opts.TheiaSemanticHighlighting = true; Opts.AsyncPreambleBuilds = true; + Opts.ClangFormatProvider = formatFallbackProvider; return Opts; } @@ -146,6 +148,7 @@ Opts.CollectMainFileRefs) : nullptr), ClangTidyProvider(Opts.ClangTidyProvider), + ClangFormatProvider(Opts.ClangFormatProvider), WorkspaceRoot(Opts.WorkspaceRoot), // Pass a callback into `WorkScheduler` to extract symbols from a newly // parsed file and rebuild the file index synchronously each time an AST @@ -201,6 +204,7 @@ Inputs.Opts = std::move(Opts); Inputs.Index = Index; Inputs.ClangTidyProvider = ClangTidyProvider; + Inputs.ClangFormatProvider = ClangFormatProvider; bool NewFile = WorkScheduler.update(File, Inputs, WantDiags); // If we loaded Foo.h, we want to make sure Foo.cpp is indexed. if (NewFile && BackgroundIdx) @@ -316,6 +320,8 @@ ParseInputs ParseInput{IP->Command, &TFS, IP->Contents.str()}; ParseInput.Index = Index; + ParseInput.ClangFormatProvider = ClangFormatProvider; + CodeCompleteOpts.MainFileSignals = IP->Signals; // FIXME(ibiryukov): even if Preamble is non-null, we may want to check // both the old and the new version in case only one of them matches. @@ -395,11 +401,8 @@ auto Action = [File = File.str(), Code = Code.str(), TriggerText = TriggerText.str(), CursorPos = *CursorPos, CB = std::move(CB), this]() mutable { - auto Style = format::getStyle(format::DefaultFormatStyle, File, - format::DefaultFallbackStyle, Code, - TFS.view(/*CWD=*/llvm::None).get()); - if (!Style) - return CB(Style.takeError()); + auto Style = ClangFormatProvider(File, Code); + assert(Style && "Provider should always return a valid style"); std::vector Result; for (const tooling::Replacement &R : @@ -466,12 +469,11 @@ return CB(R.takeError()); if (Opts.WantFormat) { - auto Style = getFormatStyleForFile(File, InpAST->Inputs.Contents, - *InpAST->Inputs.TFS); + auto Style = ClangFormatProvider(File, InpAST->Inputs.Contents); llvm::Error Err = llvm::Error::success(); for (auto &E : R->GlobalChanges) - Err = - llvm::joinErrors(reformatEdit(E.getValue(), Style), std::move(Err)); + Err = llvm::joinErrors(reformatEdit(E.getValue(), *Style), + std::move(Err)); if (Err) return CB(std::move(Err)); @@ -572,9 +574,8 @@ // Tweaks don't apply clang-format, do that centrally here. for (auto &It : (*Effect)->ApplyEdits) { Edit &E = It.second; - format::FormatStyle Style = - getFormatStyleForFile(File, E.InitialCode, TFS); - if (llvm::Error Err = reformatEdit(E, Style)) + auto Style = ClangFormatProvider(File, E.InitialCode); + if (llvm::Error Err = reformatEdit(E, *Style)) elog("Failed to format {0}: {1}", It.first(), std::move(Err)); } } else { @@ -623,15 +624,15 @@ // Call clang-format. auto Action = [File = File.str(), Code = Code.str(), Ranges = Ranges.vec(), CB = std::move(CB), this]() mutable { - format::FormatStyle Style = getFormatStyleForFile(File, Code, TFS); + auto Style = ClangFormatProvider(File, Code); tooling::Replacements IncludeReplaces = - format::sortIncludes(Style, Code, Ranges, File); + format::sortIncludes(*Style, Code, Ranges, File); auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces); if (!Changed) return CB(Changed.takeError()); CB(IncludeReplaces.merge(format::reformat( - Style, *Changed, + *Style, *Changed, tooling::calculateRangesAfterReplacements(IncludeReplaces, Ranges), File))); }; @@ -657,9 +658,8 @@ this](llvm::Expected InpAST) mutable { if (!InpAST) return CB(InpAST.takeError()); - format::FormatStyle Style = getFormatStyleForFile( - File, InpAST->Inputs.Contents, *InpAST->Inputs.TFS); - CB(clangd::getHover(InpAST->AST, Pos, std::move(Style), Index)); + auto Style = ClangFormatProvider(File, InpAST->Inputs.Contents); + CB(clangd::getHover(InpAST->AST, Pos, *Style, Index)); }; WorkScheduler.runWithAST("Hover", File, std::move(Action), diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -24,6 +24,7 @@ #include "Diagnostics.h" #include "ExpectedTypes.h" #include "FileDistance.h" +#include "FormatProvider.h" #include "FuzzyMatch.h" #include "Headers.h" #include "Hover.h" @@ -1279,13 +1280,16 @@ /// set and contains a cached request. llvm::Optional SpecReq; + FormatProviderRef FormatProvider; + public: // A CodeCompleteFlow object is only useful for calling run() exactly once. CodeCompleteFlow(PathRef FileName, const IncludeStructure &Includes, SpeculativeFuzzyFind *SpecFuzzyFind, - const CodeCompleteOptions &Opts) + const CodeCompleteOptions &Opts, + FormatProviderRef FormatProvider) : FileName(FileName), Includes(Includes), SpecFuzzyFind(SpecFuzzyFind), - Opts(Opts) {} + Opts(Opts), FormatProvider(FormatProvider) {} CodeCompleteResult run(const SemaCompleteInput &SemaCCInput) && { trace::Span Tracer("CodeCompleteFlow"); @@ -1307,9 +1311,8 @@ assert(Recorder && "Recorder is not set"); CCContextKind = Recorder->CCContext.getKind(); IsUsingDeclaration = Recorder->CCContext.isUsingDeclaration(); - auto Style = getFormatStyleForFile(SemaCCInput.FileName, - SemaCCInput.ParseInput.Contents, - *SemaCCInput.ParseInput.TFS); + auto Style = + FormatProvider(SemaCCInput.FileName, SemaCCInput.ParseInput.Contents); const auto NextToken = Lexer::findNextToken( Recorder->CCSema->getPreprocessor().getCodeCompletionLoc(), Recorder->CCSema->getSourceManager(), Recorder->CCSema->LangOpts); @@ -1318,7 +1321,7 @@ // If preprocessor was run, inclusions from preprocessor callback should // already be added to Includes. Inserter.emplace( - SemaCCInput.FileName, SemaCCInput.ParseInput.Contents, Style, + SemaCCInput.FileName, SemaCCInput.ParseInput.Contents, *Style, SemaCCInput.ParseInput.CompileCommand.Directory, &Recorder->CCSema->getPreprocessor().getHeaderSearchInfo()); for (const auto &Inc : Includes.MainFileIncludes) @@ -1397,12 +1400,12 @@ ProxSources[FileName].Cost = 0; FileProximity.emplace(ProxSources); - auto Style = getFormatStyleForFile(FileName, Content, TFS); + auto Style = FormatProvider(FileName, Content); // This will only insert verbatim headers. - Inserter.emplace(FileName, Content, Style, + Inserter.emplace(FileName, Content, *Style, /*BuildDir=*/"", /*HeaderSearchInfo=*/nullptr); - auto Identifiers = collectIdentifiers(Content, Style); + auto Identifiers = collectIdentifiers(Content, *Style); std::vector IdentifierResults; for (const auto &IDAndCount : Identifiers) { RawIdentifier ID; @@ -1420,7 +1423,7 @@ // - all-scopes query if no qualifier was typed (and it's allowed). SpecifiedScope Scopes; Scopes.AccessibleScopes = visibleNamespaces( - Content.take_front(Offset), format::getFormattingLangOpts(Style)); + Content.take_front(Offset), format::getFormattingLangOpts(*Style)); for (std::string &S : Scopes.AccessibleScopes) if (!S.empty()) S.append("::"); // visibleNamespaces doesn't include trailing ::. @@ -1821,7 +1824,7 @@ } auto Flow = CodeCompleteFlow( FileName, Preamble ? Preamble->Includes : IncludeStructure(), - SpecFuzzyFind, Opts); + SpecFuzzyFind, Opts, ParseInput.ClangFormatProvider); return (!Preamble || Opts.RunParser == CodeCompleteOptions::NeverParse) ? std::move(Flow).runWithoutSema(ParseInput.Contents, *Offset, *ParseInput.TFS) diff --git a/clang-tools-extra/clangd/Compiler.h b/clang-tools-extra/clangd/Compiler.h --- a/clang-tools-extra/clangd/Compiler.h +++ b/clang-tools-extra/clangd/Compiler.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_COMPILER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_COMPILER_H +#include "FormatProvider.h" #include "GlobalCompilationDatabase.h" #include "TidyProvider.h" #include "index/Index.h" @@ -54,6 +55,7 @@ const SymbolIndex *Index = nullptr; ParseOptions Opts = ParseOptions(); TidyProviderRef ClangTidyProvider = {}; + FormatProviderRef ClangFormatProvider = {}; }; /// Builds compiler invocation that could be used to build AST or preamble. diff --git a/clang-tools-extra/clangd/FormatProvider.h b/clang-tools-extra/clangd/FormatProvider.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/FormatProvider.h @@ -0,0 +1,36 @@ +//===--- FormatProvider.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 +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FORMATPROVIDER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FORMATPROVIDER_H + +#include "support/Path.h" +#include "support/ThreadsafeFS.h" +#include "clang/Format/Format.h" +#include "llvm/ADT/FunctionExtras.h" + +namespace clang { +namespace clangd { +using FormatProvider = + llvm::unique_function( + PathRef, llvm::StringRef) const>; +using FormatProviderRef = + llvm::function_ref( + PathRef, llvm::StringRef)>; + +/// A Fallback style provider mainly designed for tests, just returns the LLVM +/// style for the detected langauge. +std::shared_ptr +formatFallbackProvider(PathRef Filename, llvm::StringRef Contents); + +FormatProvider getClangFormatProvider(ThreadsafeFS &TFS, + StringRef FallbackStyle); + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_FORMATPROVIDER_H diff --git a/clang-tools-extra/clangd/FormatProvider.cpp b/clang-tools-extra/clangd/FormatProvider.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/FormatProvider.cpp @@ -0,0 +1,218 @@ +//===--- FormatProvider.cpp ---------------------------------*- 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 "FormatProvider.h" +#include "support/FileCache.h" +#include "support/Logger.h" +#include "support/Path.h" +#include "support/ThreadsafeFS.h" +#include "clang/Format/Format.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBufferRef.h" +#include +#include +#include + +namespace clang { +namespace clangd { + +namespace { +// Access to config from a clang format configuration file, caching IO and +// parsing. +using LanguageKind = format::FormatStyle::LanguageKind; +class ClangFormatCache : private FileCache { + mutable std::shared_ptr Value; + const LanguageKind Lang; + +public: + ClangFormatCache(PathRef Path, LanguageKind Lang) + : FileCache(Path), Lang(Lang) {} + + std::shared_ptr + get(const ThreadsafeFS &TFS, + std::chrono::steady_clock::time_point FreshTime) const { + std::shared_ptr Result; + read( + TFS, FreshTime, + [this](llvm::Optional Data) { + // Clear any stale config if one exists. + Value.reset(); + if (!Data || Data->empty()) + return; + vlog("Format: Parsing config file {0}", path()); + format::FormatStyle Style = format::getLLVMStyle(Lang); + if (auto EC = format::parseConfiguration( + llvm::MemoryBufferRef(*Data, path()), &Style, + /*AllowUnknownOptions=*/true)) + elog("clang-format style parsing error: {0}", EC.message()); + else + Value = std::make_shared(std::move(Style)); + }, + [&]() { Result = Value; }); + return Result; + } +}; + +// Clang-Format checks for ".clang-format" then "_clang-format" in each +// directory, so this wraps those 2 caches into one. +class ClangFormatDirectoryCache { +private: + SmallString<256> appendDir(StringRef Dir, StringRef Tail) { + SmallString<256> Res(Dir); + llvm::sys::path::append(Res, Tail); + return Res; + } + ClangFormatCache Items[2]; + +public: + explicit ClangFormatDirectoryCache(StringRef Dir, LanguageKind Lang) + : Items{{appendDir(Dir, ".clang-format"), Lang}, + {appendDir(Dir, "._clang-format"), Lang}} {} + + std::shared_ptr + get(const ThreadsafeFS &TFS, + std::chrono::steady_clock::time_point FreshTime) const { + for (const ClangFormatCache &Item : Items) { + if (auto Config = Item.get(TFS, FreshTime)) + return Config; + } + return nullptr; + } +}; + +// Bundles format file caches along with a fallback style, should no format file +// be found. +class CacheMapAndFallback { +public: + CacheMapAndFallback(StringRef FallbackStyle, LanguageKind Lang) + : Fallback(getFallbackStyle(FallbackStyle, Lang)), Lang(Lang) {} + + ClangFormatDirectoryCache &getAncestorCache(PathRef Ancestor) { + return Map.try_emplace(Ancestor, Ancestor, Lang).first->getValue(); + } + + std::shared_ptr getFallbackStyle() const { + return Fallback; + } + +private: + static std::shared_ptr + getFallbackStyle(StringRef FallbackStyle, LanguageKind Lang) { + auto Result = std::make_shared(format::getNoStyle()); + if (!format::getPredefinedStyle(FallbackStyle, Lang, &*Result)) { + StringRef PrettyStyle; + switch (Lang) { + case LanguageKind::LK_Cpp: + PrettyStyle = "C/C++"; + break; + case LanguageKind::LK_ObjC: + PrettyStyle = "Objective-C/Objective-C++"; + break; + default: + llvm_unreachable("Unsupported language kind"); + } + + elog("error: Couldn't get fallback style for '{0}', using LLVM Style", + PrettyStyle); + *Result = format::getLLVMStyle(Lang); + } + return Result; + } + llvm::StringMap Map; + const std::shared_ptr Fallback; + const LanguageKind Lang; +}; + +class ClangFormatConfigTree { + const ThreadsafeFS &FS; + std::chrono::steady_clock::duration MaxStaleness; + mutable std::mutex Mu; + + CacheMapAndFallback CPPConfig, ObjCConfig; + + CacheMapAndFallback &getMapForStyle(LanguageKind Lang) { + switch (Lang) { + case LanguageKind::LK_Cpp: + return CPPConfig; + case LanguageKind::LK_ObjC: + return ObjCConfig; + default: + llvm_unreachable("Unsupported language kind"); + } + } + +public: + ClangFormatConfigTree(const ThreadsafeFS &FS, StringRef FallbackStyle) + : FS(FS), MaxStaleness(std::chrono::seconds(5)), + CPPConfig(FallbackStyle, LanguageKind::LK_Cpp), + ObjCConfig(FallbackStyle, LanguageKind::LK_Cpp) {} + + std::shared_ptr get(PathRef AbsPath, + LanguageKind Lang) { + auto &MapFallback = getMapForStyle(Lang); + + namespace path = llvm::sys::path; + assert(path::is_absolute(AbsPath)); + + // Compute absolute paths to all ancestors (substrings of P.Path). + // Ensure cache entries for each ancestor exist in the map. + llvm::StringRef Parent = path::parent_path(AbsPath); + llvm::SmallVector Caches; + { + std::lock_guard Lock(Mu); + for (auto I = path::begin(Parent), E = path::end(Parent); I != E; ++I) { + assert(I->end() >= Parent.begin() && I->end() <= Parent.end() && + "Canonical path components should be substrings"); + llvm::StringRef Ancestor(Parent.begin(), I->end() - Parent.begin()); +#ifdef _WIN32 + // C:\ is an ancestor, but skip its (relative!) parent C:. + if (Ancestor.size() == 2 && Ancestor.back() == ':') + continue; +#endif + assert(path::is_absolute(Ancestor)); + Caches.push_back(&MapFallback.getAncestorCache(Ancestor)); + } + } + std::chrono::steady_clock::time_point FreshTime = + std::chrono::steady_clock::now() - MaxStaleness; + for (const ClangFormatDirectoryCache *CachePair : Caches) { + if (auto Config = CachePair->get(FS, FreshTime)) + return Config; + } + return MapFallback.getFallbackStyle(); + } +}; + +} // namespace + +FormatProvider getClangFormatProvider(ThreadsafeFS &TFS, + StringRef FallbackStyle) { + return [Tree = std::make_unique(TFS, FallbackStyle)]( + PathRef Filename, StringRef Contents) { + LanguageKind Lang = format::guessLanguage(Filename, Contents); + return Tree->get(Filename, Lang); + }; +} + +std::shared_ptr +formatFallbackProvider(PathRef Filename, llvm::StringRef Contents) { + LanguageKind Lang = format::guessLanguage(Filename, Contents); + if (Lang == LanguageKind::LK_Cpp) { + static const auto Result = std::make_shared( + format::getLLVMStyle(LanguageKind::LK_Cpp)); + return Result; + } + if (Lang == LanguageKind::LK_ObjC) { + static const auto Result = std::make_shared( + format::getLLVMStyle(LanguageKind::LK_ObjC)); + return Result; + } + llvm_unreachable("Unsupported language kind"); +} +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h --- a/clang-tools-extra/clangd/Hover.h +++ b/clang-tools-extra/clangd/Hover.h @@ -115,7 +115,7 @@ /// Get the hover information when hovering at \p Pos. llvm::Optional getHover(ParsedAST &AST, Position Pos, - format::FormatStyle Style, + const format::FormatStyle &Style, const SymbolIndex *Index); } // namespace clangd diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -829,7 +829,7 @@ } // namespace llvm::Optional getHover(ParsedAST &AST, Position Pos, - format::FormatStyle Style, + const format::FormatStyle &Style, const SymbolIndex *Index) { PrintingPolicy PP = getPrintingPolicy(AST.getASTContext().getPrintingPolicy()); diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -359,9 +359,9 @@ llvm::Optional FixIncludes; auto BuildDir = VFS->getCurrentWorkingDirectory(); if (Inputs.Index && !BuildDir.getError()) { - auto Style = getFormatStyleForFile(Filename, Inputs.Contents, *Inputs.TFS); + auto Style = Inputs.ClangFormatProvider(Filename, Inputs.Contents); auto Inserter = std::make_shared( - Filename, Inputs.Contents, Style, BuildDir.get(), + Filename, Inputs.Contents, *Style, BuildDir.get(), &Clang->getPreprocessor().getHeaderSearchInfo()); if (Preamble) { for (const auto &Inc : Preamble->Includes.MainFileIncludes) diff --git a/clang-tools-extra/clangd/SourceCode.h b/clang-tools-extra/clangd/SourceCode.h --- a/clang-tools-extra/clangd/SourceCode.h +++ b/clang-tools-extra/clangd/SourceCode.h @@ -161,15 +161,6 @@ llvm::Optional getCanonicalPath(const FileEntry *F, const SourceManager &SourceMgr); -/// Choose the clang-format style we should apply to a certain file. -/// This will usually use FS to look for .clang-format directories. -/// FIXME: should we be caching the .clang-format file search? -/// This uses format::DefaultFormatStyle and format::DefaultFallbackStyle, -/// though the latter may have been overridden in main()! -format::FormatStyle getFormatStyleForFile(llvm::StringRef File, - llvm::StringRef Content, - const ThreadsafeFS &TFS); - /// Cleanup and format the given replacements. llvm::Expected cleanupAndFormat(StringRef Code, const tooling::Replacements &Replaces, diff --git a/clang-tools-extra/clangd/SourceCode.cpp b/clang-tools-extra/clangd/SourceCode.cpp --- a/clang-tools-extra/clangd/SourceCode.cpp +++ b/clang-tools-extra/clangd/SourceCode.cpp @@ -570,20 +570,6 @@ return digest(Content); } -format::FormatStyle getFormatStyleForFile(llvm::StringRef File, - llvm::StringRef Content, - const ThreadsafeFS &TFS) { - auto Style = format::getStyle(format::DefaultFormatStyle, File, - format::DefaultFallbackStyle, Content, - TFS.view(/*CWD=*/llvm::None).get()); - if (!Style) { - log("getStyle() failed for file {0}: {1}. Fallback is LLVM style.", File, - Style.takeError()); - return format::getLLVMStyle(); - } - return *Style; -} - llvm::Expected cleanupAndFormat(StringRef Code, const tooling::Replacements &Replaces, const format::FormatStyle &Style) { diff --git a/clang-tools-extra/clangd/tool/Check.cpp b/clang-tools-extra/clangd/tool/Check.cpp --- a/clang-tools-extra/clangd/tool/Check.cpp +++ b/clang-tools-extra/clangd/tool/Check.cpp @@ -76,7 +76,7 @@ // from buildInvocation ParseInputs Inputs; std::unique_ptr Invocation; - format::FormatStyle Style; + std::shared_ptr Style; // from buildAST std::shared_ptr Preamble; llvm::Optional AST; @@ -135,6 +135,7 @@ return false; } } + Inputs.ClangFormatProvider = Opts.ClangFormatProvider; log("Parsing command..."); Invocation = buildCompilerInvocation(Inputs, CaptureInvocationDiags, &CC1Args); @@ -148,7 +149,7 @@ // FIXME: Check that resource-dir/built-in-headers exist? - Style = getFormatStyleForFile(File, Inputs.Contents, TFS); + Style = Inputs.ClangFormatProvider(File, Inputs.Contents); return true; } @@ -216,7 +217,7 @@ unsigned Definitions = locateSymbolAt(*AST, Pos, &Index).size(); vlog(" definition: {0}", Definitions); - auto Hover = getHover(*AST, Pos, Style, &Index); + auto Hover = getHover(*AST, Pos, *Style, &Index); vlog(" hover: {0}", Hover.hasValue()); // FIXME: it'd be nice to include code completion, but it's too slow. diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -9,6 +9,7 @@ #include "ClangdLSPServer.h" #include "CodeComplete.h" #include "Features.inc" +#include "FormatProvider.h" #include "PathMapping.h" #include "Protocol.h" #include "TidyProvider.h" @@ -860,6 +861,9 @@ ClangTidyOptProvider = combine(std::move(Providers)); Opts.ClangTidyProvider = ClangTidyOptProvider; } + FormatProvider ClangFormatOptProvider = + getClangFormatProvider(TFS, FallbackStyle); + Opts.ClangFormatProvider = ClangFormatOptProvider; Opts.AsyncPreambleBuilds = AsyncPreamble; Opts.QueryDriverGlobs = std::move(QueryDriverGlobs); Opts.TweakFilter = [&](const Tweak &T) { diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -11,6 +11,7 @@ #include "ClangdServer.h" #include "CodeComplete.h" #include "Compiler.h" +#include "FormatProvider.h" #include "Matchers.h" #include "Protocol.h" #include "Quality.h" @@ -154,6 +155,7 @@ MockFS FS; Annotations Test(Text); ParseInputs ParseInput{tooling::CompileCommand(), &FS, Test.code().str()}; + ParseInput.ClangFormatProvider = formatFallbackProvider; return codeComplete(FilePath, Test.point(), /*Preamble=*/nullptr, ParseInput, Opts); } diff --git a/clang-tools-extra/clangd/unittests/TestTU.h b/clang-tools-extra/clangd/unittests/TestTU.h --- a/clang-tools-extra/clangd/unittests/TestTU.h +++ b/clang-tools-extra/clangd/unittests/TestTU.h @@ -19,6 +19,7 @@ #include "../TidyProvider.h" #include "Compiler.h" +#include "FormatProvider.h" #include "ParsedAST.h" #include "TestFS.h" #include "index/Index.h" @@ -60,6 +61,8 @@ std::vector ExtraArgs; TidyProvider ClangTidyProvider = {}; + + mutable FormatProvider ClangFormatProvider = formatFallbackProvider; // Index to use when building AST. const SymbolIndex *ExternalIndex = nullptr; diff --git a/clang-tools-extra/clangd/unittests/TestTU.cpp b/clang-tools-extra/clangd/unittests/TestTU.cpp --- a/clang-tools-extra/clangd/unittests/TestTU.cpp +++ b/clang-tools-extra/clangd/unittests/TestTU.cpp @@ -9,6 +9,7 @@ #include "TestTU.h" #include "Compiler.h" #include "Diagnostics.h" +#include "FormatProvider.h" #include "TestFS.h" #include "index/FileIndex.h" #include "index/MemIndex.h" @@ -61,6 +62,9 @@ Inputs.Opts = ParseOptions(); if (ClangTidyProvider) Inputs.ClangTidyProvider = ClangTidyProvider; + ClangFormatProvider = + getClangFormatProvider(FS, format::DefaultFallbackStyle); + Inputs.ClangFormatProvider = ClangFormatProvider; Inputs.Index = ExternalIndex; return Inputs; }