forked from technomancy/leiningen
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhelp.clj
More file actions
138 lines (123 loc) · 5.95 KB
/
Copy pathhelp.clj
File metadata and controls
138 lines (123 loc) · 5.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
(ns leiningen.help
"Display a list of tasks or help for a given task."
(:require [clojure.string :as string]
[clojure.java.io :as io]
[leiningen.core.main :as main]))
(def ^{:private true
:doc "Width of task name column in list of tasks produced by help task."}
task-name-column-width 20)
(defn- get-arglists [task]
(for [args (or (:help-arglists (meta task)) (:arglists (meta task)))]
(vec (remove #(= 'project %) args))))
(def ^:private help-padding 3)
(defn- formatted-docstring [command docstring padding]
(apply str
(replace
{\newline
(apply str
(cons \newline (repeat (+ padding (count command)) \space)))}
docstring)))
(defn- formatted-help [command docstring longest-key-length]
(let [padding (+ longest-key-length help-padding (- (count command)))]
(format (str "%1s" (apply str (repeat padding \space)) "%2s")
command
(formatted-docstring command docstring padding))))
(defn- get-subtasks-and-docstrings-for [task]
(into {}
(map (fn [subtask]
(let [m (meta subtask)]
[(str (:name m)) (first (.split (:doc m "") "\n"))]))
(:subtasks (meta task)))))
(defn subtask-help-for
[task-ns task]
(if-let [subtasks (seq (get-subtasks-and-docstrings-for task))]
(let [longest-key-length (apply max (map count (keys subtasks)))]
(string/join "\n" (concat ["\n\nSubtasks available:"]
(for [[subtask doc] subtasks]
(formatted-help subtask doc
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)
task (ns-resolve task-ns (symbol task-name))]
[task-ns task])
(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
"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
(let [help-fn (ns-resolve task-ns 'help)]
(str (or (and (not= task-ns 'leiningen.help) help-fn (help-fn))
(:doc (meta task))
(:doc (meta (find-ns task-ns))))
(subtask-help-for task-ns task)
(if (some seq (get-arglists task))
(str "\n\nArguments: " (pr-str (get-arglists task))))))
(format "Task: '%s' not found" task-name))))
([project task-name]
(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))))
first-line (first (.split (help-for {} task-name) "\n"))]
;; Use first line of task docstring if ns metadata isn't present
(str task-name (apply str (repeat (- task-name-column-width
(count task-name)) " "))
(or ns-summary first-line)))
(catch Throwable e
(binding [*out* *err*]
(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 or subtask.
Also provides readme, faq, tutorial, news, sample, profiles,
deploying, mixed-source, templates, and copying info."
([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 "\nGlobal Options:")
(println " -o Run a task offline.")
(println " -U Run a task after forcing update of snapshots.")
(println " -h, --help Print this help.")
(println " -v, --version Print Leiningen's version.")
(when-let [aliases (:aliases project)]
(println "\nAliases:")
(doseq [[k v] aliases]
(println (str k " " v))))
(println "\nSee also: readme, faq, tutorial, news, sample, profiles,"
"deploying, gpg, mixed-source, templates, and copying.")))