Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

437 line
16 KiB

5 年之前
  1. ;;; rvm.el --- Emacs integration for rvm
  2. ;; Copyright (C) 2010-2011 Yves Senn
  3. ;; Author: Yves Senn <yves.senn@gmx.ch>
  4. ;; URL: http://www.emacswiki.org/emacs/RvmEl
  5. ;; Package-Version: 20150402.1442
  6. ;; Version: 1.4.0
  7. ;; Created: 5 April 2010
  8. ;; Keywords: ruby rvm
  9. ;; EmacsWiki: RvmEl
  10. ;; This file is NOT part of GNU Emacs.
  11. ;;; License:
  12. ;; This program is free software; you can redistribute it and/or modify
  13. ;; it under the terms of the GNU General Public License as published by
  14. ;; the Free Software Foundation; either version 3, or (at your option)
  15. ;; any later version.
  16. ;;
  17. ;; This program is distributed in the hope that it will be useful,
  18. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. ;; GNU General Public License for more details.
  21. ;;
  22. ;; You should have received a copy of the GNU General Public License
  23. ;; along with GNU Emacs; see the file COPYING. If not, write to the
  24. ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  25. ;; Boston, MA 02110-1301, USA.
  26. ;;; Commentary:
  27. ;; M-x rvm-use-default prepares the current Emacs session to use
  28. ;; the default ruby configured with rvm.
  29. ;; M-x rvm-use allows you to switch the current session to the ruby
  30. ;; implementation of your choice. You can also change the active gemset.
  31. ;;; Compiler support:
  32. (eval-when-compile (require 'cl))
  33. (defvar eshell-path-env)
  34. (defvar persp-mode)
  35. (defvar perspectives-hash)
  36. (declare-function persp-switch "perspective" (name))
  37. ;;; Code:
  38. (defcustom rvm-executable
  39. (or (executable-find "rvm")
  40. (and (file-readable-p "~/.rvm/bin/rvm") "~/.rvm/bin/rvm")
  41. (and (file-readable-p "/usr/local/rvm/bin/rvm") "/usr/local/rvm/bin/rvm"))
  42. "Location of RVM executable."
  43. :group 'rvm
  44. :type 'file)
  45. (defcustom rvm-configuration-file-name
  46. ".rvmrc"
  47. "RVM configuration file name"
  48. :group 'rvm
  49. :type 'string)
  50. (defcustom rvm-verbose t
  51. "If true, RVM will print messages for various tasks."
  52. :group 'rvm
  53. :type 'boolean)
  54. (defvar rvm-configuration-ruby-version-file-name
  55. ".ruby-version"
  56. "Ruby version configuration file name")
  57. (defvar rvm-configuration-ruby-gemset-file-name
  58. ".ruby-gemset"
  59. "Ruby version configuration file name")
  60. (defvar rvm-configuration-gemfile-file-name
  61. "Gemfile"
  62. "Gemfile file name")
  63. (defcustom rvm-interactive-completion-function
  64. (if ido-mode 'ido-completing-read 'completing-read)
  65. "The function which is used by rvm.el to interactivly complete user input"
  66. :group 'rvm
  67. :type 'function)
  68. (defcustom rvm-interactive-find-file-function
  69. (if ido-mode 'ido-find-file 'find-file)
  70. "The function which is used by rvm.el to interactivly open files"
  71. :group 'rvm
  72. :type 'function)
  73. (defvar rvm--current-ruby nil
  74. "Current active Ruby version.")
  75. (defvar rvm--current-gemset nil
  76. "Current active gemset.")
  77. (defvar rvm--gemset-default "global"
  78. "the default gemset per ruby interpreter")
  79. (defvar rvm--gemset-separator "@"
  80. "character that separates the ruby version from the gemset.")
  81. (defvar rvm--current-ruby-binary-path nil
  82. "reflects the path to the current 'ruby' executable.
  83. This path gets added to the PATH variable and the exec-path list.")
  84. (defvar rvm--current-gem-binary-path nil
  85. "reflects the path to the current 'rubygems' executables.
  86. This path gets added to the PATH variable and the exec-path list.")
  87. (defvar rvm--info-option-regexp "\s+\\(.+?\\):\s+\"\\(.+?\\)\""
  88. "regular expression to parse the options from rvm info")
  89. (defvar rvm--list-ruby-regexp "\s*\\(=?[>\*]\\)?\s*\\(.+?\\)\s*\\[\\(.+\\)\\]\s*$"
  90. "regular expression to parse the ruby version from the 'rvm list' output")
  91. (defvar rvm--gemset-list-filter-regexp "^\\(gemsets for\\|Gemset '\\)"
  92. "regular expression to filter the output of rvm gemset list")
  93. (defvar rvm--gemset-list-regexp "\s*\\(=>\\)?\s*\\(.+?\\)\s*$"
  94. "regular expression to parse the gemset from the 'rvm gemset list' output")
  95. (defvar rvm--gemfile-parse-ruby-regexp-as-comment "\\#ruby=\\(.+\\)"
  96. "regular expression to parse the ruby version from the Gemfile as comment")
  97. (defvar rvm--gemfile-parse-ruby-regexp-as-directive "ruby [\'\"]\\(.+\\)[\'\"]"
  98. "regular expression to parse the ruby version from the Gemfile as directive")
  99. (defvar rvm--gemfile-parse-gemset-regexp "#ruby-gemset=\\(.+\\)"
  100. "regular expression to parse the ruby gemset from the Gemfile")
  101. (defvar rvm--rvmrc-parse-regexp (concat "\\(?:^rvm\s+\\(?:use\s+\\|\\)\\|environment_id=\"\\)\s*"
  102. "\\(?:--.+\s\\)*" ;; Flags
  103. "\\([^"
  104. rvm--gemset-separator
  105. "\n]+\\)\\(?:"
  106. rvm--gemset-separator
  107. "\\([^\"\s\n]+\\)\\)?\\(?:\"\\|\\)")
  108. "regular expression to parse the .rvmrc files inside project directories.
  109. the first group matches the ruby-version and the second group is the gemset.
  110. when no gemset is set, the second group is nil")
  111. ;; Support Code
  112. ;; Put with other utils
  113. ;; From http://www.emacswiki.org/emacs/ElispCookbook
  114. (defun rvm--chomp (str)
  115. "Chomp leading and tailing whitespace from STR."
  116. (let ((s (if (symbolp str) (symbol-name str) str)))
  117. (replace-regexp-in-string "\\(^[[:space:]\n]*\\|[[:space:]\n]*$\\)" "" s)))
  118. (defun rvm--message (format-string &rest objects)
  119. "Like `message', but will only print if `rvm-verbose' is true."
  120. (when rvm-verbose
  121. (apply 'message (cons format-string objects))))
  122. ;; Application Code
  123. ;;;###autoload
  124. (defun rvm-use-default ()
  125. "use the rvm-default ruby as the current ruby version"
  126. (interactive)
  127. (when (rvm-working-p)
  128. (rvm-use (rvm--ruby-default) rvm--gemset-default)))
  129. ;;;###autoload
  130. (defun rvm-activate-corresponding-ruby ()
  131. "activate the corresponding ruby version for the file in the current buffer.
  132. This function searches for an .rvmrc file and activates the configured ruby.
  133. If no .rvmrc file is found, the default ruby is used insted."
  134. (interactive)
  135. (when (rvm-working-p)
  136. (let ((config-file-path nil)
  137. (config-gemset-file-path nil)
  138. (rvmrc-info (or (rvm--load-info-rvmrc) (rvm--load-info-ruby-version) (rvm--load-info-gemfile))))
  139. (if rvmrc-info (rvm-use (first rvmrc-info) (second rvmrc-info))
  140. (rvm-use-default)))))
  141. (defun rvm--load-info-rvmrc (&optional path)
  142. (let ((config-file-path (rvm--locate-file rvm-configuration-file-name path)))
  143. (if config-file-path
  144. (rvm--rvmrc-read-version config-file-path)
  145. nil)))
  146. (defun rvm--load-info-ruby-version (&optional path)
  147. (let ((config-file-path (rvm--locate-file rvm-configuration-ruby-version-file-name path))
  148. (gemset-file-path (rvm--locate-file rvm-configuration-ruby-gemset-file-name path)))
  149. (if config-file-path
  150. (list (rvm--chomp (rvm--get-string-from-file config-file-path))
  151. (if gemset-file-path
  152. (rvm--chomp (rvm--get-string-from-file gemset-file-path))
  153. rvm--gemset-default))
  154. nil)))
  155. (defun rvm--load-info-gemfile (&optional path)
  156. (let ((config-file-path (rvm--locate-file rvm-configuration-gemfile-file-name path)))
  157. (if config-file-path
  158. (rvm--gemfile-read-version config-file-path)
  159. nil)))
  160. ;;;###autoload
  161. (defun rvm-use (new-ruby new-gemset)
  162. "switch the current ruby version to any ruby, which is installed with rvm"
  163. (interactive
  164. (let* ((picked-ruby (rvm--completing-read "Ruby Version: "
  165. (rvm/list)))
  166. (picked-gemset (rvm--completing-read "Gemset: "
  167. (rvm/gemset-list picked-ruby))))
  168. (list picked-ruby picked-gemset)))
  169. (when (rvm-working-p)
  170. (let* ((new-ruby-with-gemset (rvm--ruby-gemset-string new-ruby new-gemset))
  171. (ruby-info (rvm/info new-ruby-with-gemset))
  172. (new-ruby-binary (cdr (assoc "ruby" ruby-info)))
  173. (new-ruby-gemhome (cdr (assoc "GEM_HOME" ruby-info)))
  174. (new-ruby-gempath (cdr (assoc "GEM_PATH" ruby-info))))
  175. (setq rvm--current-ruby new-ruby)
  176. (setq rvm--current-gemset new-gemset)
  177. (rvm--set-ruby (file-name-directory new-ruby-binary))
  178. (rvm--set-gemhome new-ruby-gemhome new-ruby-gempath new-gemset))
  179. (rvm--message (concat "Ruby: " new-ruby " Gemset: " new-gemset))))
  180. ;;;###autoload
  181. (defun rvm-open-gem (gemhome)
  182. (interactive (list (rvm--emacs-gemhome)))
  183. (when (rvm-working-p)
  184. (let* ((gems-dir (concat gemhome "/gems/"))
  185. (gem-name (rvm--completing-read "Gem: "
  186. (directory-files gems-dir nil "^[^.]")))
  187. (gem-dir (concat gems-dir gem-name)))
  188. (when (and (featurep 'perspective) persp-mode)
  189. (let ((initialize (not (gethash gem-name perspectives-hash))))
  190. (persp-switch gem-name)))
  191. (rvm--find-file gem-dir))))
  192. (defun rvm-activate-ruby-for (path &optional callback)
  193. "Activate Ruby for PATH.
  194. If CALLBACK is specified, active Ruby for PATH only in that
  195. function."
  196. (let* ((path (directory-file-name path))
  197. (prev-ruby rvm--current-ruby)
  198. (prev-gemset rvm--current-gemset)
  199. (rvmrc-info
  200. (or
  201. (rvm--load-info-rvmrc path)
  202. (rvm--load-info-ruby-version path)
  203. (rvm--load-info-gemfile path))))
  204. (apply 'rvm-use rvmrc-info)
  205. (when callback
  206. (unwind-protect
  207. (funcall callback)
  208. (rvm-use prev-ruby prev-gemset)))))
  209. ;;;; TODO: take buffer switching into account
  210. (defun rvm-autodetect-ruby ()
  211. (interactive)
  212. (when (rvm-working-p)
  213. (add-hook 'ruby-mode-hook 'rvm-activate-corresponding-ruby)
  214. (rvm--message "rvm.el is now autodetecting the ruby version")))
  215. (defun rvm-autodetect-ruby-stop ()
  216. (interactive)
  217. (when (rvm-working-p)
  218. (remove-hook 'ruby-mode-hook 'rvm-activate-corresponding-ruby)
  219. (rvm--message "stopped rvm.el from autodetecting ruby versions")))
  220. (defun rvm/list (&optional default-ruby)
  221. (let ((rubies (rvm--call-process "list" (when default-ruby "default")))
  222. (start 0)
  223. (parsed-rubies '())
  224. (current-ruby '()))
  225. (while (string-match rvm--list-ruby-regexp rubies start)
  226. (let ((ruby-version (match-string 2 rubies))
  227. (ruby-platform (match-string 3 rubies))
  228. (ruby-current-version (match-string 1 rubies)))
  229. (add-to-list 'current-ruby ruby-current-version)
  230. (if ruby-current-version (add-to-list 'parsed-rubies ruby-version)
  231. (add-to-list 'parsed-rubies ruby-version t))
  232. (setq start (match-end 0))))
  233. parsed-rubies))
  234. (defun rvm/gemset-list (ruby-version)
  235. (let* ((gemset-result (rvm--call-process "gemset" "list_all"))
  236. (gemset-lines (split-string gemset-result "\n"))
  237. (parsed-gemsets (list))
  238. (ruby-current-version nil))
  239. (loop for gemset in gemset-lines do
  240. (let ((filtered-gemset (string-match rvm--gemset-list-filter-regexp gemset)))
  241. (if filtered-gemset
  242. (if (string-match ruby-version gemset)
  243. (setq ruby-current-version ruby-version)
  244. (setq ruby-current-version nil)))
  245. (if (and (> (length gemset) 0)
  246. ruby-current-version
  247. (not filtered-gemset)
  248. (string-match rvm--gemset-list-regexp gemset))
  249. ;; replace-regexp is to handle default gemset which is returned with
  250. ;; wrapping parens but is referred to without them in rvm use command
  251. (add-to-list 'parsed-gemsets (replace-regexp-in-string "^(\\|)$" "" (match-string 2 gemset)) t))))
  252. parsed-gemsets))
  253. (defun rvm/info (&optional ruby-version)
  254. (let ((info (rvm--call-process "info" ruby-version))
  255. (start 0)
  256. (parsed-info '()))
  257. (when (not info) (error "The ruby version: %s is not installed" ruby-version))
  258. (while (string-match rvm--info-option-regexp info start)
  259. (let ((info-key (match-string 1 info))
  260. (info-value (match-string 2 info)))
  261. (add-to-list 'parsed-info (cons info-key info-value))
  262. (setq start (match-end 0))))
  263. parsed-info))
  264. (defun rvm--string-trim (string)
  265. (replace-regexp-in-string "^\\s-*\\|\\s-*$" "" string))
  266. (defun rvm--ruby-gemset-string (ruby-version gemset)
  267. (if (rvm--default-gemset-p gemset) ruby-version
  268. (concat ruby-version rvm--gemset-separator gemset)))
  269. (defun rvm--completing-read (prompt options)
  270. (let ((selected (funcall rvm-interactive-completion-function prompt options)))
  271. (rvm--string-trim selected)))
  272. (defun rvm--find-file (directory)
  273. (let ((default-directory directory))
  274. (call-interactively rvm-interactive-find-file-function)))
  275. (defun rvm--emacs-ruby-binary ()
  276. rvm--current-ruby-binary-path)
  277. (defun rvm--emacs-gemhome ()
  278. (getenv "GEM_HOME"))
  279. (defun rvm--emacs-gempath ()
  280. (getenv "GEM_PATH"))
  281. (defun rvm--change-path (current-binary-var new-binaries)
  282. (let ((current-binaries-for-path
  283. (mapconcat 'identity (eval current-binary-var) ":"))
  284. (new-binaries-for-path (mapconcat 'identity new-binaries ":")))
  285. (if (and (eval current-binary-var)
  286. (not (string= (first (eval current-binary-var)) "/bin")))
  287. (progn
  288. (setenv "PATH" (replace-regexp-in-string
  289. (regexp-quote current-binaries-for-path)
  290. new-binaries-for-path
  291. (getenv "PATH")))
  292. (dolist (binary (eval current-binary-var))
  293. (setq exec-path (remove binary exec-path))))
  294. (setenv "PATH" (concat new-binaries-for-path ":" (getenv "PATH"))))
  295. (dolist (binary new-binaries)
  296. (add-to-list 'exec-path binary))
  297. (setq eshell-path-env (getenv "PATH"))
  298. (set current-binary-var new-binaries)))
  299. (defun rvm--set-ruby (ruby-binary)
  300. (rvm--change-path 'rvm--current-ruby-binary-path (list ruby-binary)))
  301. (defun rvm--locate-file (file-name &optional path)
  302. "searches the directory tree for an given file. Returns nil if the file was not found."
  303. (let ((directory (locate-dominating-file (or path (expand-file-name (or buffer-file-name ""))) file-name)))
  304. (when directory (expand-file-name file-name directory))))
  305. (defun rvm--get-string-from-file (file-path)
  306. (with-temp-buffer
  307. (insert-file-contents file-path)
  308. (buffer-string)))
  309. (defun rvm--rvmrc-read-version (path-to-rvmrc)
  310. (rvm--rvmrc-parse-version (rvm--get-string-from-file path-to-rvmrc)))
  311. (defun rvm--gemfile-read-version (path-to-gemfile)
  312. (rvm--gemfile-parse-version (rvm--get-string-from-file path-to-gemfile)))
  313. (defun rvm--rvmrc-parse-version (rvmrc-line)
  314. (let ((rvmrc-without-comments (replace-regexp-in-string "#.*$" "" rvmrc-line)))
  315. (when (string-match rvm--rvmrc-parse-regexp rvmrc-without-comments)
  316. (list (rvm--string-trim (match-string 1 rvmrc-without-comments))
  317. (rvm--string-trim (or (match-string 2 rvmrc-without-comments) rvm--gemset-default))))))
  318. (defun rvm--gemfile-parse-version (gemfile-line)
  319. (let ((ruby-version (when (or (string-match rvm--gemfile-parse-ruby-regexp-as-comment gemfile-line)
  320. (string-match rvm--gemfile-parse-ruby-regexp-as-directive gemfile-line))
  321. (match-string 1 gemfile-line)))
  322. (ruby-gemset (when (string-match rvm--gemfile-parse-gemset-regexp gemfile-line)
  323. (match-string 1 gemfile-line))))
  324. (if ruby-version
  325. (list ruby-version (or ruby-gemset rvm--gemset-default))
  326. nil)))
  327. (defun rvm--gem-binary-path-from-gem-path (gempath)
  328. (let ((gem-paths (split-string gempath ":")))
  329. (mapcar (lambda (path) (concat path "/bin")) gem-paths)))
  330. (defun rvm--set-gemhome (gemhome gempath gemset)
  331. (if (and gemhome gempath gemset)
  332. (progn
  333. (setenv "GEM_HOME" gemhome)
  334. (setenv "GEM_PATH" gempath)
  335. (setenv "BUNDLE_PATH" gemhome)
  336. (rvm--change-path 'rvm--current-gem-binary-path (rvm--gem-binary-path-from-gem-path gempath)))
  337. (setenv "GEM_HOME" "")
  338. (setenv "GEM_PATH" "")
  339. (setenv "BUNDLE_PATH" "")))
  340. (defun rvm--ruby-default ()
  341. (car (rvm/list t)))
  342. (defun rvm-working-p ()
  343. (and rvm-executable (file-exists-p rvm-executable)))
  344. (defun rvm--default-gemset-p (gemset)
  345. (string= gemset rvm--gemset-default))
  346. (defun rvm--call-process (&rest args)
  347. (with-temp-buffer
  348. (let* ((success (apply 'call-process rvm-executable nil t nil
  349. (delete nil args)))
  350. (output (buffer-substring-no-properties
  351. (point-min) (point-max))))
  352. (if (= 0 success)
  353. output
  354. (rvm--message output)))))
  355. (defun rvm-gem-install (gem)
  356. "Install GEM into the currently active RVM Gemset."
  357. (interactive "SGem Install: ")
  358. (shell-command (format "%s install %s&" ; & executes async
  359. (concat (first rvm--current-ruby-binary-path) "/gem") gem))
  360. (pop-to-buffer "*Async Shell Command*"))
  361. (provide 'rvm)
  362. ;;; rvm.el ends here