[clang-format] Optimize processing .clang-format-ignore files (#76733)
Reuse the patterns governing the previous input file being formatted if the current input file is from the same directory.
This commit is contained in:
@@ -131,6 +131,9 @@ An easy way to create the ``.clang-format`` file is:
|
||||
|
||||
Available style options are described in :doc:`ClangFormatStyleOptions`.
|
||||
|
||||
.clang-format-ignore
|
||||
====================
|
||||
|
||||
You can create ``.clang-format-ignore`` files to make ``clang-format`` ignore
|
||||
certain files. A ``.clang-format-ignore`` file consists of patterns of file path
|
||||
names. It has the following format:
|
||||
@@ -141,7 +144,8 @@ names. It has the following format:
|
||||
* A non-comment line is a single pattern.
|
||||
* The slash (``/``) is used as the directory separator.
|
||||
* A pattern is relative to the directory of the ``.clang-format-ignore`` file
|
||||
(or the root directory if the pattern starts with a slash).
|
||||
(or the root directory if the pattern starts with a slash). Patterns
|
||||
containing drive names (e.g. ``C:``) are not supported.
|
||||
* Patterns follow the rules specified in `POSIX 2.13.1, 2.13.2, and Rule 1 of
|
||||
2.13.3 <https://pubs.opengroup.org/onlinepubs/9699919799/utilities/
|
||||
V3_chap02.html#tag_18_13>`_.
|
||||
|
||||
@@ -21,13 +21,26 @@
|
||||
|
||||
// RUN: touch .clang-format-ignore
|
||||
// RUN: clang-format -verbose foo.c foo.js 2> %t.stderr
|
||||
// RUN: grep "Formatting \[1/2] foo.c" %t.stderr
|
||||
// RUN: grep "Formatting \[2/2] foo.js" %t.stderr
|
||||
// RUN: grep -Fx "Formatting [1/2] foo.c" %t.stderr
|
||||
// RUN: grep -Fx "Formatting [2/2] foo.js" %t.stderr
|
||||
|
||||
// RUN: echo "*.js" > .clang-format-ignore
|
||||
// RUN: clang-format -verbose foo.c foo.js 2> %t.stderr
|
||||
// RUN: grep "Formatting \[1/2] foo.c" %t.stderr
|
||||
// RUN: not grep "Formatting \[2/2] foo.js" %t.stderr
|
||||
// RUN: grep -Fx "Formatting [1/2] foo.c" %t.stderr
|
||||
// RUN: not grep -F foo.js %t.stderr
|
||||
|
||||
// RUN: cd ../../..
|
||||
// RUN: rm -rf %t.dir
|
||||
// RUN: cd ../..
|
||||
// RUN: clang-format -verbose *.cc level1/*.c* level1/level2/foo.* 2> %t.stderr
|
||||
// RUN: grep -x "Formatting \[1/5] .*foo\.c" %t.stderr
|
||||
// RUN: not grep -F foo.js %t.stderr
|
||||
|
||||
// RUN: rm .clang-format-ignore
|
||||
// RUN: clang-format -verbose *.cc level1/*.c* level1/level2/foo.* 2> %t.stderr
|
||||
// RUN: grep -x "Formatting \[1/5] .*foo\.cc" %t.stderr
|
||||
// RUN: grep -x "Formatting \[2/5] .*bar\.cc" %t.stderr
|
||||
// RUN: grep -x "Formatting \[3/5] .*baz\.c" %t.stderr
|
||||
// RUN: grep -x "Formatting \[4/5] .*foo\.c" %t.stderr
|
||||
// RUN: not grep -F foo.js %t.stderr
|
||||
|
||||
// RUN: cd ..
|
||||
// RUN: rm -r %t.dir
|
||||
|
||||
@@ -571,6 +571,11 @@ static int dumpConfig(bool IsSTDIN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
using String = SmallString<128>;
|
||||
static String IgnoreDir; // Directory of .clang-format-ignore file.
|
||||
static StringRef PrevDir; // Directory of previous `FilePath`.
|
||||
static SmallVector<String> Patterns; // Patterns in .clang-format-ignore file.
|
||||
|
||||
// Check whether `FilePath` is ignored according to the nearest
|
||||
// .clang-format-ignore file based on the rules below:
|
||||
// - A blank line is skipped.
|
||||
@@ -586,33 +591,50 @@ static bool isIgnored(StringRef FilePath) {
|
||||
if (!is_regular_file(FilePath))
|
||||
return false;
|
||||
|
||||
using namespace llvm::sys::path;
|
||||
SmallString<128> Path, AbsPath{FilePath};
|
||||
String Path;
|
||||
String AbsPath{FilePath};
|
||||
|
||||
using namespace llvm::sys::path;
|
||||
make_absolute(AbsPath);
|
||||
remove_dots(AbsPath, /*remove_dot_dot=*/true);
|
||||
|
||||
StringRef IgnoreDir{AbsPath};
|
||||
do {
|
||||
IgnoreDir = parent_path(IgnoreDir);
|
||||
if (IgnoreDir.empty())
|
||||
if (StringRef Dir{parent_path(AbsPath)}; PrevDir != Dir) {
|
||||
PrevDir = Dir;
|
||||
|
||||
for (;;) {
|
||||
Path = Dir;
|
||||
append(Path, ".clang-format-ignore");
|
||||
if (is_regular_file(Path))
|
||||
break;
|
||||
Dir = parent_path(Dir);
|
||||
if (Dir.empty())
|
||||
return false;
|
||||
}
|
||||
|
||||
IgnoreDir = convert_to_slash(Dir);
|
||||
|
||||
std::ifstream IgnoreFile{Path.c_str()};
|
||||
if (!IgnoreFile.good())
|
||||
return false;
|
||||
|
||||
Path = IgnoreDir;
|
||||
append(Path, ".clang-format-ignore");
|
||||
} while (!is_regular_file(Path));
|
||||
Patterns.clear();
|
||||
|
||||
std::ifstream IgnoreFile{Path.c_str()};
|
||||
if (!IgnoreFile.good())
|
||||
for (std::string Line; std::getline(IgnoreFile, Line);) {
|
||||
if (const auto Pattern{StringRef{Line}.trim()};
|
||||
// Skip empty and comment lines.
|
||||
!Pattern.empty() && Pattern[0] != '#') {
|
||||
Patterns.push_back(Pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IgnoreDir.empty())
|
||||
return false;
|
||||
|
||||
const auto Pathname = convert_to_slash(AbsPath);
|
||||
for (std::string Line; std::getline(IgnoreFile, Line);) {
|
||||
auto Pattern = StringRef(Line).trim();
|
||||
if (Pattern.empty() || Pattern[0] == '#')
|
||||
continue;
|
||||
|
||||
const bool IsNegated = Pattern[0] == '!';
|
||||
const auto Pathname{convert_to_slash(AbsPath)};
|
||||
for (const auto &Pat : Patterns) {
|
||||
const bool IsNegated = Pat[0] == '!';
|
||||
StringRef Pattern{Pat};
|
||||
if (IsNegated)
|
||||
Pattern = Pattern.drop_front();
|
||||
|
||||
@@ -620,11 +642,14 @@ static bool isIgnored(StringRef FilePath) {
|
||||
continue;
|
||||
|
||||
Pattern = Pattern.ltrim();
|
||||
|
||||
// `Pattern` is relative to `IgnoreDir` unless it starts with a slash.
|
||||
// This doesn't support patterns containing drive names (e.g. `C:`).
|
||||
if (Pattern[0] != '/') {
|
||||
Path = convert_to_slash(IgnoreDir);
|
||||
Path = IgnoreDir;
|
||||
append(Path, Style::posix, Pattern);
|
||||
remove_dots(Path, /*remove_dot_dot=*/true, Style::posix);
|
||||
Pattern = Path.str();
|
||||
Pattern = Path;
|
||||
}
|
||||
|
||||
if (clang::format::matchFilePath(Pattern, Pathname) == !IsNegated)
|
||||
|
||||
Reference in New Issue
Block a user