suite check: закрыты пробелы в проверках шапки, иерархии и словаря
- документ без темы: ошибка, если такой не один или если он несёт ключи слоя — иначе конвенция, потерявшая topic, молча лишалась всех проверок распространения - иерархия заголовков: один заголовок первого уровня, без перескоков через уровень - имя темы: латиница и пригодность для имени файла — ошибкой, kebab-case — предупреждением - сценарные связки чужого словаря ловятся в начале строки, где их ставит сценарный блок, а не в середине фразы, где это чаще SQL
This commit is contained in:
@@ -18,11 +18,74 @@ import (
|
||||
func checkForm(s *suite.Suite, d *doc.Document, rep *Report) {
|
||||
prefix := checkFilePrefix(s, d, rep)
|
||||
checkHeadings(d, prefix, rep)
|
||||
checkHeadingHierarchy(d, rep)
|
||||
checkNumbering(d, prefix, rep)
|
||||
checkRules(s, d, rep)
|
||||
versionFrom, versionTo := checkVersionLine(s, d, rep)
|
||||
checkModalsOutside(s, d, versionFrom, versionTo, rep)
|
||||
checkForeignVocabulary(s, d, rep)
|
||||
checkForeignConnectives(s, d, rep)
|
||||
}
|
||||
|
||||
// checkHeadingHierarchy checks the ladder of headings: one title, and no level
|
||||
// skipped on the way down. A rule area runs from a heading to the next heading
|
||||
// of any level, so a skipped level does not merely look untidy — it moves the
|
||||
// boundary the whole parsing model rests on.
|
||||
func checkHeadingHierarchy(d *doc.Document, rep *Report) {
|
||||
if len(d.Headings) == 0 {
|
||||
return
|
||||
}
|
||||
if first := d.Headings[0]; first.Level != 1 {
|
||||
rep.Errorf(Form, d.Path, first.Line,
|
||||
"the document opens with a level-%d heading, while the title of a document is a level-1 heading", first.Level)
|
||||
}
|
||||
|
||||
titles := 0
|
||||
prev := 0
|
||||
for _, h := range d.Headings {
|
||||
if h.Level == 1 {
|
||||
titles++
|
||||
if titles > 1 {
|
||||
rep.Errorf(Form, d.Path, h.Line,
|
||||
"the document holds a second level-1 heading %q: the title is one, everything below it is a section", h.Text)
|
||||
}
|
||||
}
|
||||
if prev > 0 && h.Level > prev+1 {
|
||||
rep.Errorf(Form, d.Path, h.Line,
|
||||
"the heading %q jumps from level %d to level %d, skipping a level", h.Text, prev, h.Level)
|
||||
}
|
||||
prev = h.Level
|
||||
}
|
||||
}
|
||||
|
||||
// checkForeignConnectives looks for scenario connectives of a foreign
|
||||
// vocabulary. Only the start of a line counts: that is where a scenario block
|
||||
// puts them, while mid-sentence AND and OR belong to SQL far more often than to
|
||||
// a mixture of vocabularies.
|
||||
func checkForeignConnectives(s *suite.Suite, d *doc.Document, rep *Report) {
|
||||
foreign := lang.ForeignConnectives(s.Manifest.Language.Version, s.Manifest.Language.Lang)
|
||||
if len(foreign) == 0 {
|
||||
return
|
||||
}
|
||||
words := make([]string, 0, len(foreign))
|
||||
for w := range foreign {
|
||||
words = append(words, w)
|
||||
}
|
||||
sort.Strings(words)
|
||||
|
||||
d.Prose(func(n int, text string) bool {
|
||||
text = strings.TrimLeft(text, " \t>-*")
|
||||
for _, w := range words {
|
||||
if !strings.HasPrefix(text, w) || letterAt(text, len(w)) {
|
||||
continue
|
||||
}
|
||||
rep.Errorf(Form, d.Path, n,
|
||||
"the scenario block opens with %s from the %q vocabulary, while the suite declares %q",
|
||||
w, foreign[w], s.Manifest.Language.Lang)
|
||||
break
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// checkFilePrefix reconciles the prefix in the front matter with the manifest
|
||||
|
||||
Reference in New Issue
Block a user