File: ess-r-xref.el

package info (click to toggle)
ess 18.10.2-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 6,620 kB
  • sloc: lisp: 30,716; sh: 1,503; makefile: 381; asm: 170
file content (139 lines) | stat: -rw-r--r-- 5,421 bytes parent folder | download | duplicates (2)
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