(root)/
glib-2.79.0/
.gitlab-ci/
clang-format-diff.py
       1  #!/usr/bin/env python3
       2  #
       3  # === clang-format-diff.py - ClangFormat Diff Reformatter ---*- python -*-=== #
       4  #
       5  # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
       6  # See https://llvm.org/LICENSE.txt for license information.
       7  # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
       8  #
       9  # ===---------------------------------------------------------------------=== #
      10  
      11  """
      12  This script reads input from a unified diff and reformats all the changed
      13  lines. This is useful to reformat all the lines touched by a specific patch.
      14  Example usage for git/svn users:
      15  
      16    git diff -U0 --no-color HEAD^ | clang-format-diff.py -p1 -i
      17    svn diff --diff-cmd=diff -x-U0 | clang-format-diff.py -i
      18  
      19  """
      20  from __future__ import absolute_import, division, print_function
      21  
      22  import argparse
      23  import difflib
      24  import re
      25  import subprocess
      26  import sys
      27  
      28  if sys.version_info.major >= 3:
      29      from io import StringIO
      30  else:
      31      from io import BytesIO as StringIO
      32  
      33  
      34  def main():
      35      parser = argparse.ArgumentParser(
      36          description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
      37      )
      38      parser.add_argument(
      39          "-i",
      40          action="store_true",
      41          default=False,
      42          help="apply edits to files instead of displaying a " "diff",
      43      )
      44      parser.add_argument(
      45          "-p",
      46          metavar="NUM",
      47          default=0,
      48          help="strip the smallest prefix containing P slashes",
      49      )
      50      parser.add_argument(
      51          "-regex",
      52          metavar="PATTERN",
      53          default=None,
      54          help="custom pattern selecting file paths to reformat "
      55          "(case sensitive, overrides -iregex)",
      56      )
      57      parser.add_argument(
      58          "-iregex",
      59          metavar="PATTERN",
      60          default=r".*\.(cpp|cc|c\+\+|cxx|c|cl|h|hh|hpp|m|mm|inc"
      61          r"|js|ts|proto|protodevel|java|cs)",
      62          help="custom pattern selecting file paths to reformat "
      63          "(case insensitive, overridden by -regex)",
      64      )
      65      parser.add_argument(
      66          "-sort-includes",
      67          action="store_true",
      68          default=False,
      69          help="let clang-format sort include blocks",
      70      )
      71      parser.add_argument(
      72          "-v",
      73          "--verbose",
      74          action="store_true",
      75          help="be more verbose, ineffective without -i",
      76      )
      77      parser.add_argument(
      78          "-style",
      79          help="formatting style to apply (LLVM, Google, " "Chromium, Mozilla, WebKit)",
      80      )
      81      parser.add_argument(
      82          "-binary",
      83          default="clang-format",
      84          help="location of binary to use for clang-format",
      85      )
      86      args = parser.parse_args()
      87  
      88      # Extract changed lines for each file.
      89      filename = None
      90      lines_by_file = {}
      91      for line in sys.stdin:
      92          match = re.search(r"^\+\+\+\ (.*?/){%s}(\S*)" % args.p, line)
      93          if match:
      94              filename = match.group(2)
      95          if filename is None:
      96              continue
      97  
      98          if args.regex is not None:
      99              if not re.match("^%s$" % args.regex, filename):
     100                  continue
     101          else:
     102              if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE):
     103                  continue
     104  
     105          match = re.search(r"^@@.*\+(\d+)(,(\d+))?", line)
     106          if match:
     107              start_line = int(match.group(1))
     108              line_count = 1
     109              if match.group(3):
     110                  line_count = int(match.group(3))
     111              if line_count == 0:
     112                  continue
     113              end_line = start_line + line_count - 1
     114              lines_by_file.setdefault(filename, []).extend(
     115                  ["-lines", str(start_line) + ":" + str(end_line)]
     116              )
     117  
     118      # Reformat files containing changes in place.
     119      # We need to count amount of bytes generated in the output of
     120      # clang-format-diff. If clang-format-diff doesn't generate any bytes it
     121      # means there is nothing to format.
     122      format_line_counter = 0
     123      for filename, lines in lines_by_file.items():
     124          if args.i and args.verbose:
     125              print("Formatting {}".format(filename))
     126          command = [args.binary, filename]
     127          if args.i:
     128              command.append("-i")
     129          if args.sort_includes:
     130              command.append("-sort-includes")
     131          command.extend(lines)
     132          if args.style:
     133              command.extend(["-style", args.style])
     134          p = subprocess.Popen(
     135              command,
     136              stdout=subprocess.PIPE,
     137              stderr=None,
     138              stdin=subprocess.PIPE,
     139              universal_newlines=True,
     140          )
     141          stdout, _ = p.communicate()
     142          if p.returncode != 0:
     143              sys.exit(p.returncode)
     144  
     145          if not args.i:
     146              with open(filename) as f:
     147                  code = f.readlines()
     148              formatted_code = StringIO(stdout).readlines()
     149              diff = difflib.unified_diff(
     150                  code,
     151                  formatted_code,
     152                  filename,
     153                  filename,
     154                  "(before formatting)",
     155                  "(after formatting)",
     156              )
     157              diff_string = "".join(diff)
     158              if diff_string:
     159                  format_line_counter += sys.stdout.write(diff_string)
     160  
     161      if format_line_counter > 0:
     162          sys.exit(1)
     163  
     164  
     165  if __name__ == "__main__":
     166      main()