}
} ;
-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."
! 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 ;
+kernel namespaces regexp sequences sequences.deep
+sequences.parser sets sorting splitting tools.test unicode
+vocabs vocabs.loader ;
IN: lint.vocabs
<PRIVATE
: ---- ( -- ) "-------------------------------------------------------------------------" print ;
PRIVATE>
+"next-token should get the next non-blank string in the stream:" print
+{ "hello" } [ "hello world!" <sequence-parser> next-token ] unit-test
+{ "hello" } [ "\n hello \n world! " <sequence-parser> next-token ] unit-test
+
+----
+
+"next-token should ignore comments:" print
+{ "world!" } [ "! hello\nworld!" <sequence-parser> next-token ] unit-test
+{ "world!" } [ "! h\n! e\n! l\n! l\n! o\nworld!" <sequence-parser> next-token ] unit-test
+
+----
+
"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
! Copyright (C) 2022 CapitalEx
! See http://factorcode.org/license.txt for BSD license.
-USING: accessors arrays assocs combinators combinators.extras
-combinators.short-circuit compiler.units formatting
-grouping.extras hash-sets hashtables io io.encodings.utf8
-io.files kernel literals multiline namespaces peg.ebnf regexp
+USING: accessors arrays assocs combinators
+combinators.short-circuit compiler.units formatting hash-sets
+hashtables io io.encodings.utf8 io.files kernel namespaces
sequences sequences.deep sequences.parser sets sorting splitting
strings unicode vocabs vocabs.loader ;
FROM: namespaces => set ;
: vocab-loaded? ( name -- ? )
dictionary get key? ;
-USE: literals
-CONSTANT: USING-PATTERN $[ "USING: [^;]+ ;|USE: \\S+" <regexp> ]
-
! Helper words
: tokenize ( string -- sequence-parser )
<sequence-parser> ;
! prune syntax stuff
{ "FROM:" [ ";" skip-after f ] }
- { "IN:" [ skip-token f ] }
- { "SYMBOL:" [ skip-token f ] }
{ "SYMBOLS:" [ ";" skip-after f ] }
+ { "R/" [ "/" skip-after f ] }
{ "(" [ ")" skip-after f ] }
+ { "IN:" [ skip-token f ] }
+ { "SYMBOL:" [ skip-token f ] }
{ ":" [ skip-token f ] }
{ "POSTPONE:" [ skip-token f ] }
{ "\\" [ skip-token f ] }
: strip-code ( string -- string )
tokenize V{ } clone swap (strip-code) ;
-: extract-imports ( string -- seq )
- USING-PATTERN all-matching-subseqs ;
-
-: remove-imports ( string -- seq )
- USING-PATTERN "" re-replace ;
-
! Words for finding the words used ina program, stripping out import statements
: skip-imports ( sequence-parser -- sequence-parser string/? )
dup next {
: (find-imports) ( vector sequence-parser -- vector )
dup take-imports rot prepend swap [ (find-imports) ] ?keep-parsing-with ;
-: find-imports ( vector -- set )
- <sequence-parser> V{ } clone swap (find-imports) fast-set ;
+: find-imports ( vector -- seq )
+ <sequence-parser> V{ } clone swap (find-imports) ;
: (get-words) ( name -- vocab )
dup load-vocab words>> keys 2array ;
: print-no-unused-vocabs ( name _ -- )
drop "No unused vocabs found in %s.\n" printf ;
-PRIVATE>
\ No newline at end of file
+: get-words ( name -- assoc )
+ dup vocab-exists? [ (get-words) ] [ no-vocab-found ] if ;
+
+: get-imported-words ( string -- hashtable )
+ save-dictionary
+ find-imports [ get-words ] map >hashtable
+ restore-dictionary ;
+
+PRIVATE>
+
+: find-unused-in-string ( string -- seq )
+ strip-code [ get-imported-words ] [ find-used-words ] bi
+ reject-unused-vocabs natural-sort ;
+
+: 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 ;
\ No newline at end of file