From: Capital-Ex Date: Mon, 5 Sep 2022 21:45:07 +0000 (-0400) Subject: Add lint.vocabs to extra X-Git-Tag: 0.99~1100 X-Git-Url: https://gitweb.factorcode.org/gitweb.cgi?p=factor.git;a=commitdiff_plain;h=8aa90bdebc1368722b10fb89f95d879e6c4b3029 Add lint.vocabs to extra --- diff --git a/extra/lint/vocabs/authors.txt b/extra/lint/vocabs/authors.txt new file mode 100644 index 0000000000..56ce938475 --- /dev/null +++ b/extra/lint/vocabs/authors.txt @@ -0,0 +1 @@ +CapitalEx \ No newline at end of file diff --git a/extra/lint/vocabs/summary.txt b/extra/lint/vocabs/summary.txt new file mode 100644 index 0000000000..8cdbf02cbb --- /dev/null +++ b/extra/lint/vocabs/summary.txt @@ -0,0 +1 @@ +Tool for removing finding unused imports in a vocab. \ No newline at end of file diff --git a/extra/lint/vocabs/vocabs-docs.factor b/extra/lint/vocabs/vocabs-docs.factor new file mode 100644 index 0000000000..edd567e850 --- /dev/null +++ b/extra/lint/vocabs/vocabs-docs.factor @@ -0,0 +1,119 @@ +! Copyright (C) 2022 CapitalEx +! See http://factorcode.org/license.txt for BSD license. +USING: assocs hashtables help.markup help.syntax io kernel +sequences strings ; +IN: lint.vocabs + +HELP: find-unused +{ $values + { "name" "a vocab name string" } + { "seq" sequence } +} +{ $description + "Finds unusued imports in the given vocab name. Returing the result as a " { $link sequence } "." +} +{ $examples + { $example "USING: lint.vocabs prettyprint ;" + "\"lint.vocabs\" find-unused ." + "{ }" + } +} ; + +HELP: find-unused-in-file +{ $values + { "path" "a pathname string" } + { "seq" sequence } +} +{ $description + "Finds unused imports in the given file. Returing the result as a " { $link sequence } "." +} +{ $examples + { $example "USING: lint.vocabs prettyprint ;" + "\"resource:work/lint/vocabs/vocabs.factor\" find-unused-in-file ." + "{ }" + } +} ; + +HELP: find-unused-in-string +{ $values + { "string" string } + { "seq" sequence } +} +{ $description + "Finds unused imports in the given " { $link string } ". Returing the result as a " { $link sequence } "." +} ; + +HELP: find-unused. +{ $values + { "name" "a vocab name string" } +} +{ $description + "Finds unused imports in given vocab and outputs it to the current " { $link output-stream } "." +} +{ $examples + { $example "USING: lint.vocabs ;" + "\"lint.vocabs\" find-unused." + "No unused vocabs found in lint.vocabs." + } +} ; + +HELP: get-imported-words +{ $values + { "string" string } + { "hashtable" hashtable } +} +{ $description + "Gets all words that have been imported with " { $link \ USE: } " and " { $link \ USING: } " in the given string." +} ; + +HELP: get-vocabs +{ $values + { "string" string } + { "seq" sequence } +} +{ $description + "Gets all the vocabularies imported in the given string." +} ; + +HELP: get-words +{ $values + { "name" "a vocab name string" } + { "assoc" assoc } +} +{ $description + "Gets all the words used in a given vocabulary." +} +{ $examples + { $example "USING: lint.vocabs prettyprint ;" + "\"lint.vocabs\" get-words ." +"{ + \"lint.vocabs\" + { + \"get-vocabs\" + \"get-words\" + \"find-unused-in-file\" + \"get-imported-words\" + \"find-unused-in-string\" + \"find-unused.\" + \"find-unused\" + } +}" + } +} ; + +ARTICLE: "lint.vocabs" "The Unused Vocabulary Linter" +"The " { $vocab-link "lint.vocabs" } " vocabulary implements a set of words designed to find unused imports." +"It attempts to ignore USE: and USING: that are a part of a string, postponed with either POSTPONE: or \\, and" +"contained inside a " { $link "regexp" } "." +$nl +"It can sometimes be easy to lose track of what vocabularies you've imported while iterating over ideas. So to" +"find any vocabularies you feel are unused, you can run:" +$nl +{ $example + "USING: lint.vocabs ;" + "\"lint.vocabs\" find-unused." + "No unused vocabs found in lint.vocabs." +} +; + +ABOUT: "lint.vocabs" diff --git a/extra/lint/vocabs/vocabs-tests.factor b/extra/lint/vocabs/vocabs-tests.factor new file mode 100644 index 0000000000..b73c0167ca --- /dev/null +++ b/extra/lint/vocabs/vocabs-tests.factor @@ -0,0 +1,86 @@ +! Copyright (C) 2022 CapitalEx +! See http://factorcode.org/license.txt for BSD license. +USING: accessors arrays assocs compiler.units continuations +formatting hash-sets hashtables io io.encodings.utf8 io.files +kernel namespaces regexp sequences sequences.deep sets sorting +splitting tools.test unicode vocabs vocabs.loader ; +IN: lint.vocabs + +bin ; + +USE: math.complex +: test-five ( x -- ? ) + malformed-complex? ; + +USE: math.primes +" +CONSTANT: ignore-postpone-using "POSTPONE: USING: : nop ( -- ) ;" +CONSTANT: ingore-\-using "\\ USING: : nop ( -- ) ;" +CONSTANT: ignore-postpone-use "POSTPONE: USE: ignore : nop ( -- ) ;" +CONSTANT: ignore-\-use "\\ USE: ignore : nop ( -- ) ;" +CONSTANT: ignore-in-string-one "\"USE:\" \"USING:\" : nop ( -- ) ;" +CONSTANT: ignore-in-string-two "\"asdfasdf USE:\" \"asdfasdf USING:\" : nop ( -- ) ;" +CONSTANT: ignore-in-string-three "\"asdfasdf USE: asdfasdf\" : nop ( -- ) ;" +CONSTANT: ignore-in-string-four "\"asdfasdf USE: asdfasdf\" \"asdfasff USING: asdfasdf\" : nop ( -- ) ;" +CONSTANT: ignore-use-regex "R/ USE: ignore/ : nop ( -- ) ;" +CONSTANT: ignore-using-regex "R/ USING: ignore ;/ : nop ( -- ) ;" +CONSTANT: empty-using-statement "USING: ; nop ( -- ) ;" +: ---- ( -- ) "-------------------------------------------------------------------------" print ; +PRIVATE> + +"It should work on multiple lines, with multiple imports across the file: " print + +{ { "hashtables" "math.primes" "sequences" "sets" "vocabs" } } [ mock-file find-unused-in-string ] unit-test + +---- + +"It should ignore USE: and USING: that have been postponed: " print +{ { } } [ ignore-postpone-using find-unused-in-string ] unit-test +{ { } } [ ingore-\-using find-unused-in-string ] unit-test +{ { } } [ ignore-postpone-use find-unused-in-string ] unit-test +{ { } } [ ignore-\-use find-unused-in-string ] unit-test + +---- + +"It should ignore USE: and USING: that are in strings: " print +{ { } } [ ignore-in-string-one find-unused-in-string ] unit-test +{ { } } [ ignore-in-string-two find-unused-in-string ] unit-test +{ { } } [ ignore-in-string-three find-unused-in-string ] unit-test +{ { } } [ ignore-in-string-four find-unused-in-string ] unit-test + +---- + +"It should ignore USE: and USING: that are in RegEx: " print +{ { } } [ ignore-use-regex find-unused-in-string ] unit-test +{ { } } [ ignore-using-regex find-unused-in-string ] unit-test + +---- + +"IT should return empty when no imports have been found: " print +{ { } } [ empty-using-statement find-unused-in-string ] unit-test + +---- + +"It should forget vocabs that aren't already loaded: " print +dictionary get clone 1array [ + "USE: bitcoin.client" find-unused-in-string drop + dictionary get clone +] unit-test + +---- \ No newline at end of file diff --git a/extra/lint/vocabs/vocabs.factor b/extra/lint/vocabs/vocabs.factor new file mode 100644 index 0000000000..3063edb090 --- /dev/null +++ b/extra/lint/vocabs/vocabs.factor @@ -0,0 +1,95 @@ +! Copyright (C) 2022 CapitalEx +! See http://factorcode.org/license.txt for BSD license. +USING: accessors arrays assocs compiler.units continuations +formatting hash-sets hashtables io io.encodings.utf8 io.files +kernel namespaces regexp sequences sequences.deep sets sorting +splitting unicode vocabs vocabs.loader ; +FROM: namespaces => set ; +IN: lint.vocabs + +hash-set + old-dictionary get keys >hash-set + diff members [ [ forget-vocab ] each ] with-compilation-unit ; + +: vocab-loaded? ( name -- ? ) + dictionary get key? ; + +: (get-words) ( name -- vocab ) + dup load-vocab words>> keys 2array ; + +: no-vocab-found ( name -- empty ) + { } 2array ; + +: nl>space ( string -- string ) + "\n" " " replace ; + +: find-import-statements ( string -- seq ) + "USING: [^;]+ ;|USE: \\S+" all-matching-subseqs ; + +: clean-up-source ( string -- string ) + "\"(\\\"|[^\"]*)\"|(R/ (\\\\/|[^/])*/)|\\\\\\s+\\S+|POSTPONE: \\S+|! ([^\n])*" "" re-replace ; + +: strip-syntax ( seq -- seq ) + [ "USING: | ;|USE: " " " re-replace ] map ; + +: split-when-blank ( string -- seq ) + [ blank? ] split-when ; + +: split-words ( line -- words ) + [ split-when-blank ] map flatten harvest ; + +: get-unique-words ( seq -- hash-set ) + harvest split-words >hash-set ; + +: [is-used?] ( hash-set -- quot ) + '[ nip [ _ in? ] any? ] ; inline + +: reject-unused-vocabs ( assoc hash-set -- seq ) + [is-used?] assoc-reject keys ; + +: print-unused-vocabs ( name seq -- ) + swap "The following vocabs are unused in %s: \n" printf + [ " - " prepend print ] each ; + +: print-no-unused-vocabs ( name _ -- ) + drop "No unused vocabs found in %s.\n" printf ; + +PRIVATE> + +: get-words ( name -- assoc ) + dup vocab-exists? + [ (get-words) ] + [ no-vocab-found ] if ; + +: get-vocabs ( string -- seq ) + nl>space find-import-statements strip-syntax split-words harvest ; + +: get-imported-words ( string -- hashtable ) + save-dictionary + get-vocabs [ get-words ] map >hashtable + restore-dictionary + ; + +: find-unused-in-string ( string -- seq ) + clean-up-source + [ get-imported-words ] [ "\n" split get-unique-words ] bi + reject-unused-vocabs natural-sort ; inline + +: find-unused-in-file ( path -- seq ) + utf8 file-contents find-unused-in-string ; + +: find-unused ( name -- seq ) + vocab-source-path dup [ find-unused-in-file ] when ; + +: find-unused. ( name -- ) + dup find-unused dup empty? + [ print-no-unused-vocabs ] + [ print-unused-vocabs ] if ;