Skip to content
Browse files

Add 'move-ns': refactoring tool to rename a namespace (alpha)

  • Loading branch information...
1 parent ada4479 commit 4470daf4d156fc643952e505f4bcc918d633b836 @stuartsierra stuartsierra committed Nov 2, 2012
Showing with 77 additions and 0 deletions.
  1. +77 −0 src/main/clojure/clojure/tools/namespace/move.clj
View
77 src/main/clojure/clojure/tools/namespace/move.clj
@@ -0,0 +1,77 @@
+;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and
+;; distribution terms for this software are covered by the Eclipse
+;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+;; which can be found in the file epl-v10.html at the root of this
+;; distribution. By using this software in any fashion, you are
+;; agreeing to be bound by the terms of this license. You must not
+;; remove this notice, or any other, from this software.
+
+(ns clojure.tools.namespace.move
+ (:require [clojure.string :as str]
+ [clojure.java.io :as io])
+ (:import (java.io File)))
+
+(defn- update-file
+ "Read file as a string, call f on file plus any args, then write out
+ return value of f as the new contents of file."
+ [file f & args]
+ (spit file (str (apply f (slurp file) args))))
+
+(defn- ns-file-name [sym]
+ (str (-> (name sym)
+ (str/replace #"-" "_")
+ (str/replace #"\." File/separator))
+ ".clj"))
+
+(defn- clojure-source-files [dirs]
+ (->> dirs
+ (map io/file)
+ (filter #(.exists %))
+ (mapcat file-seq)
+ (filter #(and (.isFile %) (.endsWith (.getName %) ".clj")))
+ (map #(.getCanonicalFile %))))
+
+(def ^:private symbol-regex
+ ;; LispReader.java uses #"[:]?([\D&&[^/]].*/)?([\D&&[^/]][^/]*)" but
+ ;; that's too broad; we don't want a whole namespace-qualified symbol,
+ ;; just each part individually.
+ #"[a-zA-Z0-9$%*+=?!<>_-]['.a-zA-Z0-9$%*+=?!<>_-]*")
+
+(defn replace-ns-symbol
+ "ALPHA: subject to change. Given Clojure source as a string, replaces
+ all occurances of the namespace name old-sym with new-sym and
+ returns modified source as a string."
+ [source old-sym new-sym]
+ (let [old-name (name old-sym)
+ new-name (name new-sym)]
+ ;; A lossless parser would be better, but this is adequate
+ (str/replace source symbol-regex
+ (fn [match]
+ (if (= match old-name)
+ new-name
+ match)))))
+
+(defn move-ns-file
+ "ALPHA: subject to change. Moves the .clj source file (found relative
+ to source-path) for the namespace named old-sym to a file for a
+ namespace named new-sym."
+ [old-sym new-sym source-path]
+ (let [old-file (io/file source-path (ns-file-name old-sym))
+ new-file (io/file source-path (ns-file-name new-sym))]
+ (.mkdirs (.getParentFile new-file))
+ (io/copy old-file new-file)
+ (.delete old-file)
+ (loop [dir (.getParentFile old-file)]
+ (when (empty? (.listFiles dir))
+ (.delete dir)
+ (recur (.getParentFile dir))))))
+
+(defn move-ns
+ "ALPHA: subject to change. Moves the .clj source file (found relative
+ to source-path) for the namespace named old-sym to new-sym
+ and replace all occurances of the old name with the new
+ name in all Clojure source files found in dirs."
+ [old-sym new-sym source-path dirs]
+ (move-ns-file old-sym new-sym source-path)
+ (doseq [file (clojure-source-files dirs)]
+ (update-file file replace-ns-symbol old-sym new-sym)))

0 comments on commit 4470daf

Please sign in to comment.
Something went wrong with that request. Please try again.