Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add capability to jump to MRI C method sources #91

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,15 @@ rvm automatically.
* Essential features work with JRuby and the latest Rubinius.
JRuby has longer startup, Rubinius is noticeably slower at runtime
([rubinius/rubinius#2390](https://github.com/rubinius/rubinius/issues/2390)).
* Jumping to the sources of methods written in C works if you use MRI
and have MRI sources and GNU Global on your machine (see the
**Notes** section for more details).
* Mostly works on MS Windows, with minor glitches.
* Using Emacs trunk is currently recommended (24.3.50) for better recognition
of context at point.

## Notes

* We can't jump to methods defined in C (such as most of the core classes).
To read their docs, install `pry-doc` or add it to your Gemfile.
* We can't jump to lazily defined methods, such as `model.column` or `find_by_`
`ActiveRecord` methods, before they've been called. This is treatable, but low
priority.
Expand All @@ -127,6 +128,18 @@ rvm automatically.
unexpected "Method not found" errors, check if you have an older one.
* We may get the context wrong for code inside a block if the method
it's passed to uses `instance_eval` or `instance_exec`.
* To read the docs for methods defined in C (such as most of the core
classes), install `pry-doc` or add it to your Gemfile.
* We can (optionally) jump to methods defined in C if you:
* Have [GNU Global](https://gnu.org/s/global) installed, have the
Ruby C sources on your machine, and have tagged them with Global.
* Set the value of the variables `robe-use-global-p`,
`robe-ruby-source-directory`, and `robe-global-command` to
appropriate values
* If you don't feel like downloading the MRI sources or setting up GNU
Global, you can safely ignore this feature and stick with the
default behavior, where you can only read the documentation for
methods defined in C.

## TODO

Expand Down
69 changes: 65 additions & 4 deletions robe.el
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,57 @@ project."
(defun robe-const-p (thing)
(let (case-fold-search) (string-match "\\`\\([A-Z]\\|::\\)" thing)))

(defun robe-list-c-method-files ()
(let* ((default-directory robe-ruby-source-directory)
(cmd (concat robe-global-command " -r 'rb_define_method'"))
(output (shell-command-to-string cmd))
(files (split-string output)))
files))

(defun robe-determine-c-file-for (fun-or-var)
(let* ((input (robe-jump-prompt fun-or-var))
(type (downcase (first input)))
(file (concat type ".c")))
file))

(defun robe-jump-to-c-source (fun-or-var &optional debug)
"Jump to the C source location where FUN-OR-VAR is defined.
When a DEBUG argument is passed, print debugging messages."
(interactive)
(let ((default-directory robe-ruby-source-directory)
(results nil)
(c-files (robe-list-c-method-files))
(c-filename (robe-determine-c-file-for fun-or-var))
(here (point)))
(save-excursion
(loop for file in c-files do
;; The following code is adapted from find-func.el in the
;; Emacs sources
(if (string-equal file c-filename)
(progn
(xref-push-marker-stack)
(with-current-buffer (find-file-noselect file)
(goto-char (point-min))
(let ((regex
(concat
"rb_define_\\(singleton_\\)?\\(method\\)([a-z_]+, +\""
(regexp-quote fun-or-var)
"\", +\\([a-z_]+\\)")))
(if (re-search-forward regex nil t)
(let ((it (match-string-no-properties 3)))
(progn
(when debug
(message "Found %s in file %s: %s, jumping..." fun-or-var file it))
(ggtags-find-tag 'definition (shell-quote-argument it))))
(progn
(when debug
(let ((msg (concat
"Can't find source for method '%s'"
" in file '%s' using regex '%s'")))
(message msg fun-or-var file regex)))
(message "Can't find source for method '%s'"
fun-or-var file regex)))))))))))

(defun robe-jump (arg)
"Jump to the method or module at point, prompt for module or file if necessary.
If invoked with a prefix or no symbol at point, delegate to `robe-ask'."
Expand All @@ -232,7 +283,7 @@ If invoked with a prefix or no symbol at point, delegate to `robe-ask'."
((robe-const-p thing)
(robe-jump-to-module thing))
(t
(robe-jump-to (robe-jump-prompt thing))))))
(robe-jump-to (robe-jump-prompt thing) nil thing)))))

(defun robe-jump-prompt (thing)
(let* ((alist (robe-decorate-modules (robe-jump-modules thing))))
Expand Down Expand Up @@ -334,11 +385,13 @@ If invoked with a prefix or no symbol at point, delegate to `robe-ask'."
(list module (when instance t) method-name))
(list nil t nil))))

(defun robe-jump-to (spec &optional pop-to-buffer)
(defun robe-jump-to (spec &optional pop-to-buffer thing)
(let ((file (robe-spec-file spec)))
(if (null file)
(when (yes-or-no-p "Can't jump to a C method. Show documentation? ")
(robe-show-doc spec))
(if robe-use-global-p
(robe-jump-to-c-source thing)
(when (yes-or-no-p "Can't jump to a C method. Show documentation? ")
(robe-show-doc spec)))
(robe-find-file file pop-to-buffer)
(goto-char (point-min))
(forward-line (1- (robe-spec-line spec)))
Expand Down Expand Up @@ -707,6 +760,14 @@ Only works with Rails, see e.g. `rinari-console'."
(define-key map (kbd "C-c C-k") 'robe-rails-refresh)
map))

(defvar robe-use-global-p nil
"If GNU GLOBAL is installed, use it to jump to Ruby C sources.")

(defvar robe-ruby-source-directory nil "Location of Ruby C Sources.")

(defvar robe-global-command "global"
"Location of the GNU GLOBAL executable.")

;;;###autoload
(define-minor-mode robe-mode
"Improved navigation for Ruby.
Expand Down