--- /dev/null
+Cat Stevens
--- /dev/null
+USING: help help.lint.coverage help.lint.coverage.private help.markup help.syntax kernel
+sequences strings vocabs words ;
+IN: help.lint.coverage
+
+<PRIVATE
+: $related-subsections ( element -- )
+ [ related-words ] [ $subsections ] bi ;
+PRIVATE>
+
+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
+ <word-help-coverage>
+ <vocab-help-coverage>
+ <prefix-help-coverage>
+}
+
+"Coverage reports:"
+{ $related-subsections
+ word-help-coverage
+ print-coverage
+} ;
+
+{ <word-help-coverage> <vocab-help-coverage> <prefix-help-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 ;"
+ "\\ <word-help-coverage> <word-help-coverage> print-coverage"
+ "Word '<word-help-coverage>' has 100% help coverage"
+ }
+} ;
+
+HELP: <prefix-help-coverage>
+{ $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 <prefix-help-coverage> ."
+"{
+ {
+ T{ word-help-coverage
+ { word-name <prefix-help-coverage> }
+ { 100%-coverage? t }
+ }
+ T{ word-help-coverage
+ { word-name <vocab-help-coverage> }
+ { 100%-coverage? t }
+ }
+ T{ word-help-coverage
+ { word-name <word-help-coverage> }
+ { 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: <word-help-coverage>
+{ $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 ;"
+ "\\ <word-help-coverage> <word-help-coverage> ."
+"T{ word-help-coverage
+ { word-name <word-help-coverage> }
+ { 100%-coverage? t }
+}"
+ }
+} ;
+
+HELP: <vocab-help-coverage>
+{ $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\" <vocab-help-coverage> ."
+"{
+ T{ word-help-coverage
+ { word-name <prefix-help-coverage> }
+ { 100%-coverage? t }
+ }
+ T{ word-help-coverage
+ { word-name <vocab-help-coverage> }
+ { 100%-coverage? t }
+ }
+ T{ word-help-coverage
+ { word-name <word-help-coverage> }
+ { 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 }
+ }
+}"
+ }
+} ;
--- /dev/null
+USING: help.lint.coverage help.lint.coverage.private help.markup
+help.syntax kernel math.matrices sorting tools.test vocabs ;
+IN: help.lint.coverage.tests
+
+<PRIVATE
+: empty ( a v -- x y ) ;
+: nonexistent ( a v -- x y ) ;
+: defined ( x -- x ) ;
+
+HELP: empty { $examples } ;
+HELP: nonexistent ;
+HELP: defined { $examples { $example "USING: x ;" "blah" "hahaha" } } ;
+PRIVATE>
+
+{ 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-help-coverage> word-defines-sections ] unit-test
+
+{ eye } [ "eye" loaded-vocab-names resolve-name-in ] unit-test
--- /dev/null
+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 } ;
+
+<PRIVATE
+! <<
+CONSTANT: ignored-words {
+ $low-level-note
+ $prettyprinting-note
+ $values-x/y
+ $parsing-note
+ $io-error
+ $shuffle
+ $complex-shuffle
+ $nl
+}
+! >>
+
+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-help-coverage> ( word -- coverage )
+M: word <word-help-coverage>
+ dup
+ [ missing-sections ]
+ [ empty-examples? ] bi
+ 2dup 2array { { } f } =
+ word-help-coverage boa ; inline
+
+M: string <word-help-coverage>
+ loaded-vocab-names resolve-name-in <word-help-coverage> ; inline
+
+: <vocab-help-coverage> ( vocab-spec -- coverage )
+ [ auto-use? off vocab-words natural-sort [ <word-help-coverage> ] map ] with-scope ;
+
+: <prefix-help-coverage> ( prefix private? -- coverage )
+ [
+ auto-use? off group-articles vocab-articles set
+ [ sorted-loaded-child-vocabs ] dip not
+ [ filter-private ] when
+ [ <vocab-help-coverage> ] map
+ ] with-scope ;
--- /dev/null
+Documentation coverage linter
+++ /dev/null
-Cat Stevens
+++ /dev/null
-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"
- ""
- }
-} ;
+++ /dev/null
-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 } ;
-
-<PRIVATE
-DEFER: ?pluralize
-
-M: empty-examples summary
- word-name>> "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 ;
+++ /dev/null
-Pedantic help coverage checker