From 8bdb42fe15d498aeb620f11ef3a69a529b0654a4 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 25 Sep 2018 22:45:13 -0400 Subject: [PATCH] lang/ruby: major refactor + Robe is now to be started manually. + Adds more keybindings for robe (including ' 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. --- modules/lang/ruby/autoload.el | 36 +++++-- modules/lang/ruby/config.el | 190 ++++++++++++++++++++-------------- modules/lang/ruby/packages.el | 16 ++- 3 files changed, 150 insertions(+), 92 deletions(-) diff --git a/modules/lang/ruby/autoload.el b/modules/lang/ruby/autoload.el index 980fdd2ce..77bc34756 100644 --- a/modules/lang/ruby/autoload.el +++ b/modules/lang/ruby/autoload.el @@ -1,5 +1,8 @@ ;;; lang/ruby/autoload.el -*- lexical-binding: t; -*- +(defvar +ruby-version-cache (make-hash-table :test 'equal) + "TODO") + ;;;###autoload (defun +ruby|cleanup-robe-servers () "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 pipenv, unless those tools have modified the PATH that Emacs picked up when you started it." - (unless (executable-find "ruby") - (user-error "Couldn't find ruby executable in PATH")) - (with-temp-buffer - (let ((p (call-process "ruby" nil (current-buffer) nil "--version")) - (output (string-trim (buffer-string)))) - (unless (zerop p) - (user-error "ruby --version failed: %s" output)) - (nth 1 (split-string output " " t))))) + (condition-case _ + (let ((version-str (car (process-lines "ruby" "--version")))) + (puthash (doom-project-root) + (format "Ruby %s" (cadr (split-string version-str " "))) + +ruby-version-cache)) + (error "Ruby"))) + + +;; +;; 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)))) diff --git a/modules/lang/ruby/config.el b/modules/lang/ruby/config.el index a824657c5..1c55609ff 100644 --- a/modules/lang/ruby/config.el +++ b/modules/lang/ruby/config.el @@ -1,10 +1,9 @@ ;;; lang/ruby/config.el -*- lexical-binding: t; -*- -(defvar +ruby-mode-line-indicator - '("Ruby" (+ruby-version (" " +ruby-version))) +(defvar +ruby-mode-line-indicator '("" +ruby--version) "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.") @@ -12,33 +11,66 @@ ;; Packages (def-package! enh-ruby-mode - :mode "\\.rb\\'" - :mode "\\.rake\\'" - :mode "\\.gemspec\\'" - :mode "\\.\\(?:pry\\|irb\\)rc\\'" - :mode "/\\(?:Gem\\|Cap\\|Vagrant\\|Rake\\|Pod\\|Puppet\\|Berks\\)file\\'" + :mode ("\\.\\(?:pry\\|irb\\)rc\\'" . +ruby|init) + :mode ("\\.\\(?:rb\\|rake\\|rabl\\|ru\\|builder\\|gemspec\\|jbuilder\\|thor\\)\\'" . +ruby|init) + :mode ("/\\(?:Berks\\|Cap\\|Gem\\|Guard\\|Pod\\|Puppet\\|Rake\\|Thor\\|Vagrant\\)file\\'" . +ruby|init) + :preface + (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 - (set-electric! 'enh-ruby-mode :words '("else" "end" "elsif")) - (set-repl-handler! 'enh-ruby-mode #'inf-ruby) ; `inf-ruby' + (set-env! "RBENV_ROOT") + (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 ;; `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))) + ;; 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 (defun +ruby|adjust-mode-line () (setq mode-name +ruby-mode-line-indicator)) (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)) -(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 @@ -52,22 +84,45 @@ :nv "P" #'rubocop-autocorrect-project)) -(def-package! robe - :hook (enh-ruby-mode . robe-mode) - :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)) +;; +;; Package & Ruby version management +(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 :mode ("/\\.rspec\\'" . text-mode) @@ -80,64 +135,41 @@ ;; Rake (("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) 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 - (map! :map (rspec-mode-map rspec-verifiable-mode-map) + (map! :map rspec-mode-map :localleader :prefix "t" :n "r" #'rspec-rerun :n "a" #'rspec-verify-all :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 - :after enh-ruby-mode - :config - (map! :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)) - - ;; Evil integration - (when (featurep! :feature evil +everywhere) - (add-hook! '(rspec-mode-hook rspec-verifiable-mode-hook) - #'evil-normalize-keymaps))) +(def-package! minitest + :defer t + :config + (when (featurep! :feature evil) + (add-hook 'minitest-mode-hook #'evil-normalize-keymaps)) + (map! :map minitest-mode-map + :localleader + :prefix "t" + :n "r" #'minitest-rerun + :n "a" #'minitest-verify-all + :n "s" #'minitest-verify-single + :n "v" #'minitest-verify)) -(def-package! company-inf-ruby - :when (featurep! :completion company) - :after inf-ruby - :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) - +;; Evil integration +(when (featurep! :feature evil +everywhere) + (add-hook! 'rspec-mode-hook #'evil-normalize-keymaps)) diff --git a/modules/lang/ruby/packages.el b/modules/lang/ruby/packages.el index ca9d05264..c0b98741e 100644 --- a/modules/lang/ruby/packages.el +++ b/modules/lang/ruby/packages.el @@ -4,18 +4,24 @@ ;; requires ruby ruby-lint (package! enh-ruby-mode) -(package! rubocop) -(package! inf-ruby) -(package! rspec-mode) (package! yard-mode) -(package! rake) +(package! inf-ruby) (package! robe) -(package! bundler) (when (featurep! :completion company) (package! company-inf-ruby)) +;; Project tools +(package! bundler) +(package! rake) +(package! rubocop) + +;; Version management (when (featurep! +rbenv) (package! rbenv)) (when (featurep! +rvm) (package! rvm)) + +;; Testing frameworks +(package! rspec-mode) +(package! minitest)