Add lint.vocabs to extra
authorCapital-Ex <capitalex@protonmail.com>
Mon, 5 Sep 2022 21:45:07 +0000 (17:45 -0400)
committerJohn Benediktsson <mrjbq7@gmail.com>
Tue, 6 Sep 2022 15:41:04 +0000 (08:41 -0700)
extra/lint/vocabs/authors.txt [new file with mode: 0644]
extra/lint/vocabs/summary.txt [new file with mode: 0644]
extra/lint/vocabs/vocabs-docs.factor [new file with mode: 0644]
extra/lint/vocabs/vocabs-tests.factor [new file with mode: 0644]
extra/lint/vocabs/vocabs.factor [new file with mode: 0644]

diff --git a/extra/lint/vocabs/authors.txt b/extra/lint/vocabs/authors.txt
new file mode 100644 (file)
index 0000000..56ce938
--- /dev/null
@@ -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 (file)
index 0000000..8cdbf02
--- /dev/null
@@ -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 (file)
index 0000000..edd567e
--- /dev/null
@@ -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 (file)
index 0000000..b73c016
--- /dev/null
@@ -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
+
+<PRIVATE
+CONSTANT: mock-file "
+USING: arrays io kernel math math.parser sets
+hashtables sequences vocabs ;
+IN: lint.vocabs.testing
+
+: test-one ( x y -- )
+    + print ;
+
+: test-two ( x -- x )
+    dup 2array ;
+
+: test-three ( -- x )
+    HS{ } clone ;
+
+: test-four ( x -- x )
+    >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 (file)
index 0000000..3063edb
--- /dev/null
@@ -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
+
+<PRIVATE
+SYMBOL: old-dictionary
+
+: save-dictionary ( -- )
+    dictionary     get clone 
+    old-dictionary set       ;
+
+: restore-dictionary ( -- )
+    dictionary     get keys >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+" <regexp> all-matching-subseqs ;
+
+: clean-up-source ( string -- string )
+    "\"(\\\"|[^\"]*)\"|(R/ (\\\\/|[^/])*/)|\\\\\\s+\\S+|POSTPONE: \\S+|! ([^\n])*" <regexp> "" re-replace ;
+
+: strip-syntax ( seq -- seq )
+    [ "USING: | ;|USE: " <regexp> " " 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 ;