summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHilko Bengen <bengen@debian.org>2017-04-02 11:35:01 +0200
committerHilko Bengen <bengen@debian.org>2017-04-02 11:35:10 +0200
commitbf9ce4c27346c921b8b70b9650bc74e77a9ff00e (patch)
treed8c2dcf10ceff79fd2e4917538cae98036c4618c
parent468815f7da22ed21c824c8edd60e121e7ebfba70 (diff)
parent99b40584eb81185d410b46742b6bd8016e6d75a8 (diff)
Merge tag 'upstream/1.5.0'
Upstream version 1.5.0
-rw-r--r--AUTHORS14
-rw-r--r--go-guru.el551
-rw-r--r--go-mode.el312
-rw-r--r--go-rename.el108
4 files changed, 799 insertions, 186 deletions
diff --git a/AUTHORS b/AUTHORS
index eefc714..df4d988 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,9 +1,13 @@
Aaron France <aaron.l.france@gmail.com>
Alan Donovan <adonovan@google.com>
+Alan Donovan <alan@alandonovan.net>
+Andrew Gerrand <adg@golang.org>
Austin Clements <aclements@csail.mit.edu>
Ben Fried <ben.fried@gmail.com>
Bobby Powers <bobbypowers@gmail.com>
Charles Lee <zombie.fml@gmail.com>
+Daniel Morsing <daniel.morsing@gmail.com>
+Dominik Honnef <dominik.honnef@gmail.com>
Dominik Honnef <dominik@honnef.co>
Eric Eisner <eric.d.eisner@gmail.com>
Erin Keenan <erinok@gmail.com>
@@ -15,12 +19,16 @@ Iwasaki Yudai <yudai.iwasaki@ntti3.com>
James Aguilar <jaguilar@google.com>
Jan Newmarch <jan.newmarch@gmail.com>
Jean-Marc Eurin <jmeurin@google.com>
+Jeff Hodges <jeff@somethingsimilar.com>
Juergen Hoetzel <juergen@archlinux.org>
Kevin Ballard <kevin@sb.org>
+Konstantin Shaposhnikov <k.shaposhnikov@gmail.com>
Lowe Thiderman <lowe.thiderman@gmail.com>
Mark Petrovic <mark.petrovic@xoom.com>
Mats Lidell <mats.lidell@cag.se>
+Matt Armstrong <marmstrong@google.com>
Peter Kleiweg <pkleiweg@xs4all.nl>
+Philipp Stephani <phst@google.com>
Quan Yong Zhai <qyzhai@gmail.com>
Robert Zaremba <robert.zaremba@zoho.com>
Rui Ueyama <ruiu@google.com>
@@ -30,7 +38,13 @@ Rüdiger Sonderfeld <ruediger@c-plusplus.net>
Sameer Ajmani <sameer@golang.org>
Scott Lawrence <bytbox@gmail.com>
Steven Elliot Harris <seharris@gmail.com>
+Syohei YOSHIDA <syohex@gmail.com>
Taiki Sugawara <buzz.taiki@gmail.com>
Viacheslav Chimishuk <vchimishuk@yandex-team.ru>
Will <will@glozer.net>
Yasuyuki Oka <yasuyk@gmail.com>
+Yutian Li <hotpxless@gmail.com>
+Zac Bergquist <zbergquist99@gmail.com>
+kostya-sh <kostya-sh@users.noreply.github.com>
+nverno <noah.v.peart@gmail.com>
+nwidger <niels.widger@gmail.com>
diff --git a/go-guru.el b/go-guru.el
new file mode 100644
index 0000000..cab5eb3
--- /dev/null
+++ b/go-guru.el
@@ -0,0 +1,551 @@
+;;; go-guru.el --- Integration of the Go 'guru' analysis tool into Emacs.
+
+;; Copyright 2016 The Go Authors. All rights reserved.
+;; Use of this source code is governed by a BSD-style
+;; license that can be found in the LICENSE file.
+
+;; Version: 0.1
+;; Package-Requires: ((go-mode "1.3.1") (cl-lib "0.5"))
+;; Keywords: tools
+
+;;; Commentary:
+
+;; To enable the Go guru in Emacs, use this command to download,
+;; build, and install the tool in $GOROOT/bin:
+;;
+;; $ go get golang.org/x/tools/cmd/guru
+;;
+;; Verify that the tool is on your $PATH:
+;;
+;; $ guru -help
+;; Go source code guru.
+;; Usage: guru [flags] <mode> <position>
+;; ...
+;;
+;; Then copy this file to a directory on your `load-path',
+;; and add this to your ~/.emacs:
+;;
+;; (require 'go-guru)
+;;
+;; Inside a buffer of Go source code, select an expression of
+;; interest, and type `C-c C-o d' (for "describe") or run one of the
+;; other go-guru-xxx commands. If you use `menu-bar-mode', these
+;; commands are available from the Guru menu.
+;;
+;; To enable identifier highlighting mode in a Go source buffer, use:
+;;
+;; (go-guru-hl-identifier-mode)
+;;
+;; To enable it automatically in all Go source buffers,
+;; add this to your ~/.emacs:
+;;
+;; (add-hook 'go-mode-hook #'go-guru-hl-identifier-mode)
+;;
+;; See http://golang.org/s/using-guru for more information about guru.
+
+;;; Code:
+
+(require 'compile)
+(require 'easymenu)
+(require 'go-mode)
+(require 'json)
+(require 'simple)
+(require 'cl-lib)
+
+(defgroup go-guru nil
+ "Options specific to the Go guru."
+ :group 'go)
+
+(defcustom go-guru-command "guru"
+ "The Go guru command."
+ :type 'string
+ :group 'go-guru)
+
+(defcustom go-guru-scope ""
+ "The scope of the analysis. See `go-guru-set-scope'."
+ :type 'string
+ :group 'go-guru)
+
+(defvar go-guru--scope-history
+ nil
+ "History of values supplied to `go-guru-set-scope'.")
+
+(defcustom go-guru-build-tags '()
+ "Build tags passed to guru."
+ :type '(repeat string)
+ :group 'go-guru)
+
+(defface go-guru-hl-identifier-face
+ '((t (:inherit highlight)))
+ "Face used for highlighting identifiers in `go-guru-hl-identifier'."
+ :group 'go-guru)
+
+(defcustom go-guru-debug nil
+ "Print debug messages when running guru."
+ :type 'boolean
+ :group 'go-guru)
+
+(defcustom go-guru-hl-identifier-idle-time 0.5
+ "How long to wait after user input before highlighting the current identifier."
+ :type 'float
+ :group 'go-guru)
+
+(defvar go-guru--current-hl-identifier-idle-time
+ 0
+ "The current delay for hl-identifier-mode.")
+
+(defvar go-guru--hl-identifier-timer
+ nil
+ "The global timer used for highlighting identifiers.")
+
+(defvar go-guru--last-enclosing
+ nil
+ "The remaining enclosing regions of the previous go-expand-region invocation.")
+
+;; Extend go-mode-map.
+(let ((m (define-prefix-command 'go-guru-map)))
+ (define-key m "d" #'go-guru-describe)
+ (define-key m "f" #'go-guru-freevars)
+ (define-key m "i" #'go-guru-implements)
+ (define-key m "c" #'go-guru-peers) ; c for channel
+ (define-key m "r" #'go-guru-referrers)
+ (define-key m "j" #'go-guru-definition) ; j for jump
+ (define-key m "p" #'go-guru-pointsto)
+ (define-key m "s" #'go-guru-callstack) ; s for stack
+ (define-key m "e" #'go-guru-whicherrs) ; e for error
+ (define-key m "<" #'go-guru-callers)
+ (define-key m ">" #'go-guru-callees)
+ (define-key m "x" #'go-guru-expand-region)) ;; x for expand
+
+(define-key go-mode-map (kbd "C-c C-o") #'go-guru-map)
+
+(easy-menu-define go-guru-mode-menu go-mode-map
+ "Menu for Go Guru."
+ '("Guru"
+ ["Jump to Definition" go-guru-definition t]
+ ["Show Referrers" go-guru-referrers t]
+ ["Show Free Names" go-guru-freevars t]
+ ["Describe Expression" go-guru-describe t]
+ ["Show Implements" go-guru-implements t]
+ "---"
+ ["Show Callers" go-guru-callers t]
+ ["Show Callees" go-guru-callees t]
+ ["Show Callstack" go-guru-callstack t]
+ "---"
+ ["Show Points-To" go-guru-pointsto t]
+ ["Show Which Errors" go-guru-whicherrs t]
+ ["Show Channel Peers" go-guru-peers t]
+ "---"
+ ["Set pointer analysis scope..." go-guru-set-scope t]))
+
+;;;###autoload
+(defun go-guru-set-scope ()
+ "Set the scope for the Go guru, prompting the user to edit the previous scope.
+
+The scope restricts analysis to the specified packages.
+Its value is a comma-separated list of patterns of these forms:
+ golang.org/x/tools/cmd/guru # a single package
+ golang.org/x/tools/... # all packages beneath dir
+ ... # the entire workspace.
+
+A pattern preceded by '-' is negative, so the scope
+ encoding/...,-encoding/xml
+matches all encoding packages except encoding/xml."
+ (interactive)
+ (let ((scope (read-from-minibuffer "Go guru scope: "
+ go-guru-scope
+ nil
+ nil
+ 'go-guru--scope-history)))
+ (if (string-equal "" scope)
+ (error "You must specify a non-empty scope for the Go guru"))
+ (setq go-guru-scope scope)))
+
+(defun go-guru--set-scope-if-empty ()
+ (if (string-equal "" go-guru-scope)
+ (go-guru-set-scope)))
+
+(defun go-guru--json (mode)
+ "Execute the Go guru in the specified MODE, passing it the
+selected region of the current buffer, requesting JSON output.
+Parse and return the resulting JSON object."
+ ;; A "what" query works even in a buffer without a file name.
+ (let* ((filename (file-truename (or buffer-file-name "synthetic.go")))
+ (cmd (go-guru--command mode filename '("-json")))
+ (buf (current-buffer))
+ ;; Use temporary buffers to avoid conflict with go-guru--start.
+ (json-buffer (generate-new-buffer "*go-guru-json-output*"))
+ (input-buffer (generate-new-buffer "*go-guru-json-input*")))
+ (unwind-protect
+ ;; Run guru, feeding it the input buffer (modified files).
+ (with-current-buffer input-buffer
+ (go-guru--insert-modified-files)
+ (unless (buffer-file-name buf)
+ (go-guru--insert-modified-file filename buf))
+ (let ((exitcode (apply #'call-process-region
+ (append (list (point-min)
+ (point-max)
+ (car cmd) ; guru
+ nil ; delete
+ json-buffer ; output
+ nil) ; display
+ (cdr cmd))))) ; args
+ (with-current-buffer json-buffer
+ (unless (zerop exitcode)
+ ;; Failed: use buffer contents (sans final \n) as an error.
+ (error "%s" (buffer-substring (point-min) (1- (point-max)))))
+ ;; Success: parse JSON.
+ (goto-char (point-min))
+ (json-read))))
+ ;; Clean up temporary buffers.
+ (kill-buffer json-buffer)
+ (kill-buffer input-buffer))))
+
+(define-compilation-mode go-guru-output-mode "Go guru"
+ "Go guru output mode is a variant of `compilation-mode' for the
+output of the Go guru tool."
+ (set (make-local-variable 'compilation-error-screen-columns) nil)
+ (set (make-local-variable 'compilation-filter-hook) #'go-guru--compilation-filter-hook)
+ (set (make-local-variable 'compilation-start-hook) #'go-guru--compilation-start-hook))
+
+(defun go-guru--compilation-filter-hook ()
+ "Post-process a blob of input to the go-guru-output buffer."
+ ;; For readability, truncate each "file:line:col:" prefix to a fixed width.
+ ;; If the prefix is longer than 20, show "…/last/19chars.go".
+ ;; This usually includes the last segment of the package name.
+ ;; Hide the line and column numbers.
+ (let ((start compilation-filter-start)
+ (end (point)))
+ (goto-char start)
+ (unless (bolp)
+ ;; TODO(adonovan): not quite right: the filter may be called
+ ;; with chunks of output containing incomplete lines. Moving to
+ ;; beginning-of-line may cause duplicate post-processing.
+ (beginning-of-line))
+ (setq start (point))
+ (while (< start end)
+ (let ((p (search-forward ": " end t)))
+ (if (null p)
+ (setq start end) ; break out of loop
+ (setq p (1- p)) ; exclude final space
+ (let* ((posn (buffer-substring-no-properties start p))
+ (flen (cl-search ":" posn)) ; length of filename
+ (filename (if (< flen 19)
+ (substring posn 0 flen)
+ (concat "…" (substring posn (- flen 19) flen)))))
+ (put-text-property start p 'display filename)
+ (forward-line 1)
+ (setq start (point))))))))
+
+(defun go-guru--compilation-start-hook (proc)
+ "Erase default output header inserted by `compilation-mode'."
+ (with-current-buffer (process-buffer proc)
+ (let ((inhibit-read-only t))
+ (goto-char (point-min))
+ (delete-region (point) (point-max)))))
+
+(defun go-guru--start (mode)
+ "Start an asynchronous Go guru process for the specified query
+MODE, passing it the selected region of the current buffer, and
+feeding its standard input with the contents of all modified Go
+buffers. Its output is handled by `go-guru-output-mode', a
+variant of `compilation-mode'."
+ (or buffer-file-name
+ (error "Cannot use guru on a buffer without a file name"))
+ (let* ((filename (file-truename buffer-file-name))
+ (cmd (mapconcat #'shell-quote-argument (go-guru--command mode filename) " "))
+ (process-connection-type nil) ; use pipe (not pty) so EOF closes stdin
+ (procbuf (compilation-start cmd 'go-guru-output-mode)))
+ (with-current-buffer procbuf
+ (setq truncate-lines t)) ; the output is neater without line wrapping
+ (with-current-buffer (get-buffer-create "*go-guru-input*")
+ (erase-buffer)
+ (go-guru--insert-modified-files)
+ (process-send-region procbuf (point-min) (point-max))
+ (process-send-eof procbuf))
+ procbuf))
+
+(defun go-guru--command (mode filename &optional flags)
+ "Return a command and argument list for a Go guru query of MODE, passing it
+the selected region of the current buffer. FILENAME is the
+effective name of the current buffer."
+ (let* ((posn (if (use-region-p)
+ (format "%s:#%d,#%d"
+ filename
+ (1- (position-bytes (region-beginning)))
+ (1- (position-bytes (region-end))))
+ (format "%s:#%d"
+ filename
+ (1- (position-bytes (point))))))
+ (cmd (append (list go-guru-command
+ "-modified"
+ "-scope" go-guru-scope
+ (format "-tags=%s" (mapconcat 'identity go-guru-build-tags ",")))
+ flags
+ (list mode
+ posn))))
+ ;; Log the command to *Messages*, for debugging.
+ (when go-guru-debug
+ (message "go-guru--command: %s" cmd)
+ (message nil)) ; clear/shrink minibuffer
+ cmd))
+
+(defun go-guru--insert-modified-files ()
+ "Insert the contents of each modified Go buffer into the
+current buffer in the format specified by guru's -modified flag."
+ (mapc #'(lambda (b)
+ (and (buffer-modified-p b)
+ (buffer-file-name b)
+ (string= (file-name-extension (buffer-file-name b)) "go")
+ (go-guru--insert-modified-file (buffer-file-name b) b)))
+ (buffer-list)))
+
+(defun go-guru--insert-modified-file (name buffer)
+ (insert (format "%s\n%d\n" name (go-guru--buffer-size-bytes buffer)))
+ (insert-buffer-substring buffer))
+
+(defun go-guru--buffer-size-bytes (&optional buffer)
+ "Return the number of bytes in the current buffer.
+If BUFFER, return the number of characters in that buffer instead."
+ (with-current-buffer (or buffer (current-buffer))
+ (string-bytes (buffer-substring (point-min)
+ (point-max)))))
+
+(defun go-guru--goto-byte (offset)
+ "Go to the OFFSETth byte in the buffer."
+ (goto-char (byte-to-position offset)))
+
+(defun go-guru--goto-byte-column (offset)
+ "Go to the OFFSETth byte in the current line."
+ (goto-char (byte-to-position (+ (position-bytes (point-at-bol)) (1- offset)))))
+
+(defun go-guru--goto-pos (posn)
+ "Find the file containing the position POSN (of the form `file:line:col')
+set the point to it, switching the current buffer."
+ (let ((file-line-pos (split-string posn ":")))
+ (find-file (car file-line-pos))
+ (goto-char (point-min))
+ (forward-line (1- (string-to-number (cadr file-line-pos))))
+ (go-guru--goto-byte-column (string-to-number (cl-caddr file-line-pos)))))
+
+(defun go-guru--goto-pos-no-file (posn)
+ "Given `file:line:col', go to the line and column. The file
+component will be ignored."
+ (let ((file-line-pos (split-string posn ":")))
+ (goto-char (point-min))
+ (forward-line (1- (string-to-number (cadr file-line-pos))))
+ (go-guru--goto-byte-column (string-to-number (cl-caddr file-line-pos)))))
+
+;;;###autoload
+(defun go-guru-callees ()
+ "Show possible callees of the function call at the current point."
+ (interactive)
+ (go-guru--set-scope-if-empty)
+ (go-guru--start "callees"))
+
+;;;###autoload
+(defun go-guru-callers ()
+ "Show the set of callers of the function containing the current point."
+ (interactive)
+ (go-guru--set-scope-if-empty)
+ (go-guru--start "callers"))
+
+;;;###autoload
+(defun go-guru-callstack ()
+ "Show an arbitrary path from a root of the call graph to the
+function containing the current point."
+ (interactive)
+ (go-guru--set-scope-if-empty)
+ (go-guru--start "callstack"))
+
+;;;###autoload
+(defun go-guru-definition ()
+ "Jump to the definition of the selected identifier."
+ (interactive)
+ (or buffer-file-name
+ (error "Cannot use guru on a buffer without a file name"))
+ (let* ((res (go-guru--json "definition"))
+ (desc (cdr (assoc 'desc res))))
+ (push-mark)
+ (ring-insert find-tag-marker-ring (point-marker))
+ (go-guru--goto-pos (cdr (assoc 'objpos res)))
+ (message "%s" desc)))
+
+;;;###autoload
+(defun go-guru-describe ()
+ "Describe the selected syntax, its kind, type and methods."
+ (interactive)
+ (go-guru--start "describe"))
+
+;;;###autoload
+(defun go-guru-pointsto ()
+ "Show what the selected expression points to."
+ (interactive)
+ (go-guru--set-scope-if-empty)
+ (go-guru--start "pointsto"))
+
+;;;###autoload
+(defun go-guru-implements ()
+ "Describe the 'implements' relation for types in the package
+containing the current point."
+ (interactive)
+ (go-guru--start "implements"))
+
+;;;###autoload
+(defun go-guru-freevars ()
+ "Enumerate the free variables of the current selection."
+ (interactive)
+ (go-guru--start "freevars"))
+
+;;;###autoload
+(defun go-guru-peers ()
+ "Enumerate the set of possible corresponding sends/receives for
+this channel receive/send operation."
+ (interactive)
+ (go-guru--set-scope-if-empty)
+ (go-guru--start "peers"))
+
+;;;###autoload
+(defun go-guru-referrers ()
+ "Enumerate all references to the object denoted by the selected
+identifier."
+ (interactive)
+ (go-guru--start "referrers"))
+
+;;;###autoload
+(defun go-guru-whicherrs ()
+ "Show globals, constants and types to which the selected
+expression (of type 'error') may refer."
+ (interactive)
+ (go-guru--set-scope-if-empty)
+ (go-guru--start "whicherrs"))
+
+(defun go-guru-what ()
+ "Run a 'what' query and return the parsed JSON response as an
+association list."
+ (go-guru--json "what"))
+
+(defun go-guru--hl-symbols (posn face id)
+ "Highlight the symbols at the positions POSN by creating
+overlays with face FACE. The attribute 'go-guru-overlay on the
+overlays will be set to ID."
+ (save-excursion
+ (mapc (lambda (pos)
+ (go-guru--goto-pos-no-file pos)
+ (let ((x (make-overlay (point) (+ (point) (length (current-word))))))
+ (overlay-put x 'go-guru-overlay id)
+ (overlay-put x 'face face)))
+ posn)))
+
+;;;###autoload
+(defun go-guru-unhighlight-identifiers ()
+ "Remove highlights from previously highlighted identifier."
+ (remove-overlays nil nil 'go-guru-overlay 'sameid))
+
+;;;###autoload
+(defun go-guru-hl-identifier ()
+ "Highlight all instances of the identifier under point. Removes
+highlights from previously highlighted identifier."
+ (interactive)
+ (go-guru-unhighlight-identifiers)
+ (go-guru--hl-identifier))
+
+(defun go-guru--hl-identifier ()
+ "Highlight all instances of the identifier under point."
+ (let ((posn (cdr (assoc 'sameids (go-guru-what)))))
+ (go-guru--hl-symbols posn 'go-guru-hl-identifier-face 'sameid)))
+
+(defun go-guru--hl-identifiers-function ()
+ "Function run after an idle timeout, highlighting the
+identifier at point, if necessary."
+ (when go-guru-hl-identifier-mode
+ (unless (go-guru--on-overlay-p 'sameid)
+ ;; Ignore guru errors. Otherwise, we might end up with an error
+ ;; every time the timer runs, e.g. because of a malformed
+ ;; buffer.
+ (condition-case nil
+ (go-guru-hl-identifier)
+ (error nil)))
+ (unless (eq go-guru--current-hl-identifier-idle-time go-guru-hl-identifier-idle-time)
+ (go-guru--hl-set-timer))))
+
+(defun go-guru--hl-set-timer ()
+ (if go-guru--hl-identifier-timer
+ (cancel-timer go-guru--hl-identifier-timer))
+ (setq go-guru--current-hl-identifier-idle-time go-guru-hl-identifier-idle-time)
+ (setq go-guru--hl-identifier-timer (run-with-idle-timer
+ go-guru-hl-identifier-idle-time
+ t
+ #'go-guru--hl-identifiers-function)))
+
+;;;###autoload
+(define-minor-mode go-guru-hl-identifier-mode
+ "Highlight instances of the identifier at point after a short
+timeout."
+ :group 'go-guru
+ (if go-guru-hl-identifier-mode
+ (progn
+ (go-guru--hl-set-timer)
+ ;; Unhighlight if point moves off identifier
+ (add-hook 'post-command-hook #'go-guru--hl-identifiers-post-command-hook nil t)
+ ;; Unhighlight any time the buffer changes
+ (add-hook 'before-change-functions #'go-guru--hl-identifiers-before-change-function nil t))
+ (remove-hook 'post-command-hook #'go-guru--hl-identifiers-post-command-hook t)
+ (remove-hook 'before-change-functions #'go-guru--hl-identifiers-before-change-function t)
+ (go-guru-unhighlight-identifiers)))
+
+(defun go-guru--on-overlay-p (id)
+ "Return whether point is on a guru overlay of type ID."
+ (cl-find-if (lambda (el) (eq (overlay-get el 'go-guru-overlay) id)) (overlays-at (point))))
+
+(defun go-guru--hl-identifiers-post-command-hook ()
+ (if (and go-guru-hl-identifier-mode
+ (not (go-guru--on-overlay-p 'sameid)))
+ (go-guru-unhighlight-identifiers)))
+
+(defun go-guru--hl-identifiers-before-change-function (_beg _end)
+ (go-guru-unhighlight-identifiers))
+
+;; TODO(dominikh): a future feature may be to cycle through all uses
+;; of an identifier.
+
+(defun go-guru--enclosing ()
+ "Return a list of enclosing regions."
+ (cdr (assoc 'enclosing (go-guru-what))))
+
+(defun go-guru--enclosing-unique ()
+ "Return a list of enclosing regions, with duplicates removed.
+Two regions are considered equal if they have the same start and
+end point."
+ (let ((enclosing (go-guru--enclosing)))
+ (cl-remove-duplicates enclosing
+ :from-end t
+ :test (lambda (a b)
+ (and (= (cdr (assoc 'start a))
+ (cdr (assoc 'start b)))
+ (= (cdr (assoc 'end a))
+ (cdr (assoc 'end b))))))))
+
+(defun go-guru-expand-region ()
+ "Expand region to the next enclosing syntactic unit."
+ (interactive)
+ (let* ((enclosing (if (eq last-command #'go-guru-expand-region)
+ go-guru--last-enclosing
+ (go-guru--enclosing-unique)))
+ (block (if (> (length enclosing) 0) (elt enclosing 0))))
+ (when block
+ (go-guru--goto-byte (1+ (cdr (assoc 'start block))))
+ (set-mark (byte-to-position (1+ (cdr (assoc 'end block)))))
+ (setq go-guru--last-enclosing (cl-subseq enclosing 1))
+ (message "Region: %s" (cdr (assoc 'desc block)))
+ (setq deactivate-mark nil))))
+
+
+(provide 'go-guru)
+
+;; Local variables:
+;; indent-tabs-mode: t
+;; tab-width: 8
+;; End
+
+;;; go-guru.el ends here
diff --git a/go-mode.el b/go-mode.el
index 7d152a0..e569aa0 100644
--- a/go-mode.el
+++ b/go-mode.el
@@ -1,11 +1,13 @@
;;; go-mode.el --- Major mode for the Go programming language
-;; Copyright 2013 The go-mode Authors. All rights reserved.
+;;; Commentary:
+
+;; Copyright 2013 The go-mode Authors. All rights reserved.
;; Use of this source code is governed by a BSD-style
;; license that can be found in the LICENSE file.
;; Author: The go-mode Authors
-;; Version: 1.4.0
+;; Version: 1.5.0
;; Keywords: languages go
;; URL: https://github.com/dominikh/go-mode.el
;;
@@ -20,35 +22,14 @@
(require 'find-file)
(require 'ring)
(require 'url)
+(require 'xref nil :noerror) ; xref is new in Emacs 25.1
-;; XEmacs compatibility guidelines
-;; - Minimum required version of XEmacs: 21.5.32
-;; - Feature that cannot be backported: POSIX character classes in
-;; regular expressions
-;; - Functions that could be backported but won't because 21.5.32
-;; covers them: plenty.
-;; - Features that are still partly broken:
-;; - godef will not work correctly if multibyte characters are
-;; being used
-;; - Fontification will not handle unicode correctly
-;;
-;; - Do not use \_< and \_> regexp delimiters directly; use
-;; go--regexp-enclose-in-symbol
-;;
-;; - The character `_` must not be a symbol constituent but a
-;; character constituent
-;;
-;; - Do not use process-lines
-;;
-;; - Use go--old-completion-list-style when using a plain list as the
-;; collection for completing-read
-;;
-;; - Use go--position-bytes instead of position-bytes
-(defmacro go--xemacs-p ()
- (featurep 'xemacs))
-(defmacro go--has-syntax-propertize-p ()
- (boundp 'syntax-propertize-function))
+(eval-when-compile
+ (defmacro go--forward-word (&optional arg)
+ (if (fboundp 'forward-word-strictly)
+ `(forward-word-strictly ,arg)
+ `(forward-word ,arg))))
(defun go--delete-whole-line (&optional arg)
"Delete the current line without putting it in the `kill-ring'.
@@ -76,43 +57,6 @@ function."
(delete-region (progn (forward-visible-line 0) (point))
(progn (forward-visible-line arg) (point))))))
-;; declare-function is an empty macro that only byte-compile cares
-;; about. Wrap in always false if to satisfy Emacsen without that
-;; macro.
-(if nil
- (declare-function go--position-bytes "go-mode" (point)))
-
-;; XEmacs unfortunately does not offer position-bytes. We can fall
-;; back to just using (point), but it will be incorrect as soon as
-;; multibyte characters are being used.
-(if (fboundp 'position-bytes)
- (defalias 'go--position-bytes #'position-bytes)
- (defun go--position-bytes (point) point))
-
-(defun go--old-completion-list-style (list)
- (mapcar (lambda (x) (cons x nil)) list))
-
-;; GNU Emacs 24 has prog-mode, older GNU Emacs and XEmacs do not, so
-;; copy its definition for those.
-(if (not (fboundp 'prog-mode))
- (define-derived-mode prog-mode fundamental-mode "Prog"
- "Major mode for editing source code."
- (set (make-local-variable 'require-final-newline) mode-require-final-newline)
- (set (make-local-variable 'parse-sexp-ignore-comments) t)
- (setq bidi-paragraph-direction 'left-to-right)))
-
-(defun go--regexp-enclose-in-symbol (s)
- "Enclose S as regexp symbol.
-XEmacs does not support \\_<, GNU Emacs does. In GNU Emacs we
-make extensive use of \\_< to support unicode in identifiers.
-Until we come up with a better solution for XEmacs, this solution
-will break fontification in XEmacs for identifiers such as
-\"typeµ\". XEmacs will consider \"type\" a keyword, GNU Emacs
-won't."
- (if (go--xemacs-p)
- (concat "\\<" s "\\>")
- (concat "\\_<" s "\\_>")))
-
(defun go-goto-opening-parenthesis (&optional _legacy-unused)
"Move up one level of parentheses."
;; The old implementation of go-goto-opening-parenthesis had an
@@ -126,14 +70,20 @@ won't."
(defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]")
+(defconst go--max-dangling-operator-length 2
+ "The maximum length of dangling operators.
+This must be at least the length of the longest string matched by
+‘go-dangling-operators-regexp.’, and must be updated whenever
+that constant is changed.")
+
(defconst go-identifier-regexp "[[:word:][:multibyte:]]+")
(defconst go-type-name-no-prefix-regexp "\\(?:[[:word:][:multibyte:]]+\\.\\)?[[:word:][:multibyte:]]+")
(defconst go-qualified-identifier-regexp (concat go-identifier-regexp "\\." go-identifier-regexp))
(defconst go-label-regexp go-identifier-regexp)
(defconst go-type-regexp "[[:word:][:multibyte:]*]+")
-(defconst go-func-regexp (concat (go--regexp-enclose-in-symbol "func") "\\s *\\(" go-identifier-regexp "\\)"))
+(defconst go-func-regexp (concat "\\_<func\\_>\\s *\\(" go-identifier-regexp "\\)"))
(defconst go-func-meth-regexp (concat
- (go--regexp-enclose-in-symbol "func") "\\s *\\(?:(\\s *"
+ "\\_<func\\_>\\s *\\(?:(\\s *"
"\\(" go-identifier-regexp "\\s +\\)?" go-type-regexp
"\\s *)\\s *\\)?\\("
go-identifier-regexp
@@ -199,7 +149,7 @@ point to the wrapper script."
(defcustom gofmt-command "gofmt"
"The 'gofmt' command.
Some users may replace this with 'goimports'
-from https://github.com/bradfitz/goimports."
+from https://golang.org/x/tools/cmd/goimports."
:type 'string
:group 'go)
@@ -234,14 +184,13 @@ a `before-save-hook'."
:group 'go)
(defcustom go-packages-function 'go-packages-native
- "Function called by `go-packages' to determine the list of
-available packages. This is used in e.g. tab completion in
-`go-import-add'.
+ "Function called by `go-packages' to determine the list of available packages.
+This is used in e.g. tab completion in `go-import-add'.
This package provides two functions: `go-packages-native' uses
elisp to find all .a files in all /pkg/ directories.
`go-packages-go-list' uses 'go list all' to determine all Go
-packages. `go-packages-go-list' generally produces more accurate
+packages. `go-packages-go-list' generally produces more accurate
results, but can be slower than `go-packages-native'."
:type 'function
:package-version '(go-mode . 1.4.0)
@@ -254,30 +203,31 @@ results, but can be slower than `go-packages-native'."
"Functions to call in sequence to detect a project's GOPATH.
The functions in this list will be called one after another,
-until a function returns non-nil. The order of the functions in
+until a function returns non-nil. The order of the functions in
this list is important, as some project layouts may superficially
-look like others. For example, a subset of wgo projects look like
-gb projects. That's why we need to detect wgo first, to avoid
+look like others. For example, a subset of wgo projects look like
+gb projects. That's why we need to detect wgo first, to avoid
mis-identifying them as gb projects."
:type '(repeat function)
:group 'go)
(defcustom godoc-command "go doc"
- "Which executable to use for `godoc'. This can either be
-'godoc' or 'go doc', both as an absolute path or an executable in
-PATH."
+ "Which executable to use for `godoc'.
+This can either be 'godoc' or 'go doc', both as an absolute path
+or an executable in PATH."
:type 'string
:group 'go)
(defcustom godoc-and-godef-command "godoc"
- "Which executable to use for `godoc' in
-`godoc-and-godef-command'. Must be 'godoc' and not 'go doc' and
-can be an absolute path or an executable in PATH."
+ "Which executable to use for `godoc' in `godoc-and-godef-command'.
+Must be 'godoc' and not 'go doc' and can be an absolute path or
+an executable in PATH."
:type 'string
:group 'go)
(defcustom godoc-use-completing-read nil
- "Provide auto-completion for godoc. Only really desirable when using `godoc' instead of `go doc'."
+ "Provide auto-completion for godoc.
+Only really desirable when using `godoc' instead of `go doc'."
:type 'boolean
:group 'godoc)
@@ -286,24 +236,24 @@ can be an absolute path or an executable in PATH."
identifier at a given position.
This package provides two functions: `godoc-and-godef' uses a
-combination of godef and godoc to find the documentation. This
-approach has several caveats. See its documentation for more
-information. The second function, `godoc-gogetdoc' uses an
+combination of godef and godoc to find the documentation. This
+approach has several caveats. See its documentation for more
+information. The second function, `godoc-gogetdoc' uses an
additional tool that correctly determines the documentation for
-any identifier. It provides better results than
-`godoc-and-godef'. "
+any identifier. It provides better results than
+`godoc-and-godef'."
:type 'function
:group 'godoc)
(defun godoc-and-godef (point)
- "Use a combination of godef and godoc to guess the documentation.
+ "Use a combination of godef and godoc to guess the documentation at POINT.
Due to a limitation in godoc, it is not possible to differentiate
between functions and methods, which may cause `godoc-at-point'
-to display more documentation than desired. Furthermore, it
+to display more documentation than desired. Furthermore, it
doesn't work on package names or variables.
-Consider using godoc-gogetdoc instead for more accurate results."
+Consider using ‘godoc-gogetdoc’ instead for more accurate results."
(condition-case nil
(let* ((output (godef--call point))
(file (car output))
@@ -320,14 +270,14 @@ Consider using godoc-gogetdoc instead for more accurate results."
(file-error (message "Could not run godef binary"))))
(defun godoc-gogetdoc (point)
- "Use the gogetdoc tool to find the documentation for an identifier.
+ "Use the gogetdoc tool to find the documentation for an identifier at POINT.
You can install gogetdoc with 'go get -u github.com/zmb3/gogetdoc'."
(if (not (buffer-file-name (go--coverage-origin-buffer)))
;; TODO: gogetdoc supports unsaved files, but not introducing
;; new artifical files, so this limitation will stay for now.
(error "Cannot use gogetdoc on a buffer without a file name"))
- (let ((posn (format "%s:#%d" (shell-quote-argument (file-truename buffer-file-name)) (1- (go--position-bytes point))))
+ (let ((posn (format "%s:#%d" (shell-quote-argument (file-truename buffer-file-name)) (1- (position-bytes point))))
(out (godoc--get-buffer "<at point>")))
(with-current-buffer (get-buffer-create "*go-gogetdoc-input*")
(setq buffer-read-only nil)
@@ -429,15 +379,14 @@ For mode=set, all covered lines will have this weight."
(modify-syntax-entry ?= "." st)
(modify-syntax-entry ?< "." st)
(modify-syntax-entry ?> "." st)
- (modify-syntax-entry ?/ (if (go--xemacs-p) ". 1456" ". 124b") st)
+ (modify-syntax-entry ?/ ". 124b" st)
(modify-syntax-entry ?* ". 23" st)
(modify-syntax-entry ?\n "> b" st)
(modify-syntax-entry ?\" "\"" st)
(modify-syntax-entry ?\' "\"" st)
(modify-syntax-entry ?` "\"" st)
(modify-syntax-entry ?\\ "\\" st)
- ;; It would be nicer to have _ as a symbol constituent, but that
- ;; would trip up XEmacs, which does not support the \_< anchor
+ ;; TODO make _ a symbol constituent now that xemacs is gone
(modify-syntax-entry ?_ "w" st)
st)
@@ -450,9 +399,9 @@ For mode=set, all covered lines will have this weight."
`((go--match-func
,@(mapcar (lambda (x) `(,x font-lock-type-face))
(number-sequence 1 go--font-lock-func-param-num-groups)))
- (,(go--regexp-enclose-in-symbol (regexp-opt go-mode-keywords t)) . font-lock-keyword-face)
- (,(concat "\\(" (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) "\\)[[:space:]]*(") 1 font-lock-builtin-face)
- (,(go--regexp-enclose-in-symbol (regexp-opt go-constants t)) . font-lock-constant-face)
+ (,(concat "\\_<" (regexp-opt go-mode-keywords t) "\\_>") . font-lock-keyword-face)
+ (,(concat "\\(\\_<" (regexp-opt go-builtins t) "\\_>\\)[[:space:]]*(") 1 font-lock-builtin-face)
+ (,(concat "\\_<" (regexp-opt go-constants t) "\\_>") . font-lock-constant-face)
(,go-func-regexp 1 font-lock-function-name-face)) ;; function (not method) name
(if go-fontify-function-calls
@@ -462,29 +411,21 @@ For mode=set, all covered lines will have this weight."
`(
("\\(`[^`]*`\\)" 1 font-lock-multiline) ;; raw string literal, needed for font-lock-syntactic-keywords
- (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+\\([^[:space:](]+\\)") 1 font-lock-type-face) ;; types
- (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types
+ (,(concat "\\_<type\\_>[[:space:]]+\\([^[:space:](]+\\)") 1 font-lock-type-face) ;; types
+ (,(concat "\\_<type\\_>[[:space:]]+" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types
(,(concat "[^[:word:][:multibyte:]]\\[\\([[:digit:]]+\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 2 font-lock-type-face) ;; Arrays/slices
(,(concat "\\(" go-identifier-regexp "\\)" "{") 1 font-lock-type-face)
- (,(concat (go--regexp-enclose-in-symbol "map") "\\[[^]]+\\]" go-type-name-regexp) 1 font-lock-type-face) ;; map value type
- (,(concat (go--regexp-enclose-in-symbol "map") "\\[" go-type-name-regexp) 1 font-lock-type-face) ;; map key type
- (,(concat (go--regexp-enclose-in-symbol "chan") "[[:space:]]*\\(?:<-[[:space:]]*\\)?" go-type-name-regexp) 1 font-lock-type-face) ;; channel type
- (,(concat (go--regexp-enclose-in-symbol "\\(?:new\\|make\\)") "\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) ;; new/make type
+ (,(concat "\\_<map\\_>\\[[^]]+\\]" go-type-name-regexp) 1 font-lock-type-face) ;; map value type
+ (,(concat "\\_<map\\_>\\[" go-type-name-regexp) 1 font-lock-type-face) ;; map key type
+ (,(concat "\\_<chan\\_>[[:space:]]*\\(?:<-[[:space:]]*\\)?" go-type-name-regexp) 1 font-lock-type-face) ;; channel type
+ (,(concat "\\_<\\(?:new\\|make\\)\\_>\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) ;; new/make type
;; TODO do we actually need this one or isn't it just a function call?
(,(concat "\\.\\s *(" go-type-name-regexp) 1 font-lock-type-face) ;; Type conversion
;; Like the original go-mode this also marks compound literal
;; fields. There, it was marked as to fix, but I grew quite
;; accustomed to it, so it'll stay for now.
(,(concat "^[[:space:]]*\\(" go-label-regexp "\\)[[:space:]]*:\\(\\S.\\|$\\)") 1 font-lock-constant-face) ;; Labels and compound literal fields
- (,(concat (go--regexp-enclose-in-symbol "\\(goto\\|break\\|continue\\)") "[[:space:]]*\\(" go-label-regexp "\\)") 2 font-lock-constant-face)))) ;; labels in goto/break/continue
-
-(defconst go--font-lock-syntactic-keywords
- ;; Override syntax property of raw string literal contents, so that
- ;; backslashes have no special meaning in ``. Used in Emacs 23 or older.
- '((go--match-raw-string-literal
- (1 (7 . ?`))
- (2 (15 . nil)) ;; 15 = "generic string"
- (3 (7 . ?`)))))
+ (,(concat "\\_<\\(goto\\|break\\|continue\\)\\_>[[:space:]]*\\(" go-label-regexp "\\)") 2 font-lock-constant-face)))) ;; labels in goto/break/continue
(let ((m (define-prefix-command 'go-goto-map)))
(define-key m "a" #'go-goto-arguments)
@@ -506,7 +447,7 @@ For mode=set, all covered lines will have this weight."
(define-key m (kbd "C-c C-d") #'godef-describe)
(define-key m (kbd "C-c C-f") 'go-goto-map)
m)
- "Keymap used by go-mode.")
+ "Keymap used by ‘go-mode’.")
(easy-menu-define go-mode-menu go-mode-map
"Menu for Go mode."
@@ -561,13 +502,13 @@ STOP-AT-STRING is not true, over strings."
(let (pos (start-pos (point)))
(skip-chars-backward "\n\s\t")
(if (and (save-excursion (beginning-of-line) (go-in-string-p))
- (looking-back "`")
+ (= (char-before) ?`)
(not stop-at-string))
(backward-char))
(if (and (go-in-string-p)
(not stop-at-string))
(go-goto-beginning-of-string-or-comment))
- (if (looking-back "\\*/")
+ (if (looking-back "\\*/" (line-beginning-position))
(backward-char))
(if (go-in-comment-p)
(go-goto-beginning-of-string-or-comment))
@@ -587,19 +528,6 @@ STOP-AT-STRING is not true, over strings."
(- (point-max)
(point-min))))
-(defun go--match-raw-string-literal (end)
- "Search for a raw string literal.
-Set point to the end of the occurence found on success. Return nil on failure."
- (unless (go-in-string-or-comment-p)
- (when (search-forward "`" end t)
- (goto-char (match-beginning 0))
- (if (go-in-string-or-comment-p)
- (progn (goto-char (match-end 0))
- (go--match-raw-string-literal end))
- (when (looking-at "\\(`\\)\\([^`]*\\)\\(`\\)")
- (goto-char (match-end 0))
- t)))))
-
(defun go-previous-line-has-dangling-op-p ()
"Return non-nil if the current line is a continuation line."
(let* ((cur-line (line-number-at-pos))
@@ -608,7 +536,8 @@ Set point to the end of the occurence found on success. Return nil on failure."
(save-excursion
(beginning-of-line)
(go--backward-irrelevant t)
- (setq val (looking-back go-dangling-operators-regexp))
+ (setq val (looking-back go-dangling-operators-regexp
+ (- (point) go--max-dangling-operator-length)))
(if (not (go--buffer-narrowed-p))
(puthash cur-line val go-dangling-cache))))
val))
@@ -618,9 +547,9 @@ Set point to the end of the occurence found on success. Return nil on failure."
function definition.
We do this by first calling (beginning-of-defun), which will take
-us to the start of *some* function. We then look for the opening
+us to the start of *some* function. We then look for the opening
curly brace of that function and compare its position against the
-curly brace we are checking. If they match, we return non-nil."
+curly brace we are checking. If they match, we return non-nil."
(if (= (char-after) ?\{)
(save-excursion
(let ((old-point (point))
@@ -663,7 +592,9 @@ current line will be returned."
(if (go-previous-line-has-dangling-op-p)
(- (current-indentation) tab-width)
(go--indentation-for-opening-parenthesis)))
- ((progn (go--backward-irrelevant t) (looking-back go-dangling-operators-regexp))
+ ((progn (go--backward-irrelevant t)
+ (looking-back go-dangling-operators-regexp
+ (- (point) go--max-dangling-operator-length)))
;; only one nesting for all dangling operators in one operation
(if (go-previous-line-has-dangling-op-p)
(current-indentation)
@@ -738,7 +669,8 @@ current line will be returned."
(skip-chars-forward "^{")
(forward-char)
(or (go-in-string-or-comment-p)
- (looking-back "\\(struct\\|interface\\)\\s-*{"))))
+ (looking-back "\\(struct\\|interface\\)\\s-*{"
+ (line-beginning-position)))))
(setq orig-level (go-paren-level))
(while (>= (go-paren-level) orig-level)
(skip-chars-forward "^}")
@@ -792,7 +724,7 @@ parenthesis before a comma, it stops at it."
"Search for identifiers used as type names from a function
parameter list, and set the identifier positions as the results
of last search. Return t if search succeeded."
- (when (re-search-forward (go--regexp-enclose-in-symbol "func") end t)
+ (when (re-search-forward "\\_<func\\_>" end t)
(let ((regions (go--match-func-type-names end)))
(if (null regions)
;; Nothing to highlight. This can happen if the current func
@@ -823,8 +755,8 @@ of last search. Return t if search succeeded."
(nconc regions (go--match-function-result end))))))
(defun go--parameter-list-type (end)
- "Return `present' if the parameter list has names, or `absent' if
-not, assuming point is at the beginning of a parameter list, just
+ "Return `present' if the parameter list has names, or `absent' if not.
+Assumes point is at the beginning of a parameter list, just
after '('."
(save-excursion
(skip-chars-forward "[:space:]\n" end)
@@ -844,7 +776,7 @@ after '('."
(defconst go--parameter-type-regexp
(concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(" go-type-name-no-prefix-regexp "\\)[[:space:]\n]*\\([,)]\\|\\'\\)"))
(defconst go--func-type-in-parameter-list-regexp
- (concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(" (go--regexp-enclose-in-symbol "func") "\\)"))
+ (concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(\\_<func\\_>" "\\)"))
(defun go--match-parameters-common (identifier-regexp end)
(let ((acc ())
@@ -955,6 +887,12 @@ Function result is a unparenthesized type or a parameter list."
(go--match-parameter-list end))
(t nil)))
+(defun go--reset-dangling-cache-before-change (&optional _beg _end)
+ "Reset `go-dangling-cache'.
+
+This is intended to be called from `before-change-functions'."
+ (setq go-dangling-cache (make-hash-table :test 'eql)))
+
;;;###autoload
(define-derived-mode go-mode prog-mode "Go"
"Major mode for editing Go source text.
@@ -1036,11 +974,7 @@ with goflymake \(see URL `https://github.com/dougm/goflymake'), gocode
(set (make-local-variable 'end-of-defun-function) #'go-end-of-defun)
(set (make-local-variable 'parse-sexp-lookup-properties) t)
- (if (go--has-syntax-propertize-p)
- (set (make-local-variable 'syntax-propertize-function) #'go-propertize-syntax)
- (set (make-local-variable 'font-lock-syntactic-keywords)
- go--font-lock-syntactic-keywords)
- (set (make-local-variable 'font-lock-multiline) t))
+ (set (make-local-variable 'syntax-propertize-function) #'go-propertize-syntax)
(if (boundp 'electric-indent-chars)
(set (make-local-variable 'electric-indent-chars) '(?\n ?} ?\))))
@@ -1048,7 +982,7 @@ with goflymake \(see URL `https://github.com/dougm/goflymake'), gocode
(set (make-local-variable 'compilation-error-screen-columns) nil)
(set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql))
- (add-hook 'before-change-functions (lambda (x y) (setq go-dangling-cache (make-hash-table :test 'eql))) t t)
+ (add-hook 'before-change-functions #'go--reset-dangling-cache-before-change t t)
;; ff-find-other-file
(setq ff-other-file-alist 'go-other-file-alist)
@@ -1101,7 +1035,7 @@ with goflymake \(see URL `https://github.com/dougm/goflymake'), gocode
(goto-char (point-min))
(while (not (eobp))
(unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)")
- (error "invalid rcs patch or internal error in go--apply-rcs-patch"))
+ (error "Invalid rcs patch or internal error in go--apply-rcs-patch"))
(forward-line)
(let ((action (match-string 1))
(from (string-to-number (match-string 2)))
@@ -1122,7 +1056,7 @@ with goflymake \(see URL `https://github.com/dougm/goflymake'), gocode
(cl-incf line-offset len)
(go--delete-whole-line len)))
(t
- (error "invalid rcs patch or internal error in go--apply-rcs-patch")))))))))
+ (error "Invalid rcs patch or internal error in go--apply-rcs-patch")))))))))
(defun gofmt--is-goimports-p ()
(string-equal (file-name-base gofmt-command) "goimports"))
@@ -1152,7 +1086,11 @@ with goflymake \(see URL `https://github.com/dougm/goflymake'), gocode
(when (and (gofmt--is-goimports-p) buffer-file-name)
(setq our-gofmt-args
(append our-gofmt-args
- (list "-srcdir" (file-name-directory (file-truename buffer-file-name))))))
+ ;; srcdir, despite its name, supports
+ ;; accepting a full path, and some features
+ ;; of goimports rely on knowing the full
+ ;; name.
+ (list "-srcdir" (file-truename buffer-file-name)))))
(setq our-gofmt-args (append our-gofmt-args
gofmt-args
(list "-w" tmpfile)))
@@ -1205,9 +1143,9 @@ with goflymake \(see URL `https://github.com/dougm/goflymake'), gocode
;;;###autoload
(defun gofmt-before-save ()
"Add this to .emacs to run gofmt on the current buffer when saving:
- (add-hook 'before-save-hook 'gofmt-before-save).
+\(add-hook 'before-save-hook 'gofmt-before-save).
-Note that this will cause go-mode to get loaded the first time
+Note that this will cause ‘go-mode’ to get loaded the first time
you save any file, kind of defeating the point of autoloading."
(interactive)
@@ -1217,11 +1155,11 @@ you save any file, kind of defeating the point of autoloading."
"Read a godoc query from the minibuffer."
(if godoc-use-completing-read
(completing-read "godoc; "
- (go--old-completion-list-style (go-packages)) nil nil nil 'go-godoc-history)
+ (go-packages) nil nil nil 'go-godoc-history)
(read-from-minibuffer "godoc: " nil nil nil 'go-godoc-history)))
(defun godoc--get-buffer (query)
- "Get an empty buffer for a godoc query."
+ "Get an empty buffer for a godoc QUERY."
(let* ((buffer-name (concat "*godoc " query "*"))
(buffer (get-buffer buffer-name)))
;; Kill the existing buffer if it already exists.
@@ -1246,7 +1184,7 @@ you save any file, kind of defeating the point of autoloading."
;;;###autoload
(defun godoc (query)
- "Show Go documentation for QUERY, much like M-x man."
+ "Show Go documentation for QUERY, much like \\<go-mode-map>\\[man]."
(interactive (list (godoc--read-query)))
(go--godoc query godoc-command))
@@ -1317,7 +1255,7 @@ declaration."
(go-play-region (point-min) (point-max)))
(defun go-play-region (start end)
- "Send the region to the Playground.
+ "Send the region between START and END to the Playground.
If non-nil `go-play-browse-function' is called with the
Playground URL."
(interactive "r")
@@ -1381,7 +1319,7 @@ uncommented, otherwise a new import will be added."
(interactive
(list
current-prefix-arg
- (replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go--old-completion-list-style (go-packages))))))
+ (replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go-packages)))))
(save-excursion
(let (as line import-start)
(if arg
@@ -1440,8 +1378,8 @@ If IGNORE-CASE is non-nil, the comparison is case-insensitive."
(funcall go-packages-function))
(defun go-packages-native ()
- "Return a list of all installed Go packages. It looks for
-archive files in /pkg/"
+ "Return a list of all installed Go packages.
+It looks for archive files in /pkg/."
(sort
(delete-dups
(cl-mapcan
@@ -1460,7 +1398,7 @@ archive files in /pkg/"
#'string<))
(defun go-packages-go-list ()
- "Return a list of all Go packages, using `go list'"
+ "Return a list of all Go packages, using `go list'."
(process-lines go-command "list" "-e" "all"))
(defun go-unused-imports-lines ()
@@ -1523,29 +1461,27 @@ visit FILENAME and go to line LINE and column COLUMN."
(defun godef--call (point)
"Call godef, acquiring definition position and expression
description at POINT."
- (if (go--xemacs-p)
- (error "godef does not reliably work in XEmacs, expect bad results"))
(if (not (buffer-file-name (go--coverage-origin-buffer)))
(error "Cannot use godef on a buffer without a file name")
- (let ((outbuf (get-buffer-create "*godef*"))
+ (let ((outbuf (generate-new-buffer "*godef*"))
(coding-system-for-read 'utf-8)
(coding-system-for-write 'utf-8))
- (with-current-buffer outbuf
- (erase-buffer))
- (call-process-region (point-min)
- (point-max)
- godef-command
- nil
- outbuf
- nil
- "-i"
- "-t"
- "-f"
- (file-truename (buffer-file-name (go--coverage-origin-buffer)))
- "-o"
- (number-to-string (go--position-bytes point)))
- (with-current-buffer outbuf
- (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n")))))
+ (prog2
+ (call-process-region (point-min)
+ (point-max)
+ godef-command
+ nil
+ outbuf
+ nil
+ "-i"
+ "-t"
+ "-f"
+ (file-truename (buffer-file-name (go--coverage-origin-buffer)))
+ "-o"
+ (number-to-string (position-bytes point)))
+ (with-current-buffer outbuf
+ (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n"))
+ (kill-buffer outbuf)))))
(defun godef--successful-p (output)
(not (or (string= "-" output)
@@ -1580,7 +1516,10 @@ description at POINT."
(if (not (godef--successful-p file))
(message "%s" (godef--error file))
(push-mark)
- (ring-insert find-tag-marker-ring (point-marker))
+ (if (eval-when-compile (fboundp 'xref-push-marker-stack))
+ ;; TODO: Integrate this facility with XRef.
+ (xref-push-marker-stack)
+ (ring-insert find-tag-marker-ring (point-marker)))
(godef--find-file-line-column file other-window)))
(file-error (message "Could not run godef binary"))))
@@ -1758,7 +1697,7 @@ If ARG is non-nil, anonymous functions are ignored."
;; should search forward instead.
(when (not (looking-at "\\<func\\>"))
(re-search-forward "\\<func\\>" nil t)
- (forward-word -1))
+ (go--forward-word -1))
;; If we have landed at an anonymous function, it is possible that we
;; were not inside it but below it. If we were not inside it, we should
@@ -1789,7 +1728,8 @@ If ARG is non-nil, anonymous functions are ignored."
(skip-chars-forward "^{")
(forward-char)
(or (go-in-string-or-comment-p)
- (looking-back "\\(struct\\|interface\\)\\s-*{"))))
+ (looking-back "\\(struct\\|interface\\)\\s-*{"
+ (line-beginning-position)))))
(backward-char))
(defun go--in-function-p (compare-point)
@@ -1801,7 +1741,7 @@ If ARG is non-nil, anonymous functions are ignored."
(go--goto-opening-curly-brace)
(unless (looking-at "{")
- (error "expected to be looking at opening curly brace"))
+ (error "Expected to be looking at opening curly brace"))
(forward-list 1)
(and (>= compare-point start)
(<= compare-point (point))))))
@@ -1824,7 +1764,7 @@ If ARG is non-nil, anonymous functions are skipped."
(when (looking-at "\\<func (")
(setq words 3
chars 2))
- (forward-word words)
+ (go--forward-word words)
(forward-char chars)
(when (looking-at "Test")
(forward-char 4)))))
@@ -1835,7 +1775,7 @@ If ARG is non-nil, anonymous functions are skipped."
If ARG is non-nil, anonymous functions are skipped."
(interactive "P")
(go-goto-function-name arg)
- (forward-word 1)
+ (go--forward-word 1)
(forward-char 1))
(defun go--goto-return-values (&optional arg)
@@ -1870,7 +1810,7 @@ If ARG is non-nil, anonymous functions are skipped."
If there is none, add parenthesis to add one.
Anonymous functions cannot have method receivers, so when this is called
-interactively anonymous functions will be skipped. If called programmatically,
+interactively anonymous functions will be skipped. If called programmatically,
an error is raised unless ARG is non-nil."
(interactive "P")
@@ -1892,7 +1832,7 @@ an error is raised unless ARG is non-nil."
If there is none, add one beginning with the name of the current function.
Anonymous functions do not have docstrings, so when this is called
-interactively anonymous functions will be skipped. If called programmatically,
+interactively anonymous functions will be skipped. If called programmatically,
an error is raised unless ARG is non-nil."
(interactive "P")
@@ -1930,7 +1870,7 @@ an error is raised unless ARG is non-nil."
"Return the name of the surrounding function.
If ARG is non-nil, anonymous functions will be ignored and the
-name returned will be that of the top-level function. If ARG is
+name returned will be that of the top-level function. If ARG is
nil and the surrounding function is anonymous, nil will be
returned."
(when (or (not (go--in-anonymous-funcion-p))
diff --git a/go-rename.el b/go-rename.el
new file mode 100644
index 0000000..5b75e65
--- /dev/null
+++ b/go-rename.el
@@ -0,0 +1,108 @@
+;;; go-rename.el --- Integration of the 'gorename' tool into Emacs.
+
+;; Copyright 2014 The Go Authors. All rights reserved.
+;; Use of this source code is governed by a BSD-style
+;; license that can be found in the LICENSE file.
+
+;; Version: 0.1
+;; Package-Requires: ((go-mode "1.3.1"))
+;; Keywords: tools
+
+;;; Commentary:
+
+;; To install:
+
+;; % go get golang.org/x/tools/cmd/gorename
+;; % go build golang.org/x/tools/cmd/gorename
+;; % mv gorename $HOME/bin/ # or elsewhere on $PATH
+
+;; The go-rename-command variable can be customized to specify an
+;; alternative location for the installed command.
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'compile)
+(require 'go-mode)
+(require 'thingatpt)
+
+(defgroup go-rename nil
+ "Options specific to the Go rename."
+ :group 'go)
+
+(defcustom go-rename-command "gorename"
+ "The `gorename' command; by the default, $PATH is searched."
+ :type 'string
+ :group 'go-rename)
+
+;;;###autoload
+(defun go-rename (new-name &optional force)
+ "Rename the entity denoted by the identifier at point, using
+the `gorename' tool. With FORCE, call `gorename' with the
+`-force' flag."
+ (interactive (list (read-string "New name: " (thing-at-point 'symbol))
+ current-prefix-arg))
+ (if (not buffer-file-name)
+ (error "Cannot use go-rename on a buffer without a file name"))
+ ;; It's not sufficient to save the current buffer if modified,
+ ;; since if gofmt-before-save is on the before-save-hook,
+ ;; saving will disturb the selected region.
+ (if (buffer-modified-p)
+ (error "Please save the current buffer before invoking go-rename"))
+ ;; Prompt-save all other modified Go buffers, since they might get written.
+ (save-some-buffers nil #'(lambda ()
+ (and (buffer-file-name)
+ (string= (file-name-extension (buffer-file-name)) ".go"))))
+ (let* ((posflag (format "-offset=%s:#%d"
+ buffer-file-name
+ (1- (position-bytes (point)))))
+ (env-vars (go-root-and-paths))
+ (goroot-env (concat "GOROOT=" (car env-vars)))
+ (gopath-env (concat "GOPATH=" (mapconcat #'identity (cdr env-vars) ":")))
+ success)
+ (with-current-buffer (get-buffer-create "*go-rename*")
+ (setq buffer-read-only nil)
+ (erase-buffer)
+ (let ((args (append (list go-rename-command nil t nil posflag "-to" new-name) (if force '("-force")))))
+ ;; Log the command to *Messages*, for debugging.
+ (message "Command: %s:" args)
+ (message "Running gorename...")
+ ;; Use dynamic binding to modify/restore the environment
+ (setq success (zerop (let ((process-environment (cl-list* goroot-env gopath-env process-environment)))
+ (apply #'call-process args))))
+ (insert "\n")
+ (compilation-mode)
+ (setq compilation-error-screen-columns nil)
+
+ ;; On success, print the one-line result in the message bar,
+ ;; and hide the *go-rename* buffer.
+ (if success
+ (progn
+ (message "%s" (go--buffer-string-no-trailing-space))
+ (gofmt--kill-error-buffer (current-buffer)))
+ ;; failure
+ (let ((w (display-buffer (current-buffer))))
+ (message "gorename exited")
+ (set-window-point w (point-min)))))))
+
+ ;; Reload the modified files, saving line/col.
+ ;; (Don't restore the point since the text has changed.)
+ ;;
+ ;; TODO(adonovan): should we also do this for all other files
+ ;; that were updated (the tool can print them)?
+ (let ((line (line-number-at-pos))
+ (col (current-column)))
+ (revert-buffer t t t) ; safe, because we just saved it
+ (goto-char (point-min))
+ (forward-line (1- line))
+ (forward-char col)))
+
+
+(defun go--buffer-string-no-trailing-space ()
+ (replace-regexp-in-string "[\t\n ]*\\'"
+ ""
+ (buffer-substring (point-min) (point-max))))
+
+(provide 'go-rename)
+
+;;; go-rename.el ends here