Skip to content

Commit 6cd85ed

Browse files
committed
check xref link text uses title case
1 parent 2f28724 commit 6cd85ed

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
extends: script
3+
scope: raw
4+
level: warning
5+
message: "Use title case for xref link text."
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+
// Words that should not be capitalized in title case (unless first/last word)
14+
// Articles, coordinating conjunctions, and short prepositions only
15+
lowercase_words := ["a", "an", "the", "and", "but", "or", "for", "nor", "so", "yet",
16+
"at", "by", "in", "of", "on", "to", "up", "as", "via", "per"]
17+
18+
for i, line in lines {
19+
// Find all xref patterns: xref:path[Link Text]
20+
xref_pattern := "xref:[^\\[]+\\[([^\\]]+)\\]"
21+
xref_matches := text.re_find(xref_pattern, line)
22+
23+
for match_group in xref_matches {
24+
// match_group[0] is the full match, match_group[1] is the capture group (link text)
25+
if len(match_group) > 1 {
26+
link_text := match_group[1]["text"]
27+
28+
// Skip if empty or contains only special chars
29+
if link_text == "" || text.trim_space(link_text) == "" {
30+
continue
31+
}
32+
33+
// Split into words
34+
words := text.split(link_text, " ")
35+
has_error := false
36+
37+
for word_idx, word in words {
38+
// Skip empty words
39+
if word == "" || text.trim_space(word) == "" {
40+
continue
41+
}
42+
43+
// Remove any trailing punctuation for checking
44+
clean_word := text.trim_right(word, ".,;:!?")
45+
46+
// Skip if not alphabetic (numbers, etc.)
47+
if !text.re_match("[a-zA-Z]", clean_word) {
48+
continue
49+
}
50+
51+
// Get first character
52+
first_char := text.substr(clean_word, 0, 1)
53+
is_capitalized := text.re_match("[A-Z]", first_char)
54+
55+
// Check if word should be lowercase (not first or last word)
56+
is_first := word_idx == 0
57+
is_last := word_idx == len(words) - 1
58+
should_be_lowercase := false
59+
60+
if !is_first && !is_last {
61+
lower_word := text.to_lower(clean_word)
62+
for lw in lowercase_words {
63+
if lower_word == lw {
64+
should_be_lowercase = true
65+
break
66+
}
67+
}
68+
}
69+
70+
// Check for violations
71+
if should_be_lowercase && is_capitalized {
72+
has_error = true
73+
break
74+
} else if !should_be_lowercase && !is_capitalized {
75+
has_error = true
76+
break
77+
}
78+
}
79+
80+
if has_error {
81+
start := text.index(scope, line)
82+
matches = append(matches, {begin: start, end: start + len(line)})
83+
break // Only report once per line
84+
}
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)