github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/cmd/oracle/oracle.el (about) 1 ;;; 2 ;;; Integration of the Go 'oracle' analysis tool into Emacs. 3 ;;; 4 ;;; To install the Go oracle, run: 5 ;;; % export GOROOT=... GOPATH=... 6 ;;; % go get golang.org/x/tools/cmd/oracle 7 ;;; % mv $GOPATH/bin/oracle $GOROOT/bin/ 8 ;;; 9 ;;; Load this file into Emacs and set go-oracle-scope to your 10 ;;; configuration. Then, find a file of Go source code, enable 11 ;;; go-oracle-mode, select an expression of interest, and press `C-c C-o d' 12 ;;; (for "describe") or run one of the other go-oracle-xxx commands. 13 ;;; 14 ;;; TODO(adonovan): simplify installation and configuration by making 15 ;;; oracle a subcommand of 'go tool'. 16 17 (require 'compile) 18 (require 'go-mode) 19 (require 'cl) 20 21 (defgroup go-oracle nil 22 "Options specific to the Go oracle." 23 :group 'go) 24 25 (defcustom go-oracle-command "oracle" 26 "The Go oracle command." 27 :type 'string 28 :group 'go-oracle) 29 30 (defcustom go-oracle-scope "" 31 "The scope of the analysis. See `go-oracle-set-scope'." 32 :type 'string 33 :group 'go-oracle) 34 35 (defvar go-oracle--scope-history 36 nil 37 "History of values supplied to `go-oracle-set-scope'.") 38 39 ;; Extend go-mode-map. 40 (let ((m go-mode-map)) 41 (define-key m (kbd "C-c C-o t") #'go-oracle-describe) ; t for type 42 (define-key m (kbd "C-c C-o f") #'go-oracle-freevars) 43 (define-key m (kbd "C-c C-o g") #'go-oracle-callgraph) 44 (define-key m (kbd "C-c C-o i") #'go-oracle-implements) 45 (define-key m (kbd "C-c C-o c") #'go-oracle-peers) ; c for channel 46 (define-key m (kbd "C-c C-o r") #'go-oracle-referrers) 47 (define-key m (kbd "C-c C-o d") #'go-oracle-definition) 48 (define-key m (kbd "C-c C-o p") #'go-oracle-pointsto) 49 (define-key m (kbd "C-c C-o s") #'go-oracle-callstack) 50 (define-key m (kbd "C-c C-o <") #'go-oracle-callers) 51 (define-key m (kbd "C-c C-o >") #'go-oracle-callees) 52 (define-key m (kbd "<f5>") #'go-oracle-describe) 53 (define-key m (kbd "<f6>") #'go-oracle-referrers)) 54 55 ;; TODO(dominikh): Rethink set-scope some. Setting it to a file is 56 ;; painful because it doesn't use find-file, and variables/~ aren't 57 ;; expanded. Setting it to an import path is somewhat painful because 58 ;; it doesn't make use of go-mode's import path completion. One option 59 ;; would be having two different functions, but then we can't 60 ;; automatically call it when no scope has been set. Also it wouldn't 61 ;; easily allow specifying more than one file/package. 62 (defun go-oracle-set-scope () 63 "Set the scope for the Go oracle, prompting the user to edit the 64 previous scope. 65 66 The scope specifies a set of arguments, separated by spaces. 67 It may be: 68 1) a set of packages whose main() functions will be analyzed. 69 2) a list of *.go filenames; they will treated like as a single 70 package (see #3). 71 3) a single package whose main() function and/or Test* functions 72 will be analyzed. 73 74 In the common case, this is similar to the argument(s) you would 75 specify to 'go build'." 76 (interactive) 77 (let ((scope (read-from-minibuffer "Go oracle scope: " 78 go-oracle-scope 79 nil 80 nil 81 'go-oracle--scope-history))) 82 (if (string-equal "" scope) 83 (error "You must specify a non-empty scope for the Go oracle")) 84 (setq go-oracle-scope scope))) 85 86 (defun go-oracle--run (mode &optional need-scope) 87 "Run the Go oracle in the specified MODE, passing it the 88 selected region of the current buffer. If NEED-SCOPE, prompt for 89 a scope if not already set. Process the output to replace each 90 file name with a small hyperlink. Display the result." 91 (if (not buffer-file-name) 92 (error "Cannot use oracle on a buffer without a file name")) 93 ;; It's not sufficient to save a modified buffer since if 94 ;; gofmt-before-save is on the before-save-hook, saving will 95 ;; disturb the selected region. 96 (if (buffer-modified-p) 97 (error "Please save the buffer before invoking go-oracle")) 98 (and need-scope 99 (string-equal "" go-oracle-scope) 100 (go-oracle-set-scope)) 101 (let* ((filename (file-truename buffer-file-name)) 102 (posflag (if (use-region-p) 103 (format "-pos=%s:#%d,#%d" 104 filename 105 (1- (go--position-bytes (region-beginning))) 106 (1- (go--position-bytes (region-end)))) 107 (format "-pos=%s:#%d" 108 filename 109 (1- (position-bytes (point)))))) 110 (env-vars (go-root-and-paths)) 111 (goroot-env (concat "GOROOT=" (car env-vars))) 112 (gopath-env (concat "GOPATH=" (mapconcat #'identity (cdr env-vars) ":")))) 113 (with-current-buffer (get-buffer-create "*go-oracle*") 114 (setq buffer-read-only nil) 115 (erase-buffer) 116 (insert "Go Oracle\n") 117 (let ((args (append (list go-oracle-command nil t nil posflag mode) 118 (split-string go-oracle-scope " " t)))) 119 ;; Log the command to *Messages*, for debugging. 120 (message "Command: %s:" args) 121 (message nil) ; clears/shrinks minibuffer 122 123 (message "Running oracle...") 124 ;; Use dynamic binding to modify/restore the environment 125 (let ((process-environment (list* goroot-env gopath-env process-environment))) 126 (apply #'call-process args))) 127 (insert "\n") 128 (compilation-mode) 129 (setq compilation-error-screen-columns nil) 130 131 ;; Hide the file/line info to save space. 132 ;; Replace each with a little widget. 133 ;; compilation-mode + this loop = slooow. 134 ;; TODO(adonovan): have oracle give us JSON 135 ;; and we'll do the markup directly. 136 (let ((buffer-read-only nil) 137 (p 1)) 138 (while (not (null p)) 139 (let ((np (compilation-next-single-property-change p 'compilation-message))) 140 (if np 141 (when (equal (line-number-at-pos p) (line-number-at-pos np)) 142 ;; Using a fixed width greatly improves readability, so 143 ;; if the filename is longer than 20, show ".../last/17chars.go". 144 ;; This usually includes the last segment of the package name. 145 ;; Don't show the line or column number. 146 (let* ((loc (buffer-substring p np)) ; "/home/foo/go/pkg/file.go:1:2-3:4" 147 (i (search ":" loc))) 148 (setq loc (cond 149 ((null i) "...") 150 ((>= i 17) (concat "..." (substring loc (- i 17) i))) 151 (t (substring loc 0 i)))) 152 ;; np is (typically) the space following ":"; consume it too. 153 (put-text-property p np 'display (concat loc ":"))) 154 (goto-char np) 155 (insert " ") 156 (incf np))) ; so we don't get stuck (e.g. on a panic stack dump) 157 (setq p np))) 158 (message nil)) 159 160 (let ((w (display-buffer (current-buffer)))) 161 (balance-windows) 162 (shrink-window-if-larger-than-buffer w) 163 (set-window-point w (point-min)))))) 164 165 (defun go-oracle-callees () 166 "Show possible callees of the function call at the current point." 167 (interactive) 168 (go-oracle--run "callees" t)) 169 170 (defun go-oracle-callers () 171 "Show the set of callers of the function containing the current point." 172 (interactive) 173 (go-oracle--run "callers" t)) 174 175 (defun go-oracle-callgraph () 176 "Show the callgraph of the current program." 177 (interactive) 178 (go-oracle--run "callgraph" t)) 179 180 (defun go-oracle-callstack () 181 "Show an arbitrary path from a root of the call graph to the 182 function containing the current point." 183 (interactive) 184 (go-oracle--run "callstack" t)) 185 186 (defun go-oracle-definition () 187 "Show the definition of the selected identifier." 188 (interactive) 189 (go-oracle--run "definition")) 190 191 (defun go-oracle-describe () 192 "Describe the selected syntax, its kind, type and methods." 193 (interactive) 194 (go-oracle--run "describe")) 195 196 (defun go-oracle-pointsto () 197 "Show what the selected expression points to." 198 (interactive) 199 (go-oracle--run "pointsto" t)) 200 201 (defun go-oracle-implements () 202 "Describe the 'implements' relation for types in the package 203 containing the current point." 204 (interactive) 205 (go-oracle--run "implements")) 206 207 (defun go-oracle-freevars () 208 "Enumerate the free variables of the current selection." 209 (interactive) 210 (go-oracle--run "freevars")) 211 212 (defun go-oracle-peers () 213 "Enumerate the set of possible corresponding sends/receives for 214 this channel receive/send operation." 215 (interactive) 216 (go-oracle--run "peers" t)) 217 218 (defun go-oracle-referrers () 219 "Enumerate all references to the object denoted by the selected 220 identifier." 221 (interactive) 222 (go-oracle--run "referrers")) 223 224 (defun go-oracle-whicherrs () 225 "Show globals, constants and types to which the selected 226 expression (of type 'error') may refer." 227 (interactive) 228 (go-oracle--run "whicherrs" t)) 229 230 (provide 'go-oracle)