diff options
Diffstat (limited to 'misc/emacs')
-rw-r--r-- | misc/emacs/go-mode-load.el | 96 | ||||
-rw-r--r-- | misc/emacs/go-mode.el | 1196 |
2 files changed, 0 insertions, 1292 deletions
diff --git a/misc/emacs/go-mode-load.el b/misc/emacs/go-mode-load.el deleted file mode 100644 index 3fc35c116..000000000 --- a/misc/emacs/go-mode-load.el +++ /dev/null @@ -1,96 +0,0 @@ -;;; go-mode-load.el --- automatically extracted autoloads -;;; Commentary: - -;; To install go-mode, add the following lines to your .emacs file: -;; (add-to-list 'load-path "PATH CONTAINING go-mode-load.el" t) -;; (require 'go-mode-load) -;; -;; After this, go-mode will be used for files ending in '.go'. -;; -;; To compile go-mode from the command line, run the following -;; emacs -batch -f batch-byte-compile go-mode.el -;; -;; See go-mode.el for documentation. -;; -;; To update this file, evaluate the following form -;; (let ((generated-autoload-file buffer-file-name)) (update-file-autoloads "go-mode.el")) - -;;; Code: - - -;;;### (autoloads (go-download-play godoc gofmt-before-save go-mode) -;;;;;; "go-mode" "go-mode.el" (20767 50749)) -;;; Generated autoloads from go-mode.el - -(autoload 'go-mode "go-mode" "\ -Major mode for editing Go source text. - -This mode provides (not just) basic editing capabilities for -working with Go code. It offers almost complete syntax -highlighting, indentation that is almost identical to gofmt, -proper parsing of the buffer content to allow features such as -navigation by function, manipulation of comments or detection of -strings. - -Additionally to these core features, it offers various features to -help with writing Go code. You can directly run buffer content -through gofmt, read godoc documentation from within Emacs, modify -and clean up the list of package imports or interact with the -Playground (uploading and downloading pastes). - -The following extra functions are defined: - -- `gofmt' -- `godoc' -- `go-import-add' -- `go-remove-unused-imports' -- `go-goto-imports' -- `go-play-buffer' and `go-play-region' -- `go-download-play' - -If you want to automatically run `gofmt' before saving a file, -add the following hook to your emacs configuration: - -\(add-hook 'before-save-hook 'gofmt-before-save) - -If you're looking for even more integration with Go, namely -on-the-fly syntax checking, auto-completion and snippets, it is -recommended to look at goflymake -\(https://github.com/dougm/goflymake), gocode -\(https://github.com/nsf/gocode) and yasnippet-go -\(https://github.com/dominikh/yasnippet-go) - -\(fn)" t nil) - -(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode)) - -(autoload 'gofmt-before-save "go-mode" "\ -Add this to .emacs to run gofmt on the current buffer when saving: - (add-hook 'before-save-hook 'gofmt-before-save). - -Note that this will cause go-mode to get loaded the first time -you save any file, kind of defeating the point of autoloading. - -\(fn)" t nil) - -(autoload 'godoc "go-mode" "\ -Show go documentation for a query, much like M-x man. - -\(fn QUERY)" t nil) - -(autoload 'go-download-play "go-mode" "\ -Downloads a paste from the playground and inserts it in a Go -buffer. Tries to look for a URL at point. - -\(fn URL)" t nil) - -;;;*** - -(provide 'go-mode-load) -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; coding: utf-8 -;; End: -;;; go-mode-load.el ends here diff --git a/misc/emacs/go-mode.el b/misc/emacs/go-mode.el deleted file mode 100644 index 6333ff966..000000000 --- a/misc/emacs/go-mode.el +++ /dev/null @@ -1,1196 +0,0 @@ -;;; go-mode.el --- Major mode for the Go programming language - -;; Copyright 2013 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. - -(require 'cl) -(require 'etags) -(require 'ffap) -(require 'find-file) -(require 'ring) -(require 'url) - -;; 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--kill-whole-line instead of kill-whole-line (called -;; kill-entire-line in XEmacs) -;; -;; - Use go--position-bytes instead of position-bytes -(defmacro go--xemacs-p () - `(featurep 'xemacs)) - -(defalias 'go--kill-whole-line - (if (fboundp 'kill-whole-line) - #'kill-whole-line - #'kill-entire-line)) - -;; Delete the current line without putting it in the kill-ring. -(defun go--delete-whole-line (&optional arg) - ;; Emacs uses both kill-region and kill-new, Xemacs only uses - ;; kill-region. In both cases we turn them into operations that do - ;; not modify the kill ring. This solution does depend on the - ;; implementation of kill-line, but it's the only viable solution - ;; that does not require to write kill-line from scratch. - (flet ((kill-region (beg end) - (delete-region beg end)) - (kill-new (s) ())) - (go--kill-whole-line arg))) - -;; 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) - ;; 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 "\\_>"))) - -;; Move up one level of parentheses. -(defun go-goto-opening-parenthesis (&optional legacy-unused) - ;; The old implementation of go-goto-opening-parenthesis had an - ;; optional argument to speed up the function. It didn't change the - ;; function's outcome. - - ;; Silently fail if there's no matching opening parenthesis. - (condition-case nil - (backward-up-list) - (scan-error nil))) - - -(defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]") -(defconst go-identifier-regexp "[[:word:][:multibyte:]]+") -(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-meth-regexp (concat - (go--regexp-enclose-in-symbol "func") "\\s *\\(?:(\\s *" - "\\(" go-identifier-regexp "\\s +\\)?" go-type-regexp - "\\s *)\\s *\\)?\\(" - go-identifier-regexp - "\\)(")) -(defconst go-builtins - '("append" "cap" "close" "complex" "copy" - "delete" "imag" "len" "make" "new" - "panic" "print" "println" "real" "recover") - "All built-in functions in the Go language. Used for font locking.") - -(defconst go-mode-keywords - '("break" "default" "func" "interface" "select" - "case" "defer" "go" "map" "struct" - "chan" "else" "goto" "package" "switch" - "const" "fallthrough" "if" "range" "type" - "continue" "for" "import" "return" "var") - "All keywords in the Go language. Used for font locking.") - -(defconst go-constants '("nil" "true" "false" "iota")) -(defconst go-type-name-regexp (concat "\\(?:[*(]\\)*\\(?:" go-identifier-regexp "\\.\\)?\\(" go-identifier-regexp "\\)")) - -(defvar go-dangling-cache) -(defvar go-godoc-history nil) -(defvar go--coverage-current-file-name) - -(defgroup go nil - "Major mode for editing Go code" - :group 'languages) - -(defgroup go-cover nil - "Options specific to `cover`" - :group 'go) - -(defcustom go-fontify-function-calls t - "Fontify function and method calls if this is non-nil." - :type 'boolean - :group 'go) - -(defcustom go-mode-hook nil - "Hook called by `go-mode'." - :type 'hook - :group 'go) - -(defcustom go-command "go" - "The 'go' command. Some users have multiple Go development -trees and invoke the 'go' tool via a wrapper that sets GOROOT and -GOPATH based on the current directory. Such users should -customize this variable to point to the wrapper script." - :type 'string - :group 'go) - -(defcustom gofmt-command "gofmt" - "The 'gofmt' command. Some users may replace this with 'goimports' -from https://github.com/bradfitz/goimports." - :type 'string - :group 'go) - -(defcustom go-other-file-alist - '(("_test\\.go\\'" (".go")) - ("\\.go\\'" ("_test.go"))) - "See the documentation of `ff-other-file-alist' for details." - :type '(repeat (list regexp (choice (repeat string) function))) - :group 'go) - -(defface go-coverage-untracked - '((t (:foreground "#505050"))) - "Coverage color of untracked code." - :group 'go-cover) - -(defface go-coverage-0 - '((t (:foreground "#c00000"))) - "Coverage color for uncovered code." - :group 'go-cover) -(defface go-coverage-1 - '((t (:foreground "#808080"))) - "Coverage color for covered code with weight 1." - :group 'go-cover) -(defface go-coverage-2 - '((t (:foreground "#748c83"))) - "Coverage color for covered code with weight 2." - :group 'go-cover) -(defface go-coverage-3 - '((t (:foreground "#689886"))) - "Coverage color for covered code with weight 3." - :group 'go-cover) -(defface go-coverage-4 - '((t (:foreground "#5ca489"))) - "Coverage color for covered code with weight 4." - :group 'go-cover) -(defface go-coverage-5 - '((t (:foreground "#50b08c"))) - "Coverage color for covered code with weight 5." - :group 'go-cover) -(defface go-coverage-6 - '((t (:foreground "#44bc8f"))) - "Coverage color for covered code with weight 6." - :group 'go-cover) -(defface go-coverage-7 - '((t (:foreground "#38c892"))) - "Coverage color for covered code with weight 7." - :group 'go-cover) -(defface go-coverage-8 - '((t (:foreground "#2cd495"))) - "Coverage color for covered code with weight 8. -For mode=set, all covered lines will have this weight." - :group 'go-cover) -(defface go-coverage-9 - '((t (:foreground "#20e098"))) - "Coverage color for covered code with weight 9." - :group 'go-cover) -(defface go-coverage-10 - '((t (:foreground "#14ec9b"))) - "Coverage color for covered code with weight 10." - :group 'go-cover) -(defface go-coverage-covered - '((t (:foreground "#2cd495"))) - "Coverage color of covered code." - :group 'go-cover) - -(defvar go-mode-syntax-table - (let ((st (make-syntax-table))) - (modify-syntax-entry ?+ "." st) - (modify-syntax-entry ?- "." st) - (modify-syntax-entry ?% "." st) - (modify-syntax-entry ?& "." st) - (modify-syntax-entry ?| "." st) - (modify-syntax-entry ?^ "." st) - (modify-syntax-entry ?! "." st) - (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 ?* ". 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 - (modify-syntax-entry ?_ "w" st) - - st) - "Syntax table for Go mode.") - -(defun go--build-font-lock-keywords () - ;; we cannot use 'symbols in regexp-opt because GNU Emacs <24 - ;; doesn't understand that - (append - `((,(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) - (,go-func-regexp 1 font-lock-function-name-face)) ;; function (not method) name - - (if go-fontify-function-calls - `((,(concat "\\(" go-identifier-regexp "\\)[[:space:]]*(") 1 font-lock-function-name-face) ;; function call/method name - (,(concat "[^[:word:][:multibyte:]](\\(" go-identifier-regexp "\\))[[:space:]]*(") 1 font-lock-function-name-face)) ;; bracketed function call - `((,go-func-meth-regexp 2 font-lock-function-name-face))) ;; method name - - `( - ("\\(`[^`]*`\\)" 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 "[^[: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:]]*\\(?:<-\\)?" 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 - ;; 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 - (,(concat (go--regexp-enclose-in-symbol "func") "[[:space:]]+(" go-identifier-regexp "[[:space:]]+" go-type-name-regexp ")") 1 font-lock-type-face) ;; Method receiver - (,(concat (go--regexp-enclose-in-symbol "func") "[[:space:]]+(" go-type-name-regexp ")") 1 font-lock-type-face) ;; Method receiver without variable name - ;; 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 . ?`))))) - -(defvar go-mode-map - (let ((m (make-sparse-keymap))) - (define-key m "}" #'go-mode-insert-and-indent) - (define-key m ")" #'go-mode-insert-and-indent) - (define-key m "," #'go-mode-insert-and-indent) - (define-key m ":" #'go-mode-insert-and-indent) - (define-key m "=" #'go-mode-insert-and-indent) - (define-key m (kbd "C-c C-a") #'go-import-add) - (define-key m (kbd "C-c C-j") #'godef-jump) - (define-key m (kbd "C-x 4 C-c C-j") #'godef-jump-other-window) - (define-key m (kbd "C-c C-d") #'godef-describe) - m) - "Keymap used by Go mode to implement electric keys.") - -(defun go-mode-insert-and-indent (key) - "Invoke the global binding of KEY, then reindent the line." - - (interactive (list (this-command-keys))) - (call-interactively (lookup-key (current-global-map) key)) - (indent-according-to-mode)) - -(defmacro go-paren-level () - `(car (syntax-ppss))) - -(defmacro go-in-string-or-comment-p () - `(nth 8 (syntax-ppss))) - -(defmacro go-in-string-p () - `(nth 3 (syntax-ppss))) - -(defmacro go-in-comment-p () - `(nth 4 (syntax-ppss))) - -(defmacro go-goto-beginning-of-string-or-comment () - `(goto-char (nth 8 (syntax-ppss)))) - -(defun go--backward-irrelevant (&optional stop-at-string) - "Skips backwards over any characters that are irrelevant for -indentation and related tasks. - -It skips over whitespace, comments, cases and labels and, if -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 "`") (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 "\\*/") - (backward-char)) - (if (go-in-comment-p) - (go-goto-beginning-of-string-or-comment)) - (setq pos (point)) - (beginning-of-line) - (if (or (looking-at (concat "^" go-label-regexp ":")) (looking-at "^[[:space:]]*\\(case .+\\|default\\):")) - (end-of-line 0) - (goto-char pos)) - (if (/= start-pos (point)) - (go--backward-irrelevant stop-at-string)) - (/= start-pos (point)))) - -(defun go--buffer-narrowed-p () - "Return non-nil if the current buffer is narrowed." - (/= (buffer-size) - (- (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. Returns nil on failure." - (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 () - "Returns non-nil if the current line is a continuation line." - (let* ((cur-line (line-number-at-pos)) - (val (gethash cur-line go-dangling-cache 'nope))) - (if (or (go--buffer-narrowed-p) (equal val 'nope)) - (save-excursion - (beginning-of-line) - (go--backward-irrelevant t) - (setq val (looking-back go-dangling-operators-regexp)) - (if (not (go--buffer-narrowed-p)) - (puthash cur-line val go-dangling-cache)))) - val)) - -(defun go--at-function-definition () - "Return non-nil if point is on the opening curly brace of a -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 -curly brace of that function and compare its position against the -curly brace we are checking. If they match, we return non-nil." - (if (= (char-after) ?\{) - (save-excursion - (let ((old-point (point)) - start-nesting) - (beginning-of-defun) - (when (looking-at "func ") - (setq start-nesting (go-paren-level)) - (skip-chars-forward "^{") - (while (> (go-paren-level) start-nesting) - (forward-char) - (skip-chars-forward "^{") 0) - (if (and (= (go-paren-level) start-nesting) (= old-point (point))) - t)))))) - -(defun go--indentation-for-opening-parenthesis () - "Return the semantic indentation for the current opening parenthesis. - -If point is on an opening curly brace and said curly brace -belongs to a function declaration, the indentation of the func -keyword will be returned. Otherwise the indentation of the -current line will be returned." - (save-excursion - (if (go--at-function-definition) - (progn - (beginning-of-defun) - (current-indentation)) - (current-indentation)))) - -(defun go-indentation-at-point () - (save-excursion - (let (start-nesting) - (back-to-indentation) - (setq start-nesting (go-paren-level)) - - (cond - ((go-in-string-p) - (current-indentation)) - ((looking-at "[])}]") - (go-goto-opening-parenthesis) - (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)) - ;; only one nesting for all dangling operators in one operation - (if (go-previous-line-has-dangling-op-p) - (current-indentation) - (+ (current-indentation) tab-width))) - ((zerop (go-paren-level)) - 0) - ((progn (go-goto-opening-parenthesis) (< (go-paren-level) start-nesting)) - (if (go-previous-line-has-dangling-op-p) - (current-indentation) - (+ (go--indentation-for-opening-parenthesis) tab-width))) - (t - (current-indentation)))))) - -(defun go-mode-indent-line () - (interactive) - (let (indent - shift-amt - (pos (- (point-max) (point))) - (point (point)) - (beg (line-beginning-position))) - (back-to-indentation) - (if (go-in-string-or-comment-p) - (goto-char point) - (setq indent (go-indentation-at-point)) - (if (looking-at (concat go-label-regexp ":\\([[:space:]]*/.+\\)?$\\|case .+:\\|default:")) - (decf indent tab-width)) - (setq shift-amt (- indent (current-column))) - (if (zerop shift-amt) - nil - (delete-region beg (point)) - (indent-to indent)) - ;; If initial point was within line's indentation, - ;; position after the indentation. Else stay at same point in text. - (if (> (- (point-max) pos) (point)) - (goto-char (- (point-max) pos)))))) - -(defun go-beginning-of-defun (&optional count) - (setq count (or count 1)) - (let ((first t) - failure) - (dotimes (i (abs count)) - (while (and (not failure) - (or first (go-in-string-or-comment-p))) - (if (>= count 0) - (progn - (go--backward-irrelevant) - (if (not (re-search-backward go-func-meth-regexp nil t)) - (setq failure t))) - (if (looking-at go-func-meth-regexp) - (forward-char)) - (if (not (re-search-forward go-func-meth-regexp nil t)) - (setq failure t))) - (setq first nil))) - (if (< count 0) - (beginning-of-line)) - (not failure))) - -(defun go-end-of-defun () - (let (orig-level) - ;; It can happen that we're not placed before a function by emacs - (if (not (looking-at "func")) - (go-beginning-of-defun -1)) - (skip-chars-forward "^{") - (forward-char) - (setq orig-level (go-paren-level)) - (while (>= (go-paren-level) orig-level) - (skip-chars-forward "^}") - (forward-char)))) - -;;;###autoload -(define-derived-mode go-mode prog-mode "Go" - "Major mode for editing Go source text. - -This mode provides (not just) basic editing capabilities for -working with Go code. It offers almost complete syntax -highlighting, indentation that is almost identical to gofmt and -proper parsing of the buffer content to allow features such as -navigation by function, manipulation of comments or detection of -strings. - -In addition to these core features, it offers various features to -help with writing Go code. You can directly run buffer content -through gofmt, read godoc documentation from within Emacs, modify -and clean up the list of package imports or interact with the -Playground (uploading and downloading pastes). - -The following extra functions are defined: - -- `gofmt' -- `godoc' -- `go-import-add' -- `go-remove-unused-imports' -- `go-goto-imports' -- `go-play-buffer' and `go-play-region' -- `go-download-play' -- `godef-describe' and `godef-jump' -- `go-coverage' - -If you want to automatically run `gofmt' before saving a file, -add the following hook to your emacs configuration: - -\(add-hook 'before-save-hook #'gofmt-before-save) - -If you want to use `godef-jump' instead of etags (or similar), -consider binding godef-jump to `M-.', which is the default key -for `find-tag': - -\(add-hook 'go-mode-hook (lambda () - (local-set-key (kbd \"M-.\") #'godef-jump))) - -Please note that godef is an external dependency. You can install -it with - -go get code.google.com/p/rog-go/exp/cmd/godef - - -If you're looking for even more integration with Go, namely -on-the-fly syntax checking, auto-completion and snippets, it is -recommended that you look at goflymake -\(https://github.com/dougm/goflymake), gocode -\(https://github.com/nsf/gocode), go-eldoc -\(github.com/syohex/emacs-go-eldoc) and yasnippet-go -\(https://github.com/dominikh/yasnippet-go)" - - ;; Font lock - (set (make-local-variable 'font-lock-defaults) - '(go--build-font-lock-keywords)) - - ;; Indentation - (set (make-local-variable 'indent-line-function) #'go-mode-indent-line) - - ;; Comments - (set (make-local-variable 'comment-start) "// ") - (set (make-local-variable 'comment-end) "") - (set (make-local-variable 'comment-use-syntax) t) - (set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *") - - (set (make-local-variable 'beginning-of-defun-function) #'go-beginning-of-defun) - (set (make-local-variable 'end-of-defun-function) #'go-end-of-defun) - - (set (make-local-variable 'parse-sexp-lookup-properties) t) - (if (boundp 'syntax-propertize-function) - (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 '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) - - ;; ff-find-other-file - (setq ff-other-file-alist 'go-other-file-alist) - - (setq imenu-generic-expression - '(("type" "^type *\\([^ \t\n\r\f]*\\)" 1) - ("func" "^func *\\(.*\\) {" 1))) - (imenu-add-to-menubar "Index") - - ;; Go style - (setq indent-tabs-mode t) - - ;; Handle unit test failure output in compilation-mode - ;; - ;; Note the final t argument to add-to-list for append, ie put these at the - ;; *ends* of compilation-error-regexp-alist[-alist]. We want go-test to be - ;; handled first, otherwise other elements will match that don't work, and - ;; those alists are traversed in *reverse* order: - ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2001-12/msg00674.html - (when (and (boundp 'compilation-error-regexp-alist) - (boundp 'compilation-error-regexp-alist-alist)) - (add-to-list 'compilation-error-regexp-alist 'go-test t) - (add-to-list 'compilation-error-regexp-alist-alist - '(go-test . ("^\t+\\([^()\t\n]+\\):\\([0-9]+\\):? .*$" 1 2)) t))) - -;;;###autoload -(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode)) - -(defun go--apply-rcs-patch (patch-buffer) - "Apply an RCS-formatted diff from PATCH-BUFFER to the current -buffer." - (let ((target-buffer (current-buffer)) - ;; Relative offset between buffer line numbers and line numbers - ;; in patch. - ;; - ;; Line numbers in the patch are based on the source file, so - ;; we have to keep an offset when making changes to the - ;; buffer. - ;; - ;; Appending lines decrements the offset (possibly making it - ;; negative), deleting lines increments it. This order - ;; simplifies the forward-line invocations. - (line-offset 0)) - (save-excursion - (with-current-buffer patch-buffer - (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")) - (forward-line) - (let ((action (match-string 1)) - (from (string-to-number (match-string 2))) - (len (string-to-number (match-string 3)))) - (cond - ((equal action "a") - (let ((start (point))) - (forward-line len) - (let ((text (buffer-substring start (point)))) - (with-current-buffer target-buffer - (decf line-offset len) - (goto-char (point-min)) - (forward-line (- from len line-offset)) - (insert text))))) - ((equal action "d") - (with-current-buffer target-buffer - (go--goto-line (- from line-offset)) - (incf line-offset len) - (go--delete-whole-line len))) - (t - (error "invalid rcs patch or internal error in go--apply-rcs-patch"))))))))) - -(defun gofmt () - "Formats the current buffer according to the gofmt tool." - - (interactive) - (let ((tmpfile (make-temp-file "gofmt" nil ".go")) - (patchbuf (get-buffer-create "*Gofmt patch*")) - (errbuf (get-buffer-create "*Gofmt Errors*")) - (coding-system-for-read 'utf-8) - (coding-system-for-write 'utf-8)) - - (with-current-buffer errbuf - (setq buffer-read-only nil) - (erase-buffer)) - (with-current-buffer patchbuf - (erase-buffer)) - - (write-region nil nil tmpfile) - - ;; We're using errbuf for the mixed stdout and stderr output. This - ;; is not an issue because gofmt -w does not produce any stdout - ;; output in case of success. - (if (zerop (call-process gofmt-command nil errbuf nil "-w" tmpfile)) - (if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile)) - (progn - (kill-buffer errbuf) - (message "Buffer is already gofmted")) - (go--apply-rcs-patch patchbuf) - (kill-buffer errbuf) - (message "Applied gofmt")) - (message "Could not apply gofmt. Check errors for details") - (gofmt--process-errors (buffer-file-name) tmpfile errbuf)) - - (kill-buffer patchbuf) - (delete-file tmpfile))) - - -(defun gofmt--process-errors (filename tmpfile errbuf) - ;; Convert the gofmt stderr to something understood by the compilation mode. - (with-current-buffer errbuf - (goto-char (point-min)) - (insert "gofmt errors:\n") - (while (search-forward-regexp (concat "^\\(" (regexp-quote tmpfile) "\\):") nil t) - (replace-match (file-name-nondirectory filename) t t nil 1)) - (compilation-mode) - (display-buffer errbuf))) - -;;;###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). - -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) - (when (eq major-mode 'go-mode) (gofmt))) - -(defun godoc--read-query () - "Read a godoc query from the minibuffer." - ;; Compute the default query as the symbol under the cursor. - ;; TODO: This does the wrong thing for e.g. multipart.NewReader (it only grabs - ;; half) but I see no way to disambiguate that from e.g. foobar.SomeMethod. - (let* ((bounds (bounds-of-thing-at-point 'symbol)) - (symbol (if bounds - (buffer-substring-no-properties (car bounds) - (cdr bounds))))) - (completing-read (if symbol - (format "godoc (default %s): " symbol) - "godoc: ") - (go--old-completion-list-style (go-packages)) nil nil nil 'go-godoc-history symbol))) - -(defun godoc--get-buffer (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. - (when buffer (kill-buffer buffer)) - (get-buffer-create buffer-name))) - -(defun godoc--buffer-sentinel (proc event) - "Sentinel function run when godoc command completes." - (with-current-buffer (process-buffer proc) - (cond ((string= event "finished\n") ;; Successful exit. - (goto-char (point-min)) - (view-mode 1) - (display-buffer (current-buffer) t)) - ((/= (process-exit-status proc) 0) ;; Error exit. - (let ((output (buffer-string))) - (kill-buffer (current-buffer)) - (message (concat "godoc: " output))))))) - -;;;###autoload -(defun godoc (query) - "Show go documentation for a query, much like M-x man." - (interactive (list (godoc--read-query))) - (unless (string= query "") - (set-process-sentinel - (start-process-shell-command "godoc" (godoc--get-buffer query) - (concat "godoc " query)) - 'godoc--buffer-sentinel) - nil)) - -(defun go-goto-imports () - "Move point to the block of imports. - -If using - - import ( - \"foo\" - \"bar\" - ) - -it will move point directly behind the last import. - -If using - - import \"foo\" - import \"bar\" - -it will move point to the next line after the last import. - -If no imports can be found, point will be moved after the package -declaration." - (interactive) - ;; FIXME if there's a block-commented import before the real - ;; imports, we'll jump to that one. - - ;; Generally, this function isn't very forgiving. it'll bark on - ;; extra whitespace. It works well for clean code. - (let ((old-point (point))) - (goto-char (point-min)) - (cond - ((re-search-forward "^import ()" nil t) - (backward-char 1) - 'block-empty) - ((re-search-forward "^import ([^)]+)" nil t) - (backward-char 2) - 'block) - ((re-search-forward "\\(^import \\([^\"]+ \\)?\"[^\"]+\"\n?\\)+" nil t) - 'single) - ((re-search-forward "^[[:space:]\n]*package .+?\n" nil t) - (message "No imports found, moving point after package declaration") - 'none) - (t - (goto-char old-point) - (message "No imports or package declaration found. Is this really a Go file?") - 'fail)))) - -(defun go-play-buffer () - "Like `go-play-region', but acts on the entire buffer." - (interactive) - (go-play-region (point-min) (point-max))) - -(defun go-play-region (start end) - "Send the region to the Playground and stores the resulting -link in the kill ring." - (interactive "r") - (let* ((url-request-method "POST") - (url-request-extra-headers - '(("Content-Type" . "application/x-www-form-urlencoded"))) - (url-request-data - (encode-coding-string - (buffer-substring-no-properties start end) - 'utf-8)) - (content-buf (url-retrieve - "http://play.golang.org/share" - (lambda (arg) - (cond - ((equal :error (car arg)) - (signal 'go-play-error (cdr arg))) - (t - (re-search-forward "\n\n") - (kill-new (format "http://play.golang.org/p/%s" (buffer-substring (point) (point-max)))) - (message "http://play.golang.org/p/%s" (buffer-substring (point) (point-max))))))))))) - -;;;###autoload -(defun go-download-play (url) - "Downloads a paste from the playground and inserts it in a Go -buffer. Tries to look for a URL at point." - (interactive (list (read-from-minibuffer "Playground URL: " (ffap-url-p (ffap-string-at-point 'url))))) - (with-current-buffer - (let ((url-request-method "GET") url-request-data url-request-extra-headers) - (url-retrieve-synchronously (concat url ".go"))) - (let ((buffer (generate-new-buffer (concat (car (last (split-string url "/"))) ".go")))) - (goto-char (point-min)) - (re-search-forward "\n\n") - (copy-to-buffer buffer (point) (point-max)) - (kill-buffer) - (with-current-buffer buffer - (go-mode) - (switch-to-buffer buffer))))) - -(defun go-propertize-syntax (start end) - (save-excursion - (goto-char start) - (while (search-forward "\\" end t) - (put-text-property (1- (point)) (point) 'syntax-table (if (= (char-after) ?`) '(1) '(9)))))) - -(defun go-import-add (arg import) - "Add a new import to the list of imports. - -When called with a prefix argument asks for an alternative name -to import the package as. - -If no list exists yet, one will be created if possible. - -If an identical import has been commented, it will be -uncommented, otherwise a new import will be added." - - ;; - If there's a matching `// import "foo"`, uncomment it - ;; - If we're in an import() block and there's a matching `"foo"`, uncomment it - ;; - Otherwise add a new import, with the appropriate syntax - (interactive - (list - current-prefix-arg - (replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go--old-completion-list-style (go-packages)))))) - (save-excursion - (let (as line import-start) - (if arg - (setq as (read-from-minibuffer "Import as: "))) - (if as - (setq line (format "%s \"%s\"" as import)) - (setq line (format "\"%s\"" import))) - - (goto-char (point-min)) - (if (re-search-forward (concat "^[[:space:]]*//[[:space:]]*import " line "$") nil t) - (uncomment-region (line-beginning-position) (line-end-position)) - (case (go-goto-imports) - ('fail (message "Could not find a place to add import.")) - ('block-empty - (insert "\n\t" line "\n")) - ('block - (save-excursion - (re-search-backward "^import (") - (setq import-start (point))) - (if (re-search-backward (concat "^[[:space:]]*//[[:space:]]*" line "$") import-start t) - (uncomment-region (line-beginning-position) (line-end-position)) - (insert "\n\t" line))) - ('single (insert "import " line "\n")) - ('none (insert "\nimport (\n\t" line "\n)\n"))))))) - -(defun go-root-and-paths () - (let* ((output (split-string (shell-command-to-string (concat go-command " env GOROOT GOPATH")) - "\n")) - (root (car output)) - (paths (split-string (cadr output) ":"))) - (append (list root) paths))) - -(defun go--string-prefix-p (s1 s2 &optional ignore-case) - "Return non-nil if S1 is a prefix of S2. -If IGNORE-CASE is non-nil, the comparison is case-insensitive." - (eq t (compare-strings s1 nil nil - s2 0 (length s1) ignore-case))) - -(defun go--directory-dirs (dir) - "Recursively return all subdirectories in DIR." - (if (file-directory-p dir) - (let ((dir (directory-file-name dir)) - (dirs '()) - (files (directory-files dir nil nil t))) - (dolist (file files) - (unless (member file '("." "..")) - (let ((file (concat dir "/" file))) - (if (file-directory-p file) - (setq dirs (append (cons file - (go--directory-dirs file)) - dirs)))))) - dirs) - '())) - - -(defun go-packages () - (sort - (delete-dups - (mapcan - (lambda (topdir) - (let ((pkgdir (concat topdir "/pkg/"))) - (mapcan (lambda (dir) - (mapcar (lambda (file) - (let ((sub (substring file (length pkgdir) -2))) - (unless (or (go--string-prefix-p "obj/" sub) (go--string-prefix-p "tool/" sub)) - (mapconcat #'identity (cdr (split-string sub "/")) "/")))) - (if (file-directory-p dir) - (directory-files dir t "\\.a$")))) - (if (file-directory-p pkgdir) - (go--directory-dirs pkgdir))))) - (go-root-and-paths))) - #'string<)) - -(defun go-unused-imports-lines () - ;; FIXME Technically, -o /dev/null fails in quite some cases (on - ;; Windows, when compiling from within GOPATH). Practically, - ;; however, it has the same end result: There won't be a - ;; compiled binary/archive, and we'll get our import errors when - ;; there are any. - (reverse (remove nil - (mapcar - (lambda (line) - (if (string-match "^\\(.+\\):\\([[:digit:]]+\\): imported and not used: \".+\".*$" line) - (if (string= (file-truename (match-string 1 line)) (file-truename buffer-file-name)) - (string-to-number (match-string 2 line))))) - (split-string (shell-command-to-string - (concat go-command - (if (string-match "_test\.go$" buffer-file-truename) - " test -c" - " build -o /dev/null"))) "\n"))))) - -(defun go-remove-unused-imports (arg) - "Removes all unused imports. If ARG is non-nil, unused imports -will be commented, otherwise they will be removed completely." - (interactive "P") - (save-excursion - (let ((cur-buffer (current-buffer)) flymake-state lines) - (when (boundp 'flymake-mode) - (setq flymake-state flymake-mode) - (flymake-mode-off)) - (save-some-buffers nil (lambda () (equal cur-buffer (current-buffer)))) - (if (buffer-modified-p) - (message "Cannot operate on unsaved buffer") - (setq lines (go-unused-imports-lines)) - (dolist (import lines) - (go--goto-line import) - (beginning-of-line) - (if arg - (comment-region (line-beginning-position) (line-end-position)) - (go--delete-whole-line))) - (message "Removed %d imports" (length lines))) - (if flymake-state (flymake-mode-on))))) - -(defun godef--find-file-line-column (specifier other-window) - "Given a file name in the format of `filename:line:column', -visit FILENAME and go to line LINE and column COLUMN." - (if (not (string-match "\\(.+\\):\\([0-9]+\\):\\([0-9]+\\)" specifier)) - ;; We've only been given a directory name - (funcall (if other-window #'find-file-other-window #'find-file) specifier) - (let ((filename (match-string 1 specifier)) - (line (string-to-number (match-string 2 specifier))) - (column (string-to-number (match-string 3 specifier)))) - (with-current-buffer (funcall (if other-window #'find-file-other-window #'find-file) filename) - (go--goto-line line) - (beginning-of-line) - (forward-char (1- column)) - (if (buffer-modified-p) - (message "Buffer is modified, file position might not have been correct")))))) - -(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*"))) - (with-current-buffer outbuf - (erase-buffer)) - (call-process-region (point-min) - (point-max) - "godef" - 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"))))) - -(defun godef-describe (point) - "Describe the expression at POINT." - (interactive "d") - (condition-case nil - (let ((description (cdr (butlast (godef--call point) 1)))) - (if (not description) - (message "No description found for expression at point") - (message "%s" (mapconcat #'identity description "\n")))) - (file-error (message "Could not run godef binary")))) - -(defun godef-jump (point &optional other-window) - "Jump to the definition of the expression at POINT." - (interactive "d") - (condition-case nil - (let ((file (car (godef--call point)))) - (cond - ((string= "-" file) - (message "godef: expression is not defined anywhere")) - ((string= "godef: no identifier found" file) - (message "%s" file)) - ((go--string-prefix-p "godef: no declaration found for " file) - (message "%s" file)) - ((go--string-prefix-p "error finding import path for " file) - (message "%s" file)) - (t - (push-mark) - (ring-insert find-tag-marker-ring (point-marker)) - (godef--find-file-line-column file other-window)))) - (file-error (message "Could not run godef binary")))) - -(defun godef-jump-other-window (point) - (interactive "d") - (godef-jump point t)) - -(defun go--goto-line (line) - (goto-char (point-min)) - (forward-line (1- line))) - -(defun go--line-column-to-point (line column) - (save-excursion - (go--goto-line line) - (forward-char (1- column)) - (point))) - -(defstruct go--covered - start-line start-column end-line end-column covered count) - -(defun go--coverage-file () - "Return the coverage file to use, either by reading it from the -current coverage buffer or by prompting for it." - (if (boundp 'go--coverage-current-file-name) - go--coverage-current-file-name - (read-file-name "Coverage file: " nil nil t))) - -(defun go--coverage-origin-buffer () - "Return the buffer to base the coverage on." - (or (buffer-base-buffer) (current-buffer))) - -(defun go--coverage-face (count divisor) - "Return the intensity face for COUNT when using DIVISOR -to scale it to a range [0,10]. - -DIVISOR scales the absolute cover count to values from 0 to 10. -For DIVISOR = 0 the count will always translate to 8." - (let* ((norm (cond - ((= count 0) - -0.1) ;; Uncovered code, set to -0.1 so n becomes 0. - ((= divisor 0) - 0.8) ;; covermode=set, set to 0.8 so n becomes 8. - (t - (/ (log count) divisor)))) - (n (1+ (floor (* norm 9))))) ;; Convert normalized count [0,1] to intensity [0,10] - (concat "go-coverage-" (number-to-string n)))) - -(defun go--coverage-make-overlay (range divisor) - "Create a coverage overlay for a RANGE of covered/uncovered -code. Uses DIVISOR to scale absolute counts to a [0,10] scale." - (let* ((count (go--covered-count range)) - (face (go--coverage-face count divisor)) - (ov (make-overlay (go--line-column-to-point (go--covered-start-line range) - (go--covered-start-column range)) - (go--line-column-to-point (go--covered-end-line range) - (go--covered-end-column range))))) - - (overlay-put ov 'face face) - (overlay-put ov 'help-echo (format "Count: %d" count)))) - -(defun go--coverage-clear-overlays () - "Remove existing overlays and put a single untracked overlay -over the entire buffer." - (remove-overlays) - (overlay-put (make-overlay (point-min) (point-max)) - 'face - 'go-coverage-untracked)) - -(defun go--coverage-parse-file (coverage-file file-name) - "Parse COVERAGE-FILE and extract coverage information and -divisor for FILE-NAME." - (let (ranges - (max-count 0)) - (with-temp-buffer - (insert-file-contents coverage-file) - (go--goto-line 2) ;; Skip over mode - (while (not (eobp)) - (let* ((parts (split-string (buffer-substring (point-at-bol) (point-at-eol)) ":")) - (file (car parts)) - (rest (split-string (nth 1 parts) "[., ]"))) - - (destructuring-bind - (start-line start-column end-line end-column num count) - (mapcar #'string-to-number rest) - - (when (string= (file-name-nondirectory file) file-name) - (if (> count max-count) - (setq max-count count)) - (push (make-go--covered :start-line start-line - :start-column start-column - :end-line end-line - :end-column end-column - :covered (/= count 0) - :count count) - ranges))) - - (forward-line))) - - (list ranges (if (> max-count 0) (log max-count) 0))))) - -(defun go-coverage (&optional coverage-file) - "Open a clone of the current buffer and overlay it with -coverage information gathered via go test -coverprofile=COVERAGE-FILE. - -If COVERAGE-FILE is nil, it will either be inferred from the -current buffer if it's already a coverage buffer, or be prompted -for." - (interactive) - (let* ((cur-buffer (current-buffer)) - (origin-buffer (go--coverage-origin-buffer)) - (gocov-buffer-name (concat (buffer-name origin-buffer) "<gocov>")) - (coverage-file (or coverage-file (go--coverage-file))) - (ranges-and-divisor (go--coverage-parse-file - coverage-file - (file-name-nondirectory (buffer-file-name origin-buffer)))) - (cov-mtime (nth 5 (file-attributes coverage-file))) - (cur-mtime (nth 5 (file-attributes (buffer-file-name origin-buffer))))) - - (if (< (float-time cov-mtime) (float-time cur-mtime)) - (message "Coverage file is older than the source file.")) - - (with-current-buffer (or (get-buffer gocov-buffer-name) - (make-indirect-buffer origin-buffer gocov-buffer-name t)) - (set (make-local-variable 'go--coverage-current-file-name) coverage-file) - - (save-excursion - (go--coverage-clear-overlays) - (dolist (range (car ranges-and-divisor)) - (go--coverage-make-overlay range (cadr ranges-and-divisor)))) - - (if (not (eq cur-buffer (current-buffer))) - (display-buffer (current-buffer) #'display-buffer-reuse-window))))) - -(provide 'go-mode) |