Support subtask specific help via 'lein help task subtask'
The help task will first look for static help under 'leiningen/help/task-subtask', then for a function name 'help-subtask' in the subtask's namespace, then a docstring on the subtask function. For the latter two options, the arglists are also printed.
This commit is contained in:
parent
102dcadf9f
commit
6998dd6929
3 changed files with 41 additions and 5 deletions
|
@ -79,6 +79,9 @@ that all your arguments will be strings, so it's up to you to call
|
||||||
Often more complicated tasks get divided up into subtasks. Placing
|
Often more complicated tasks get divided up into subtasks. Placing
|
||||||
`:subtasks` metadata on a task defn which contains a vector of subtask
|
`:subtasks` metadata on a task defn which contains a vector of subtask
|
||||||
vars will allow `lein help $TASK_CONTAINING_SUBTASKS` to list them.
|
vars will allow `lein help $TASK_CONTAINING_SUBTASKS` to list them.
|
||||||
|
This list of subtasks will show the first line of the docstring for each
|
||||||
|
subtask. The full help for a subtask can be viewed via
|
||||||
|
`lein help $TASK_CONTAINING_SUBTASKS $SUBTASK`.
|
||||||
|
|
||||||
## Code Evaluation
|
## Code Evaluation
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,9 @@
|
||||||
(string/join "\n" (concat ["\n\nSubtasks available:"]
|
(string/join "\n" (concat ["\n\nSubtasks available:"]
|
||||||
(for [[subtask doc] subtasks]
|
(for [[subtask doc] subtasks]
|
||||||
(formatted-help subtask doc
|
(formatted-help subtask doc
|
||||||
longest-key-length)))))))
|
longest-key-length))
|
||||||
|
[(str "\nRun `lein help " (:name (meta task))
|
||||||
|
" $SUBTASK` for subtask details.")])))))
|
||||||
|
|
||||||
(defn- resolve-task [task-name]
|
(defn- resolve-task [task-name]
|
||||||
(try (let [task-ns (doto (symbol (str "leiningen." task-name)) require)
|
(try (let [task-ns (doto (symbol (str "leiningen." task-name)) require)
|
||||||
|
@ -51,13 +53,19 @@
|
||||||
(catch java.io.FileNotFoundException e
|
(catch java.io.FileNotFoundException e
|
||||||
[nil nil])))
|
[nil nil])))
|
||||||
|
|
||||||
|
(defn- resolve-subtask [task-name subtask-name]
|
||||||
|
(let [[_ task] (resolve-task task-name)]
|
||||||
|
(some #(if (= (symbol subtask-name) (:name (meta %))) %)
|
||||||
|
(:subtasks (meta task)))))
|
||||||
|
|
||||||
(defn- static-help [name]
|
(defn- static-help [name]
|
||||||
(if-let [resource (io/resource (format "leiningen/help/%s" name))]
|
(if-let [resource (io/resource (format "leiningen/help/%s" name))]
|
||||||
(slurp resource)))
|
(slurp resource)))
|
||||||
|
|
||||||
(defn help-for
|
(defn help-for
|
||||||
"Help for a task is stored in its docstring, or if that's not present
|
"Returns a string containing help for a task.
|
||||||
in its namespace."
|
Looks for a function named 'help' in the subtask's namespace,
|
||||||
|
then a docstring on the task, then a docstring on the task ns."
|
||||||
([task-name]
|
([task-name]
|
||||||
(let [[task-ns task] (resolve-task task-name)]
|
(let [[task-ns task] (resolve-task task-name)]
|
||||||
(if task
|
(if task
|
||||||
|
@ -73,6 +81,24 @@
|
||||||
(let [aliases (merge main/aliases (:aliases project))]
|
(let [aliases (merge main/aliases (:aliases project))]
|
||||||
(help-for (aliases task-name task-name)))))
|
(help-for (aliases task-name task-name)))))
|
||||||
|
|
||||||
|
(defn help-for-subtask
|
||||||
|
"Returns a string containing help for a subtask.
|
||||||
|
Looks for a function named 'help-<subtask>' in the subtask's namespace,
|
||||||
|
using the subtask's docstring if the help function is not found."
|
||||||
|
([task-name subtask-name]
|
||||||
|
(if-let [subtask (resolve-subtask task-name subtask-name)]
|
||||||
|
(let [subtask-meta (meta subtask)
|
||||||
|
help-fn (ns-resolve (:ns subtask-meta)
|
||||||
|
(symbol (str "help-" subtask-name)))
|
||||||
|
arglists (get-arglists subtask)]
|
||||||
|
(str (or (and help-fn (help-fn)) (:doc subtask-meta))
|
||||||
|
(if (some seq arglists)
|
||||||
|
(str "\n\nArguments: " (pr-str arglists)))))
|
||||||
|
(format "Subtask: '%s %s' not found" task-name subtask-name)))
|
||||||
|
([project task-name subtask-name]
|
||||||
|
(let [aliases (merge main/aliases (:aliases project))]
|
||||||
|
(help-for-subtask (aliases task-name task-name) subtask-name))))
|
||||||
|
|
||||||
(defn help-summary-for [task-ns]
|
(defn help-summary-for [task-ns]
|
||||||
(try (let [task-name (last (.split (name task-ns) "\\."))
|
(try (let [task-name (last (.split (name task-ns) "\\."))
|
||||||
ns-summary (:doc (meta (find-ns (doto task-ns require))))
|
ns-summary (:doc (meta (find-ns (doto task-ns require))))
|
||||||
|
@ -86,18 +112,20 @@
|
||||||
(str task-ns " Problem loading: " (.getMessage e))))))
|
(str task-ns " Problem loading: " (.getMessage e))))))
|
||||||
|
|
||||||
(defn ^:no-project-needed ^:higher-order help
|
(defn ^:no-project-needed ^:higher-order help
|
||||||
"Display a list of tasks or help for a given task.
|
"Display a list of tasks or help for a given task or subtask.
|
||||||
|
|
||||||
Also provides readme, faq, tutorial, news, sample, profiles,
|
Also provides readme, faq, tutorial, news, sample, profiles,
|
||||||
deploying and copying info."
|
deploying and copying info."
|
||||||
;; TODO: explain partial aliases in specific help
|
;; TODO: explain partial aliases in specific help
|
||||||
|
([project task subtask] (println (or (static-help (str task "-" subtask))
|
||||||
|
(help-for-subtask project task subtask))))
|
||||||
([project task] (println (or (static-help task) (help-for project task))))
|
([project task] (println (or (static-help task) (help-for project task))))
|
||||||
([project]
|
([project]
|
||||||
(println "Leiningen is a tool for working with Clojure projects.\n")
|
(println "Leiningen is a tool for working with Clojure projects.\n")
|
||||||
(println "Several tasks are available:")
|
(println "Several tasks are available:")
|
||||||
(doseq [task-ns (main/tasks)]
|
(doseq [task-ns (main/tasks)]
|
||||||
(println (help-summary-for task-ns)))
|
(println (help-summary-for task-ns)))
|
||||||
(println "\nRun lein help $TASK for details.")
|
(println "\nRun `lein help $TASK` for details.")
|
||||||
;; TODO: show user-level aliases too
|
;; TODO: show user-level aliases too
|
||||||
(if-let [aliases (:aliases project)]
|
(if-let [aliases (:aliases project)]
|
||||||
(do
|
(do
|
||||||
|
|
|
@ -20,6 +20,11 @@
|
||||||
(is (re-find #"template\s+A meta-template for 'lein new' templates."
|
(is (re-find #"template\s+A meta-template for 'lein new' templates."
|
||||||
subtask-help))))
|
subtask-help))))
|
||||||
|
|
||||||
|
(deftest subtask-help-for-new-default
|
||||||
|
(let [subtask-help (help-for-subtask "new" "default")]
|
||||||
|
(is (re-find #"^A general project template." subtask-help))
|
||||||
|
(is (re-find #"Arguments: \(\[name\]\)" subtask-help))))
|
||||||
|
|
||||||
(deftest test-docstring-formatting
|
(deftest test-docstring-formatting
|
||||||
(is (= "This is an
|
(is (= "This is an
|
||||||
AWESOME command
|
AWESOME command
|
||||||
|
|
Loading…
Reference in a new issue