Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .dir-locals.el
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
((nil . ((cider-clojure-cli-aliases . ":dev:behave/app")
(cider-default-cljs-repl . figwheel-main))))
(cider-default-cljs-repl . figwheel-main)
(eval . (progn
(load (expand-file-name "scripts/behave.el"
(locate-dominating-file buffer-file-name ".dir-locals.el")))
(add-hook 'after-save-hook #'behave-format-on-save nil t))))))

;; VMS Configuration
;; TODO: Fix to avoid having two separate aliases for projects
Expand Down
74 changes: 74 additions & 0 deletions scripts/align_requires.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env bb
(ns align-requires
(:require [babashka.fs :as fs]
[clojure.string :as str]
[rewrite-clj.zip :as z]
[rewrite-clj.node :as n]))

(defn- keyword-entry?
"True if a require vector has :as or :refer as its second element.
Uses z/right (skips whitespace) to find the keyword directly."
[zloc]
(when-let [second-child (-> zloc z/down z/right)]
(try (#{:as :refer} (z/sexpr second-child))
(catch Exception _ nil))))

(defn- require-vectors
"Returns all zipper locations of [ns-sym :as/:refer ...] vectors
within the :require clause of the given file zipper."
[root-zloc]
(->> root-zloc
(iterate z/next)
(take-while (complement z/end?))
(filter #(and (z/vector? %) (keyword-entry? %)))))

(defn- ns-sym-len
[entry-zloc]
(count (str (z/sexpr (z/down entry-zloc)))))

(defn- set-whitespace-after-ns-sym
[entry-zloc spaces]
(let [ws-node (n/whitespace-node (str/join (repeat spaces " ")))]
(-> entry-zloc
z/down ; at ns-sym
z/right* ; at raw whitespace node between ns-sym and keyword
(z/replace ws-node)
z/up)))

(defn- align-file!
"Aligns all `:imports`/`:requires` dependencies."
[path]
(let [content (slurp path)
zloc (z/of-string content {:track-position? true})
entries (require-vectors zloc)]
(when (seq entries)
(let [max-len (apply max (map ns-sym-len entries))
;; Re-parse to get fresh zipper for mutations
aligned (loop [acc (z/of-string content)]
(let [es (require-vectors acc)
;; Find first entry whose spacing doesn't match target
target (fn [e] (- (inc max-len) (ns-sym-len e)))
bad-e (first (filter (fn [e]
(let [ws (-> e z/down z/right*)]
(not= (str/join (repeat (target e) " "))
(z/string ws))))
es))]
(if bad-e
(recur (set-whitespace-after-ns-sym bad-e (target bad-e)))
acc)))
new-content (z/root-string aligned)]
(when (not= content new-content)
(println "Aligned:" path)
(spit path new-content))))))

(let [arg (or (first *command-line-args*) ".")
paths (if (fs/regular-file? arg)
[arg]
(->> (fs/glob arg "**/*.{clj,cljs,cljc}")
(map str)
(remove #(re-find #"/(target|node_modules|resources/public/cljs)/" %))))]
(doseq [path paths]
(align-file! path)))

;; usage
;; bb align_requires.clj <file>
32 changes: 32 additions & 0 deletions scripts/behave.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
;;; behave.el --- Project-local Emacs helpers for behave-polylith

(defun behave-format-on-save ()
"Run cljfmt fix + align_requires on save, then silently revert buffer."
(let* ((project-root (locate-dominating-file buffer-file-name ".dir-locals.el"))
(align-script (expand-file-name "scripts/align_requires.clj" project-root))
(file (buffer-file-name))
(buf (current-buffer))
(cmd (format "cd %s && bb %s %s && clojure -M:format fix %s"
(shell-quote-argument project-root)
(shell-quote-argument align-script)
(shell-quote-argument file)
(shell-quote-argument file))))
(make-process
:name "clj-format-on-save"
:buffer nil
:command (list "bash" "-c" cmd)
:sentinel (lambda (_proc event)
(when (and (string-prefix-p "finished" event)
(buffer-live-p buf))
(with-current-buffer buf
(unless (buffer-modified-p)
(revert-buffer t t t))))))))

(defun behave-jack-in-cms ()
"Start the VMS/CMS REPL via `cider-jack-in-clj&cljs` with the :dev:behave/cms alias."
(interactive)
(let ((cider-clojure-cli-aliases ":dev:behave/cms"))
(cider-jack-in-clj&cljs nil)))

(provide 'behave)
;;; behave.el ends here
Loading