| 169 | |
| 170 | |
| 171 | class ClangFormatHelper(FormatHelper): |
| 172 | name = "git-clang-format" |
| 173 | friendly_name = "C/C++ code formatter" |
| 174 | |
| 175 | |
| 176 | @property |
| 177 | def instructions(self) -> str: |
| 178 | return " ".join(self.cf_cmd) |
| 179 | |
| 180 | def should_include_extensionless_file(self, path: str) -> bool: |
| 181 | return path.startswith("libcxx/include") |
| 182 | |
| 183 | def filter_changed_files(self, changed_files: List[str]) -> List[str]: |
| 184 | filtered_files = [] |
| 185 | for path in changed_files: |
| 186 | _, ext = os.path.splitext(path) |
| 187 | if ext in (".cpp", ".c", ".h", ".hpp", ".hxx", ".cxx", ".inc", ".cppm"): |
| 188 | filtered_files.append(path) |
| 189 | elif ext == "" and self.should_include_extensionless_file(path): |
| 190 | filtered_files.append(path) |
| 191 | return filtered_files |
| 192 | |
| 193 | @property |
| 194 | def clang_fmt_path(self) -> str: |
| 195 | if "CLANG_FORMAT_PATH" in os.environ: |
| 196 | return os.environ["CLANG_FORMAT_PATH"] |
| 197 | return "git-clang-format-19" |
| 198 | |
| 199 | def has_tool(self) -> bool: |
| 200 | cmd = [self.clang_fmt_path, "-h"] |
| 201 | proc = None |
| 202 | try: |
| 203 | proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 204 | except: |
| 205 | return False |
| 206 | return proc.returncode == 0 |
| 207 | |
| 208 | def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]: |
| 209 | cpp_files = self.filter_changed_files(changed_files) |
| 210 | if not cpp_files: |
| 211 | return None |
| 212 | |
| 213 | cf_cmd = [ |
| 214 | self.clang_fmt_path, |
| 215 | "--binary=clang-format-19", |
| 216 | "--diff", |
| 217 | ] |
| 218 | |
| 219 | if args.start_rev and args.end_rev: |
| 220 | cf_cmd.append(args.start_rev) |
| 221 | cf_cmd.append(args.end_rev) |
| 222 | |
| 223 | cf_cmd.append("--") |
| 224 | cf_cmd += cpp_files |
| 225 | |
| 226 | if args.verbose: |
| 227 | print(f"Running: {' '.join(cf_cmd)}") |
| 228 | self.cf_cmd = cf_cmd |