diff --git a/squint.edn b/squint.edn index 3d76b43d..187a09bf 100644 --- a/squint.edn +++ b/squint.edn @@ -1,2 +1,2 @@ -{:paths ["src-shared" "src-squint"] +{:paths ["src-shared" "src-squint" "test"] :output-dir "dist"} diff --git a/src/nextjournal/clojure_mode/test_utils.cljs b/src-shared/nextjournal/clojure_mode/test_utils.cljc similarity index 88% rename from src/nextjournal/clojure_mode/test_utils.cljs rename to src-shared/nextjournal/clojure_mode/test_utils.cljc index 8d6a83ae..554842cb 100644 --- a/src/nextjournal/clojure_mode/test_utils.cljs +++ b/src-shared/nextjournal/clojure_mode/test_utils.cljc @@ -1,10 +1,10 @@ (ns nextjournal.clojure-mode.test-utils (:require ["@codemirror/state" :as cm-state - :refer [EditorState EditorSelection Extension StateCommand - ChangeSet ChangeDesc TransactionSpec StrictTransactionSpec]] - [applied-science.js-interop :as j] - [clojure.string :as str] - [nextjournal.clojure-mode.extensions.formatting :as format])) + :refer [EditorState EditorSelection]] + #?@(:squint [] + :cljs [[applied-science.js-interop :as j]]) + [clojure.string :as str]) + (:require-macros [applied-science.js-interop :as j])) ;; (de)serialize cursors| and for testing diff --git a/src-squint/nextjournal/clojure_mode_tests.cljs b/src-squint/nextjournal/clojure_mode_tests.cljs deleted file mode 100644 index 53d700cc..00000000 --- a/src-squint/nextjournal/clojure_mode_tests.cljs +++ /dev/null @@ -1,455 +0,0 @@ -(ns nextjournal.clojure-mode-tests - (:require ["./clojure_mode.mjs" :as cm-clojure] - ["./clojure_mode/commands.mjs" :as commands] - ["./clojure_mode/extensions/close_brackets.mjs" :as close-brackets] - ["./clojure_mode/extensions/formatting.mjs" :as format] - ["@codemirror/state" :as cm-state - :refer [EditorState EditorSelection]] - ["assert" :as assert] - #_[nextjournal.clojure-mode.test-utils :as test-utils]) - (:require-macros [nextjournal.clojure-mode-tests.macros :refer [deftest are testing]])) - -#_(assert/equal 1 2) - -(defn make-state [extensions doc] - (let [[doc ranges] (->> (re-seq #"\||<[^>]*?>|[^<>|]+" doc) - (reduce (fn [[^string doc ranges] match] - (cond (= match "|") - [doc (conj ranges (.cursor EditorSelection (count doc)))] - - (.startsWith match "<") - [(str doc (subs match 1 (dec (count match)))) - (conj ranges (.range EditorSelection - (count doc) - (+ (count doc) (- (count match) 2))))] - :else - [(str doc match) ranges])) ["" []]))] - (.create EditorState - #js{:doc doc - :selection (if (seq ranges) - (.create EditorSelection (into-array ranges)) - js/undefined) - :extensions (cond-> #js[(.. EditorState -allowMultipleSelections (of true))] - extensions - (doto (.push extensions)))}))) - -(defn state-str [^js state] - (let [doc (str (.-doc state))] - (->> (.. state -selection -ranges) - reverse - (reduce (fn [doc ^:js {:keys [empty from to]}] - (if empty - (str (subs doc 0 from) "|" (subs doc from)) - (str (subs doc 0 from) "<" (subs doc from to) ">" (subs doc to)))) doc)))) - -(defn apply-f* [extensions cmd doc] - ;; TODO: fix in squint - #_{:pre [(array? extensions) - (fn? cmd) - (string? doc)]} - (let [state (make-state extensions doc) - tr (cmd state)] - (state-str (if tr (.-state tr) state)))) - -(defn apply-cmd* [extensions cmd doc] - (let [state (make-state extensions doc) - !tr (atom nil) - _ (cmd #js{:state state - :dispatch #(reset! !tr %)}) - tr @!tr] - (state-str (get tr :state)))) - -(def extensions - cm-clojure/default-extensions - ;; optionally test with live grammar - #_#js[(cm-clojure/syntax live-grammar/parser) - (.slice cm-clojure/default-extensions 1)]) - -(def apply-f (partial apply-f* extensions)) -(def apply-cmd (partial apply-cmd* extensions)) - -;; nav -(doseq [[input dir expected] [["|()" 1 "()|"] - ["()|" -1 "|()"] - ["a|b" 1 "ab|"] - ["a|b" -1 "|ab"] - ["| ab" 1 " ab|"] - ["ab |" -1 "|ab "] - ["(|)" 1 "()|"] - ["(|)" -1 "|()"] - ["a|\nb" 1 "a\nb|"]]] - (assert.equal (apply-f (commands/nav dir) input) - expected)) - -;; nav-select -(doseq [[input dir expected] [["|()" 1 "<()>"] - ["()|" -1 "<()>"] - ["a|b" 1 "a"] - ["(|)" 1 "<()>"] - ["\"a|b\"" 1 "\"a\""] - ["\"a\"" 1 "<\"ab\">"] - ["a|b" -1 "b"] - ["| ab" 1 "< ab>"] - ["ab |" -1 ""] - ["(|)" 1 "<()>"] - ["(|)" -1 "<()>"] - ["a|\nb" 1 "a<\nb>"]]] - (assert.equal (apply-f (commands/nav-select dir) input) - expected)) - -;; close brackets > handle open -(doseq [[input insert expected] - (partition 3 ["|" \( "(|)" ;; auto-close brackets - "(|" \( "((|)" - "|(" \( "(|)(" - "|)" \( "(|))" - "#|" \( "#(|)" - "\"|\"" \( "\"(|\"" ;; no auto-close inside strings - ])] - (assert.equal (apply-f #(close-brackets/handle-open % insert) input) - expected)) - -;; close brackets > handle close -(doseq [[input bracket expected] - (partition 3 ["|" \) "|" - "|(" \) "|(" - "|)" \) ")|" - "(|)" \) "()|" - "() |()" \) "() ()|" - "[(|)]" \) "[()|]" - "[()|]" \) "[()]|" - "([]| s)" \) "([] s)|" - "(|" \) "()|" ;; close unclosed parent - "[(|]" \} "[(]|" ;; non-matching bracket doesn't close ancestor - "((|)" \] "(()|" ;; non-matching bracket doesn't close ancestor - "((|)" \) "(())|" ;; a bit weird - it finds an unclosed ancestor, and closes that. - "\"|\"" \) "\")|\"" ;; normal behaviour inside strings - ])] - (assert.equal (apply-f #(close-brackets/handle-close % bracket) input) - expected)) - -;; close brackets > handle open string -(doseq [[input expected] - (partition 2 ["|" "\"|\"" ;; auto-close strings - "\"|\"" "\"\\\"|\"" ;; insert quoted " inside strings - ] - )] - (assert.equal (apply-f #(close-brackets/handle-open % \") input) expected)) - -;; close brackets > handle backspace -(doseq [[input expected] - (partition 2 ["|" "|" - "(|" "|" ;; delete an unbalanced paren - "()|" "(|)" ;; enter a form from the right (do not "unbalance") - "#|()" "|()" ;; delete prefix form - "[[]]|" "[[]|]" - "(| )" "|" ;; delete empty form - "(| a)" "(| a)" ;; don't delete non-empty forms - "@|" "|" ;; delete @ - "@|x" "|x" - "\"|\"" "|" ;; delete empty string - "\"\"|" "\"|\"" - "\"| \"" "\"| \"" ;; do not delete string with whitespace - ":x :a |" ":x :a|" ;; do not format on backspace - "\"[|]\"" "\"|]\"" ;; normal deletion inside strings - ])] - (assert.equal (apply-f close-brackets/handle-backspace input) - expected)) - -;; indent selection -(doseq [[input expected] - (partition 2 [" ()" "()" ;; top-level => 0 indent - "(\n)" "(\n )" - "(b\n)" "(b\n )" ;; operator gets extra indent (symbol in 1st position) - "(0\n)" "(0\n )" ;; a number is not operator - "(:a\n)" "(:a\n )" ;; a keyword is not operator - "(a\n\nb)" "(a\n \n b)" ;; empty lines get indent - ])] - (assert.equal (apply-f format/format (str "<" input ">")) - (str "<" expected ">"))) - -;; nav -(doseq [[input dir expected] - (partition 3 - ["|()" 1 "()|" - "()|" -1 "|()" - "a|b" 1 "ab|" - "a|b" -1 "|ab" - "| ab" 1 " ab|" - "ab |" -1 "|ab " - "(|)" 1 "()|" - "(|)" -1 "|()" - "a|\nb" 1 "a\nb|"])] - (assert.equal (apply-f (commands/nav dir) input) - expected)) -;; nav-select -(doseq [[input dir expected] - (partition 3 ["|()" 1 "<()>" - "()|" -1 "<()>" - "a|b" 1 "a" - "(|)" 1 "<()>" - "\"a|b\"" 1 "\"a\"" - "\"a\"" 1 "<\"ab\">" - "a|b" -1 "b" - "| ab" 1 "< ab>" - "ab |" -1 "" - "(|)" 1 "<()>" - "(|)" -1 "<()>" - "a|\nb" 1 "a<\nb>" - ])] - (assert.equal (apply-f (commands/nav-select dir) input) - expected)) - -;; close-brackets -(doseq [[input insert expected] - (partition 3 ["|" \( "(|)" ;; auto-close brackets - "(|" \( "((|)" - "|(" \( "(|)(" - "|)" \( "(|))" - "#|" \( "#(|)" - "\"|\"" \( "\"(|\"" ;; no auto-close inside strings - ])] - (assert.equal (apply-f #(close-brackets/handle-open % insert) input) - expected)) - -;; handle-close -(doseq [[input bracket expected] - (partition 3 ["|" \) "|" - "|(" \) "|(" - "|)" \) ")|" - "(|)" \) "()|" - "() |()" \) "() ()|" - "[(|)]" \) "[()|]" - "[()|]" \) "[()]|" - "([]| s)" \) "([] s)|" - "(|" \) "()|" ;; close unclosed parent - "[(|]" \} "[(]|" ;; non-matching bracket doesn't close ancestor - "((|)" \] "(()|" ;; non-matching bracket doesn't close ancestor - "((|)" \) "(())|" ;; a bit weird - it finds an unclosed ancestor, and closes that. - "\"|\"" \) "\")|\"" ;; normal behaviour inside strings - ])] - (assert.equal (apply-f #(close-brackets/handle-close % bracket) input) - expected)) - -(deftest close-brackets - (testing "handle-open" - ) - - (testing "handle-close" - ) - - (testing "handle-open string" - (are [input expected] - (= (apply-f #(close-brackets/handle-open % \") input) expected) - "|" "\"|\"" ;; auto-close strings - "\"|\"" "\"\\\"|\"" ;; insert quoted " inside strings - )) - - (testing "handle-backspace" - (are [input expected] - (= (apply-f close-brackets/handle-backspace input) - expected) - "|" "|" - "(|" "|" ;; delete an unbalanced paren - "()|" "(|)" ;; enter a form from the right (do not "unbalance") - "#|()" "|()" ;; delete prefix form - "[[]]|" "[[]|]" - "(| )" "|" ;; delete empty form - "(| a)" "(| a)" ;; don't delete non-empty forms - "@|" "|" ;; delete @ - "@|x" "|x" - "\"|\"" "|" ;; delete empty string - "\"\"|" "\"|\"" - "\"| \"" "\"| \"" ;; do not delete string with whitespace - ":x :a |" ":x :a|" ;; do not format on backspace - "\"[|]\"" "\"|]\"" ;; normal deletion inside strings - )) - - #_(testing "handle backspace (embedded)" - (are [input expected] - (= (apply-embedded-f close-brackets/handle-backspace input) - expected) - "```\n()|\n```" "```\n(|)\n```" - "```\n[[]]|\n```" "```\n[[]|]\n```" - "```\n(| )\n```" "```\n|\n```"))) - -(do - - - - (deftest indentSelection - - (are [input expected] - (= (apply-f format/format (str "<" input ">")) - (str "<" expected ">")) - " ()" "()" ;; top-level => 0 indent - "(\n)" "(\n )" - "(b\n)" "(b\n )" ;; operator gets extra indent (symbol in 1st position) - "(0\n)" "(0\n )" ;; a number is not operator - "(:a\n)" "(:a\n )" ;; a keyword is not operator - "(a\n\nb)" "(a\n \n b)" ;; empty lines get indent - ) - - (testing "prefix-all" - (are [before after] - (= (apply-f (partial format/prefix-all "a") before) - after) - "z|z\nzz|\n|zz" "az|z\nazz|\n|azz" - "z\nz" "az\naz"))) - - (deftest indent-all ;; same as indentSelection but applies to entire doc - (are [input expected] - (= (apply-f format/indent-all input) - expected) - "| ()" "|()" - "|()[\n]" "|()[\n ]" - "|(\n)" "|(\n )" - "(\n)" "(\n )" - "|(0\nx<)>" "|(0\n x<)>" - "<(:a\n)>" "<(:a\n )>" - "|(a\n\nb)" "|(a\n\n b)")) - - (deftest format-all - (are [input expected] - (= (apply-f format/format-all input) - expected) - "a :b 3 |" "a :b 3|" ;; remove extra spaces - "\"\" |:a " "\"\" |:a" - "(|a )" "(|a)" - "| ( )" "|()" - "|()a" "|() a" ;; add needed spaces - "() |a" "() |a" ;; cursor position - "()| a" "()| a" - "() | a" "() |a" - "|(\n )" "|(\n )" - "(\n)" "(\n )" - "<(:a\n)>" "<(:a\n )>" - "|(a\n\nb)" "|(a\n\n b)" - "|\"a\"" "|\"a\"" - "#_a|" "#_a|" - "[ | ]" "[|]" - "|[] " "|[]" - "#(|a )" "#(|a)" - - "|@ a" "|@a" - "|&" "|&" - "[_ & |_]" "[_ & |_]" - - "|[a [\n]]" "|[a [\n ]]" - "|[a [\n]]" "|[a [\n ]]" - - "|[ a \n]" "|[a\n ]" - "|[ a [\n]]" "|[a [\n ]]" - "|[ \n[ \n[ ]]]" "|[\n [\n []]]" - "|()[\n]" "|() [\n ]" ;; closing-bracket 1 space in front of opening-bracket - "|()[\n]" "|() [\n ]")) - - (deftest format-selection - (are [input expected] - (= (apply-f format/format input) - expected) - "\nc d" "\nc d" ;; only selected lines are formatted - " c \na b" " c \na b" ;; multiple selectons on one line - )) - - (deftest kill - (are [input expected] - (= (apply-cmd commands/kill input) - expected) - "| ()\nx" "|\nx" ;; top-level - " \"ab|c\" " "\"ab|\"" ;; kill to end of string - " \"|a\nb\"" "\"|b\"" ;; TODO - stop at newline within string - "(|)" "(|)" ;; no-op in empty coll - "(| x y [])" "(|)" ;; kill all coll contents - "a| \nb" "a|b" ;; bring next line up - )) - - (deftest unwrap - (are [input expected] - (= (apply-cmd commands/unwrap input) - expected) - "(|)" "|" - "[a | b]" "a |b" - "a|b" "a|b")) - - (deftest balance-ranges - (are [input expected] - (= (apply-f commands/balance-ranges input) - expected) - "" "" - "a" "a" - " \"a<\"> " " <\"a\"> " - "(<)>" "<()>" - "(" "<(a) b>")) - - (deftest slurp - (are [input dir expected] - (= (apply-f (commands/slurp dir) input) expected) - "(|) a" 1 "(|a)" - "((|)) a" 1 "((|) a)" - "(|) ;;comment\na" 1 "(|;;comment\n a)" ;; slurp around comments - "a(|)" -1 "(a|)" - "a ;; hello\n(|)" -1 "(a ;; hello\n | )" - "a #:b{|}" -1 "#:b{a|}" - - "a #(|)" -1 "#(a|)" - "#(|) a" 1 "#(|a)" - "@(|) a" 1 "@(|a)" - "#::a{|:a} 1" 1 "#::a{|:a 1}" - "'(|) 1" 1 "'(|1)" - - "^{|} :x :a " 1 "^{|:x} :a" - "^{|} :x 1" 1 "^{|:x} 1" - "^{} [|] :x" 1 "^{} [|:x]" - - "('is-d|ata) :x" 1 "('is-d|ata :x)" - "('xy|z 1) 2" 1 "('xy|z 1 2)" - "'ab|c 1" 1 "'ab|c 1")) - - #_(deftest slurp-embedded - (are [input dir expected] - (= (apply-embedded-f (commands/slurp dir) input) expected) - "```\n(|) a\n```" 1 "```\n(|a)\n```" - "```\n((|)) a\n```" 1 "```\n((|) a)\n```" - "```\n(|) ;;comment\na\n```" 1 "```\n(|;;comment\n a)\n```" - "```\n('xy|z 1) 2\n```" 1 "```\n('xy|z 1 2)\n```")) - - (deftest barf - (are [input dir expected] - (= (apply-f (commands/barf dir) input) expected) - "(|a)" 1 "(|) a" - "(|a)" -1 "a (|)" - "((|)a)" 1 "((|)a)" - - "#(|a)" -1 "a #(|)" - "#(|a)" 1 "#(|) a" - - "#:b{a|}" -1 "a #:b{|}")) - - (deftest grow-selections - (are [input expected] - (= (apply-cmd commands/selection-grow input) expected) - - "(|)" "<()>" - "(|a)" "()" - "(a|)" "()" - "\"|\"" "<\"\">" - "\"a|b\"" "\"\"" - "[|]" "<[]>" - ";; hell|o" "<;; hello>" - - "( a|)" "( )" - "( )" "(< a>)" - "(< a>)" "<( a)>" - - "@" "<@deref>")) - - (deftest enter-and-indent - (are [input expected] - (= (apply-cmd commands/enter-and-indent input) expected) - - "(|)" "(\n |)" - "((|))" "((\n |))" - "(()|)" "(()\n |)" - "(a |b)" "(a\n |b)" - "(a b|c)" "(a b\n |c)"))) diff --git a/src-squint/nextjournal/clojure_mode_tests/macros.cljc b/src-squint/nextjournal/clojure_mode_tests/macros.cljc index 97b1b327..fd40bacd 100644 --- a/src-squint/nextjournal/clojure_mode_tests/macros.cljc +++ b/src-squint/nextjournal/clojure_mode_tests/macros.cljc @@ -1,8 +1,10 @@ (ns nextjournal.clojure-mode-tests.macros (:require [clojure.walk :as walk])) -(defmacro deftest [_var-name & body] - `(do ~@body)) +(defmacro deftest [var-name & body] + `(do + (~'js* "// ~{}\n" ~var-name) + ~@body)) (defmacro testing [_str & body] `(do ~@body)) @@ -63,7 +65,12 @@ (and (pos? (count argv)) (pos? (count args)) (zero? (mod (count args) (count argv))))) - `(do ~@(map (fn [a] (apply-template argv (->assert expr) a)) - (partition (count args) args))) + (let [processed (map (fn [a] + (apply-template argv (->assert expr) a)) + (partition (count argv) args))] + #_(println "======") + #_(println args) + #_(println processed) + `(do ~@processed)) #?(:clj (throw (IllegalArgumentException. "The number of args doesn't match are's argv.")) :cljs (throw (js/Error "The number of args doesn't match are's argv."))))) diff --git a/test/nextjournal/clojure_mode_tests.cljs b/test/nextjournal/clojure_mode_tests.cljc similarity index 86% rename from test/nextjournal/clojure_mode_tests.cljs rename to test/nextjournal/clojure_mode_tests.cljc index 130bfbca..b5ad414b 100644 --- a/test/nextjournal/clojure_mode_tests.cljs +++ b/test/nextjournal/clojure_mode_tests.cljc @@ -1,12 +1,15 @@ (ns nextjournal.clojure-mode-tests - (:require [cljs.test :refer [is are testing deftest]] + (:require #?@(:squint [] + :cljs [[cljs.test :refer [are testing deftest]]]) [nextjournal.clojure-mode :as cm-clojure] [nextjournal.clojure-mode.test-utils :as test-utils] [nextjournal.clojure-mode.extensions.close-brackets :as close-brackets] [nextjournal.clojure-mode.commands :as commands] [nextjournal.clojure-mode.extensions.formatting :as format] - [nextjournal.livedoc :as livedoc] - [nextjournal.clojure-mode.live-grammar :as live-grammar])) + #?@(:squint [] + :cljs [[nextjournal.livedoc :as livedoc]]) + #?(:squint ["assert" :as assert])) + #?(:squint (:require-macros [nextjournal.clojure-mode-tests.macros :refer [deftest are testing]]))) (def extensions cm-clojure/default-extensions @@ -18,8 +21,11 @@ (def apply-f (partial test-utils/apply-f extensions)) (def apply-cmd (partial test-utils/apply-cmd extensions)) -(def apply-embedded-f (partial test-utils/apply-f #js [livedoc/markdown-language-support])) -(def apply-embedded-cmd (partial test-utils/apply-cmd #js [livedoc/markdown-language-support])) +#?(:squint nil + :cljs (def apply-embedded-f (partial test-utils/apply-f #js [livedoc/markdown-language-support]))) + +#?(:squint nil + :cljs (def apply-embedded-cmd (partial test-utils/apply-cmd #js [livedoc/markdown-language-support]))) (do (deftest nav @@ -117,14 +123,15 @@ "\"[|]\"" "\"|]\"" ;; normal deletion inside strings )) - (testing "handle backspace (embedded)" - (are [input expected] - (= (apply-embedded-f close-brackets/handle-backspace input) - expected) - "```\n()|\n```" "```\n(|)\n```" - "```\n[[]]|\n```" "```\n[[]|]\n```" - "```\n(| )\n```" "```\n|\n```" - ))) + #?(:squint nil + :cljs (testing "handle backspace (embedded)" + (are [input expected] + (= (apply-embedded-f close-brackets/handle-backspace input) + expected) + "```\n()|\n```" "```\n(|)\n```" + "```\n[[]]|\n```" "```\n[[]|]\n```" + "```\n(| )\n```" "```\n|\n```" + )))) (deftest indentSelection @@ -263,14 +270,16 @@ "'ab|c 1" 1 "'ab|c 1" )) - (deftest slurp-embedded - (are [input dir expected] - (= (apply-embedded-f (commands/slurp dir) input) expected) - "```\n(|) a\n```" 1 "```\n(|a)\n```" - "```\n((|)) a\n```" 1 "```\n((|) a)\n```" - "```\n(|) ;;comment\na\n```" 1 "```\n(|;;comment\n a)\n```" - "```\n('xy|z 1) 2\n```" 1 "```\n('xy|z 1 2)\n```" - )) + #?(:squint nil + :cljs + (deftest slurp-embedded + (are [input dir expected] + (= (apply-embedded-f (commands/slurp dir) input) expected) + "```\n(|) a\n```" 1 "```\n(|a)\n```" + "```\n((|)) a\n```" 1 "```\n((|) a)\n```" + "```\n(|) ;;comment\na\n```" 1 "```\n(|;;comment\n a)\n```" + "```\n('xy|z 1) 2\n```" 1 "```\n('xy|z 1 2)\n```" + ))) (deftest barf (are [input dir expected]