Revision control
Copy as Markdown
// LINE LENGTH detector: report source lines longer than 80 columns.
//
// A `script` rule, not `existence`. The regex form this replaced was routed
// through Vale's token pipeline, where a standalone vocabulary term anywhere
// on the line suppressed the match: `Qul::PlatformInterface::DrawingEngine`,
// `\QUL`, `Renesas` and 200-odd other accepted terms all silenced it. Recall
// measured 46% over doc/src (112 of 245 over-long lines), and the blind spot
// tracked Qt API names, so it was worst exactly where Qt docs are densest.
// Reading the raw text here bypasses that pipeline entirely, which is also
// why the sibling Underfilled.tengo has never had the problem.
text := import("text")
matches := []
MAX := 80
// Commented-out source is exempt, matching the previous rule's `(?!\s*//)`.
// Only `//` — a QDoc `\` command is prose markup, not a comment.
is_comment := func(line) {
return text.has_prefix(text.trim_space(line), "//")
}
// Column count, not byte count. `len()` is bytes, so a line carrying ™, ®,
// µ or an em dash measures long and reports a false positive -- exactly what
// qtul-supported-platforms.qdoc:363 did (79 columns, 81 bytes). `.` matches
// one rune in Go's regexp, so counting matches gives the column count.
// Only called for lines already over MAX in bytes: bytes >= runes always,
// so a shorter line cannot be over the limit, and the scan stays cheap on
// the long files (attributions.qdoc is 82k lines).
runelen := func(s) {
m := text.re_find(".", s, -1)
if is_undefined(m) { return 0 }
return len(m)
}
// Only lines inside a `/*! ... */` block are measured. .cpp/.h are routed
// through the qdoc format, so `raw` is the whole source file, code included:
// `int v = f(); // trailing comment` past column 80 was flagged, as was any
// long signature. The 80-column rule is for documentation; code follows the
// Qt coding style. .qdoc/.qdocinc/.qml keep their docs in `/*!` blocks too.
in_doc := false
// Snippet lines inside \code, \badcode, or \qml are code, not prose, so
// they're exempt for the same reason. The opener can follow another
// command on its line (`\li \code`); the line holding the opener or the
// closer is still measured, since the rest of it is doc markup. The block
// set mirrors opens_code() in QDocImplicitLink.tengo; keep them in sync.
CODE_END := {"code": "endcode", "badcode": "endcode", "qml": "endqml"}
code_end := ""
// The verbatim-block closer a line opens ("endcode" for `\code`), or "".
opens_code := func(line) {
found := text.re_find("\\\\(code|badcode|qml)\\b", line, 1)
if is_undefined(found) { return "" }
return CODE_END[found[0][1].text]
}
lines := text.split(scope, "\n")
offset := 0
for line in lines {
if text.contains(line, "/*!") { in_doc = true }
n := len(line)
in_code := code_end != ""
if in_code && text.contains(line, "\\" + code_end) {
code_end = ""
in_code = false
} else if in_doc && !in_code {
code_end = opens_code(line)
}
if in_doc && !in_code && n > MAX && !is_comment(line) && runelen(line) > MAX {
matches = append(matches, {begin: offset, end: offset + n})
}
if in_doc && text.contains(line, "*/") { in_doc = false; code_end = "" }
// +1 for the newline consumed by the split.
offset = offset + n + 1
}