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
|
;;; ess-r-xref.el --- An xref backend for R. -*- lexical-binding: t -*-
;;
;; Author: Aaron Jacobs
;; Created: 21 January 2018
;; Maintainer: ESS-core <ESS-core@r-project.org>
;;
;; Keywords: languages, statistics, xref
;; Package-Requires: ((emacs "25"))
;;
;; This file is part of ESS.
;;
;; This file 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 2, or (at your option)
;; any later version.
;;
;; This file 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.
;;
;; A copy of the GNU General Public License is available at
;; https://www.r-project.org/Licenses/
;;; Commentary:
;; This file contains an xref backend for `R-mode'.
;;; Code:
(when (>= emacs-major-version 25)
(require 'subr-x)
(require 'xref))
;; Cludge to silence the byte compiler until we drop support for Emacs 24.
(declare-function xref-make "xref")
(declare-function xref-make-buffer-location "xref")
(declare-function xref-make-file-location "xref")
(require 'ess-inf)
(require 'ess-utils)
(require 'ess-r-package)
(require 'ess-tracebug)
;; Silence the byte compiler. OK because this file is only loaded by ess-r-mode.
(declare-function inferior-ess-r-force "ess-r-mode")
(defvar ess-r-xref-pkg-sources nil
"Alist of R package->directory associations.
This variable is used as a cache of package->directory
associations, but could be used by the users for a more refined
control of package locations than `ess-r-package-library-paths'.")
(defun ess-r-xref-backend ()
"An `xref-backend-functions' implementation for `R-mode'.
R's xref backend searches for `ess-r-package-library-paths' when
srcrefs point to temporary locations."
'ess-r)
(cl-defmethod xref-backend-identifier-at-point ((_backend (eql ess-r)))
(let ((sym (ess-symbol-at-point)))
(when sym
(symbol-name sym))))
(cl-defmethod xref-backend-definitions ((_backend (eql ess-r)) symbol)
(let ((xref (ess-r-xref--xref symbol)))
(when xref
(list xref))))
(cl-defmethod xref-backend-apropos ((_backend (eql ess-r)))
;; Not yet supported.
nil)
(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql ess-r)))
(inferior-ess-r-force)
(ess-get-words-from-vector ".ess_all_functions()\n"))
(defun ess-r-xref--srcref (symbol)
(inferior-ess-r-force)
;; Look for `symbol' inside the package namespace
(let* ((pkg (ess-r-package-name))
(pkg (if pkg
(concat "\"" pkg "\"")
"NULL")))
(with-current-buffer (ess-command (format ".ess_srcref(\"%s\", %s)\n" symbol pkg))
(goto-char (point-min))
(when (re-search-forward "(" nil 'noerror)
(goto-char (match-beginning 0))
(read (current-buffer))))))
(defun ess-r-xref--pkg-srcfile (symbol r-src-file)
"Look in the source directory of the R package containing symbol SYMBOL for R-SRC-FILE."
(let* ((env-name (ess-string-command (format ".ess_fn_pkg(\"%s\")\n" symbol)))
(pkg (if (string-equal env-name "")
(error "Can't find package for symbol %s" symbol)
env-name))
(dir (or (assoc-default pkg ess-r-xref-pkg-sources)
(cond ((stringp ess-r-package-library-paths)
(expand-file-name pkg ess-r-package-library-paths))
((listp ess-r-package-library-paths)
(cl-loop for d in ess-r-package-library-paths
for p = (expand-file-name pkg d)
when (file-exists-p p) return p))
(t (error "Invalid value of `ess-r-package-library-paths'")))))
(file (when dir (expand-file-name r-src-file dir))))
(when file
(unless (file-readable-p file)
(error "Can't read %s" file))
;; Cache package's source directory.
(unless (assoc pkg ess-r-xref-pkg-sources)
(push `(,pkg . ,dir) ess-r-xref-pkg-sources))
file)))
(defun ess-r-xref--xref (symbol)
"Create an xref for the source file reference of R symbol SYMBOL."
(let ((ref (ess-r-xref--srcref symbol)))
(when ref
(let ((file (nth 0 ref))
(line (nth 1 ref))
(col (nth 2 ref)))
(or
;; 1) Result of ESS evaluation
(let* ((ess-ref (gethash file ess--srcrefs))
(ess-buff (when ess-ref (ess--dbg-find-buffer (car ess-ref)))))
(when ess-buff
;; FIXME: this breaks when eval is on larger spans than function
(xref-make symbol (xref-make-buffer-location ess-buff (nth 2 ess-ref)))))
;; 2) Actual file location
(when (file-readable-p file)
(xref-make symbol (xref-make-file-location file line col)))
;; 3) Temporary sources - truncate and locate in ess-r-package-library-paths
(when (string-match "/\\(R/.*\\)$" file)
(let ((pkg-file (ess-r-xref--pkg-srcfile symbol (match-string 1 file))))
(when pkg-file
(xref-make symbol (xref-make-file-location
(expand-file-name pkg-file) line col))))))))))
(provide 'ess-r-xref)
;;; ess-r-xref.el ends here
|