-
Notifications
You must be signed in to change notification settings - Fork 131
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Automatically apply suggestions to source files
Introduces `--replace` and `--interactive` cli arguments to automatically replace suggestions in source files. Initial port from jpb/kibit-replace b49410c Resolves #155
- Loading branch information
Showing
6 changed files
with
207 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
(ns kibit.replace | ||
(:require [clojure.string :as str] | ||
[clojure.java.io :as io] | ||
[rewrite-clj.zip :as rewrite.zip] | ||
[rewrite-clj.node :as rewrite.node] | ||
[kibit.check :as check] | ||
[kibit.reporters :as reporters])) | ||
|
||
(defn- prompt | ||
"Create a yes/no prompt using the given message. | ||
From `leiningen.ancient.console`." | ||
[& msg] | ||
(let [msg (str (str/join msg) " [yes/no] ")] | ||
(locking *out* | ||
(loop [i 0] | ||
(when (= (mod i 4) 2) | ||
(println "*** please type in one of 'yes'/'y' or 'no'/'n' ***")) | ||
(print msg) | ||
(flush) | ||
(let [r (or (read-line) "") | ||
r (.toLowerCase ^String r)] | ||
(case r | ||
("yes" "y") true | ||
("no" "n") false | ||
(recur (inc i)))))))) | ||
|
||
(defn- report-or-prompt | ||
"" | ||
[file interactive? {:keys [line expr alt]}] | ||
(if interactive? | ||
(prompt (with-out-str | ||
(println "Would you like to replace") | ||
(reporters/pprint-code expr) | ||
(println " with") | ||
(reporters/pprint-code alt) | ||
(print (format "in %s:%s?" file line)))) | ||
(do | ||
(println "Replacing") | ||
(reporters/pprint-code expr) | ||
(println " with") | ||
(reporters/pprint-code alt) | ||
(println (format "in %s:%s" file line)) | ||
|
||
true))) | ||
|
||
(def ^:private expr? (comp not rewrite.node/printable-only? rewrite.zip/node)) | ||
|
||
(defn- map-zipper | ||
"Apply `f` to all code forms in `zipper0`" | ||
[f zipper0] | ||
(let [zipper (if (expr? zipper0) | ||
(rewrite.zip/postwalk zipper0 | ||
expr? | ||
f) | ||
zipper0)] | ||
(if (rewrite.zip/rightmost? zipper) | ||
zipper | ||
(recur f (rewrite.zip/right zipper))))) | ||
|
||
(defn- replace-expr* | ||
"" | ||
[expr reporter kw-opts] | ||
(if-let [check-map (apply check/check-expr | ||
(rewrite.zip/sexpr expr) | ||
kw-opts)] | ||
(if (reporter (assoc check-map | ||
:line | ||
(-> expr rewrite.zip/node meta :row))) | ||
(recur (rewrite.zip/edit expr | ||
(fn -replace-expr [sexpr] | ||
(:alt check-map))) | ||
reporter | ||
kw-opts) | ||
expr) | ||
expr)) | ||
|
||
(defn replace-expr | ||
"Apply any suggestions to `expr`. | ||
`expr` - Code form to check and replace in | ||
`kw-opts` - any valid option for `check/check-expr`, as well as: | ||
- `:file` current filename | ||
- `:interactive` prompt for confirmation before replacement or not | ||
Returns a string of the replaced form" | ||
[expr & kw-opts] | ||
(let [options (apply hash-map kw-opts)] | ||
;; TODO use (:reporter options) to determine format? | ||
(replace-expr* expr | ||
(partial report-or-prompt | ||
(:file options) | ||
(:interactive options)) | ||
kw-opts))) | ||
|
||
(defn replace-file | ||
"Apply any suggestions to `file`. | ||
`file` - File to check and replace in | ||
`kw-opts` - any valid option for `check/check-expr`, as well as: | ||
- `:interactive` prompt for confirmation before replacement or not | ||
Modifies `file`, returns `nil`" | ||
[file & kw-opts] | ||
(->> (slurp file) | ||
rewrite.zip/of-string | ||
(map-zipper (fn -replace-expr [node] | ||
(apply replace-expr | ||
node | ||
:file (str file) | ||
kw-opts))) | ||
rewrite.zip/root-string | ||
(spit file))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
(ns kibit.test.replace | ||
(:require [kibit.check :as check] | ||
[kibit.rules :as rules] | ||
[kibit.replace :as replace]) | ||
(:use [clojure.test]) | ||
(:import java.io.File)) | ||
|
||
(deftest replace-file-are | ||
(are [expected-form test-form] | ||
(= expected-form | ||
(let [file (doto (File/createTempFile "replace-file" ".clj") | ||
(.deleteOnExit) | ||
(spit test-form))] | ||
(with-out-str (replace/replace-file file)) | ||
(slurp file))) | ||
|
||
"(inc a)" | ||
"(+ 1 a)" | ||
|
||
"(ns replace-file) | ||
(defn \"Documentation\" ^{:my-meta 1} [a] | ||
;; a comment | ||
(inc a))" | ||
"(ns replace-file) | ||
(defn \"Documentation\" ^{:my-meta 1} [a] | ||
;; a comment | ||
(+ 1 a))")) |