Browse files

WIP leiningen

  • Loading branch information...
1 parent 594214b commit 3db61a9f7de4411746bed58e97e52712ca5297a6 @laurentpetit committed May 11, 2012
18 ccw.core/plugin.xml
@@ -1337,6 +1337,24 @@
+ <perspectiveExtension
+ targetID="org.eclipse.debug.ui.DebugPerspective">
+ <newWizardShortcut
+ id="">
+ </newWizardShortcut>
+ <newWizardShortcut
+ id="ccw.newClojureFileWizard">
+ </newWizardShortcut>
+ <viewShortcut
+ id="ccw.view.namespacebrowser">
+ </viewShortcut>
+ <view
+ id="ccw.view.namespacebrowser"
+ minimized="false"
+ relationship="stack"
+ relative="org.eclipse.ui.views.ContentOutline">
+ </view>
+ </perspectiveExtension>
7 ccw.core/src/clj/ccw/clojure_project_nature.clj
@@ -124,6 +124,7 @@
(defn- add-lib-on-classpath!
[java-project lib-path libSrc-path copy?]
+ (println "about to add lib-path=" lib-path ", libSrc-path=" libSrc-path)
(if (nil? lib-path)
(throw (CoreException. (Status/CANCEL_STATUS)))
@@ -149,10 +150,12 @@
(conj entries-old
(JavaCore/newLibraryEntry (make-ws-path in-project-lib) (make-ws-path in-project-libSrc) nil)))]
+ (println "java-project:" java-project)
+ (println "entries-new:" (seq entries-new))
(doto java-project
(.setRawClasspath entries-new nil)
- (.save nil true)
- (-> .getProject (.refreshLocal (IResource/DEPTH_ONE) nil))))))))))
+ (.save nil false)
+ (-> .getProject (.refreshLocal (IResource/DEPTH_INFINITE) nil))))))))))
(defn- file-to-path
[file] (Path/fromOSString (.getAbsolutePath file)))
3 ccw.leiningen/META-INF/MANIFEST.MF
@@ -19,7 +19,8 @@ Require-Bundle: org.eclipse.core.resources,
- org.eclipse.platform
+ org.eclipse.platform,
+ org.eclipse.ui.ide
Bundle-ClassPath: .,
BIN ccw.leiningen/leiningen32x32.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 ccw.leiningen/plugin.xml
@@ -353,5 +353,43 @@
+ point="org.eclipse.ui.newWizards">
+ <category
+ id=""
+ name="Leiningen">
+ </category>
+ <wizard
+ category=""
+ class="ccw.leiningen.NewLeiningenProjectWizard"
+ descriptionImage="leiningen32x32.png"
+ hasPages="true"
+ icon="leiningen32x32.png"
+ id=""
+ name="Leiningen Project"
+ project="true">
+ </wizard>
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="org.eclipse.jdt.ui.JavaPerspective">
+ <newWizardShortcut
+ id="">
+ </newWizardShortcut>
+ </perspectiveExtension>
+ <perspectiveExtension
+ targetID="org.eclipse.pde.ui.PDEPerspective">
+ <newWizardShortcut
+ id="">
+ </newWizardShortcut>
+ </perspectiveExtension>
+ <perspectiveExtension
+ targetID="org.eclipse.debug.ui.DebugPerspective">
+ <newWizardShortcut
+ id="">
+ </newWizardShortcut>
+ </perspectiveExtension>
43 ccw.leiningen/src/clj/ccw/leiningen/handlers.clj
@@ -83,28 +83,37 @@
(.exists (.getFile project "project.clj")))))]
-(defn add-leiningen-nature
- "Pre-requisites:
- - The event's selection has size one, and is an open project
- - The project does not already have the leiningen nature"
- [handler event]
- (let [project (e/project event)]
- (when-not (n/has-nature? project n/NATURE-ID)
- (e/run-in-background
+;; TODO too many imbrications here, decomplecting the code
+;; from the job, etc. will help ...
+(defn add-natures
+ [project natures legend]
+ (println "add-natures:" project ", natures:" (seq natures) ", legend:'" legend "'")
+ (e/run-in-background
+ (e/runnable-with-progress-in-workspace
(fn [monitor]
+ (println "add-natures: background job started for natures:" (seq natures))
(.beginTask monitor
- (str "Adding leiningen support to project " (.getName project))
+ (str legend)
- (-> (n/description project)
- (n/add-natures! JavaCore/NATURE_ID)))
- (n/set-description!
- project
- (-> (n/description project)
- (n/add-natures! n/NATURE-ID)))
- ; TODO : report done ?
- (.done monitor))))))
+ (apply n/add-natures! (n/description project) natures))
+ (.worked monitor 1)
+ (.done monitor)
+ (println "add-natures: background job stopped for natures:" (seq natures)))
+ (e/workspace-root))))
+(defn add-leiningen-nature
+ "Pre-requisites:
+ - The event's selection has size one, and is an open project
+ - The project does not already have the leiningen nature"
+ ([handler event]
+ (add-leiningen-nature (e/project event)))
+ ([project]
+ (add-natures
+ project
+ [(JavaCore/NATURE_ID) n/NATURE-ID]
+ (str "Adding leiningen support to project " (.getName project)))))
(defn- upgrade-project-build-path [java-project overwrite?]
17 ccw.leiningen/src/clj/ccw/leiningen/nature.clj
@@ -64,11 +64,12 @@
(defn set-description!
([proj desc] (set-description! proj desc nil))
([proj desc progress-monitor]
- (doto proj
- (.setDescription
- desc,
- progress-monitor))))
+ (println "proj:" proj)
+ (println "desc:" desc)
+ (.setDescription
+ proj
+ desc
+ progress-monitor)))
(defn has-builder? [desc builder-id]
(let [spec (.getBuildSpec desc)]
@@ -83,13 +84,17 @@
(.hasNature project nature-id))
(defn set-natures! [desc natures]
+ (println "natures:" natures)
(doto desc
(.setNatureIds (into-array String natures))))
(defn add-natures! [desc & nature-ids]
(let [natures (.getNatureIds desc)
+ _ (println "old natures:" natures)
missing-natures (remove (set natures) nature-ids)
- new-natures (concat natures missing-natures)]
+ _ (println "missing natures:" natures)
+ new-natures (concat natures missing-natures)
+ _ (println "new-natures:" new-natures)]
(set-natures! desc new-natures)))
(defn remove-nature! [desc nature-id]
177 ccw.leiningen/src/clj/ccw/leiningen/util.clj
@@ -14,28 +14,38 @@
(println "ccw.leiningen.util load starts")
-;; from ccw.core
+;; TODO port back to ccw.core for the nature (place in ccw.util)
(defn bundle-file
+ "given bundle-name (a String), return the corresponding to the
+ currently loaded instance of this bundle"
(-> bundle-name Platform/getBundle FileLocator/getBundleFile))
(defn bundle-dir
+ "given bundle-name (a String), assumes there exists a currently loaded instance
+ of it, and that it has been installed as a directory and not a jar.
+ Return the corresponding to its directory.
+ (So, it's really just bundle-file with a check)"
(let [bundle-dir (bundle-file bundle-name)]
(if (.isFile bundle-dir)
(throw (RuntimeException. (str bundle-name " bundle should be deployed as a directory.")))
(defn- plugin-entry
+ "given a plugin-name and an entry name inside the plugin, returns the
+ instance for the entry if it is found in the plugin."
[plugin-name entry-name]
(let [bundle-dir (bundle-dir plugin-name)
entry (File. bundle-dir entry-name)]
(if (.exists entry)
(throw (RuntimeException. (str "Unable to locate " + entry-name " in " plugin-name " plugin."))))))
-(defn lein-env []
+(defn lein-env
+ "Create a new classlojure based leiningen environment by fetching all jars
+ and classes directories found inside the ccw.leiningen-core plugin."
+ []
(let [leiningen-core (bundle-dir "ccw.leiningen-core")
jar (fn [f] (and (.isFile f) (.endsWith (.getName f) ".jar")))
classes-dir (fn [d] (let [manifest (io/file d "META-INF" "MANIFEST.MF")]
@@ -44,24 +54,37 @@
libs (->> leiningen-core file-seq (filter #(or (jar %) (classes-dir %))))]
(apply c/classlojure libs)))
-(defonce projects-envs (ref {}))
+(defonce ^{:doc
+ "Ref of map of \"project-name\" -> delay of classlojure environment.
+ e.g. : (ref {\"project-foo\" (delay (lein-env))})" }
+ projects-envs (ref {}))
-(defn project-env! [project & recreate?]
- (let [pname (-> project e/project .getName)]
+(defn project-env!
+ "Returns a classlojure environment for project, creating one if none exists yet,
+ or if recreate? is true."
+ [project & recreate?]
+ (let [pname (if (string? project) (-> project e/project .getName))]
(commute projects-envs
#(if (or recreate? (not (% pname)))
(assoc % pname (delay (lein-env)))
@(@projects-envs pname)))
-(defn file-exists? "Return the file if it exists, or nil" [f]
+(defn file-exists?
+ "Return the file if it exists, or nil"
+ [f]
(when (.exists f) f))
-(defn eval-in-project [project form & args]
+(defn eval-in-project
+ "Evaluates form in the leiningen environment for project. If args are provided,
+ consider form is a function and call it with args applied to it."
+ [project form & args]
(apply c/eval-in (project-env! project) form args))
(defn project-clj
+ "Given project (which must extend IProjectCoercible), returns its project.clj
+ absolute path in the filesystem."
(-> project
@@ -71,16 +94,144 @@
(defn lein-project
"Given a project (anything that coerces to ccw.util.eclipse/IProjectCoercion),
- analyze its project.clj file and return the project map"
- [project]
+ analyze its project.clj file and return the project map.
+ If static-loading? is true, does not dynamically load plugins, middlewares, etc.
+ (e.g. does not call leiningen.core.project/init-project).
+ project can be the specific key :project-less to get the environment associated with no specific project"
+ [project & {:keys [static-loading? enhance-fn] :or {enhance-fn identity}}]
(eval-in-project project '(require 'leiningen.core.project))
- (eval-in-project project 'leiningen.core.project/read (project-clj project)))
+ (let [project-map (if (= :project-less project)
+ {}
+ (eval-in-project project
+ 'leiningen.core.project/read
+ (project-clj project)))
+ project-map (enhance-fn project-map)]
+ (when-not static-loading?
+ (if (= :project-less project)
+ (eval-in-project
+ project
+ `(leiningen.core.project/load-plugins
+ (leiningen.core.project/merge-profiles
+ '~(enhance-fn project-map)
+ [:user :default])))
+ (eval-in-project
+ project
+ `(leiningen.core.project/init-project '~project-map))))
+ project-map))
(defn lein-native-platform-path [lein-project]
(eval/native-arch-path lein-project))
-(defn lein-new [project]
- (binding [leiningen.core.user/profiles (constantly {:user {:plugins [[lein-newnew "0.2.6"]]}})]))
+;TODO: suggest a patch for getting rid of this monkey patch:
+;lpetit: Raynes: I have some feedback for you wrt lein newnew
+;[11:46pm] Raynes: I'm scared.
+;[11:46pm] Raynes: technomancy: He has feedback. Prepare the cannons.
+;[11:47pm] lpetit: Raynes: don't panic
+;[11:47pm] lpetit: Raynes: I had to monkey patch ->files to be able to make lein new new work in CCW
+;[11:47pm] lpetit: Raynes: 2 problems.
+;[11:49pm] lpetit: 1/ lein new new assumes implicitly that "user.dir" directory is the project's parent directory. In Eclipse (or other tools wanting to embed the plugin I guess), this generally is not the case
+;[11:49pm] Raynes: I don't think it does.
+;[11:49pm] Raynes: Oh.
+;[11:49pm] Raynes: Never mind, you said user.dir, not user.home.
+;[11:49pm] fbru02 left the chat room. (Ping timeout: 260 seconds)
+;[11:50pm] lpetit: 2/ In Eclipse, the user can choose a name for his project, which can be different from the project's directory name
+;[11:50pm] lpetit: Raynes: yeah, (System/getProperty "user.dir") is the equivalent of a "working current directory"
+;[11:50pm] Raynes: I don't quite understand why user.dir wouldn't be the parent directory. It is meant to be called from lein and it is, in every case, the right directory.
+;[11:51pm] Raynes: How is Eclipse calling it?
+;[11:51pm] Raynes: Are you calling the plugin code from Eclipse itself, or are you calling out to lein?
+;[11:51pm] lpetit: 3/ (yeah there's a 3/ finally) In Eclipse, the project's directory is created before lein new new has a chance to work, which make it fail by saying "directory already existing"
+;[11:52pm] lpetit: Raynes: talking about embedding leiningen-core in the IDE JVM, and calling tasks from it
+;[11:52pm] Raynes: Seems like this could be solved with an option like --to-dir to set the directory that the project will be added to.
+;[11:52pm] Raynes: We could make that ignore the fact that a directory already exists.
+;[11:52pm] Raynes: Would that help?
+;[11:52pm] lpetit: Raynes: I played with classlojure and I maintain a map of project-name -> classlojure env. per Eclipse project
+;[11:52pm] technomancy: yeah, I foresee the whole current directory thing being a common problem with leiningen-core outside leiningen
+;[11:52pm] technomancy: it's just too easy to write code that assumes the current directory is the project root
+;[11:53pm] lpetit: Raynes: I don't know if users would complain or not if you remove the "safeguard" ?
+;[11:53pm] technomancy: I try to avoid it, but it sneaks in
+;[11:53pm] Raynes: lpetit: I'm not removing the safeguard, just giving a way for Eclipse to get around it.
+;[11:53pm] lpetit: Raynes: perfect, so point 3/ is solved
+;[11:54pm] Raynes: Number 1 is solved too.
+;[11:54pm] Raynes: Since this lets you set the directory yourself and not assume user.dir.
+;[11:54pm] technomancy: lpetit: does would System/setProperty work for user.dir?
+;[11:55pm] lpetit: technomancy: that's what I'm doing currently, but it's not thread safe
+;[11:55pm] technomancy: oh, of course
+;[11:55pm] lpetit: Ah, system classloader ...
+;[11:56pm] lpetit: Raynes: indeed, for 1/, if there's also a programmatic way to change/bind the var --to-dir sets, we're good with that
+;[11:56pm] Raynes: Why would that be necessary?
+;[11:57pm] lpetit: Raynes: I don't start a separate jvm
+;[11:58pm] Raynes: Yeah, but you call the task. You can pass the option, right?
+;[11:58pm] lpetit: Raynes: oh, via a String-based interface, yeah, I can do that if that's the only option
+;[11:58pm] Raynes: Well, it's a task, not a library.
+;[11:59pm] lpetit: Raynes: shouldn't a task be based on a library ? (just kidding)
+;[11:59pm] Raynes: Heh
+;[11:59pm] Raynes: It could have a better library-like interface.
+;[11:59pm] Raynes: This should solve number 2 as well.
+;[11:59pm] lpetit: Raynes: I'm fine with what you suggested
+;[11:59pm] Raynes: The project name and directory name can be different with --to-dir.
+;[11:59pm] lpetit: Raynes: indeed, if the --to-dir is the project's directory, that would be just fine
+;[12:00am] Raynes: lpetit, technomancy: I don't know when I'll have time to implement this though, so feel free to patch it if you want.
+;[12:00am] Raynes: Shouldn't be hard.
+;[12:01am] technomancy: hm; yeah but I don't know if this should block the preview4 work
+;[12:01am] lpetit: Raynes, technomancy: definitely something I can do. I also may have some time problem, but since I definitely want to get rid of my big monkey patch, I'll do this sooner or later if it hasn't been done then
+;[12:01am] lpetit: technomancy: definitely not
+;[12:01am] technomancy: yeah, now that it's not on the bootclasspath it's easy to bump independently
+;[12:01am] lpetit: don't block it, I'm not blocked myself
+;[12:01am] technomancy: ok, good to hear
+;[12:02am] antares_: Raynes: you know what's funny? Gosu is not open source
+;[12:02am] Raynes: Heh
+(defn monkey-patch-lein-new [project]
+ (eval-in-project
+ project
+ '(do
+ (require '
+ (in-ns '
+ (defn- template-path [_ path data]
+ (io/file (render-text path data)))
+ (defn
+ ->files
+ "Generate a file with content. path can be a or string.\n It will be turned into a File regardless. Any parent directories will\n be created automatically. Data should include a key for :name so that\n the project is created in the correct directory"
+ [{:keys [name], :as data} & paths]
+ (let
+ [normalized-name (normalize-project-name name)]
+ (.mkdir (io/file (System/getProperty "user.dir")))
+ (doseq
+ [path paths]
+ (if
+ (string? path)
+ (.mkdirs (template-path normalized-name path data))
+ (let
+ [[path content]
+ path
+ path
+ (template-path normalized-name path data)
+ path (io/file (System/getProperty "user.dir") path)]
+ (.mkdirs (.getParentFile path))
+ (io/copy content (io/file path))))))))))
+(defn lein-new [location & args]
+ (let [project-map (lein-project :project-less
+ :enhance-fn
+ (fn [p]
+ (eval-in-project :project-less
+ `(-> '~p
+ (leiningen.core.project/add-profiles
+ '{:user
+ ~'{:plugins [[lein-newnew "0.2.6"]]}})
+ (leiningen.core.project/merge-profiles
+ [:user])))))]
+ (monkey-patch-lein-new :project-less)
+ (let [user-dir (System/getProperty "user.dir")]
+ (try
+ (System/setProperty "user.dir" location)
+ (eval-in-project
+ :project-less
+ `(do
+ (require '
+ (apply nil '~args)))
+ (finally (System/setProperty "user.dir" user-dir))))))
;;; JDT utilities
31 ccw.leiningen/src/clj/ccw/leiningen/wizard.clj
@@ -0,0 +1,31 @@
+(ns ccw.leiningen.wizard
+ (:require [ccw.leiningen.util :as u]
+ [ccw.util.eclipse :as e]
+ [ccw.leiningen.nature :as n]
+ [ccw.leiningen.handlers :as handlers]
+ [ :as io])
+ (:import [org.eclipse.core.resources IResource]
+ [org.eclipse.jdt.core JavaCore]))
+(defn perform-finish [project]
+ (let [project-name (.getName project)
+ project-file (-> project .getLocation .toFile)]
+ (println "project-name:" project-name
+ \newline
+ "project-file:" project-file)
+ ;(handlers/add-leiningen-nature (e/project project-name))
+ ;(.refreshLocal (e/project project-name) (IResource/DEPTH_INFINITE) nil)
+ ;(Thread/sleep 2000)
+ (u/lein-new (.getAbsolutePath project-file) project-name)
+ (.refreshLocal project (IResource/DEPTH_INFINITE) nil)
+ (handlers/add-natures
+ project
+ [(JavaCore/NATURE_ID) n/NATURE-ID]
+ (str "Adding leiningen support to project " project-name))
+ #_(handlers/add-natures
+ project
+ ["ccw.nature"]
+ "Adding Clojure Support")
+ ;(.refreshLocal (e/project project-name) (IResource/DEPTH_INFINITE) nil)
+ ))
55 ccw.leiningen/src/java/ccw/leiningen/
@@ -0,0 +1,55 @@
+package ccw.leiningen;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard;
+import ccw.util.ClojureUtils;
+import clojure.osgi.ClojureOSGi;
+public class NewLeiningenProjectWizard extends BasicNewProjectResourceWizard {
+ private static final String WizardNamespace = "ccw.leiningen.wizard";
+ private static final String performFinish = "perform-finish";
+ static {
+ try {
+ ClojureOSGi.require(Activator.getDefault().getBundle().getBundleContext(), WizardNamespace);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ public NewLeiningenProjectWizard() {
+ super();
+ }
+ public void addPages() {
+ super.addPages();
+ getStartingPage().setDescription("Create a new Leiningen project.");
+ getStartingPage().setTitle("Leiningen project");
+ setWindowTitle("New Leiningen project");
+ }
+ public boolean performFinish() {
+ if (super.performFinish()) {
+ IProject project = getNewProject();
+ try {
+ ClojureUtils.invoke(
+ WizardNamespace,
+ performFinish,
+ project
+ //,
+ //project.getLocation().toFile()
+ );
+ return true;
+ } catch (Exception e) {
+ Activator.logError("Exception while creating new project " + project.getName(), e);
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
1 ccw.util/src/clj/ccw/util/eclipse.clj
@@ -2,6 +2,7 @@
(:require [ :as io])
(:import [org.eclipse.core.resources IResource
+ IProject

0 comments on commit 3db61a9

Please sign in to comment.