Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -1843,7 +1843,7 @@ /// /// Returns ``true`` if the Style has been set. bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language, - FormatStyle *Style); + FormatStyle *Style, vfs::FileSystem *FS = nullptr); /// Parse configuration from YAML-formatted text. /// Index: lib/Basic/VirtualFileSystem.cpp =================================================================== --- lib/Basic/VirtualFileSystem.cpp +++ lib/Basic/VirtualFileSystem.cpp @@ -285,7 +285,7 @@ std::error_code RealFileSystem::getRealPath(const Twine &Path, SmallVectorImpl &Output) const { - return llvm::sys::fs::real_path(Path, Output); + return llvm::sys::fs::real_path(Path, Output, true); } IntrusiveRefCntPtr vfs::getRealFileSystem() { Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -928,8 +928,42 @@ return NoStyle; } +// Try loading config file from path, in argument to -style and BasedOnStyle. +bool loadSystemStyle(StringRef Name, FormatStyle *Style, + vfs::FileSystem *FS = nullptr) { + if (!FS) { + FS = vfs::getRealFileSystem().get(); + } + const llvm::SmallVector paths = { + {"/usr/local/share/clang-format/", Name}, + {"~/.local/share/clang-format/", Name}, + Name + }; + for (Twine Path: paths) { + llvm::SmallVector RealPath; + Twine ConfigFile{!FS->getRealPath(Path, RealPath) ? RealPath : Path}; + if (FS->exists(ConfigFile)) { + llvm::ErrorOr> Text = + FS->getBufferForFile(ConfigFile); + if (std::error_code EC = Text.getError()) { + LLVM_DEBUG(llvm::dbgs() << "Error reading " << ConfigFile << ": " + << EC.message() << "\n"); + return false; + } + if (std::error_code EC = + parseConfiguration(Text.get()->getBuffer(), Style)) { + LLVM_DEBUG(llvm::dbgs() << "Error parsing " << ConfigFile << ": " + << EC.message() << "\n"); + return false; + } + return true; + } + } + return false; +} + bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language, - FormatStyle *Style) { + FormatStyle *Style, vfs::FileSystem *FS) { if (Name.equals_lower("llvm")) { *Style = getLLVMStyle(); } else if (Name.equals_lower("chromium")) { @@ -944,7 +978,7 @@ *Style = getGNUStyle(); } else if (Name.equals_lower("none")) { *Style = getNoStyle(); - } else { + } else if (!loadSystemStyle(Name, Style, FS)) { return false; } @@ -2103,13 +2137,16 @@ const char *StyleOptionHelpDescription = "Coding style, currently supports:\n" " LLVM, Google, Chromium, Mozilla, WebKit.\n" + "Additional styles can be installed in /usr/local/share/clang-format\n" + "or ~/.local/share/clang-format.\n" "Use -style=file to load style configuration from\n" ".clang-format file located in one of the parent\n" "directories of the source file (or current\n" "directory for stdin).\n" "Use -style=\"{key: value, ...}\" to set specific\n" "parameters, e.g.:\n" - " -style=\"{BasedOnStyle: llvm, IndentWidth: 8}\""; + " -style=\"{BasedOnStyle: llvm, IndentWidth: 8}\"" + "Other value of the parameter must the path of the config file to load"; static FormatStyle::LanguageKind getLanguageByFileName(StringRef FileName) { if (FileName.endswith(".java")) @@ -2174,7 +2211,7 @@ } if (!StyleName.equals_lower("file")) { - if (!getPredefinedStyle(StyleName, Style.Language, &Style)) + if (!getPredefinedStyle(StyleName, Style.Language, &Style, FS)) return make_string_error("Invalid value for -style"); return Style; } Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -12113,6 +12113,61 @@ ASSERT_EQ(*Style1, getGoogleStyle()); } +TEST(FormatStyle, GetExternalStyle) { + vfs::InMemoryFileSystem FS; + // Test 1: format file in /usr/local/share/clang-format/ + ASSERT_TRUE( + FS.addFile("/usr/local/share/clang-format/style1", 0, + llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: Google"))); + auto Style1 = getStyle("style1", "", "LLVM", "", &FS); + ASSERT_TRUE((bool)Style1); + ASSERT_EQ(*Style1, getGoogleStyle()); + + // Test 2: format file in ~/.local/share/clang-format/ + ASSERT_TRUE( + FS.addFile("~/.local/share/clang-format/style2", 0, + llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: Google"))); + auto Style2 = getStyle("style2", "", "LLVM", "", &FS); + ASSERT_TRUE((bool)Style2); + ASSERT_EQ(*Style2, getGoogleStyle()); + + // Test 3: format file in absolute path + ASSERT_TRUE( + FS.addFile("/clang-format-styles/style3", 0, + llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: Google"))); + auto Style3 = getStyle("/clang-format-styles/style3", "", "LLVM", "", &FS); + ASSERT_TRUE((bool)Style3); + ASSERT_EQ(*Style3, getGoogleStyle()); + + // Test 4: format file in relative path + ASSERT_TRUE( + FS.addFile("/home/clang-format-styles/style4", 0, + llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: Google"))); + FS.setCurrentWorkingDirectory("/home/clang-format-styles"); + auto Style4 = getStyle("style4", "", "LLVM", "", &FS); + ASSERT_TRUE((bool)Style4); + ASSERT_EQ(*Style4, getGoogleStyle()); + + // Test 5: file does not exist + auto Style5 = getStyle("style5", "", "LLVM", "", &FS); + ASSERT_FALSE((bool)Style5); + llvm::consumeError(Style5.takeError()); + + // Test 6: absolute file does not exist + auto Style6 = getStyle("/style6", "", "LLVM", "", &FS); + ASSERT_FALSE((bool)Style6); + llvm::consumeError(Style6.takeError()); + + // Test 7: file is not a format style + ASSERT_TRUE( + FS.addFile("/usr/local/share/clang-format/nostyle", 0, + llvm::MemoryBuffer::getMemBuffer("This is not a style..."))); + FS.setCurrentWorkingDirectory("/home/clang-format-styles"); + auto Style7 = getStyle("nostyle", "", "LLVM", "", &FS); + ASSERT_FALSE((bool)Style7); + llvm::consumeError(Style7.takeError()); +} + TEST(FormatStyle, GetStyleOfFile) { vfs::InMemoryFileSystem FS; // Test 1: format file in the same directory.