Skip to content

Commit 2f28724

Browse files
committed
add rules to check for block titles
1 parent 5d1c472 commit 2f28724

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
extends: script
3+
scope: raw
4+
level: warning
5+
message: "Code blocks should have a title (line starting with '.' before the code block)."
6+
link: https://github.com/circleci/circleci-docs
7+
script: |
8+
text := import("text")
9+
matches := []
10+
11+
lines := text.split(scope, "\n")
12+
13+
// Track delimiter counts for different types
14+
listing_count := 0 // ----
15+
fenced_count := 0 // ````
16+
literal_count := 0 // ....
17+
18+
for i, line in lines {
19+
trimmed := text.trim_space(line)
20+
21+
// Check for code block delimiters
22+
is_listing_block := text.re_match("^-{4,}$", trimmed)
23+
is_fenced_block := text.re_match("^`{3,}$", trimmed)
24+
is_literal_block := text.re_match("^\\.{4,}$", trimmed)
25+
26+
if is_listing_block || is_fenced_block || is_literal_block {
27+
// Determine which count to use
28+
current_count := 0
29+
if is_listing_block {
30+
current_count = listing_count
31+
} else if is_fenced_block {
32+
current_count = fenced_count
33+
} else if is_literal_block {
34+
current_count = literal_count
35+
}
36+
37+
// Even count = opening delimiter, odd count = closing delimiter
38+
is_opening := current_count % 2 == 0
39+
40+
if is_opening {
41+
// Look back for a title, skipping empty lines and attributes
42+
has_title := false
43+
44+
for j := i - 1; j >= 0; j-- {
45+
prev_trimmed := text.trim_space(lines[j])
46+
47+
// Skip empty lines and attribute lines (like [,yaml])
48+
if prev_trimmed == "" || text.has_prefix(prev_trimmed, "[") {
49+
continue
50+
}
51+
52+
// Check if this line is a title
53+
if text.has_prefix(prev_trimmed, ".") {
54+
has_title = true
55+
break
56+
}
57+
58+
// Found a non-attribute, non-empty line that's not a title
59+
break
60+
}
61+
62+
if !has_title {
63+
start := text.index(scope, line)
64+
matches = append(matches, {begin: start, end: start + len(line)})
65+
}
66+
}
67+
68+
// Increment the appropriate counter
69+
if is_listing_block {
70+
listing_count = listing_count + 1
71+
} else if is_fenced_block {
72+
fenced_count = fenced_count + 1
73+
} else if is_literal_block {
74+
literal_count = literal_count + 1
75+
}
76+
}
77+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
extends: script
3+
scope: raw
4+
level: warning
5+
message: "Images should have a title (line starting with '.' before the image)."
6+
link: https://github.com/circleci/circleci-docs
7+
script: |
8+
text := import("text")
9+
matches := []
10+
11+
lines := text.split(scope, "\n")
12+
13+
for i, line in lines {
14+
trimmed := text.trim_space(line)
15+
16+
// Check for image
17+
if text.has_prefix(trimmed, "image::") {
18+
// Look back for a title, skipping empty lines and attributes
19+
has_title := false
20+
21+
for j := i - 1; j >= 0; j-- {
22+
prev_trimmed := text.trim_space(lines[j])
23+
24+
// Skip empty lines and attribute lines
25+
if prev_trimmed == "" || text.has_prefix(prev_trimmed, "[") {
26+
continue
27+
}
28+
29+
// Check if this line is a title
30+
if text.has_prefix(prev_trimmed, ".") {
31+
has_title = true
32+
break
33+
}
34+
35+
// Found a non-attribute, non-empty line that's not a title
36+
break
37+
}
38+
39+
if !has_title {
40+
start := text.index(scope, line)
41+
matches = append(matches, {begin: start, end: start + len(line)})
42+
}
43+
}
44+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
extends: script
3+
scope: raw
4+
level: warning
5+
message: "Tables should have a title (line starting with '.' before the table)."
6+
link: https://github.com/circleci/circleci-docs
7+
script: |
8+
text := import("text")
9+
matches := []
10+
11+
lines := text.split(scope, "\n")
12+
table_delimiter_count := 0
13+
14+
for i, line in lines {
15+
trimmed := text.trim_space(line)
16+
17+
// Check for table delimiter
18+
if trimmed == "|===" {
19+
// Even count = opening delimiter, odd count = closing delimiter
20+
is_opening := table_delimiter_count % 2 == 0
21+
22+
if is_opening {
23+
// Look back for a title, skipping empty lines, attributes, and block delimiters
24+
has_title := false
25+
26+
for j := i - 1; j >= 0; j-- {
27+
prev_trimmed := text.trim_space(lines[j])
28+
29+
// Skip empty lines, attribute lines, and block delimiters (-- or ==)
30+
if prev_trimmed == "" || text.has_prefix(prev_trimmed, "[") || prev_trimmed == "--" || prev_trimmed == "==" {
31+
continue
32+
}
33+
34+
// Check if this line is a title
35+
if text.has_prefix(prev_trimmed, ".") {
36+
has_title = true
37+
break
38+
}
39+
40+
// Found a non-attribute, non-empty line that's not a title
41+
break
42+
}
43+
44+
if !has_title {
45+
start := text.index(scope, line)
46+
matches = append(matches, {begin: start, end: start + len(line)})
47+
}
48+
}
49+
50+
table_delimiter_count = table_delimiter_count + 1
51+
}
52+
}

0 commit comments

Comments
 (0)