| ;;; 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. |
| ;;; |
| ;;; Integration of the 'gorename' tool into Emacs. |
| ;;; |
| ;;; 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. |
| |
| (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) |
| |
| (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- (go--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 (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") |
| (shrink-window-if-larger-than-buffer w) |
| (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) |