lang/ruby: major refactor

+ Robe is now to be started manually.
+ Adds more keybindings for robe (including <localleader> ' for
  robe-start).
+ Use ruby-mode if ruby isn't available (e.g. editing ruby files on
  remote systems), enh-ruby-mode otherwise.
+ Added rake, bundler, rvm, rbenv and minitest packages
+ Added $RBENV_ROOT/shims to exec-path. This should fix rbenv support
  for the ruby version display in the modeline.
This commit is contained in:
Henrik Lissner 2018-09-25 22:45:13 -04:00
parent b91a8f0d2f
commit 8bdb42fe15
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
3 changed files with 150 additions and 92 deletions

View file

@ -1,5 +1,8 @@
;;; lang/ruby/autoload.el -*- lexical-binding: t; -*- ;;; lang/ruby/autoload.el -*- lexical-binding: t; -*-
(defvar +ruby-version-cache (make-hash-table :test 'equal)
"TODO")
;;;###autoload ;;;###autoload
(defun +ruby|cleanup-robe-servers () (defun +ruby|cleanup-robe-servers ()
"Clean up dangling inf robe processes if there are no more `enh-ruby-mode' "Clean up dangling inf robe processes if there are no more `enh-ruby-mode'
@ -22,11 +25,28 @@ ruby executable found in your PATH).
This is not necessarily aware of env management tools like virtualenv, pyenv or This is not necessarily aware of env management tools like virtualenv, pyenv or
pipenv, unless those tools have modified the PATH that Emacs picked up when you pipenv, unless those tools have modified the PATH that Emacs picked up when you
started it." started it."
(unless (executable-find "ruby") (condition-case _
(user-error "Couldn't find ruby executable in PATH")) (let ((version-str (car (process-lines "ruby" "--version"))))
(with-temp-buffer (puthash (doom-project-root)
(let ((p (call-process "ruby" nil (current-buffer) nil "--version")) (format "Ruby %s" (cadr (split-string version-str " ")))
(output (string-trim (buffer-string)))) +ruby-version-cache))
(unless (zerop p) (error "Ruby")))
(user-error "ruby --version failed: %s" output))
(nth 1 (split-string output " " t)))))
;;
;; Hooks
;;;###autoload
(defun +ruby|update-version (&rest _)
"Update `+ruby--version' by consulting `+ruby-version' function."
(setq +ruby--version
(or (gethash (doom-project-root) +python-version-cache)
(+ruby-version))))
;;;###autoload
(defun +ruby|update-version-in-all-buffers ()
"Update `+ruby--version' in all `enh-ruby-mode' buffers."
(dolist (buffer (doom-buffers-in-mode 'enh-ruby-mode))
(setq +ruby-version-cache (clrhash +ruby-version-cache))
(with-current-buffer buffer
(+ruby|update-version))))

View file

@ -1,10 +1,9 @@
;;; lang/ruby/config.el -*- lexical-binding: t; -*- ;;; lang/ruby/config.el -*- lexical-binding: t; -*-
(defvar +ruby-mode-line-indicator (defvar +ruby-mode-line-indicator '("" +ruby--version)
'("Ruby" (+ruby-version (" " +ruby-version)))
"Format for the ruby version/env indicator in the mode-line.") "Format for the ruby version/env indicator in the mode-line.")
(defvar-local +ruby-version nil (defvar-local +ruby--version nil
"The ruby version in the current buffer.") "The ruby version in the current buffer.")
@ -12,33 +11,66 @@
;; Packages ;; Packages
(def-package! enh-ruby-mode (def-package! enh-ruby-mode
:mode "\\.rb\\'" :mode ("\\.\\(?:pry\\|irb\\)rc\\'" . +ruby|init)
:mode "\\.rake\\'" :mode ("\\.\\(?:rb\\|rake\\|rabl\\|ru\\|builder\\|gemspec\\|jbuilder\\|thor\\)\\'" . +ruby|init)
:mode "\\.gemspec\\'" :mode ("/\\(?:Berks\\|Cap\\|Gem\\|Guard\\|Pod\\|Puppet\\|Rake\\|Thor\\|Vagrant\\)file\\'" . +ruby|init)
:mode "\\.\\(?:pry\\|irb\\)rc\\'" :preface
:mode "/\\(?:Gem\\|Cap\\|Vagrant\\|Rake\\|Pod\\|Puppet\\|Berks\\)file\\'" (after! ruby-mode (require 'enh-ruby-mode))
(defun +ruby|init ()
"Enable `enh-ruby-mode' if ruby is available, otherwise `ruby-mode'."
(if (executable-find "ruby")
(enh-ruby-mode)
(ruby-mode)))
:config :config
(set-electric! 'enh-ruby-mode :words '("else" "end" "elsif")) (set-env! "RBENV_ROOT")
(set-repl-handler! 'enh-ruby-mode #'inf-ruby) ; `inf-ruby' (set-electric! '(ruby-mode enh-ruby-mode) :words '("else" "end" "elsif"))
(set-repl-handler! '(ruby-mode enh-ruby-mode) #'inf-ruby)
(after! company-dabbrev-code
(add-to-list 'company-dabbrev-code-modes 'enh-ruby-mode nil #'eq)
(add-to-list 'company-dabbrev-code-modes 'ruby-mode nil #'eq))
(after! dtrt-indent (after! dtrt-indent
;; `dtrt-indent' supports ruby-mode. Make it aware of enh-ruby-mode ;; `dtrt-indent' supports ruby-mode. Make it aware of enh-ruby-mode
(add-to-list 'dtrt-indent-hook-mapping-list '(enh-ruby-mode ruby enh-ruby-indent-level))) (add-to-list 'dtrt-indent-hook-mapping-list '(enh-ruby-mode ruby enh-ruby-indent-level)))
;; so class and module pairs work ;; so class and module pairs work
(setq-hook! 'enh-ruby-mode-hook sp-max-pair-length 6) (setq-hook! (ruby-mode enh-ruby-mode) sp-max-pair-length 6)
;; Add ruby version string to the major mode in the modeline ;; Add ruby version string to the major mode in the modeline
(defun +ruby|adjust-mode-line () (defun +ruby|adjust-mode-line ()
(setq mode-name +ruby-mode-line-indicator)) (setq mode-name +ruby-mode-line-indicator))
(add-hook 'enh-ruby-mode-hook #'+ruby|adjust-mode-line) (add-hook 'enh-ruby-mode-hook #'+ruby|adjust-mode-line)
(defun +ruby|update-version (&rest _)
(setq +ruby-version (+ruby-version)))
(+ruby|update-version)
(add-hook 'enh-ruby-mode-hook #'+ruby|update-version)) (add-hook 'enh-ruby-mode-hook #'+ruby|update-version))
(def-package! yard-mode :hook enh-ruby-mode) (def-package! robe
:hook (enh-ruby-mode . robe-mode)
:config
(set-repl-handler! 'enh-ruby-mode #'robe-start)
(set-company-backend! 'enh-ruby-mode 'company-robe)
(set-lookup-handlers! 'enh-ruby-mode
:definition #'robe-jump
:documentation #'robe-doc)
(map! :localleader
:map robe-mode-map
:n "'" #'robe-start
;; robe mode specific
:n "h" #'robe-doc
:n "rr" #'robe-rails-refresh
;; inf-enh-ruby-mode
:prefix "s"
:n "f" #'ruby-send-definition
:n "F" #'ruby-send-definition-and-go
:n "r" #'ruby-send-region
:n "R" #'ruby-send-region-and-go
:n "i" #'ruby-switch-to-inf))
;; NOTE Must be loaded before `robe-mode'
(def-package! yard-mode
:hook (ruby-mode enh-ruby-mode))
(def-package! rubocop (def-package! rubocop
@ -52,22 +84,45 @@
:nv "P" #'rubocop-autocorrect-project)) :nv "P" #'rubocop-autocorrect-project))
(def-package! robe ;;
:hook (enh-ruby-mode . robe-mode) ;; Package & Ruby version management
:init
;; robe-start errors if you hit no.
(defun +ruby|init-robe ()
(when (executable-find "ruby")
(cl-letf (((symbol-function #'yes-or-no-p) (lambda (_) t)))
(save-window-excursion
(with-demoted-errors "ROBE ERROR: %s"
(robe-start)))
(when (robe-running-p)
(add-hook 'kill-buffer-hook #'+ruby|cleanup-robe-servers nil t)))))
(add-hook 'enh-ruby-mode-hook #'+ruby|init-robe)
:config
(set-company-backend! 'robe-mode 'company-robe))
(def-package! rake
:defer t
:init
(setq rake-cache-file (concat doom-cache-dir "rake.cache"))
(map! :after enh-ruby-mode
:localleader
:map enh-ruby-mode-map
:prefix "k"
:n "k" #'rake
:n "r" #'rake-rerun
:n "R" #'rake-regenerate-cache
:n "f" #'rake-find-task))
(def-package! bundler
:defer t
:init
(map! :after enh-ruby-mode
:localleader
:map enh-ruby-mode-map
:prefix "b"
:n "c" #'bundle-check
:n "C" #'bundle-console
:n "i" #'bundle-install
:n "u" #'bundle-update
:n "e" #'bundle-exec
:n "o" #'bundle-open))
;; `rvm'
(setq rspec-use-rvm t)
(after! rbenv
(add-to-list 'exec-path (expand-file-name "shims" rbenv-installation-dir)))
;;
;; Testing frameworks
(def-package! rspec-mode (def-package! rspec-mode
:mode ("/\\.rspec\\'" . text-mode) :mode ("/\\.rspec\\'" . text-mode)
@ -80,64 +135,41 @@
;; Rake ;; Rake
(("task" "namespace") () "end"))) (("task" "namespace") () "end")))
(unless (featurep! :feature evil) (if (featurep! :feature evil)
(add-hook 'rspec-mode-hook #'evil-normalize-keymaps)
(setq rspec-verifiable-mode-keymap (make-sparse-keymap) (setq rspec-verifiable-mode-keymap (make-sparse-keymap)
rspec-mode-keymap (make-sparse-keymap))) rspec-mode-keymap (make-sparse-keymap)))
(defun +ruby*init-appropriate-rspec-mode ()
"TODO"
(cond ((rspec-buffer-is-spec-p)
(rspec-mode +1))
((let ((proot (doom-project-root 'nocache)))
(or (file-directory-p (expand-file-name "spec" proot))
(file-exists-p (expand-file-name ".rspec" proot))))
(rspec-verifiable-mode +1))))
(advice-add #'rspec-enable-appropriate-mode :override #'+ruby*init-appropriate-rspec-mode)
:config :config
(map! :map (rspec-mode-map rspec-verifiable-mode-map) (map! :map rspec-mode-map
:localleader :localleader
:prefix "t" :prefix "t"
:n "r" #'rspec-rerun :n "r" #'rspec-rerun
:n "a" #'rspec-verify-all :n "a" #'rspec-verify-all
:n "s" #'rspec-verify-single :n "s" #'rspec-verify-single
:n "v" #'rspec-verify) :n "v" #'rspec-verify
:n "c" #'rspec-verify-continue
:n "e" #'rspec-toggle-example-pendingness
:n "f" #'rspec-verify-method
:n "l" #'rspec-run-last-failed
:n "m" #'rspec-verify-matching
:n "t" #'rspec-toggle-spec-and-target-find-example
:n "T" #'rspec-toggle-spec-and-target))
(def-package! bundler (def-package! minitest
:after enh-ruby-mode :defer t
:config :config
(map! :localleader (when (featurep! :feature evil)
:map enh-ruby-mode-map (add-hook 'minitest-mode-hook #'evil-normalize-keymaps))
:prefix "b" (map! :map minitest-mode-map
:n "c" #'bundle-check :localleader
:n "C" #'bundle-console :prefix "t"
:n "i" #'bundle-install :n "r" #'minitest-rerun
:n "u" #'bundle-update :n "a" #'minitest-verify-all
:n "e" #'bundle-exec :n "s" #'minitest-verify-single
:n "o" #'bundle-open)) :n "v" #'minitest-verify))
;; Evil integration
(when (featurep! :feature evil +everywhere)
(add-hook! '(rspec-mode-hook rspec-verifiable-mode-hook)
#'evil-normalize-keymaps)))
(def-package! company-inf-ruby ;; Evil integration
:when (featurep! :completion company) (when (featurep! :feature evil +everywhere)
:after inf-ruby (add-hook! 'rspec-mode-hook #'evil-normalize-keymaps))
:config (set-company-backend! 'inf-ruby-mode 'company-inf-ruby))
;;
;; Version managers
(def-package! rbenv
:when (featurep! +rbenv)
:after enh-ruby-mode
:config (set-env! "RBENV_ROOT"))
(def-package! rvm
:when (featurep! +rvm)
:after enh-ruby-mode)

View file

@ -4,18 +4,24 @@
;; requires ruby ruby-lint ;; requires ruby ruby-lint
(package! enh-ruby-mode) (package! enh-ruby-mode)
(package! rubocop)
(package! inf-ruby)
(package! rspec-mode)
(package! yard-mode) (package! yard-mode)
(package! rake) (package! inf-ruby)
(package! robe) (package! robe)
(package! bundler)
(when (featurep! :completion company) (when (featurep! :completion company)
(package! company-inf-ruby)) (package! company-inf-ruby))
;; Project tools
(package! bundler)
(package! rake)
(package! rubocop)
;; Version management
(when (featurep! +rbenv) (when (featurep! +rbenv)
(package! rbenv)) (package! rbenv))
(when (featurep! +rvm) (when (featurep! +rvm)
(package! rvm)) (package! rvm))
;; Testing frameworks
(package! rspec-mode)
(package! minitest)