1import re
2from gitlint.rules import CommitRule, RuleViolation
3from gitlint.options import IntOption
4
5
6class BodyMaxLineLengthWithExceptions(CommitRule):
7    name = "body-max-line-length-with-exceptions"
8    id = "UC1"
9
10    options_spec = [IntOption("line-length", 80, "Max line length")]
11    line_length_violation_message = """Line exceeds max length ({0}>{1}).
12    It's possible you intended to use one of the following exceptions:
13    1. Put logs or shell script in a quoted section with triple quotes (''') before and after the section
14    2. Put a long link at the bottom in a footnote.  example: [1] https://my_long_link.com
15    Line that was too long:
16"""
17    tabs_violation_message = "Line contains hard tab characters (\\t)"
18
19    def validate(self, commit):
20        in_block_comment = False
21        for line in commit.message.body:
22            # allow a quoted string to be over the line limit
23            if (
24                line.startswith("'''")
25                or line.startswith('"""')
26                or line.startswith("```")
27            ):
28                in_block_comment = not in_block_comment
29
30            if in_block_comment:
31                continue
32
33            if "\t" in line:
34                return [RuleViolation(self.id, self.tabs_violation_message, line)]
35
36            # allow footnote url links to be as long as needed example
37            # [1] http://www.myspace.com
38            ret = re.match(r"^\[\d+\]:? ", line)
39            if ret is not None:
40                continue
41
42            # allow signed-off-by
43            if line.startswith("Signed-off-by:"):
44                continue
45
46            max_length = self.options["line-length"].value
47            if len(line) > max_length:
48                return [
49                    RuleViolation(
50                        self.id,
51                        self.line_length_violation_message.format(
52                            len(line), max_length
53                        ),
54                        line,
55                    )
56                ]
57        return None
58