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:
Tobias Crawley 2012-10-05 14:05:44 -04:00
parent 102dcadf9f
commit 6998dd6929
3 changed files with 41 additions and 5 deletions

View file

@ -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
`:subtasks` metadata on a task defn which contains a vector of subtask
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

View file

@ -42,7 +42,9 @@
(string/join "\n" (concat ["\n\nSubtasks available:"]
(for [[subtask doc] subtasks]
(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]
(try (let [task-ns (doto (symbol (str "leiningen." task-name)) require)
@ -51,13 +53,19 @@
(catch java.io.FileNotFoundException e
[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]
(if-let [resource (io/resource (format "leiningen/help/%s" name))]
(slurp resource)))
(defn help-for
"Help for a task is stored in its docstring, or if that's not present
in its namespace."
"Returns a string containing help for a task.
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]
(let [[task-ns task] (resolve-task task-name)]
(if task
@ -73,6 +81,24 @@
(let [aliases (merge main/aliases (:aliases project))]
(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]
(try (let [task-name (last (.split (name task-ns) "\\."))
ns-summary (:doc (meta (find-ns (doto task-ns require))))
@ -86,18 +112,20 @@
(str task-ns " Problem loading: " (.getMessage e))))))
(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,
deploying and copying info."
;; 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]
(println "Leiningen is a tool for working with Clojure projects.\n")
(println "Several tasks are available:")
(doseq [task-ns (main/tasks)]
(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
(if-let [aliases (:aliases project)]
(do

View file

@ -20,6 +20,11 @@
(is (re-find #"template\s+A meta-template for 'lein new' templates."
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
(is (= "This is an
AWESOME command