-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathbundler.el
259 lines (225 loc) · 8.24 KB
/
bundler.el
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
;;; bundler.el --- Interact with Bundler from Emacs
;; Copyright (c) 2011 Tobias Svensson <tob@tobiassvensson.co.uk>
;; Author: Tobias Svensson <tob@tobiassvensson.co.uk>
;; URL: http://github.com/endofunky/bundler.el
;; Keywords: bundler ruby
;; Created: 31 Dec 2011
;; Version: 1.1.1
;; Package-Requires: ((inf-ruby "2.1") (cl-lib "0.5"))
;; This file is NOT part of GNU Emacs.
;;; Commentary:
;; Interact with Bundler from Emacs.
;;
;; 1) bundle-open
;;
;; Wraps 'bundle open' which, if the given gem is installed and has been
;; required correctly, will open the gem's source directory with dired.
;;
;; 2) bundle-console
;;
;; Starts an inferior ruby process in the context of the current bundle
;; using 'bundle console' (requires inf-ruby to be installed).
;;
;; 3) bundle-install, bundle-update, bundle-check
;;
;; Runs the corresponding Bundler command with async-shell-command and
;; *Bundler* as the target buffer. This exists so the output won't mess
;; with the default buffer used by M-& and async-shell-command.
;;; Install
;; $ cd ~/.emacs.d/vendor
;; $ git clone git://github.com/endofunky/bundler.el.git
;;
;; In your emacs config:
;;
;; (add-to-list 'load-path "~/.emacs.d/vendor/bundler.el")
;; (require 'bundler)
;;; License:
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Code:
(require 'cl-lib)
(require 'inf-ruby)
;;;###autoload
(defun bundle-open (gem-name)
"Queries for a gem name and opens the location of the gem in dired."
(interactive (list (completing-read "Bundled gem: " (bundle-list-gems-cached))))
(if (= (length gem-name) 0)
(message "No gem name given.")
(let ((gem-location (bundle-gem-location gem-name)))
(cond
((eq gem-location 'no-gemfile)
(message "Could not find Gemfile"))
(gem-location
(dired gem-location))
(t
(message "Gem '%s' not found" gem-name))))))
;;;###autoload
(defun bundle-console ()
"Run an inferior Ruby process in the context of the current bundle."
(interactive)
(run-ruby "bundle console"))
;;;###autoload
(defun bundle-check ()
"Run bundle check for the current bundle."
(interactive)
(bundle-command "bundle check"))
;;;###autoload
(defun bundle-install ()
"Run bundle install for the current bundle."
(interactive)
(bundle-command "bundle install"))
;;;###autoload
(defun bundle-update (&optional update-cmd-args)
"Run bundle update for the current bundle."
(interactive "P")
(let ((command "bundle update"))
;; For customization of the command with prefix arg.
(setq command (if update-cmd-args
(read-string "Run: " (concat command " "))
command))
(bundle-command command)))
;;;###autoload
(defun bundle-exec (command)
(interactive "sBundle Exec: ")
(run-bundled-command command))
;;;###autoload
(defun bundle-gemfile (&optional gemfile)
"Set BUNDLE_GEMFILE environment variable."
(interactive
(list
(let ((default-p
(let ((gemfile-dir (bundle-locate-gemfile)))
(if (not gemfile-dir)
"Gemfile"
(concat gemfile-dir "Gemfile")))))
(read-string (format "Gemfile (%s): " default-p)
default-p nil default-p))))
(if gemfile
(if (file-readable-p gemfile)
(progn
(setq bundle-gem-list-cache (make-hash-table))
(setenv "BUNDLE_GEMFILE" gemfile)
(message "BUNDLE_GEMFILE set to: %s." gemfile))
(message "Warning: couldn't read file \"%s\". BUNDLE_GEMFILE unchanged." gemfile))
(setenv "BUNDLE_GEMFILE")))
;;;###autoload
(defun bundle-outdated ()
"List installed gems with newer versions available."
(interactive)
(bundle-command "bundle outdated"))
;;;###autoload
(defun bundle-major-version ()
"Returns the bundler major version. If no version is available it returns nil."
(let ((output (shell-command-to-string "bundle version")))
(when (string-match "bundler version \\([0-9]+\\)" output)
(string-to-number (match-string 1 output)))))
;;;###autoload
(defun bundle-show ()
"Shows all gems that are part of the bundle, or the path to a given gem."
(interactive)
(let* ((ver (bundle-major-version))
(cmd (if (and ver (< 2 ver)
"bundle show"
"bundle list"))))
(bundle-command cmd)))
;;;###autoload
(defun bundle-version ()
"Prints version information."
(interactive)
(shell-command "bundle version"))
(defun bundle-command (cmd)
"Run cmd in an async buffer."
(async-shell-command cmd "*Bundler*"))
(defun run-bundled-command (cmd &rest args)
"Run bundle exec for the given command, optionally with args"
(interactive)
(let (command)
(setq command
(if args
(concat "bundle exec " cmd " "(mapconcat 'identity args " "))
(concat "bundle exec " cmd)))
(bundle-command command)))
(defun bundle-gem-location (gem-name)
"Returns the location of the given gem, or 'no-gemfile if the
Gemfile could not be found, or nil if the Gem could not be
found."
(let* ((ver (bundle-major-version))
(cmd (if (and ver (< 2 ver))
"bundle show"
"bundle info --path"))
(bundler-stdout
(shell-command-to-string
(format "%s %s" cmd (shell-quote-argument gem-name))))
(remote (file-remote-p default-directory)))
(cond
((string-match "Could not locate Gemfile" bundler-stdout)
'no-gemfile)
((string-match "Could not find " bundler-stdout)
nil)
(t
(concat remote
(replace-regexp-in-string
"Resolving dependencies...\\|The dependency .* will be unused by .*$\\|\n" ""
bundler-stdout)
"/")))))
(defvar bundle-gem-list-cache
(make-hash-table)
"Holds a hash table of gem lists per directory.")
(cl-defun bundle-locate-gemfile (&optional (dir default-directory))
(let ((has-gemfile (directory-files dir nil "^Gemfile$"))
(is-root (equal dir "/")))
(cond
(has-gemfile dir)
(is-root
(print (format
"No Gemfile found in either %s or any parent directory!"
default-directory))
nil)
((bundle-locate-gemfile (expand-file-name ".." dir))))))
(defun bundle-list-gems-cached ()
(let* ((gemfile-dir (bundle-locate-gemfile))
(gem-list (gethash gemfile-dir bundle-gem-list-cache)))
(if (not gemfile-dir)
nil
(unless gem-list
(print (format "Don't have directory %s in cache yet, updating." gemfile-dir))
(setq gem-list (bundle-list-gems))
(puthash gemfile-dir gem-list bundle-gem-list-cache))
gem-list)))
(defun bundle-list-gems ()
(save-excursion
(let* ((cmd "bundle list")
(bundle-out (shell-command-to-string cmd))
(bundle-lines (split-string bundle-out "\n")))
(defun parse-bundle-list-line (line)
(cond
((string-match "^ \\* \\([^\s]+\\).*$" line)
(match-string 1 line))
((string-match "Could not \\(find\\|locate\\)" line)
(message line) nil)
((string-match "Gems included by the bundle:\\|^ *$" line)
nil)
(t
(message "Warning: couldn't parse line from \"%s\":\n%s"
cmd line)
nil)))
(remq nil (mapcar 'parse-bundle-list-line bundle-lines)))))
(defun bundle-list-gem-paths ()
(save-excursion
(let* ((cmd "bundle list --paths")
(bundle-out (shell-command-to-string cmd)))
(split-string bundle-out "\n"))))
(provide 'bundler)
;;; bundler.el ends here.