Skip to content

Commit 6d293f3

Browse files
authored
Merge pull request #2267 from strictdoc-project/stanislaw/fixit_comments
Code climate: add custom fixit linting check: StrictDoc_CheckCommentsRule
2 parents 12be1fd + 6e16bb0 commit 6d293f3

File tree

4 files changed

+117
-6
lines changed

4 files changed

+117
-6
lines changed

developer/fixit/check_comments.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
from typing import List, Optional, Tuple
2+
3+
from fixit import LintRule
4+
from libcst import Comment, Module
5+
from libcst.metadata import ParentNodeProvider, PositionProvider
6+
7+
RESERVED_STRINGS = [
8+
"fmt:",
9+
"http",
10+
"mypy:",
11+
"noqa",
12+
"pragma",
13+
"pylint",
14+
"@relation",
15+
"type:",
16+
]
17+
18+
19+
def find_first_significant_char_index(text: str) -> Optional[int]:
20+
for i, char in enumerate(text):
21+
if char not in {'"', " ", "\t", "#", "\n"}:
22+
return i
23+
return None
24+
25+
26+
def should_check_string(string: str) -> bool:
27+
return not any(rs in string for rs in RESERVED_STRINGS)
28+
29+
30+
def find_last_significant_char_index(text: str) -> Optional[int]:
31+
for i in range(len(text) - 1, -1, -1):
32+
if text[i] not in {'"', " ", "\t", "#", "\n"}:
33+
return i
34+
return None
35+
36+
37+
class StrictDoc_CheckCommentsRule(LintRule):
38+
METADATA_DEPENDENCIES = (
39+
ParentNodeProvider,
40+
PositionProvider,
41+
)
42+
43+
def __init__(self) -> None:
44+
super().__init__()
45+
self.comments_so_far: List[Tuple[int, int, Comment]] = []
46+
47+
def leave_Module(self, original_node: "Module") -> None: # noqa: ARG002
48+
self.comments_so_far.sort(key=lambda x: x[0])
49+
50+
current_line, current_column = -1, -1
51+
for comment_index_, (
52+
comment_line_,
53+
comment_column_,
54+
comment_,
55+
) in enumerate(self.comments_so_far):
56+
if (
57+
comment_column_ != current_column
58+
or comment_line_ != (current_line + 1)
59+
) and should_check_string(comment_.value):
60+
first_index = find_first_significant_char_index(comment_.value)
61+
if first_index is not None:
62+
if comment_.value[first_index].islower():
63+
patched_node_value = (
64+
comment_.value[:first_index]
65+
+ comment_.value[first_index].upper()
66+
+ comment_.value[first_index + 1 :]
67+
)
68+
patched_comment = Comment(patched_node_value)
69+
self.report(
70+
comment_,
71+
(
72+
"The comments first sentence must start with "
73+
"a capital letter."
74+
),
75+
replacement=patched_comment,
76+
)
77+
if comment_index_ > 0:
78+
last_comment = self.comments_so_far[comment_index_ - 1][2]
79+
self._patch_last_comment_sentence_if_needed(last_comment)
80+
81+
current_column = comment_column_
82+
current_line = comment_line_
83+
84+
if len(self.comments_so_far) > 0:
85+
self._patch_last_comment_sentence_if_needed(
86+
self.comments_so_far[-1][2]
87+
)
88+
89+
def visit_Comment(self, node: "Comment") -> Optional[bool]:
90+
position = self.get_metadata(PositionProvider, node)
91+
line = position.start.line
92+
column = position.start.column
93+
self.comments_so_far.append((line, column, node))
94+
95+
def _patch_last_comment_sentence_if_needed(self, comment: Comment):
96+
last_index = find_last_significant_char_index(comment.value)
97+
if last_index is not None and should_check_string(comment.value):
98+
if comment.value[last_index] != ".":
99+
patched_node_value = (
100+
comment.value[:last_index]
101+
+ comment.value[last_index]
102+
+ "."
103+
+ comment.value[last_index + 1 :]
104+
)
105+
self.report(
106+
comment,
107+
("The comment's last sentence must end with a dot."),
108+
replacement=Comment(patched_node_value),
109+
)

developer/fixit/check_docstrings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Optional
22

33
from fixit import LintRule
4-
from libcst import SimpleString, Expr
4+
from libcst import Expr, SimpleString
55
from libcst.metadata import ParentNodeProvider, PositionProvider
66

77
RESERVED_STRINGS = ["@relation", "FIXME"]

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ pythonpath = [
125125

126126
[tool.fixit]
127127
enable = [
128+
".developer.fixit.check_comments",
128129
".developer.fixit.check_docstrings",
129130
]
130131

tasks.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -595,22 +595,23 @@ def lint_mypy(context):
595595

596596

597597
@task(aliases=["lf"])
598-
def lint_fixit(context, fix=False):
598+
def lint_fixit(context, fix=False, auto=False, path="strictdoc/"):
599599
if fix:
600+
auto_argument = "--automatic" if auto else ""
600601
run_invoke_with_tox(
601602
context,
602603
ToxEnvironment.CHECK,
603-
"""
604-
fixit fix strictdoc/
604+
f"""
605+
fixit fix {path} {auto_argument}
605606
""",
606607
pty=True,
607608
)
608609
else:
609610
run_invoke_with_tox(
610611
context,
611612
ToxEnvironment.CHECK,
612-
"""
613-
fixit lint --diff strictdoc/
613+
f"""
614+
fixit lint --diff {path}
614615
""",
615616
)
616617

0 commit comments

Comments
 (0)