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
176 lines (157 loc) · 7.55 KB
/
Copy pathhelp.clj
File metadata and controls
176 lines (157 loc) · 7.55 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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
(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]
[bultitude.core :as b]))
(def docstrings
(memoize
(fn []
(apply hash-map
(mapcat (juxt second b/doc-from-ns-form)
(b/namespace-forms-on-classpath :prefix "leiningen"))))))
(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- clean-static-help
"Returns a string containing help content. Removes doctoc comments if they
are present."
[help-text]
(let [doctoc-text "<!-- END doctoc generated TOC please keep comment here to allow auto update -->"]
(if (string/includes? help-text doctoc-text)
(string/triml (second (string/split help-text (re-pattern doctoc-text))))
help-text)))
(defn- static-help [name]
(if-let [resource (io/resource (format "leiningen/help/%s" name))]
(clean-static-help (slurp resource))))
(declare help-for)
(defn- alias-help
"Returns a string containing help for an alias, or nil if the string is not an
alias."
[aliases task-name]
(if (aliases task-name)
(let [alias-expansion (aliases task-name)
explanation (-> alias-expansion meta :doc)]
(cond explanation (str task-name ": " explanation)
(string? alias-expansion) (str
(format
(str "'%s' is an alias for '%s',"
" which has following help doc:\n")
task-name alias-expansion)
(help-for alias-expansion))
:no-explanation-or-string (str task-name " is an alias, expands to "
alias-expansion)))))
(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))))))
(main/abort (format "Task: '%s' not found" task-name)))))
([project task-name]
(let [aliases (merge main/aliases (:aliases project))]
(or (alias-help aliases task-name)
(help-for 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)))))
(main/abort (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) "\\."))]
;; 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 (task-ns (docstrings))
(first (.split (help-for {} task-name) "\n")))))
(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 or help for a specific task.")
(println " -v, --version Print Leiningen's version.")
(when-let [aliases (:aliases project)]
(println "\nThese aliases are available:")
(doseq [[k v] aliases]
(if-let [explanation (-> v meta :doc)]
(println (str k ": " explanation))
(println (str k ", expands to " v)))))
(println "\nSee also: readme, faq, tutorial, news, sample, profiles,"
"deploying, gpg,\nmixed-source, templates, and copying.")))