diff --git a/extra/help/lint/pedantic/authors.txt b/extra/help/lint/coverage/authors.txt similarity index 100% rename from extra/help/lint/pedantic/authors.txt rename to extra/help/lint/coverage/authors.txt diff --git a/extra/help/lint/coverage/coverage-docs.factor b/extra/help/lint/coverage/coverage-docs.factor new file mode 100644 index 0000000000..1191b1f1df --- /dev/null +++ b/extra/help/lint/coverage/coverage-docs.factor @@ -0,0 +1,137 @@ +USING: help help.lint.coverage help.lint.coverage.private help.markup help.syntax kernel +sequences strings vocabs words ; +IN: help.lint.coverage + + + +ABOUT: "help.lint.coverage" + +ARTICLE: "help.lint.coverage" "Help coverage linting" +"The " { $vocab-link "help.lint.coverage" } " vocabulary implements a very picky documentation completeness checker." +$nl +"The documentation coverage linter requires most words to have " { $link POSTPONE: HELP: } " declarations defining some of the " +{ $links $values $description $error-description $class-description $examples } " sections (see " { $links "element-types" } ")." +$nl +"This vocabulary is intended to be used alongside and after " { $vocab-link "help.lint" } ", not as a replacement for it." +$nl +"These words are provided to aid in writing more complete documentation:" +{ $related-subsections + + + +} + +"Coverage reports:" +{ $related-subsections + word-help-coverage + print-coverage +} ; + +{ word-help-coverage } +related-words + +HELP: word-help-coverage +{ $class-description "A documentation coverage report for a single word." } ; + +HELP: print-coverage +{ $values { "coverage" word-help-coverage } } +{ $contract "Displays a coverage object." } +{ $examples + { $example + "USING: help.lint.coverage io ;" + "\\ print-coverage" + "Word '' has 100% help coverage" + } +} ; + +HELP: +{ $values { "prefix" string } { "private?" boolean } { "coverage" sequence } } +{ $description "Runs the help coverage checker on every child vocabulary of the given " { $snippet "prefix" } ", including the base vocabulary. If " { $snippet "private?" } " is " { $snippet "f" } ", the prefix's child " { $snippet ".private" } " vocabularies are not checked. If " { $snippet "private?" } " is " { $snippet "t" } ", " { $emphasis "all" } " child vocabularies are checked." } +{ $examples + { $example + "USING: help.lint.coverage prettyprint ;" + "\"help.lint.coverage\" f ." +"{ + { + T{ word-help-coverage + { word-name } + { 100%-coverage? t } + } + T{ word-help-coverage + { word-name } + { 100%-coverage? t } + } + T{ word-help-coverage + { word-name } + { 100%-coverage? t } + } + T{ word-help-coverage + { word-name print-coverage } + { 100%-coverage? t } + } + T{ word-help-coverage + { word-name word-help-coverage } + { 100%-coverage? t } + } + T{ word-help-coverage + { word-name word-help-coverage? } + { 100%-coverage? t } + } + } + { } +}" + } +} ; + +HELP: +{ $values { "word" { $or string word } } { "coverage" word-help-coverage } } +{ $contract "Looks up a word in the current scope and generates a documentation coverage report for it."} +{ $examples + { $example + "USING: help.lint.coverage prettyprint ;" + "\\ ." +"T{ word-help-coverage + { word-name } + { 100%-coverage? t } +}" + } +} ; + +HELP: +{ $values { "vocab-spec" { $or vocab string } } { "coverage" sequence } } +{ $description "Runs the help coverage checker on the vocabulary in the given " { $snippet "vocab-spec" } "." } +{ $examples + { $example + "USING: help.lint.coverage prettyprint ;" + "\"help.lint.coverage\" ." +"{ + T{ word-help-coverage + { word-name } + { 100%-coverage? t } + } + T{ word-help-coverage + { word-name } + { 100%-coverage? t } + } + T{ word-help-coverage + { word-name } + { 100%-coverage? t } + } + T{ word-help-coverage + { word-name print-coverage } + { 100%-coverage? t } + } + T{ word-help-coverage + { word-name word-help-coverage } + { 100%-coverage? t } + } + T{ word-help-coverage + { word-name word-help-coverage? } + { 100%-coverage? t } + } +}" + } +} ; diff --git a/extra/help/lint/coverage/coverage-tests.factor b/extra/help/lint/coverage/coverage-tests.factor new file mode 100644 index 0000000000..b0faa881b9 --- /dev/null +++ b/extra/help/lint/coverage/coverage-tests.factor @@ -0,0 +1,36 @@ +USING: help.lint.coverage help.lint.coverage.private help.markup +help.syntax kernel math.matrices sorting tools.test vocabs ; +IN: help.lint.coverage.tests + + + +{ t } [ \ empty empty-examples? ] unit-test +{ f } [ \ nonexistent empty-examples? ] unit-test +{ f } [ \ defined empty-examples? ] unit-test +{ f } [ \ keep empty-examples? ] unit-test + +{ { $description $values } } [ \ empty missing-sections natural-sort ] unit-test +{ { $description $values } } [ \ defined missing-sections natural-sort ] unit-test +{ { } } [ \ keep missing-sections ] unit-test + +{ { "a.b" "a.b.c" } } [ { "a.b" "a.b.private" "a.b.c.private" "a.b.c" } filter-private ] unit-test + +{ "sections" } [ 0 "section" ?pluralize ] unit-test +{ "section" } [ 1 "section" ?pluralize ] unit-test +{ "sections" } [ 10 "section" ?pluralize ] unit-test + +{ { $examples } } [ \ empty word-defines-sections ] unit-test +{ { $examples } } [ \ defined word-defines-sections ] unit-test +{ { } } [ \ nonexistent word-defines-sections ] unit-test +{ { $values $description $examples } } [ \ keep word-defines-sections ] unit-test +{ { $values $contract $examples } } [ \ word-defines-sections ] unit-test + +{ eye } [ "eye" loaded-vocab-names resolve-name-in ] unit-test diff --git a/extra/help/lint/coverage/coverage.factor b/extra/help/lint/coverage/coverage.factor new file mode 100644 index 0000000000..25a6383955 --- /dev/null +++ b/extra/help/lint/coverage/coverage.factor @@ -0,0 +1,115 @@ +USING: accessors arrays classes classes.error combinators +combinators.short-circuit continuations english eval formatting +fry generic help help.lint help.lint.checks help.markup io +kernel math namespaces parser prettyprint sequences sets sorting +splitting strings summary vocabs words ; +FROM: namespaces => set ; +IN: help.lint.coverage + +TUPLE: word-help-coverage + { word-name word initial: POSTPONE: f } + { omitted-sections sequence initial: { } } + { empty-examples? boolean initial: f } + { 100%-coverage? boolean initial: f } ; + +> + +DEFER: ?pluralize + +M: word-help-coverage summary + [ word-name>> [ vocabulary>> ] [ name>> ] bi "[%s] %s: " sprintf ] keep + dup 100%-coverage?>> + [ drop "full help coverage" append ] + [ + [ empty-examples?>> "defined empty { $examples }, " "" ? ] + [ omitted-sections>> dup [ + length "section" ?pluralize + ] dip + [ name>> ] map ", " join + ] bi + "%sshould define help %s %s" sprintf append + ] if ; inline + +: sorted-loaded-child-vocabs ( prefix -- assoc ) + loaded-child-vocab-names natural-sort ; inline + +: resolve-name-in ( name namespaces -- word ) + "syntax" swap remove " " join + "USING: " " ; \\ " surround + prepend eval( -- word ) ; + +: filter-private ( seq -- no-private ) + [ ".private" ?tail nip not ] filter ; inline + +: ?pluralize ( n singular -- singular/plural ) + count-of-things " " split1 nip ; + +: should-define ( word -- spec ) + { + { [ dup predicate? ] [ drop { } ] } ! predicate?s have generated docs + { [ dup error-class? ] [ drop { $values $description $error-description } ] } + { [ dup class? ] [ drop { $class-description } ] } + { [ dup generic? ] [ drop { $values $contract $examples } ] } + { [ dup word? ] [ drop { $values $description $examples } ] } + [ drop no-cond ] + } cond ; + +: word-defines-sections ( word -- seq ) + word-help [ ignored-words member? not ] filter [ ?first ] map ; + +! only words that need examples, need to have them nonempty +! not defining examples is not the same as an empty { $examples } +: empty-examples? ( word -- ? ) + word-help \ $examples swap elements [ f ] [ first rest empty? ] if-empty ; + +: missing-sections ( word -- missing ) + [ should-define ] [ word-defines-sections ] bi diff ; +PRIVATE> + +GENERIC: print-coverage ( coverage-seq -- ) +M: sequence print-coverage + [ + [ print-coverage ] each + ] [ + [ [ 100%-coverage?>> ] count ] [ length ] bi /f + 100 * + "\n%3.1f%% of words have complete documentation\n" + printf + ] bi ; + +M: word-help-coverage print-coverage + summary print ; + +GENERIC: ( word -- coverage ) +M: word + dup + [ missing-sections ] + [ empty-examples? ] bi + 2dup 2array { { } f } = + word-help-coverage boa ; inline + +M: string + loaded-vocab-names resolve-name-in ; inline + +: ( vocab-spec -- coverage ) + [ auto-use? off vocab-words natural-sort [ ] map ] with-scope ; + +: ( prefix private? -- coverage ) + [ + auto-use? off group-articles vocab-articles set + [ sorted-loaded-child-vocabs ] dip not + [ filter-private ] when + [ ] map + ] with-scope ; diff --git a/extra/help/lint/coverage/summary.txt b/extra/help/lint/coverage/summary.txt new file mode 100644 index 0000000000..7b8e1cf8c3 --- /dev/null +++ b/extra/help/lint/coverage/summary.txt @@ -0,0 +1 @@ +Documentation coverage linter diff --git a/extra/help/lint/pedantic/tags.txt b/extra/help/lint/coverage/tags.txt similarity index 100% rename from extra/help/lint/pedantic/tags.txt rename to extra/help/lint/coverage/tags.txt diff --git a/extra/help/lint/pedantic/pedantic-docs.factor b/extra/help/lint/pedantic/pedantic-docs.factor deleted file mode 100644 index 5db259717b..0000000000 --- a/extra/help/lint/pedantic/pedantic-docs.factor +++ /dev/null @@ -1,83 +0,0 @@ -USING: help help.lint.pedantic help.lint.pedantic.private help.markup help.syntax kernel -sequences strings vocabs words ; -IN: help.lint.pedantic - -ABOUT: "help.lint.pedantic" - -ARTICLE: "help.lint.pedantic" "Pedantic help coverage" -"pedant, " { $emphasis "n." } " one who pays more attention to formal rules and book learning than they merit." -$nl -"The " { $vocab-link "help.lint.pedantic" } " vocabulary implements a very picky documentation completeness checker -- your very own documentation pedant." -$nl -"The pedantic linter requires most words to have documentation defining the " -{ $links $values $description $error-description $class-description $examples } " sections (see " { $links "element-types" } ")." -$nl -"This vocabulary is intended to be used alongside and after " { $vocab-link "help.lint" } ", not as a replacement for it." -$nl -"These words are provided to aid in writing more complete documentation:" -{ $subsections - word-pedant - vocab-pedant - prefix-pedant -} ; - -{ word-pedant vocab-pedant prefix-pedant } related-words -{ missing-sections empty-examples } related-words - -HELP: missing-sections -{ $values { "missing-sections" sequence } { "word-name" word } } -{ $description "Throws an " { $link missing-sections } " error." } -{ $error-description "Thrown when a word's documentation is missing one or more sections required for it by " { $link should-define } "." } ; - -HELP: empty-examples -{ $values { "word-name" word } } -{ $description "Throws an " { $link empty-examples } " error." } -{ $error-description "Thrown when a word's " { $link $examples } " section is missing or empty." } ; - -HELP: prefix-pedant -{ $values { "prefix" string } { "private?" boolean } } -{ $description "Runs the help coverage checker on every child vocabulary of the given " { $snippet "prefix" } ", including the base vocabulary. If " { $snippet "private?" } " is " { $snippet "f" } ", the prefix's child " { $snippet ".private" } " vocabularies are not checked. If " { $snippet "private?" } " is " { $snippet "t" } ", " { $emphasis "all" } " child vocabularies are checked." } -{ $errors - { $link empty-examples } " if a word has an empty " { $snippet "$examples" } " section -" - { $link missing-sections } " if a word is missing a section entirely" -} -{ $examples - { $example - "USING: help.lint.pedantic ;" - "\"help.lint.pedantic\" f prefix-pedant" - "" - } -} ; - -HELP: word-pedant -{ $values { "word" { $or string word } } } -{ $description "Runs the help coverage checker on the word described by " { $snippet "word-desc" } "." } -{ $errors - { $link empty-examples } " if a word has an empty " { $snippet "$examples" } " section -" - { $link missing-sections } " if a word is missing a section entirely" -} -{ $examples - { $example - "USING: help.lint.pedantic ;" - "\\ word-pedant word-pedant" - "" - } -} ; - -HELP: vocab-pedant -{ $values { "vocab-spec" { $or vocab string } } } -{ $description "Runs the help coverage checker on the vocabulary in the given " { $snippet "vocab-spec" } "." } -{ $errors - { $link empty-examples } " if a word has an empty " { $snippet "$examples" } " section -" - { $link missing-sections } " if a word is missing a section entirely" -} -{ $examples - { $example - "USING: help.lint.pedantic ;" - "\"help.lint.pedantic\" vocab-pedant" - "" - } -} ; diff --git a/extra/help/lint/pedantic/pedantic.factor b/extra/help/lint/pedantic/pedantic.factor deleted file mode 100644 index 14056e5c4b..0000000000 --- a/extra/help/lint/pedantic/pedantic.factor +++ /dev/null @@ -1,82 +0,0 @@ -USING: accessors arrays classes combinators -combinators.short-circuit continuations english eval formatting -fry help help.lint help.lint.checks help.markup kernel -namespaces parser prettyprint sequences sets sorting splitting -strings summary vocabs words ; -FROM: namespaces => set ; -IN: help.lint.pedantic - -ERROR: missing-sections - { word-name word initial: POSTPONE: f } - { missing-sections sequence initial: { } } ; -ERROR: empty-examples { word-name initial: POSTPONE: f } ; - -> "Word '%s' has defined empty $examples section" sprintf ; - -M: missing-sections summary - [ word-name>> ] [ - missing-sections>> dup [ - length "section" ?pluralize - ] dip - [ name>> ] map ", " join - ] bi - "Word '%s' should define help %s: %s" sprintf ; - -: sorted-loaded-child-vocabs ( prefix -- assoc ) - loaded-child-vocab-names natural-sort ; inline - -: filter-private ( seq -- no-private ) - [ ".private" ?tail nip not ] filter ; inline - -: ?pluralize ( n singular -- singular/plural ) - count-of-things " " split1 nip ; - -: should-define ( word -- spec ) - { - { [ dup predicate? ] [ drop { } ] } ! predicate?s have generated docs - { [ dup error? ] [ drop { $values $description $error-description } ] } - { [ dup class? ] [ drop { $class-description } ] } - { [ dup word? ] [ drop { $values $description $examples } ] } - [ drop no-cond ] - } cond ; - -: word-defines-sections ( word -- seq ) - word-help [ first ] map ; - -: missing-examples? ( word -- ? ) - word-help \ $examples swap elements empty? ; - -: check-examples ( word -- ) - [ missing-examples? ] keep '[ _ empty-examples ] when ; - -: check-sections ( word -- ) - [ ] [ should-define ] [ word-defines-sections ] tri - diff [ drop ] [ missing-sections ] if-empty ; -PRIVATE> - -GENERIC: word-pedant ( word -- ) -M: word word-pedant - { - { [ dup predicate? ] [ drop ] } - { [ dup error? ] [ check-sections ] } - { [ dup word? ] [ [ check-sections ] [ check-examples ] bi ] } - [ drop no-cond ] - } cond ; inline - -M: string word-pedant - "\\ " prepend eval( -- word ) word-pedant ; inline - -: vocab-pedant ( vocab-spec -- ) - [ auto-use? off vocab-words natural-sort [ word-pedant ] each ] with-scope ; - -: prefix-pedant ( prefix private? -- ) - [ - auto-use? off group-articles vocab-articles set - [ sorted-loaded-child-vocabs ] dip not - [ filter-private ] when - [ vocab-pedant ] each - ] with-scope ; diff --git a/extra/help/lint/pedantic/summary.txt b/extra/help/lint/pedantic/summary.txt deleted file mode 100644 index c42033cd5e..0000000000 --- a/extra/help/lint/pedantic/summary.txt +++ /dev/null @@ -1 +0,0 @@ -Pedantic help coverage checker