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)