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