|
| 1 | +;; Copyright (c) 2013 Sung Pae <self@sungpae.com> |
| 2 | +;; Distributed under the MIT license. |
| 3 | +;; http://www.opensource.org/licenses/mit-license.php |
| 4 | + |
| 5 | +(ns vim-clojure-static.test |
| 6 | + (:require [clojure.java.io :as io] |
| 7 | + [clojure.java.shell :as shell] |
| 8 | + [clojure.edn :as edn] |
| 9 | + [clojure.string :as string] |
| 10 | + [clojure.test :as test])) |
| 11 | + |
| 12 | +(defn syn-id-names |
| 13 | + "Map lines of clojure text to vim synID names at each column as keywords: |
| 14 | +
|
| 15 | + (syn-id-names \"foo\" …) -> {\"foo\" [:clojureString :clojureString :clojureString] …} |
| 16 | +
|
| 17 | + First parameter is the file that is used to communicate with Vim. The file |
| 18 | + is not deleted to allow manual inspection." |
| 19 | + [file & lines] |
| 20 | + (io/make-parents file) |
| 21 | + (spit file (string/join \newline lines)) |
| 22 | + (shell/sh "vim" "-u" "NONE" "-N" "-S" "vim/syn-id-names.vim" file) |
| 23 | + ;; The last line of the file will contain valid EDN |
| 24 | + (into {} (map (fn [l ids] [l (mapv keyword ids)]) |
| 25 | + lines |
| 26 | + (edn/read-string (peek (string/split-lines (slurp file))))))) |
| 27 | + |
| 28 | +(defn subfmt |
| 29 | + "Extract a subsequence of seq s corresponding to the character positions of |
| 30 | + %s in format spec fmt" |
| 31 | + [fmt s] |
| 32 | + (let [f (seq (format fmt \o001)) |
| 33 | + i (.indexOf f \o001)] |
| 34 | + (->> s |
| 35 | + (drop i) |
| 36 | + (drop-last (- (count f) i 1))))) |
| 37 | + |
| 38 | +(defmacro defsyntaxtest |
| 39 | + "Create a new testing var with tests in the format: |
| 40 | +
|
| 41 | + (defsyntaxtest example |
| 42 | + (with-format \"#\\\"%s\\\"\" |
| 43 | + \"123\" #(every? (partial = :clojureRegexp) %) |
| 44 | + …) |
| 45 | + (with-format …)) |
| 46 | +
|
| 47 | + At runtime the syn-id-names of the strings (which are placed in the format |
| 48 | + spec) are passed to their associated predicates. The format spec should |
| 49 | + contain a single `%s`." |
| 50 | + [name & body] |
| 51 | + (assert (every? #(= 'with-format (first %)) body)) |
| 52 | + (assert (every? #(string? (second %)) body)) |
| 53 | + (assert (every? #(even? (count %)) body)) |
| 54 | + (let [[strings contexts] (reduce (fn [[strings contexts] [_ fmt & forms]] |
| 55 | + (let [[ss λs] (apply map list (partition 2 forms)) |
| 56 | + ss (map #(format fmt %) ss)] |
| 57 | + [(concat strings ss) |
| 58 | + (conj contexts {:fmt fmt :ss ss :λs λs})])) |
| 59 | + [[] []] body)] |
| 60 | + `(test/deftest ~name |
| 61 | + ;; Shellout to vim should happen at runtime |
| 62 | + (let [~'synids (syn-id-names (str "tmp/" ~(str name) ".clj") ~@strings)] |
| 63 | + ~@(map (fn [{:keys [fmt ss λs]}] |
| 64 | + `(test/testing ~fmt |
| 65 | + ~@(map (fn [s λ] `(test/is (~λ (subfmt ~fmt (get ~'synids ~s))))) |
| 66 | + ss λs))) |
| 67 | + contexts))))) |
| 68 | + |
| 69 | +(comment |
| 70 | + |
| 71 | + (macroexpand-1 |
| 72 | + '(defsyntaxtest number-literals-test |
| 73 | + (with-format "%s" |
| 74 | + "123" #(every? (partial = :clojureNumber) %) |
| 75 | + "456" #(every? (partial = :clojureNumber) %)) |
| 76 | + (with-format "#\"%s\"" |
| 77 | + "^" #(= % [:clojureRegexpBoundary])))) |
| 78 | + (test #'number-literals-test) |
| 79 | + |
| 80 | + ) |
0 commit comments