Typz created this revision.
Typz added reviewers: krasimir, djasper, klimek.
Herald added a subscriber: acoomans.

This patch allows defining external styles, which can be used exactly
like the embedded styles (llvm, google, mozilla...).

These styles are clang-format files installed either systemwide in
/usr/local/share/clang-format, or per-user in ~/.local/share/clang-
format. These can be specified by simply using the name of the file,
and clang-format will search the directories for the style:

  clang-format -style=foo-1.0

The patch also allows loading specifying a file name directly, either
relative or absolute:

  clang-format -style=/home/clang-format-styles/foo-1.0
  clang-format -style=styles/foo-1.0

This works also in `BaseOnStyle` field, which allows defining compagny-
wide (and possibly versionned) clang-format styles, without having to
maintain many copies in each repository: each repository will simply
need to store a short .clang-format, which simply references the
compagny-wide style.

The drawback is that these style need to be installed on each computer,
but this may be automated through an OS package. In any case, the error
cannot be ignored, as the user will be presented with an error message
if he does not have the style.

NOTE: this may be further improved by also allowing URL (http://,
git://...) in this field, which would allow clang-format to automatically
download the missing styles.


Repository:
  rC Clang

https://reviews.llvm.org/D50147

Files:
  include/clang/Format/Format.h
  lib/Basic/VirtualFileSystem.cpp
  lib/Format/Format.cpp
  unittests/Format/FormatTest.cpp

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.
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<Twine, 3> paths = {
+    {"/usr/local/share/clang-format/", Name},
+    {"~/.local/share/clang-format/", Name},
+    Name
+  };
+  for (Twine Path: paths) {
+    llvm::SmallVector<char, 100> RealPath;
+    Twine ConfigFile{!FS->getRealPath(Path, RealPath) ? RealPath : Path};
+    if (FS->exists(ConfigFile)) {
+      llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 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: 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<char> &Output) const {
-  return llvm::sys::fs::real_path(Path, Output);
+  return llvm::sys::fs::real_path(Path, Output, true);
 }
 
 IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
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.
 ///
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D50147: clang-for... Francois Ferrand via Phabricator via cfe-commits

Reply via email to