dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

go-mode.el (112537B)


      1 ;;; go-mode.el --- Major mode for the Go programming language
      2 
      3 ;;; Commentary:
      4 
      5 ;; Copyright 2013 The go-mode Authors.  All rights reserved.
      6 ;; Use of this source code is governed by a BSD-style
      7 ;; license that can be found in the LICENSE file.
      8 
      9 ;; Author: The go-mode Authors
     10 ;; Version: 1.6.0
     11 ;; Keywords: languages go
     12 ;; Package-Requires: ((emacs "26.1"))
     13 ;; URL: https://github.com/dominikh/go-mode.el
     14 ;;
     15 ;; This file is not part of GNU Emacs.
     16 
     17 ;;; Code:
     18 
     19 (require 'cl-lib)
     20 (require 'compile)
     21 (require 'etags)
     22 (require 'ffap)
     23 (require 'find-file)
     24 (require 'ring)
     25 (require 'url)
     26 (require 'xref)
     27 
     28 
     29 (eval-when-compile
     30   (defmacro go--forward-word (&optional arg)
     31    (if (fboundp 'forward-word-strictly)
     32        `(forward-word-strictly ,arg)
     33      `(forward-word ,arg))))
     34 
     35 (defun go--delete-whole-line (&optional arg)
     36   "Delete the current line without putting it in the `kill-ring'.
     37 Derived from function `kill-whole-line'.  ARG is defined as for that
     38 function."
     39   (setq arg (or arg 1))
     40   (if (and (> arg 0)
     41            (eobp)
     42            (save-excursion (forward-visible-line 0) (eobp)))
     43       (signal 'end-of-buffer nil))
     44   (if (and (< arg 0)
     45            (bobp)
     46            (save-excursion (end-of-visible-line) (bobp)))
     47       (signal 'beginning-of-buffer nil))
     48   (cond ((zerop arg)
     49          (delete-region (progn (forward-visible-line 0) (point))
     50                         (progn (end-of-visible-line) (point))))
     51         ((< arg 0)
     52          (delete-region (progn (end-of-visible-line) (point))
     53                         (progn (forward-visible-line (1+ arg))
     54                                (unless (bobp)
     55                                  (backward-char))
     56                                (point))))
     57         (t
     58          (delete-region (progn (forward-visible-line 0) (point))
     59                         (progn (forward-visible-line arg) (point))))))
     60 
     61 (defun go-goto-opening-parenthesis (&optional _legacy-unused)
     62   "Move up one level of parentheses.
     63 
     64 Return non-nil if there was a paren to move up to."
     65   ;; The old implementation of go-goto-opening-parenthesis had an
     66   ;; optional argument to speed up the function.  It didn't change the
     67   ;; function's outcome.
     68 
     69   ;; Silently fail if there's no matching opening parenthesis.
     70   (let ((open-char (nth 1 (syntax-ppss))))
     71     (when open-char
     72       (goto-char open-char))))
     73 
     74 
     75 (defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]")
     76 (defconst go--max-dangling-operator-length 2
     77   "The maximum length of dangling operators.
     78 This must be at least the length of the longest string matched by
     79 ‘go-dangling-operators-regexp’ and must be updated whenever that
     80 constant is changed.")
     81 
     82 (defconst go-identifier-regexp "[[:word:][:multibyte:]]+")
     83 (defconst go-type-name-no-prefix-regexp "\\(?:[[:word:][:multibyte:]]+\\.\\)?[[:word:][:multibyte:]]+")
     84 (defconst go-qualified-identifier-regexp (concat go-identifier-regexp "\\." go-identifier-regexp))
     85 (defconst go-label-regexp go-identifier-regexp)
     86 (defconst go-type-regexp "[[:word:][:multibyte:]*]+")
     87 (defconst go-func-regexp (concat "\\_<func\\_>\\s *\\(" go-identifier-regexp "\\)"))
     88 (defconst go-func-meth-regexp (concat
     89                                "\\_<func\\_>\\s *\\(?:(\\s *"
     90                                "\\(" go-identifier-regexp "\\s +\\)?" go-type-regexp
     91                                "\\s *)\\s *\\)?\\("
     92                                go-identifier-regexp
     93                                "\\)("))
     94 
     95 (defconst go--comment-start-regexp "[[:space:]]*\\(?:/[/*]\\)")
     96 (defconst go--case-regexp "\\([[:space:]]*case\\([[:space:]]\\|$\\)\\)")
     97 (defconst go--case-or-default-regexp (concat "\\(" go--case-regexp "\\|"  "[[:space:]]*default:\\)"))
     98 
     99 (defconst go-builtins
    100   '("append" "cap"   "close"   "complex" "copy"
    101     "delete" "imag"  "len"     "make"    "new"
    102     "panic"  "print" "println" "real"    "recover")
    103   "All built-in functions in the Go language.  Used for font locking.")
    104 
    105 (defconst go-mode-keywords
    106   '("break"    "default"     "func"   "interface" "select"
    107     "case"     "defer"       "go"     "map"       "struct"
    108     "chan"     "else"        "goto"   "package"   "switch"
    109     "const"    "fallthrough" "if"     "range"     "type"
    110     "continue" "for"         "import" "return"    "var")
    111   "All keywords in the Go language.  Used for font locking.")
    112 
    113 (defconst go-constants '("nil" "true" "false" "iota"))
    114 (defconst go-type-name-regexp (concat "\\**\\(\\(?:" go-identifier-regexp "\\.\\)?" go-identifier-regexp "\\)"))
    115 
    116 (defvar go-dangling-cache)
    117 (defvar go-godoc-history nil)
    118 (defvar go--coverage-current-file-name)
    119 
    120 (defgroup go nil
    121   "Major mode for editing Go code."
    122   :link '(url-link "https://github.com/dominikh/go-mode.el")
    123   :group 'languages)
    124 
    125 (defgroup go-cover nil
    126   "Options specific to `cover`."
    127   :group 'go)
    128 
    129 (defgroup godoc nil
    130   "Options specific to `godoc'."
    131   :group 'go)
    132 
    133 (defcustom go-fontify-function-calls t
    134   "Fontify function and method calls if this is non-nil."
    135   :type 'boolean
    136   :group 'go)
    137 
    138 (defcustom go-fontify-variables t
    139   "Fontify variable declarations if this is non-nil."
    140   :type 'boolean
    141   :group 'go)
    142 
    143 (defcustom go-mode-hook nil
    144   "Hook called by `go-mode'."
    145   :type 'hook
    146   :group 'go)
    147 
    148 (defcustom go-command "go"
    149   "The 'go' command.
    150 Some users have multiple Go development trees and invoke the 'go'
    151 tool via a wrapper that sets GOROOT and GOPATH based on the
    152 current directory.  Such users should customize this variable to
    153 point to the wrapper script."
    154   :type 'string
    155   :group 'go)
    156 
    157 (defcustom gofmt-command "gofmt"
    158   "The 'gofmt' command.
    159 Some users may replace this with 'goimports'
    160 from https://golang.org/x/tools/cmd/goimports."
    161   :type 'string
    162   :group 'go)
    163 
    164 (defcustom gofmt-args nil
    165   "Additional arguments to pass to gofmt."
    166   :type '(repeat string)
    167   :group 'go)
    168 
    169 (defcustom gofmt-show-errors 'buffer
    170   "Where to display gofmt error output.
    171 It can either be displayed in its own buffer, in the echo area, or not at all.
    172 
    173 Please note that Emacs outputs to the echo area when writing
    174 files and will overwrite gofmt's echo output if used from inside
    175 a `before-save-hook'."
    176   :type '(choice
    177           (const :tag "Own buffer" buffer)
    178           (const :tag "Echo area" echo)
    179           (const :tag "None" nil))
    180   :group 'go)
    181 
    182 (defcustom godef-command "godef"
    183   "The 'godef' command."
    184   :type 'string
    185   :group 'go)
    186 
    187 (defcustom go-other-file-alist
    188   '(("_test\\.go\\'" (".go"))
    189     ("\\.go\\'" ("_test.go")))
    190   "See the documentation of `ff-other-file-alist' for details."
    191   :type '(repeat (list regexp (choice (repeat string) function)))
    192   :group 'go)
    193 
    194 (defcustom go-packages-function 'go-packages-go-list
    195   "Function called by `go-packages' to determine the list of available packages.
    196 This is used in e.g. tab completion in `go-import-add'.
    197 
    198 This package provides two functions: `go-packages-go-list' uses
    199 'go list all' to determine all Go packages. `go-packages-native' uses
    200 elisp to find all .a files in all /pkg/ directories.
    201 `go-packages-native' is obsolete as it doesn't behave correctly with
    202 the Go build cache or Go modules."
    203   :type 'function
    204   :package-version '(go-mode . 1.4.0)
    205   :group 'go)
    206 
    207 (defcustom go-guess-gopath-functions (list #'go-plain-gopath)
    208   "Functions to call in sequence to detect a project's GOPATH.
    209 
    210 The functions in this list will be called one after another,
    211 until a function returns non-nil.  The order of the functions in
    212 this list is important, as some project layouts may superficially
    213 look like others."
    214   :type '(repeat function)
    215   :group 'go)
    216 
    217 (defcustom go-confirm-playground-uploads t
    218   "Ask before uploading code to the public Go Playground.
    219 
    220 Set this to nil to upload without prompting.
    221 "
    222   :type 'boolean
    223   :group 'go)
    224 
    225 (defcustom godoc-command "go doc"
    226   "Which executable to use for `godoc'.
    227 This can be either an absolute path or an executable in PATH."
    228   :type 'string
    229   :group 'go)
    230 
    231 (defcustom godoc-and-godef-command "go doc"
    232   "Which executable to use for `godoc-and-godef'.
    233 This can be either an absolute path or an executable in PATH."
    234   :type 'string
    235   :group 'go)
    236 
    237 (defcustom godoc-use-completing-read nil
    238   "Provide auto-completion for godoc.
    239 Only really desirable when using `godoc' instead of `go doc'."
    240   :type 'boolean
    241   :group 'godoc)
    242 
    243 (defcustom godoc-reuse-buffer nil
    244   "Reuse a single *godoc* buffer to display godoc-at-point calls.
    245 The default behavior is to open a separate buffer for each call."
    246   :type 'boolean
    247   :group 'godoc)
    248 
    249 (defcustom godoc-at-point-function #'godoc-and-godef
    250   "Function to call to display the documentation for an
    251 identifier at a given position.
    252 
    253 This package provides two functions: `godoc-and-godef' uses a
    254 combination of godef and godoc to find the documentation.  This
    255 approach has several caveats.  See its documentation for more
    256 information.  The second function, `godoc-gogetdoc' uses an
    257 additional tool that correctly determines the documentation for
    258 any identifier.  It provides better results than
    259 `godoc-and-godef'."
    260   :type 'function
    261   :group 'godoc)
    262 
    263 (defun godoc-and-godef (point)
    264   "Use a combination of godef and godoc to guess the documentation at POINT.
    265 
    266 Due to a limitation in godoc, it is not possible to differentiate
    267 between functions and methods, which may cause `godoc-at-point'
    268 to display more documentation than desired.  Furthermore, it
    269 doesn't work on package names or variables.
    270 
    271 Consider using ‘godoc-gogetdoc’ instead for more accurate results."
    272   (condition-case nil
    273       (let* ((output (godef--call point))
    274              (file (car output))
    275              (name-parts (split-string (cadr output) " "))
    276              (first (car name-parts)))
    277         (if (not (godef--successful-p file))
    278             (message "%s" (godef--error file))
    279           (go--godoc (format "%s %s"
    280                          (file-name-directory file)
    281                          (if (or (string= first "type") (string= first "const"))
    282                              (cadr name-parts)
    283                            (car name-parts)))
    284                     godoc-and-godef-command)))
    285     (file-error (message "Could not run godef binary"))))
    286 
    287 (defun godoc-gogetdoc (point)
    288   "Use the gogetdoc tool to find the documentation for an identifier at POINT.
    289 
    290 You can install gogetdoc with 'go get -u github.com/zmb3/gogetdoc'."
    291   (if (not (buffer-file-name (go--coverage-origin-buffer)))
    292       ;; TODO: gogetdoc supports unsaved files, but not introducing
    293       ;; new artificial files, so this limitation will stay for now.
    294       (error "Cannot use gogetdoc on a buffer without a file name"))
    295   (let ((posn (format "%s:#%d" (file-truename buffer-file-name) (1- (position-bytes point))))
    296         (out (godoc--get-buffer "<at point>")))
    297   (with-temp-buffer
    298     (go--insert-modified-files)
    299     (call-process-region (point-min) (point-max) "gogetdoc" nil out nil
    300                          "-modified"
    301                          (format "-pos=%s" posn)))
    302   (with-current-buffer out
    303     (goto-char (point-min))
    304     (godoc-mode)
    305     (display-buffer (current-buffer) t))))
    306 
    307 (defun go--kill-new-message (url)
    308   "Make URL the latest kill and print a message."
    309   (kill-new url)
    310   (message "%s" url))
    311 
    312 (defcustom go-play-browse-function 'go--kill-new-message
    313   "Function to call with the Playground URL.
    314 See `go-play-region' for more details."
    315   :type '(choice
    316           (const :tag "Nothing" nil)
    317           (const :tag "Kill + Message" go--kill-new-message)
    318           (const :tag "Browse URL" browse-url)
    319           (function :tag "Call function"))
    320   :group 'go)
    321 
    322 (defcustom go-coverage-display-buffer-func 'display-buffer-reuse-window
    323   "How `go-coverage' should display the coverage buffer.
    324 See `display-buffer' for a list of possible functions."
    325   :type 'function
    326   :group 'go-cover)
    327 
    328 (defface go-coverage-untracked
    329   '((t (:foreground "#505050")))
    330   "Coverage color of untracked code."
    331   :group 'go-cover)
    332 
    333 (defface go-coverage-0
    334   '((t (:foreground "#c00000")))
    335   "Coverage color for uncovered code."
    336   :group 'go-cover)
    337 (defface go-coverage-1
    338   '((t (:foreground "#808080")))
    339   "Coverage color for covered code with weight 1."
    340   :group 'go-cover)
    341 (defface go-coverage-2
    342   '((t (:foreground "#748c83")))
    343   "Coverage color for covered code with weight 2."
    344   :group 'go-cover)
    345 (defface go-coverage-3
    346   '((t (:foreground "#689886")))
    347   "Coverage color for covered code with weight 3."
    348   :group 'go-cover)
    349 (defface go-coverage-4
    350   '((t (:foreground "#5ca489")))
    351   "Coverage color for covered code with weight 4."
    352   :group 'go-cover)
    353 (defface go-coverage-5
    354   '((t (:foreground "#50b08c")))
    355   "Coverage color for covered code with weight 5."
    356   :group 'go-cover)
    357 (defface go-coverage-6
    358   '((t (:foreground "#44bc8f")))
    359   "Coverage color for covered code with weight 6."
    360   :group 'go-cover)
    361 (defface go-coverage-7
    362   '((t (:foreground "#38c892")))
    363   "Coverage color for covered code with weight 7."
    364   :group 'go-cover)
    365 (defface go-coverage-8
    366   '((t (:foreground "#2cd495")))
    367   "Coverage color for covered code with weight 8.
    368 For mode=set, all covered lines will have this weight."
    369   :group 'go-cover)
    370 (defface go-coverage-9
    371   '((t (:foreground "#20e098")))
    372   "Coverage color for covered code with weight 9."
    373   :group 'go-cover)
    374 (defface go-coverage-10
    375   '((t (:foreground "#14ec9b")))
    376   "Coverage color for covered code with weight 10."
    377   :group 'go-cover)
    378 (defface go-coverage-covered
    379   '((t (:foreground "#2cd495")))
    380   "Coverage color of covered code."
    381   :group 'go-cover)
    382 
    383 (defvar go-mode-syntax-table
    384   (let ((st (make-syntax-table)))
    385     (modify-syntax-entry ?+  "." st)
    386     (modify-syntax-entry ?-  "." st)
    387     (modify-syntax-entry ?%  "." st)
    388     (modify-syntax-entry ?&  "." st)
    389     (modify-syntax-entry ?|  "." st)
    390     (modify-syntax-entry ?^  "." st)
    391     (modify-syntax-entry ?!  "." st)
    392     (modify-syntax-entry ?=  "." st)
    393     (modify-syntax-entry ?<  "." st)
    394     (modify-syntax-entry ?>  "." st)
    395     (modify-syntax-entry ?/  ". 124b" st)
    396     (modify-syntax-entry ?*  ". 23" st)
    397     (modify-syntax-entry ?\n "> b" st)
    398     (modify-syntax-entry ?\" "\"" st)
    399     (modify-syntax-entry ?\' "\"" st)
    400     (modify-syntax-entry ?`  "\"" st)
    401     (modify-syntax-entry ?\\ "\\" st)
    402     ;; TODO make _ a symbol constituent now that xemacs is gone
    403     (modify-syntax-entry ?_  "w" st)
    404 
    405     st)
    406   "Syntax table for Go mode.")
    407 
    408 (defun go--fontify-type-switch-case-pre ()
    409   "Move point to line following the end of case statement.
    410 
    411 This is used as an anchored font lock keyword PRE-MATCH-FORM. We
    412 expand the font lock region to include multiline type switch case
    413 statements."
    414   (save-excursion
    415     (beginning-of-line)
    416     (while (or (looking-at "[[:space:]]*\\($\\|//\\)") (go--line-suffix-p ","))
    417       (forward-line))
    418     (when (go--line-suffix-p ":")
    419       (forward-line))
    420     (point)))
    421 
    422 (defun go--build-font-lock-keywords ()
    423   ;; we cannot use 'symbols in regexp-opt because GNU Emacs <24
    424   ;; doesn't understand that
    425   (append
    426    `(
    427      ;; Match param lists in func signatures. This uses the
    428      ;; MATCH-ANCHORED format (see `font-lock-keywords' docs).
    429      ;;
    430      ;; Parent/anchor match. It matches the param list opening "(".
    431      (go--match-param-start
    432       ;; Sub-matcher that matches individual params in the param list.
    433       (go--fontify-param
    434        ;; Pre-match form that runs before the first sub-match.
    435        (go--fontify-param-pre)
    436        ;; Post-match form that runs after last sub-match.
    437        (go--fontify-param-post)
    438        ;; Subexp 1 is the param variable name, if any.
    439        (1 font-lock-variable-name-face nil t)
    440        ;; Subexp 2 is the param type name, if any. We set the LAXMATCH
    441        ;; flag to allow optional regex groups.
    442        (2 font-lock-type-face nil t)))
    443 
    444      ;; Special case to match non-parenthesized function results. For
    445      ;; example, "func(i int) string".
    446      (go--match-single-func-result 1 font-lock-type-face)
    447 
    448      ;; Match name+type pairs, such as "foo bar" in "var foo bar".
    449      (go--match-ident-type-pair 2 font-lock-type-face)
    450 
    451      ;; An anchored matcher for type switch case clauses.
    452      (go--match-type-switch-case
    453       (go--fontify-type-switch-case
    454        (go--fontify-type-switch-case-pre)
    455        nil
    456        (1 font-lock-type-face)))
    457 
    458      ;; Match variable names in var decls, constant names in const
    459      ;; decls, and type names in type decls.
    460      (go--match-decl
    461       (1 font-lock-variable-name-face nil t)
    462       (2 font-lock-constant-face nil t)
    463       (3 font-lock-type-face nil t))
    464 
    465      (,(concat "\\_<" (regexp-opt go-mode-keywords t) "\\_>") . font-lock-keyword-face)
    466      (,(concat "\\(\\_<" (regexp-opt go-builtins t) "\\_>\\)[[:space:]]*(") 1 font-lock-builtin-face)
    467      (,(concat "\\_<" (regexp-opt go-constants t) "\\_>") . font-lock-constant-face)
    468 
    469      ;; Function (not method) name
    470      (,go-func-regexp 1 font-lock-function-name-face))
    471 
    472    (if go-fontify-function-calls
    473        ;; Function call/method name
    474        `((,(concat "\\(" go-identifier-regexp "\\)[[:space:]]*(") 1 font-lock-function-name-face)
    475          ;; Bracketed function call
    476          (,(concat "[^[:word:][:multibyte:]](\\(" go-identifier-regexp "\\))[[:space:]]*(") 1 font-lock-function-name-face))
    477      ;; Method name
    478      `((,go-func-meth-regexp 2 font-lock-function-name-face)))
    479 
    480    `(
    481      ;; Raw string literal, needed for font-lock-syntactic-keywords
    482      ("\\(`[^`]*`\\)" 1 font-lock-multiline)
    483 
    484      ;; RHS of type alias.
    485      (go--match-type-alias 2 font-lock-type-face)
    486 
    487      ;; Arrays/slices: []<type> | [123]<type> | [some.Const]<type> | [someConst]<type> | [...]<type>
    488      (,(concat "\\(?:^\\|[^[:word:][:multibyte:]]\\)\\[\\(?:[[:digit:]]+\\|" go-qualified-identifier-regexp "\\|" go-identifier-regexp "\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 1 font-lock-type-face)
    489 
    490      ;; Unary "!"
    491      ("\\(!\\)[^=]" 1 font-lock-negation-char-face)
    492 
    493      ;; Composite literal type
    494      (,(concat go-type-name-regexp "{") 1 font-lock-type-face)
    495 
    496      ;; Map value type
    497      (go--match-map-value 1 font-lock-type-face)
    498 
    499      ;; Map key type
    500      (,(concat "\\_<map\\_>\\[" go-type-name-regexp) 1 font-lock-type-face)
    501 
    502      ;; Channel type
    503      (,(concat "\\_<chan\\_>[[:space:]]*\\(?:<-[[:space:]]*\\)?" go-type-name-regexp) 1 font-lock-type-face)
    504 
    505      ;; "new()"/"make()" type
    506      (,(concat "\\_<\\(?:new\\|make\\)\\_>\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face)
    507 
    508      ;; Type assertion
    509      (,(concat "\\.\\s *(" go-type-name-regexp) 1 font-lock-type-face)
    510 
    511      ;; Composite literal field names and label definitions.
    512      (go--match-ident-colon 1 font-lock-constant-face)
    513 
    514      ;; Labels in goto/break/continue
    515      (,(concat "\\_<\\(?:goto\\|break\\|continue\\)\\_>[[:space:]]*\\(" go-label-regexp "\\)") 1 font-lock-constant-face))))
    516 
    517 (let ((m (define-prefix-command 'go-goto-map)))
    518   (define-key m "a" #'go-goto-arguments)
    519   (define-key m "d" #'go-goto-docstring)
    520   (define-key m "f" #'go-goto-function)
    521   (define-key m "i" #'go-goto-imports)
    522   (define-key m "m" #'go-goto-method-receiver)
    523   (define-key m "n" #'go-goto-function-name)
    524   (define-key m "r" #'go-goto-return-values))
    525 
    526 (defvar go-mode-map
    527   (let ((m (make-sparse-keymap)))
    528     (unless (boundp 'electric-indent-chars)
    529       (define-key m "}" #'go-mode-insert-and-indent)
    530       (define-key m ")" #'go-mode-insert-and-indent))
    531     (define-key m (kbd "C-c C-a") #'go-import-add)
    532     (define-key m (kbd "C-c C-j") #'godef-jump)
    533     (define-key m (kbd "C-x 4 C-c C-j") #'godef-jump-other-window)
    534     (define-key m (kbd "C-c C-d") #'godef-describe)
    535     (define-key m (kbd "C-c C-f") 'go-goto-map)
    536     m)
    537   "Keymap used by ‘go-mode’.")
    538 
    539 (easy-menu-define go-mode-menu go-mode-map
    540   "Menu for Go mode."
    541   '("Go"
    542     ["Describe Expression"   godef-describe t]
    543     ["Jump to Definition"    godef-jump t]
    544     "---"
    545     ["Add Import"            go-import-add t]
    546     ["Remove Unused Imports" go-remove-unused-imports t]
    547     ["Go to Imports"         go-goto-imports t]
    548     "---"
    549     ("Playground"
    550      ["Send Buffer"          go-play-buffer t]
    551      ["Send Region"          go-play-region t]
    552      ["Download"             go-download-play t])
    553     "---"
    554     ["Coverage"              go-coverage t]
    555     ["Gofmt"                 gofmt t]
    556     ["Godoc"                 godoc t]
    557     "---"
    558     ["Customize Mode"        (customize-group 'go) t]))
    559 
    560 (defun go-mode-insert-and-indent (key)
    561   "Invoke the global binding of KEY, then reindent the line."
    562 
    563   (interactive (list (this-command-keys)))
    564   (call-interactively (lookup-key (current-global-map) key))
    565   (indent-according-to-mode))
    566 
    567 (defmacro go-paren-level ()
    568   `(car (syntax-ppss)))
    569 
    570 (defmacro go-in-string-or-comment-p ()
    571   `(nth 8 (syntax-ppss)))
    572 
    573 (defmacro go-in-string-p ()
    574   `(nth 3 (syntax-ppss)))
    575 
    576 (defmacro go-in-comment-p ()
    577   `(nth 4 (syntax-ppss)))
    578 
    579 (defmacro go-goto-beginning-of-string-or-comment ()
    580   `(goto-char (nth 8 (syntax-ppss))))
    581 
    582 (defun go--backward-irrelevant (&optional stop-at-string)
    583   "Skip backwards over any characters that are irrelevant for
    584 indentation and related tasks.
    585 
    586 It skips over whitespace, comments, cases and labels and, if
    587 STOP-AT-STRING is not true, over strings."
    588 
    589   (let (pos (start-pos (point)))
    590     (skip-chars-backward "\n\s\t")
    591     (if (and (save-excursion (beginning-of-line) (go-in-string-p))
    592              (= (char-before) ?`)
    593              (not stop-at-string))
    594         (backward-char))
    595     (if (and (go-in-string-p)
    596              (not stop-at-string))
    597         (go-goto-beginning-of-string-or-comment))
    598     (if (looking-back "\\*/" (line-beginning-position))
    599         (backward-char))
    600     (if (go-in-comment-p)
    601         (go-goto-beginning-of-string-or-comment))
    602     (setq pos (point))
    603     (beginning-of-line)
    604     (if (or (looking-at (concat "^" go-label-regexp ":"))
    605             (looking-at "^[[:space:]]*\\(case .+\\|default\\):"))
    606         (end-of-line 0)
    607       (goto-char pos))
    608     (if (/= start-pos (point))
    609         (go--backward-irrelevant stop-at-string))
    610     (/= start-pos (point))))
    611 
    612 (defun go--buffer-narrowed-p ()
    613   "Return non-nil if the current buffer is narrowed."
    614   (/= (buffer-size)
    615       (- (point-max)
    616          (point-min))))
    617 
    618 (defun go-previous-line-has-dangling-op-p ()
    619   "Return non-nil if the current line is a continuation line.
    620 The return value is cached based on the current `line-beginning-position'."
    621   (let* ((line-begin (line-beginning-position))
    622          (val (gethash line-begin go-dangling-cache 'nope)))
    623     (when (or (go--buffer-narrowed-p) (equal val 'nope))
    624       (save-excursion
    625         (go--forward-line -1)
    626         (if (go--current-line-has-dangling-op-p)
    627             (setq val (line-end-position))
    628           (setq val nil))
    629 
    630         (if (not (go--buffer-narrowed-p))
    631             (puthash line-begin val go-dangling-cache))))
    632     val))
    633 
    634 (defun go--current-line-has-dangling-op-p ()
    635   "Return non-nil if current line ends in a dangling operator.
    636 The return value is not cached."
    637   (or
    638    (and
    639     (go--line-suffix-p go-dangling-operators-regexp)
    640 
    641     ;; "=" does not behave like a dangling operator in decl statements.
    642     (not (go--line-suffix-p "\\(?:var\\|type\\|const\\)[[:space:]].*="))
    643 
    644     ;; Don't mistake "1234." for a dangling operator.
    645     (not (go--line-suffix-p "[[:space:]]-?[[:digit:]][_0-9]*\\.")))
    646 
    647    ;; treat comma as dangling operator in certain cases
    648    (and
    649     (go--line-suffix-p ",")
    650     (save-excursion (end-of-line) (go--commas-indent-p)))))
    651 
    652 
    653 (defun go--commas-indent-p ()
    654   "Return non-nil if in a context where dangling commas indent next line."
    655   (not (or
    656         (go--open-paren-position)
    657         (go--in-composite-literal-p)
    658         (go--in-case-clause-list-p)
    659         (go--in-struct-definition-p))))
    660 
    661 (defun go--in-case-clause-list-p ()
    662   "Return non-nil if inside a multi-line case cause list.
    663 
    664 This function is only concerned with list items on lines after the
    665 case keyword. It returns nil for the case line itself."
    666   (save-excursion
    667     (beginning-of-line)
    668     (when (not (looking-at go--case-or-default-regexp))
    669       (let (saw-colon)
    670         ;; optionally skip line with the colon
    671         (when (go--line-suffix-p ":")
    672           (setq saw-colon t)
    673           (forward-line -1))
    674 
    675         ;; go backwards while at a comment or a line ending in comma
    676         (while (and
    677                 (or
    678                  (go--boring-line-p)
    679                  (go--line-suffix-p ","))
    680                 (not (looking-at go--case-regexp))
    681                 (go--forward-line -1)))
    682 
    683         (and
    684          (looking-at-p go--case-regexp)
    685          ;; we weren't in case list if first line ended in colon
    686          ;; and the "case" line ended in colon
    687          (not (and saw-colon (looking-at ".*:[[:space:]]*$"))))))))
    688 
    689 (defun go--in-composite-literal-p ()
    690   "Return non-nil if point is in a composite literal."
    691   (save-excursion
    692     (save-match-data
    693       (and
    694        (go-goto-opening-parenthesis)
    695 
    696        ;; Opening paren-like character is a curly.
    697        (eq (char-after) ?{)
    698 
    699        (or
    700         ;; Curly is preceded by non space (e.g. "Foo{"), definitely
    701         ;; composite literal.
    702         (zerop (skip-syntax-backward " "))
    703 
    704         ;; Curly preceded by comma or semicolon. This is a composite
    705         ;; literal with implicit type name.
    706         (looking-back "[,:]" (1- (point)))
    707 
    708         ;; If we made it to the beginning of line we are either a naked
    709         ;; block or a composite literal with implicit type name. If we
    710         ;; are the latter, we must be contained in another composite
    711         ;; literal.
    712         (and (bolp) (go--in-composite-literal-p)))))))
    713 
    714 (defun go--in-paren-with-prefix-p (paren prefix)
    715   (save-excursion
    716     (and
    717      (go-goto-opening-parenthesis)
    718      (eq (char-after) paren)
    719      (skip-syntax-backward " ")
    720      (> (point) (length prefix))
    721      (string= prefix (buffer-substring (- (point) (length prefix)) (point))))))
    722 
    723 (defun go--in-struct-definition-p ()
    724   "Return non-nil if point is inside a struct definition."
    725   (go--in-paren-with-prefix-p ?{ "struct"))
    726 
    727 (defun go--in-interface-p ()
    728   "Return non-nil if point is inside an interface definition."
    729   (go--in-paren-with-prefix-p ?{ "interface"))
    730 
    731 
    732 (defun go--in-type-switch-p ()
    733   "Return non-nil if point is inside a type switch statement."
    734   (go--in-paren-with-prefix-p ?{ ".(type)"))
    735 
    736 (defun go--fill-prefix ()
    737   "Return fill prefix for following comment paragraph."
    738   (save-excursion
    739     (beginning-of-line)
    740 
    741     ;; Skip over empty lines and empty comment openers/closers.
    742     (while (and
    743             (or (go--empty-line-p) (go--boring-comment-p))
    744             (zerop (forward-line 1))))
    745 
    746     ;; If we are in a block comment, set prefix based on first line
    747     ;; with content.
    748     (if (go-in-comment-p)
    749         (progn
    750           (looking-at "[[:space:]]*")
    751           (match-string-no-properties 0))
    752 
    753       ;; Else if we are looking at the start of an interesting comment, our
    754       ;; prefix is the comment opener and any space following.
    755       (if (looking-at (concat go--comment-start-regexp "[[:space:]]*"))
    756           ;; Replace "/*" opener with spaces so following lines don't
    757           ;; get "/*" prefix.
    758           (replace-regexp-in-string "/\\*" "  "
    759                                     (match-string-no-properties 0))))))
    760 
    761 (defun go--open-paren-position ()
    762   "Return non-nil if point is between '(' and ')'.
    763 
    764 The return value is the position of the opening paren."
    765   (save-excursion
    766     (let ((start-paren-level (go-paren-level)))
    767       (and
    768        (go-goto-opening-parenthesis)
    769 
    770        ;; opening paren-like character is actually a paren
    771        (eq (char-after) ?\()
    772 
    773        ;; point is before the closing paren
    774        (< (go-paren-level) start-paren-level)
    775 
    776        (point)))))
    777 
    778 (defun go-indentation-at-point ()
    779   "Return the appropriate indentation for the current line."
    780   (save-excursion
    781     (beginning-of-line)
    782 
    783     (if (go-in-comment-p)
    784         (go--multiline-comment-indent)
    785       (go--indentation-at-point))))
    786 
    787 ;; It's unfortunate that the user cannot reindent the current line to
    788 ;; align with the previous line; however, if they could, then people
    789 ;; who use reindent-then-newline-and-indent wouldn't be able to
    790 ;; explicitly indent lines inside comments.
    791 (defun go--multiline-comment-indent ()
    792   "Return the appropriate indent inside multiline comment.
    793 
    794 Assumes point is at beginning of line within comment. This
    795 function has basic logic to indent as you add new lines to a
    796 multiline comment, and to line up all the `*' if each line starts
    797 with `*'. The gofmt behavior for multiline comments is
    798 surprisingly complex and strange/buggy, so we just aim to do
    799 something simple rather than encode all the subtle behavior."
    800   (let* (;; Indent of current line.
    801          (indent (current-indentation))
    802          ;; Indent of opening "/*".
    803          start-indent
    804          ;; Default indent to use based on preceding context.
    805          natural-indent
    806          ;; Non-nil means keep existing indent and give up calculating indent.
    807          give-up
    808          ;; Whether all comment lines (except first) begin with "*".
    809          (all-star t))
    810 
    811     (save-excursion
    812       (go-goto-beginning-of-string-or-comment)
    813 
    814       (setq start-indent (current-indentation))
    815 
    816       ;; If other stuff precedes start of multiline comment, give up.
    817       (setq give-up (/= (current-column) start-indent))
    818 
    819       ;; Skip "/*".
    820       (forward-char 2)
    821 
    822       (skip-syntax-forward " ")
    823 
    824       (if (not (eolp))
    825           ;; If we aren't at EOL, we have content on the first line.
    826           ;; Base our natural indent on that.
    827           (setq natural-indent (current-column))
    828         ;; Otherwise default to 1 space beyond "/*".
    829         (setq natural-indent (+ start-indent 3)))
    830 
    831       (let (done)
    832         (while (not done)
    833           (setq done (or (looking-at ".*\\*/") (not (zerop (forward-line)))))
    834           (setq all-star (and all-star (looking-at "[[:space:]]*\\*"))))))
    835 
    836     ;; If previous line has comment content, use its indent as our
    837     ;; natural indent.
    838     (save-excursion
    839       (when (zerop (forward-line -1))
    840         (beginning-of-line)
    841         (when (and (go-in-comment-p) (> (current-indentation) 0))
    842           (setq natural-indent (current-indentation)))))
    843 
    844     (cond
    845      (give-up indent)
    846 
    847      (all-star (1+ start-indent))
    848 
    849      ;; Closing "*/" with no preceding content always lines up with "/*".
    850      ((looking-at "[[:space:]]*\\*/") start-indent)
    851 
    852      ;; If the line is already indented, leave it.
    853      (t (if (zerop indent) natural-indent indent)))))
    854 
    855 (defun go--indentation-at-point ()
    856   "Return the appropriate indentation for the current non-comment line.
    857 
    858 This function works by walking a line's characters backwards. When it
    859 encounters a closing paren or brace it bounces to the corresponding
    860 opener. If it arrives at the beginning of the line you are indenting,
    861 it moves to the end of the previous line if the current line is a
    862 continuation line, else it moves to the containing opening paren or
    863 brace. If it arrives at the beginning of a line other than the line
    864 you are indenting, it will continue to the previous dangling line if
    865 the line you are indenting was not a continuation line, otherwise it
    866 is done."
    867   (save-excursion
    868     (beginning-of-line)
    869 
    870     (let (
    871           ;; Beginning of our starting line.
    872           (start-line (point))
    873 
    874           ;; Whether this is our first iteration of the outer while loop.
    875           (first t)
    876 
    877           ;; Whether we start in a block (i.e. our first line is not a
    878           ;; continuation line and is in an "if", "for", etc. block).
    879           (in-block)
    880 
    881           ;; Our desired indent relative to our ending line's indent.
    882           (indent 0))
    883 
    884       ;; Skip leading whitespace.
    885       (skip-syntax-forward " ")
    886 
    887       ;; Decrement indent if the first character on the line is a closer.
    888       (when (or (eq (char-after) ?\)) (eq (char-after) ?}))
    889         (cl-decf indent tab-width))
    890 
    891       (while (or
    892               ;; Always run the first iteration so we process empty lines.
    893               first
    894 
    895               ;; Otherwise stop if we are at the start of a line.
    896               (not (bolp)))
    897         (setq first nil)
    898 
    899         (cl-case (char-before)
    900 
    901           ;; We have found a closer (paren or brace).
    902           ((?\) ?})
    903            (backward-char)
    904            (let ((bol (line-beginning-position)))
    905 
    906              ;; Jump back to corresponding opener.
    907              (go-goto-opening-parenthesis)
    908 
    909              ;; Here we decrement the indent if we are closing an indented
    910              ;; expression. In other words, the closer's line was indented
    911              ;; relative to the opener's line, and that indent should not
    912              ;; be inherited by our starting line.
    913              (when (and
    914                     ;; We care about dangling expressions, not child blocks.
    915                     (not in-block)
    916 
    917                     ;; Opener and closer aren't on same line.
    918                     (< (point) bol)
    919 
    920                     (go-previous-line-has-dangling-op-p)
    921 
    922                     ;; Opener is at same paren level as start of line (ignore sub-expressions).
    923                     (eq (go-paren-level) (save-excursion (beginning-of-line) (go-paren-level)))
    924 
    925                     ;; This dangling line opened indent relative to previous dangling line.
    926                     (go--continuation-line-indents-p))
    927                (cl-decf indent tab-width))))
    928 
    929           ;; Brackets don't affect indentation, so just skip them.
    930           ((?\])
    931            (backward-char)))
    932 
    933         ;; Skip non-closers since we are only interested in closing parens/braces.
    934         (skip-syntax-backward "^)" (line-beginning-position))
    935 
    936         (when (go-in-string-or-comment-p)
    937           (go-goto-beginning-of-string-or-comment))
    938 
    939         ;; At the beginning of the starting line.
    940         (when (= start-line (point))
    941 
    942           ;; We are a continuation line.
    943           (if (go-previous-line-has-dangling-op-p)
    944               (progn
    945                 ;; Presume a continuation line always gets an extra indent.
    946                 ;; We reduce the indent after the loop, if necessary.
    947                 (cl-incf indent tab-width)
    948 
    949                 ;; Go to the end of the dangling line.
    950                 (goto-char (go-previous-line-has-dangling-op-p)))
    951 
    952             ;; If we aren't a continuation line and we have an enclosing paren
    953             ;; or brace, jump to opener and increment our indent.
    954             (when (go-goto-opening-parenthesis)
    955               (setq in-block (go--flow-block-p))
    956               (cl-incf indent tab-width))))
    957 
    958         ;; If we started in a child block we must follow dangling lines
    959         ;; until they don't dangle anymore. This is to handle cases like:
    960         ;;
    961         ;; if foo ||
    962         ;;      foo &&
    963         ;;        foo {
    964         ;;   X
    965         ;;
    966         ;; There can be an arbitrary number of indents, so we must go back to
    967         ;; the "if" to determine the indent of "X".
    968         (when (and in-block (bolp) (go-previous-line-has-dangling-op-p))
    969           (goto-char (go-previous-line-has-dangling-op-p))))
    970 
    971       ;; If our ending line is a continuation line but doesn't open
    972       ;; an extra indent, reduce indent. We tentatively gave indents to all
    973       ;; dangling lines and all lines inside open parens, so here we take that
    974       ;; indent back.
    975       ;;
    976       ;;                1 +                      1 +
    977       ;; ending line      1 + foo(                 1 + foo(
    978       ;; starting line      1,        becomes      1,
    979       ;;                  )                      )
    980       ;;
    981       ;;
    982       ;;                1 +                     1 +
    983       ;; ending line      1 +         becomes     1 +
    984       ;; starting line      1                     1
    985       (when (and
    986              (go-previous-line-has-dangling-op-p)
    987              (not (go--continuation-line-indents-p)))
    988         (cl-decf indent tab-width))
    989 
    990       ;; Apply our computed indent relative to the indent of the
    991       ;; ending line, or 0 if we are at the top level.
    992       (if (and
    993            (= 0 (go-paren-level))
    994            (not (go-previous-line-has-dangling-op-p)))
    995           indent
    996         (+ indent (current-indentation))))))
    997 
    998 (defconst go--operator-chars "*/%<>&\\^+\\-|=!,."
    999   "Individual characters that appear in operators.
   1000 Comma and period are included because they can be dangling operators, so
   1001 they need to be considered by `go--continuation-line-indents-p'")
   1002 
   1003 (defun go--operator-precedence (op)
   1004   "Go operator precedence (higher binds tighter)."
   1005   (cl-case (intern op)
   1006     (\. 7) ; "." in "foo.bar", binds tightest
   1007     (! 6)
   1008     ((* / % << >> & &^) 5)
   1009     ((+ - | ^) 4)
   1010     ((== != < <= > >=) 3)
   1011     (&& 2)
   1012     (|| 1)
   1013     (t 0)))
   1014 
   1015 (defun go--flow-block-p ()
   1016   "Return whether looking at a { that opens a control flow block.
   1017 
   1018 We check for a { that is preceded by a space and is not a func
   1019 literal opening brace."
   1020   (save-excursion
   1021     (when (and
   1022            (eq (char-after) ?{)
   1023            (not (zerop (skip-syntax-backward " "))))
   1024 
   1025       (let ((eol (line-end-position))
   1026             (level (go-paren-level))
   1027             (found-func-literal))
   1028 
   1029         (beginning-of-line)
   1030 
   1031         ;; See if we find any "func" keywords on this line at the same paren
   1032         ;; level as the curly.
   1033         (while (and
   1034                 (not found-func-literal)
   1035                 (re-search-forward "\\_<func\\_>" eol t))
   1036           (setq found-func-literal (and
   1037                                     (= level (go-paren-level))
   1038                                     (not (go-in-string-or-comment-p)))))
   1039         (not found-func-literal)))))
   1040 
   1041 (defun go--continuation-line-indents-p ()
   1042   "Return non-nil if the current continuation line opens an additional indent.
   1043 
   1044 This function works by looking at the Go operators used on the current
   1045 line. If all the operators bind tighter than the previous line's
   1046 dangling operator and the current line ends in a dangling operator or
   1047 open paren, the next line will have an additional indent.
   1048 
   1049 For example:
   1050 foo ||
   1051   foo && // this continuation line opens another indent
   1052     foo
   1053 "
   1054   (save-excursion
   1055     (let (prev-op (all-tighter t))
   1056 
   1057       ;; Record the dangling operator from previous line.
   1058       (save-excursion
   1059         (goto-char (go-previous-line-has-dangling-op-p))
   1060         (go--end-of-line)
   1061         (skip-syntax-backward " ")
   1062         (let ((end (point)))
   1063           (skip-chars-backward go--operator-chars)
   1064           (setq prev-op (buffer-substring-no-properties (point) end))))
   1065 
   1066       (beginning-of-line)
   1067 
   1068       (when (or
   1069              ;; We can only open indent if we have a dangling operator, or
   1070              (go--current-line-has-dangling-op-p)
   1071 
   1072              (save-excursion
   1073                (go--end-of-line)
   1074                (backward-char)
   1075                (or
   1076                 ;; Line ends in a "(" or ",", or
   1077                 (eq (char-after) ?\()
   1078                 (eq (char-after) ?,)
   1079 
   1080                 ;; Line ends in a "{" that isn't a control block.
   1081                 (and
   1082                  (eq (char-after) ?{)
   1083                  (not (go--flow-block-p))))))
   1084 
   1085         (let ((prev-precedence (go--operator-precedence prev-op))
   1086               (start-depth (go-paren-level))
   1087               (line-start (line-beginning-position)))
   1088 
   1089           (end-of-line)
   1090 
   1091           ;; While we haven't found a looser operator and are on the starting line...
   1092           (while (and all-tighter (> (point) line-start))
   1093 
   1094             ;; Skip over non-operator characters.
   1095             (skip-chars-backward (concat "^" go--operator-chars) line-start)
   1096 
   1097             (let ((end (point)))
   1098               (cond
   1099                ;; Ignore sub-expressions at different paren levels.
   1100                ((/= (go-paren-level) start-depth)
   1101                 (skip-syntax-backward "^()"))
   1102 
   1103                ((go-in-string-or-comment-p)
   1104                 (go-goto-beginning-of-string-or-comment))
   1105 
   1106                ;; We found an operator. Check if it has lower precedence.
   1107                ((/= (skip-chars-backward go--operator-chars) 0)
   1108                 (when (>=
   1109                        prev-precedence
   1110                        (go--operator-precedence (buffer-substring (point) end)))
   1111                   (setq all-tighter nil)))))))
   1112         all-tighter))))
   1113 
   1114 (defun go--end-of-line ()
   1115   "Move to the end of the code on the current line.
   1116 Point will be left before any trailing comments. Point will be left
   1117 after the opening backtick of multiline strings."
   1118   (end-of-line)
   1119   (let ((keep-going t))
   1120     (while keep-going
   1121       (skip-syntax-backward " ")
   1122       (when (looking-back "\\*/" (- (point) 2))
   1123         ;; back up so we are in the /* comment */
   1124         (backward-char))
   1125       (if (go-in-comment-p)
   1126           (go-goto-beginning-of-string-or-comment)
   1127         (setq keep-going nil))))
   1128   (when (go-in-string-p)
   1129     (go-goto-beginning-of-string-or-comment)
   1130     ;; forward one so point is after the opening "`"
   1131     (forward-char)))
   1132 
   1133 (defun go--line-suffix-p (re)
   1134   "Return non-nil if RE matches the end of the line starting from `point'.
   1135 
   1136 Trailing whitespace, trailing comments and trailing multiline strings are
   1137 ignored."
   1138   (let ((start (point))
   1139         (end (save-excursion (go--end-of-line) (point))))
   1140     (when (< start end)
   1141       (string-match-p
   1142        (concat "\\(?:" re "\\)$")
   1143        (buffer-substring-no-properties start end)))))
   1144 
   1145 (defun go--boring-line-p ()
   1146   "Return non-nil if the current line probably doesn't impact indentation.
   1147 
   1148 A boring line is one that starts with a comment, is empty, is part of a
   1149 multiline comment, or starts and ends in a multiline string."
   1150   (or
   1151    (looking-at (concat go--comment-start-regexp "\\|[[:space:]]*$"))
   1152    (go-in-comment-p)
   1153    (and (go-in-string-p) (save-excursion (end-of-line) (go-in-string-p)))))
   1154 
   1155 (defun go--forward-line (&optional count)
   1156   "Like `forward-line' but skip comments and empty lines.
   1157 
   1158 Return non-nil if point changed lines."
   1159   (let (moved)
   1160     (while (and
   1161             (zerop (forward-line count))
   1162             (setq moved t)
   1163             (go--boring-line-p))
   1164       (setq count (if (and count (< count 0 )) -1 1)))
   1165     moved))
   1166 
   1167 (defun go--case-comment-p (indent)
   1168   "Return non-nil if looking at a comment attached to a case statement.
   1169 
   1170 INDENT is the normal indent of this line, i.e. that of the case body."
   1171   (when (and
   1172          (> (current-indentation) 0)
   1173          (looking-at go--comment-start-regexp))
   1174 
   1175     (let (switch-before
   1176           case-after
   1177           has-case-aligned-preceding-comment)
   1178 
   1179       (save-excursion
   1180         ;; Search for previous case-aligned comment.
   1181         (while (and
   1182                 (zerop (forward-line -1))
   1183                 (cond
   1184                  ((looking-at "^[[:space:]]*$"))
   1185 
   1186                  ((looking-at go--comment-start-regexp)
   1187                   (when (= (current-indentation) (- indent tab-width))
   1188                     (setq has-case-aligned-preceding-comment t))
   1189                   t)
   1190 
   1191                  ((go-in-comment-p)))))
   1192 
   1193         ;; Record if a switch (or select) precedes us.
   1194         (setq switch-before (looking-at "^[[:space:]]*\\(switch\\|select\\)[[:space:]]")))
   1195 
   1196       ;; Record if first proceeding non-comment line is a case statement.
   1197       (save-excursion
   1198         (while (and
   1199                 (zerop (forward-line 1))
   1200                 (or
   1201                  (looking-at go--comment-start-regexp)
   1202                  (looking-at "^[[:space:]]*$")
   1203                  (go-in-comment-p))))
   1204 
   1205         (setq case-after (looking-at go--case-or-default-regexp)))
   1206 
   1207       (and
   1208        ;; a "case" statement comes after our comment
   1209        case-after
   1210 
   1211        (or
   1212         ;; "switch" statement precedes us, always align with "case"
   1213         switch-before
   1214 
   1215         ;; a preceding comment is aligned with "case", we should too
   1216         has-case-aligned-preceding-comment
   1217 
   1218         ;; other cases are ambiguous, so if comment is currently
   1219         ;; aligned with "case", leave it that way
   1220         (= (current-indentation) (- indent tab-width)))))))
   1221 
   1222 (defun go-mode-indent-line ()
   1223   (interactive)
   1224   (let (indent
   1225         ;; case sensitively match "case", "default", etc.
   1226         (case-fold-search nil)
   1227         (pos (- (point-max) (point)))
   1228         (point (point))
   1229         (beg (line-beginning-position))
   1230         (non-tab-indents 0))
   1231     (back-to-indentation)
   1232     (if (go-in-string-p)
   1233         (goto-char point)
   1234       (setq indent (go-indentation-at-point))
   1235       (when (or
   1236              (and
   1237               (looking-at (concat go-label-regexp ":\\([[:space:]]*/.+\\)?$\\|" go--case-or-default-regexp))
   1238               ;; don't think last part of multiline case statement is a label
   1239               (not (go-previous-line-has-dangling-op-p))
   1240               (not (go--in-case-clause-list-p))
   1241               (not (go--in-composite-literal-p)))
   1242 
   1243              ;; comment attached above a "case" statement
   1244              (go--case-comment-p indent))
   1245         (cl-decf indent tab-width))
   1246 
   1247       ;; Don't do anything if current indent is correct.
   1248       (when (/= indent (current-column))
   1249         ;; Don't use tabs for indenting beyond "/*" in multiline
   1250         ;; comments. They don't play well with gofmt.
   1251         (when (go-in-comment-p)
   1252           (save-excursion
   1253             (go-goto-beginning-of-string-or-comment)
   1254             (when (> indent (current-indentation))
   1255               (setq non-tab-indents (- indent (current-indentation)))
   1256               (setq indent (current-indentation)))))
   1257 
   1258         (delete-region beg (point))
   1259         (indent-to indent)
   1260         (insert-char ?  non-tab-indents))
   1261 
   1262       ;; If initial point was within line's indentation,
   1263       ;; position after the indentation.  Else stay at same point in text.
   1264       (if (> (- (point-max) pos) (point))
   1265           (goto-char (- (point-max) pos))))))
   1266 
   1267 (defun go-beginning-of-defun (&optional count)
   1268   (unless (bolp)
   1269     (end-of-line))
   1270   (setq count (or count 1))
   1271   (let (first failure)
   1272     (dotimes (i (abs count))
   1273       (setq first t)
   1274       (while (and (not failure)
   1275                   (or first (go-in-string-or-comment-p)))
   1276         (if (>= count 0)
   1277             (progn
   1278               (go--backward-irrelevant)
   1279               (if (not (re-search-backward go-func-meth-regexp nil t))
   1280                   (setq failure t)))
   1281           (if (looking-at go-func-meth-regexp)
   1282               (forward-char))
   1283           (if (not (re-search-forward go-func-meth-regexp nil t))
   1284               (setq failure t)))
   1285         (setq first nil)))
   1286     (if (< count 0)
   1287         (beginning-of-line))
   1288     (not failure)))
   1289 
   1290 (defun go-end-of-defun ()
   1291   (let (orig-level)
   1292     ;; It can happen that we're not placed before a function by emacs
   1293     (if (not (looking-at "func"))
   1294         (go-beginning-of-defun -1))
   1295     ;; Find the { that starts the function, i.e., the next { that isn't
   1296     ;; preceded by struct or interface, or a comment or struct tag.  BUG:
   1297     ;; breaks if there's a comment between the struct/interface keyword and
   1298     ;; bracket, like this:
   1299     ;;
   1300     ;;     struct /* why? */ {
   1301     (while (progn
   1302       (skip-chars-forward "^{")
   1303       (forward-char)
   1304       (or (go-in-string-or-comment-p)
   1305           (looking-back "\\(struct\\|interface\\)\\s-*{"
   1306                         (line-beginning-position)))))
   1307     (setq orig-level (go-paren-level))
   1308     (while (>= (go-paren-level) orig-level)
   1309       (skip-chars-forward "^}")
   1310       (forward-char))))
   1311 
   1312 
   1313 (defvar go--fontify-param-has-name nil
   1314   "Whether the current params list has names.
   1315 
   1316 This is used during fontification of function signatures.")
   1317 
   1318 (defvar go--fontify-param-beg nil
   1319   "Position of \"(\" starting param list.
   1320 
   1321 This is used during fontification of function signatures.")
   1322 
   1323 (defun go--fontify-param-pre ()
   1324   "Set `go--fontify-param-has-name' and `go--fontify-param-beg' appropriately.
   1325 
   1326 This is used as an anchored font lock keyword PRE-MATCH-FORM. We
   1327 must set `go--fontify-param-has-name' ahead of time because you
   1328 can't know if the param list is types only or names and types
   1329 until you see the end. For example:
   1330 
   1331 // types only
   1332 func foo(int, string) {}
   1333 
   1334 // names and types (don't know so until you see the \"int\").
   1335 func foo(i, j int) {}
   1336 "
   1337   (setq go--fontify-param-has-name (eq
   1338                                     (go--parameter-list-type (point-max))
   1339                                     'present))
   1340 
   1341   ;; Remember where our match started so we can continue our search
   1342   ;; from here.
   1343   (setq go--fontify-param-beg (point))
   1344 
   1345   ;; Return position of closing paren so we process the entire
   1346   ;; multiline param list.
   1347   (save-excursion
   1348     (let ((depth (go-paren-level)))
   1349       ;; First check that our paren is closed by the end of the file. This
   1350       ;; avoids expanding the fontification region to the entire file when you
   1351       ;; have an unclosed paren at file scope.
   1352       (when (save-excursion
   1353               (goto-char (1+ (buffer-size)))
   1354               (< (go-paren-level) depth))
   1355         (while (and
   1356                 (re-search-forward ")" nil t)
   1357                 (>= (go-paren-level) depth)))))
   1358     (point)))
   1359 
   1360 (defun go--fontify-param-post ()
   1361   "Move point back to opening paren.
   1362 
   1363 This is used as an anchored font lock keyword POST-MATCH-FORM. We
   1364 move point back to the opening \"(\" so we find nested param
   1365 lists.
   1366 "
   1367   (goto-char go--fontify-param-beg))
   1368 
   1369 (defun go--match-param-start (end)
   1370   "Search for the starting of param lists.
   1371 
   1372 Search for the opening `(' of function signature param lists.
   1373 This covers the func receiver, params, and results. Interface
   1374 declarations are also included."
   1375   (let (found-match)
   1376     (while (and
   1377             (not found-match)
   1378             (re-search-forward (concat "\\(\\_<" go-identifier-regexp "\\)?(") end t))
   1379       (when (not (go-in-string-or-comment-p))
   1380         (save-excursion
   1381           (goto-char (match-beginning 0))
   1382 
   1383           (let ((name (match-string 1)))
   1384             (when name
   1385               ;; We are in a param list if "func" preceded the "(" (i.e.
   1386               ;; func literal), or if we are in an interface
   1387               ;; declaration, e.g. "interface { foo(i int) }".
   1388               (setq found-match (or (string= name "func") (go--in-interface-p))))
   1389 
   1390             ;; Otherwise we are in a param list if our "(" is preceded
   1391             ;; by ") " or "func ".
   1392             (when (and (not found-match) (not (zerop (skip-syntax-backward " "))))
   1393               (setq found-match (or
   1394                                  (eq (char-before) ?\))
   1395                                  (looking-back "\\_<func" (- (point) 4)))))))))
   1396     found-match))
   1397 
   1398 
   1399 (defconst go--named-param-re
   1400   (concat "[[:space:]\n]*\\(" go-identifier-regexp "\\)\\(?:[[:space:]]+\\(?:\\.\\.\\.\\)?" go-type-name-regexp "[[:space:]]*[,)]\\)?")
   1401   "Regexp to match named param such as \"s *string\" in:
   1402 
   1403 func(i int, s *string) { }")
   1404 
   1405 (defconst go--unnamed-param-re
   1406   (concat "\\(\\)[[:space:]\n]*\\(?:\\.\\.\\.\\)?" go-type-name-regexp "[[:space:]]*[,)]")
   1407   "Regexp to match unnamed param such as \"*string\" in:
   1408 
   1409 func(int, *string) { }
   1410 
   1411 We start with an empty subexp since our font lock keyword expects
   1412 subexp 1 to a variable name, but we have no variable.")
   1413 
   1414 (defun go--fontify-param (end)
   1415   "Match a param within a param list.
   1416 
   1417 Our parent font lock matcher is anchored to the beginning of the
   1418 param list. `go--fontify-param-has-name' has been set
   1419 appropriately. We match the next param and advance point to after
   1420 the next comma or to the closing paren."
   1421   (let (found-match done)
   1422     ;; We loop until match because there are some params that we can't
   1423     ;; handle (but we may need to handle subsequent params). For
   1424     ;; example:
   1425     ;;
   1426     ;; // We don't handle the interface, so we must skip it and handle
   1427     ;; // "string".
   1428     ;; func(int, interface { foo() }, string)
   1429     (while (and (not found-match) (not done))
   1430       (if go--fontify-param-has-name
   1431           (when (looking-at go--named-param-re)
   1432             (when (not go-fontify-variables)
   1433               (let ((md (match-data)))
   1434                 (setf (nth 2 md) nil (nth 3 md) nil)
   1435                 (set-match-data md)))
   1436             (setq found-match t))
   1437         (when (looking-at go--unnamed-param-re)
   1438           (setq found-match t)))
   1439 
   1440       ;; Advance to next comma. We are done if there are no more commas.
   1441       (setq done (not (go--search-next-comma end))))
   1442     found-match))
   1443 
   1444 (defun go--search-next-comma (end)
   1445   "Search forward from point for a comma whose nesting level is
   1446 the same as point. If it reaches a closing parenthesis before a
   1447 comma, it stops at it. Return non-nil if comma was found."
   1448   (let ((orig-level (go-paren-level)))
   1449     (while (and (< (point) end)
   1450                 (or (looking-at-p "[^,)]")
   1451                     (> (go-paren-level) orig-level)))
   1452       (forward-char))
   1453     (when (and (looking-at-p ",")
   1454                (< (point) (1- end)))
   1455       (forward-char)
   1456       t)))
   1457 
   1458 (defun go--looking-at-keyword ()
   1459   (and (looking-at (concat "\\(" go-identifier-regexp "\\)"))
   1460        (member (match-string 1) go-mode-keywords)))
   1461 
   1462 (defun go--match-type-switch-case (end)
   1463   "Match a \"case\" clause within a type switch."
   1464   (let (found-match)
   1465     (while (and
   1466             (not found-match)
   1467 
   1468             ;; Search for "case" statements.
   1469             (re-search-forward "^[[:space:]]*case " end t))
   1470 
   1471       ;; Make sure we are in a type switch statement.
   1472       (setq found-match (go--in-type-switch-p)))
   1473     found-match))
   1474 
   1475 (defun go--fontify-type-switch-case (end)
   1476   "Match a single type within a type switch case."
   1477   (let (found-match done)
   1478     ;; Loop until we find a match because we must skip types we don't
   1479     ;; handle, such as "interface { foo() }".
   1480     (while (and (not found-match) (not done))
   1481       (when (looking-at (concat "\\(?:[[:space:]]*\\|//.*\\|\n\\)*" go-type-name-regexp "[[:space:]]*[,:]"))
   1482         (goto-char (match-end 1))
   1483         (unless (member (match-string 1) go-constants)
   1484           (setq found-match t)))
   1485       (setq done (not (go--search-next-comma end))))
   1486     found-match))
   1487 
   1488 (defun go--containing-decl ()
   1489   "Return containing decl kind var|const|type, if any."
   1490   (save-match-data
   1491     (or
   1492      (save-excursion
   1493        (and
   1494         (go-goto-opening-parenthesis)
   1495         (eq (char-after) ?\()
   1496         (skip-syntax-backward " ")
   1497         (skip-syntax-backward "w")
   1498         (looking-at "\\(var\\|const\\|type\\)[[:space:]]")
   1499         (match-string-no-properties 1)))
   1500 
   1501      (save-excursion
   1502        (let ((depth (go-paren-level)))
   1503          (beginning-of-line)
   1504          (and
   1505           (= (go-paren-level) depth)
   1506           (looking-at "[[:space:]]*\\(var\\|const\\|type\\)[[:space:]]")
   1507           (match-string-no-properties 1)))))))
   1508 
   1509 (defconst go--decl-ident-re (concat "\\(?:^\\|[[:space:]]\\)\\(\\(\\(" go-identifier-regexp "\\)\\)\\)\\_>"))
   1510 
   1511 (defun go--match-decl (end)
   1512   "Match identifiers in \"var\", \"type\" and \"const\" decls, as
   1513 well as \":=\" assignments.
   1514 
   1515 In order to only scan once, the regex has three subexpressions
   1516 that match the same identifier. Depending on the kind of
   1517 containing decl we zero out the subexpressions so the right one
   1518 gets highlighted by the font lock keyword."
   1519   (let (found-match decl)
   1520     (while (and
   1521             (not found-match)
   1522             (re-search-forward go--decl-ident-re end t))
   1523 
   1524       (save-excursion
   1525         ;; Skip keywords.
   1526         (cond
   1527          ((member (match-string 1) go-mode-keywords))
   1528 
   1529          ((and
   1530            ;; We are in a decl of some kind.
   1531            (setq decl (go--containing-decl))
   1532 
   1533            ;; We aren't on right side of equals sign.
   1534            (not (go--looking-back-p "=")))
   1535 
   1536           (setq found-match t)
   1537 
   1538           ;; Unset match data subexpressions that don't apply based on
   1539           ;; the decl kind.
   1540           (let ((md (match-data)))
   1541             (cond
   1542              ((string= decl "var")
   1543               (setf (nth 4 md) nil (nth 5 md) nil (nth 6 md) nil (nth 7 md) nil)
   1544               (when (not go-fontify-variables)
   1545                 (setf (nth 2 md) nil (nth 3 md) nil)))
   1546              ((string= decl "const")
   1547               (setf (nth 2 md) nil (nth 3 md) nil (nth 6 md) nil (nth 7 md) nil))
   1548              ((string= decl "type")
   1549               (setf (nth 2 md) nil (nth 3 md) nil (nth 4 md) nil (nth 5 md) nil)))
   1550             (set-match-data md)))
   1551 
   1552          (go-fontify-variables
   1553           (save-match-data
   1554             ;; Left side of ":=" assignment.
   1555             (when (looking-at ".*:=")
   1556               (let ((depth (go-paren-level)))
   1557                 (goto-char (match-end 0))
   1558                 ;; Make sure the ":=" isn't in a comment or a sub-block.
   1559                 (setq found-match (and
   1560                                    (not (go-in-string-or-comment-p))
   1561                                    (= depth (go-paren-level)))))))))))
   1562     found-match))
   1563 
   1564 (defun go--looking-back-p (re)
   1565   "Return non-nil if RE matches beginning of line to point.
   1566 
   1567 RE is not anchored automatically."
   1568   (string-match-p
   1569    re
   1570    (buffer-substring-no-properties (point) (line-beginning-position))))
   1571 
   1572 
   1573 (defconst go--ident-type-pair-re (concat "\\_<\\(" go-identifier-regexp "\\)[[:space:]]+" go-type-name-regexp))
   1574 
   1575 (defun go--match-ident-type-pair (end)
   1576   "Search for identifier + type-name pairs.
   1577 
   1578 For example, this looks for the \"foo bar\" in \"var foo bar\",
   1579 yielding match-data for \"bar\" since that is a type name to be
   1580 fontified. This approach matches type names in var and const
   1581 decls, and in struct definitions. Return non-nil if search
   1582 succeeds."
   1583   (let (found-match)
   1584     (while (and
   1585             (not found-match)
   1586             (re-search-forward go--ident-type-pair-re end t))
   1587 
   1588       ;; Make sure the neither match is a keyword.
   1589       (if (member (match-string 2) go-mode-keywords)
   1590           (goto-char (match-end 2))
   1591         (if (member (match-string 1) go-mode-keywords)
   1592             (goto-char (match-end 1))
   1593           (setq found-match t))))
   1594 
   1595     found-match))
   1596 
   1597 (defconst go--single-func-result-re (concat ")[[:space:]]+" go-type-name-regexp "\\(?:$\\|[[:space:]),]\\)"))
   1598 
   1599 (defun go--match-single-func-result (end)
   1600   "Match single result types.
   1601 
   1602 Parenthetical result lists are handled by the param list keyword,
   1603 so we need a separate keyword to handle singular result types
   1604 such as \"string\" in:
   1605 
   1606 func foo(i int) string"
   1607   (let (found-match)
   1608     (while (and
   1609             (not found-match)
   1610             (re-search-forward go--single-func-result-re end t))
   1611       (when (not (member (match-string 1) go-mode-keywords))
   1612         (setq found-match t)
   1613         (goto-char (match-end 1))))
   1614     found-match))
   1615 
   1616 (defconst go--type-alias-re
   1617   (concat "^[[:space:]]*\\(type\\)?[[:space:]]*" go-identifier-regexp "[[:space:]]*=[[:space:]]*" go-type-name-regexp))
   1618 
   1619 (defun go--match-type-alias (end)
   1620   "Search for type aliases.
   1621 
   1622 We are looking for the right-hand-side of the type alias"
   1623   (let (found-match)
   1624     (while (and
   1625             (not found-match)
   1626             (re-search-forward go--type-alias-re end t))
   1627       ;; Either line started with "type", or we are in a "type" block.
   1628       (setq found-match (or
   1629                          (match-string 1)
   1630                          (go--in-paren-with-prefix-p ?\( "type"))))
   1631     found-match))
   1632 
   1633 
   1634 (defconst go--map-value-re
   1635   (concat "\\_<map\\_>\\[\\(?:\\[[^]]*\\]\\)*[^]]*\\]" go-type-name-regexp))
   1636 
   1637 (defun go--match-map-value (end)
   1638   "Search for map value types."
   1639   (when (re-search-forward go--map-value-re end t)
   1640     ;; Move point to beginning of map value in case value itself is
   1641     ;; also a map (we will match it next iteration).
   1642     (goto-char (match-beginning 1))
   1643     t))
   1644 
   1645 (defconst go--label-re (concat "\\(" go-label-regexp "\\):"))
   1646 
   1647 (defun go--match-ident-colon (end)
   1648   "Search for composite literal field names and label definitions."
   1649   (let (found-match)
   1650     (while (and
   1651             (not found-match)
   1652             (re-search-forward go--label-re end t))
   1653 
   1654       (save-excursion
   1655         (goto-char (match-beginning 1))
   1656         (skip-syntax-backward " ")
   1657 
   1658         (setq found-match (or
   1659                            ;; We are a label/field name if we are at the
   1660                            ;; beginning of the line.
   1661                            (bolp)
   1662 
   1663                            ;; Composite literal field names, e.g. "Foo{Bar:". Note
   1664                            ;; that this gives false positives for literal maps,
   1665                            ;; arrays, and slices.
   1666                            (and
   1667                             (or (eq (char-before) ?,) (eq (char-before) ?{))
   1668                             (go--in-composite-literal-p))))))
   1669 
   1670     found-match))
   1671 
   1672 (defun go--parameter-list-type (end)
   1673   "Return `present' if the parameter list has names, or `absent' if not.
   1674 Assumes point is at the beginning of a parameter list, just
   1675 after '('."
   1676   (save-excursion
   1677     (skip-chars-forward "[:space:]\n" end)
   1678     (cond ((> (point) end)
   1679            nil)
   1680           ((looking-at (concat go-identifier-regexp "[[:space:]\n]*,"))
   1681            (goto-char (match-end 0))
   1682            (go--parameter-list-type end))
   1683           ((or (looking-at go-qualified-identifier-regexp)
   1684                (looking-at (concat go-type-name-no-prefix-regexp "[[:space:]\n]*\\(?:)\\|\\'\\)"))
   1685                (go--looking-at-keyword)
   1686                (looking-at "[*\\[]\\|\\.\\.\\.\\|\\'"))
   1687            'absent)
   1688           (t 'present))))
   1689 
   1690 (defun go--reset-dangling-cache-before-change (&optional _beg _end)
   1691   "Reset `go-dangling-cache'.
   1692 
   1693 This is intended to be called from `before-change-functions'."
   1694   (setq go-dangling-cache (make-hash-table :test 'eql)))
   1695 
   1696 (defun go--electric-indent-function (inserted-char)
   1697   (let ((prev (char-before (1- (point)))))
   1698     (cond
   1699      ;; Indent after starting/ending a comment. This is handy for
   1700      ;; comments above "case" statements and closing multiline
   1701      ;; comments.
   1702      ((or
   1703        (and (eq inserted-char ?/) (eq prev ?/))
   1704        (and (eq inserted-char ?/) (eq prev ?*))
   1705        (and (eq inserted-char ?*) (eq prev ?/)))
   1706       'do-indent)
   1707 
   1708      ((eq inserted-char ? )
   1709       (and
   1710        (eq prev ?e)
   1711        (eq (char-before (- (point) 2)) ?s)
   1712        (eq (char-before (- (point) 3)) ?a)
   1713        (eq (char-before (- (point) 4)) ?c)))
   1714 
   1715      ;; Trick electric-indent-mode into indenting inside multiline
   1716      ;; comments.
   1717      ((and (eq inserted-char ?\n) (go-in-comment-p))
   1718       'do-indent))))
   1719 
   1720 (defun go--comment-region (beg end &optional arg)
   1721   "Switch to block comment when commenting a partial line."
   1722   (save-excursion
   1723     (goto-char beg)
   1724     (let ((beg-bol (line-beginning-position)))
   1725       (goto-char end)
   1726       (if (and
   1727            ;; beg and end are on the same line
   1728            (eq (line-beginning-position) beg-bol)
   1729            ;; end is not at end of line
   1730            (not (eq end (line-end-position))))
   1731           (let ((comment-start "/* ")
   1732                 (comment-end " */")
   1733                 (comment-padding ""))
   1734             (comment-region-default beg end arg))
   1735         (comment-region-default beg end arg)))))
   1736 
   1737 ;;;###autoload
   1738 (define-derived-mode go-mode prog-mode "Go"
   1739   "Major mode for editing Go source text.
   1740 
   1741 This mode provides (not just) basic editing capabilities for
   1742 working with Go code. It offers almost complete syntax
   1743 highlighting, indentation that is almost identical to gofmt and
   1744 proper parsing of the buffer content to allow features such as
   1745 navigation by function, manipulation of comments or detection of
   1746 strings.
   1747 
   1748 In addition to these core features, it offers various features to
   1749 help with writing Go code. You can directly run buffer content
   1750 through gofmt, read godoc documentation from within Emacs, modify
   1751 and clean up the list of package imports or interact with the
   1752 Playground (uploading and downloading pastes).
   1753 
   1754 The following extra functions are defined:
   1755 
   1756 - `gofmt'
   1757 - `godoc' and `godoc-at-point'
   1758 - `go-import-add'
   1759 - `go-remove-unused-imports'
   1760 - `go-goto-arguments'
   1761 - `go-goto-docstring'
   1762 - `go-goto-function'
   1763 - `go-goto-function-name'
   1764 - `go-goto-imports'
   1765 - `go-goto-return-values'
   1766 - `go-goto-method-receiver'
   1767 - `go-play-buffer' and `go-play-region'
   1768 - `go-download-play'
   1769 - `godef-describe' and `godef-jump'
   1770 - `go-coverage'
   1771 - `go-set-project'
   1772 - `go-reset-gopath'
   1773 
   1774 If you want to automatically run `gofmt' before saving a file,
   1775 add the following hook to your emacs configuration:
   1776 
   1777 \(add-hook 'before-save-hook #'gofmt-before-save)
   1778 
   1779 If you want to use `godef-jump' instead of etags (or similar),
   1780 consider binding godef-jump to `M-.', which is the default key
   1781 for `find-tag':
   1782 
   1783 \(add-hook 'go-mode-hook (lambda ()
   1784                           (local-set-key (kbd \"M-.\") #'godef-jump)))
   1785 
   1786 Please note that godef is an external dependency. You can install
   1787 it with
   1788 
   1789 go get github.com/rogpeppe/godef
   1790 
   1791 
   1792 If you're looking for even more integration with Go, namely
   1793 on-the-fly syntax checking, auto-completion and snippets, it is
   1794 recommended that you look at flycheck
   1795 \(see URL `https://github.com/flycheck/flycheck') or flymake in combination
   1796 with goflymake (see URL `https://github.com/dougm/goflymake'), gocode
   1797 \(see URL `https://github.com/nsf/gocode'), go-eldoc
   1798 \(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go
   1799 \(see URL `https://github.com/dominikh/yasnippet-go')"
   1800 
   1801   ;; Font lock
   1802   (setq font-lock-defaults '(go--build-font-lock-keywords))
   1803   (setq font-lock-multiline t)
   1804 
   1805   ;; Indentation
   1806   (set (make-local-variable 'indent-line-function) #'go-mode-indent-line)
   1807 
   1808   ;; Comments
   1809   (set (make-local-variable 'comment-start) "// ")
   1810   (set (make-local-variable 'comment-end)   "")
   1811   (set (make-local-variable 'comment-use-syntax) t)
   1812   (set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *")
   1813   (set (make-local-variable 'comment-region-function) #'go--comment-region)
   1814   ;; Set comment-multi-line to t so that comment-indent-new-line
   1815   ;; doesn't use one /* */ per line. Thanks to comment-use-syntax,
   1816   ;; Emacs is smart enough to still insert new // for single-line
   1817   ;; comments.
   1818   (set (make-local-variable 'comment-multi-line) t)
   1819 
   1820   (set (make-local-variable 'beginning-of-defun-function) #'go-beginning-of-defun)
   1821   (set (make-local-variable 'end-of-defun-function) #'go-end-of-defun)
   1822   (setq-local paragraph-start
   1823               (concat "[[:space:]]*\\(?:"
   1824                       comment-start-skip
   1825                       "\\|\\*/?[[:space:]]*\\|\\)$"))
   1826   (setq-local paragraph-separate paragraph-start)
   1827   (setq-local fill-paragraph-function #'go-fill-paragraph)
   1828   (setq-local fill-forward-paragraph-function #'go--fill-forward-paragraph)
   1829   (setq-local adaptive-fill-function #'go--find-fill-prefix)
   1830   (setq-local adaptive-fill-first-line-regexp "")
   1831   (setq-local comment-line-break-function #'go--comment-indent-new-line)
   1832 
   1833   (set (make-local-variable 'parse-sexp-lookup-properties) t)
   1834   (set (make-local-variable 'syntax-propertize-function) #'go-propertize-syntax)
   1835 
   1836   (when (boundp 'electric-indent-chars)
   1837     (set (make-local-variable 'electric-indent-chars) '(?\n ?} ?\) ?:))
   1838     (add-hook 'electric-indent-functions #'go--electric-indent-function nil t))
   1839 
   1840   (set (make-local-variable 'compilation-error-screen-columns) nil)
   1841 
   1842   (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql))
   1843   (add-hook 'before-change-functions #'go--reset-dangling-cache-before-change t t)
   1844 
   1845   ;; ff-find-other-file
   1846   (setq ff-other-file-alist 'go-other-file-alist)
   1847 
   1848   (setq imenu-generic-expression
   1849         '(("type" "^type *\\([^ \t\n\r\f]*\\)" 1)
   1850           ("func" "^func *\\(.*\\) {" 1)))
   1851   (imenu-add-to-menubar "Index")
   1852 
   1853   ;; Go style
   1854   (setq indent-tabs-mode t)
   1855 
   1856   ;; Handle unit test failure output in compilation-mode
   1857   ;;
   1858   ;; Note that we add our entry to the beginning of
   1859   ;; compilation-error-regexp-alist. In older versions of Emacs, the
   1860   ;; list was processed from the end, and we would've wanted to add
   1861   ;; ours last. But at some point this changed, and now the list is
   1862   ;; processed from the beginning. It's important that our entry comes
   1863   ;; before gnu, because gnu matches go test output, but includes the
   1864   ;; leading whitespace in the file name.
   1865   ;;
   1866   ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2001-12/msg00674.html
   1867   ;; documents the old, reversed order.
   1868   (when (and (boundp 'compilation-error-regexp-alist)
   1869              (boundp 'compilation-error-regexp-alist-alist))
   1870     (add-to-list 'compilation-error-regexp-alist 'go-test)
   1871     (add-to-list 'compilation-error-regexp-alist-alist
   1872                  '(go-test . ("^\\s-+\\([^()\t\n]+\\):\\([0-9]+\\):? .*$" 1 2)) t)))
   1873 
   1874 ;;;###autoload
   1875 (add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode))
   1876 
   1877 (defun go--apply-rcs-patch (patch-buffer)
   1878   "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer."
   1879   (let ((target-buffer (current-buffer))
   1880         ;; Relative offset between buffer line numbers and line numbers
   1881         ;; in patch.
   1882         ;;
   1883         ;; Line numbers in the patch are based on the source file, so
   1884         ;; we have to keep an offset when making changes to the
   1885         ;; buffer.
   1886         ;;
   1887         ;; Appending lines decrements the offset (possibly making it
   1888         ;; negative), deleting lines increments it. This order
   1889         ;; simplifies the forward-line invocations.
   1890         (line-offset 0)
   1891         (column (current-column)))
   1892     (save-excursion
   1893       (with-current-buffer patch-buffer
   1894         (goto-char (point-min))
   1895         (while (not (eobp))
   1896           (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)")
   1897             (error "Invalid rcs patch or internal error in go--apply-rcs-patch"))
   1898           (forward-line)
   1899           (let ((action (match-string 1))
   1900                 (from (string-to-number (match-string 2)))
   1901                 (len  (string-to-number (match-string 3))))
   1902             (cond
   1903              ((equal action "a")
   1904               (let ((start (point)))
   1905                 (forward-line len)
   1906                 (let ((text (buffer-substring start (point))))
   1907                   (with-current-buffer target-buffer
   1908                     (cl-decf line-offset len)
   1909                     (goto-char (point-min))
   1910                     (forward-line (- from len line-offset))
   1911                     (insert text)))))
   1912              ((equal action "d")
   1913               (with-current-buffer target-buffer
   1914                 (go--goto-line (- from line-offset))
   1915                 (cl-incf line-offset len)
   1916                 (go--delete-whole-line len)))
   1917              (t
   1918               (error "Invalid rcs patch or internal error in go--apply-rcs-patch")))))))
   1919     (move-to-column column)))
   1920 
   1921 (defun gofmt--is-goimports-p ()
   1922   (string-equal (file-name-base gofmt-command) "goimports"))
   1923 
   1924 (defun gofmt ()
   1925   "Format the current buffer according to the formatting tool.
   1926 
   1927 The tool used can be set via ‘gofmt-command’ (default: gofmt) and additional
   1928 arguments can be set as a list via ‘gofmt-args’."
   1929   (interactive)
   1930   (let ((tmpfile (make-nearby-temp-file "gofmt" nil ".go"))
   1931         (patchbuf (get-buffer-create "*Gofmt patch*"))
   1932         (errbuf (if gofmt-show-errors (get-buffer-create "*Gofmt Errors*")))
   1933         (coding-system-for-read 'utf-8)
   1934         (coding-system-for-write 'utf-8)
   1935         our-gofmt-args)
   1936 
   1937     (unwind-protect
   1938         (save-restriction
   1939           (widen)
   1940           (if errbuf
   1941               (with-current-buffer errbuf
   1942                 (setq buffer-read-only nil)
   1943                 (erase-buffer)))
   1944           (with-current-buffer patchbuf
   1945             (erase-buffer))
   1946 
   1947           (write-region nil nil tmpfile)
   1948 
   1949           (when (and (gofmt--is-goimports-p) buffer-file-name)
   1950             (setq our-gofmt-args
   1951                   (append our-gofmt-args
   1952                           ;; srcdir, despite its name, supports
   1953                           ;; accepting a full path, and some features
   1954                           ;; of goimports rely on knowing the full
   1955                           ;; name.
   1956                           (list "-srcdir" (file-local-name
   1957                                            (file-truename buffer-file-name))))))
   1958           (setq our-gofmt-args
   1959                 (append our-gofmt-args gofmt-args
   1960                         (list "-w" (file-local-name tmpfile))))
   1961           (message "Calling gofmt: %s %s" gofmt-command our-gofmt-args)
   1962           ;; We're using errbuf for the mixed stdout and stderr output. This
   1963           ;; is not an issue because gofmt -w does not produce any stdout
   1964           ;; output in case of success.
   1965           (if (zerop (apply #'process-file gofmt-command nil errbuf nil our-gofmt-args))
   1966               (progn
   1967                 ;; There is no remote variant of ‘call-process-region’, but we
   1968                 ;; can invoke diff locally, and the results should be the same.
   1969                 (if (zerop (let ((local-copy (file-local-copy tmpfile)))
   1970                              (unwind-protect
   1971                                  (call-process-region
   1972                                   (point-min) (point-max) "diff" nil patchbuf
   1973                                   nil "-n" "-" (or local-copy tmpfile))
   1974                                (when local-copy (delete-file local-copy)))))
   1975                     (message "Buffer is already gofmted")
   1976                   (go--apply-rcs-patch patchbuf)
   1977                   (message "Applied gofmt"))
   1978                 (if errbuf (gofmt--kill-error-buffer errbuf)))
   1979             (message "Could not apply gofmt")
   1980             (if errbuf (gofmt--process-errors (buffer-file-name) tmpfile errbuf))))
   1981 
   1982       (kill-buffer patchbuf)
   1983       (delete-file tmpfile))))
   1984 
   1985 
   1986 (defun gofmt--process-errors (filename tmpfile errbuf)
   1987   (with-current-buffer errbuf
   1988     (if (eq gofmt-show-errors 'echo)
   1989         (progn
   1990           (message "%s" (buffer-string))
   1991           (gofmt--kill-error-buffer errbuf))
   1992       ;; Convert the gofmt stderr to something understood by the compilation mode.
   1993       (goto-char (point-min))
   1994       (if (save-excursion
   1995             (save-match-data
   1996               (search-forward "flag provided but not defined: -srcdir" nil t)))
   1997           (insert "Your version of goimports is too old and doesn't support vendoring. Please update goimports!\n\n"))
   1998       (insert "gofmt errors:\n")
   1999       (let ((truefile
   2000              (if (gofmt--is-goimports-p)
   2001                  (concat (file-name-directory filename) (file-name-nondirectory tmpfile))
   2002                tmpfile)))
   2003         (while (search-forward-regexp
   2004                 (concat "^\\(" (regexp-quote (file-local-name truefile))
   2005                         "\\):")
   2006                 nil t)
   2007           (replace-match (file-name-nondirectory filename) t t nil 1)))
   2008       (compilation-mode)
   2009       (display-buffer errbuf))))
   2010 
   2011 (defun gofmt--kill-error-buffer (errbuf)
   2012   (let ((win (get-buffer-window errbuf)))
   2013     (if win
   2014         (quit-window t win)
   2015       (kill-buffer errbuf))))
   2016 
   2017 ;;;###autoload
   2018 (defun gofmt-before-save ()
   2019   "Add this to .emacs to run gofmt on the current buffer when saving:
   2020 \(add-hook 'before-save-hook 'gofmt-before-save).
   2021 
   2022 Note that this will cause ‘go-mode’ to get loaded the first time
   2023 you save any file, kind of defeating the point of autoloading."
   2024 
   2025   (interactive)
   2026   (when (eq major-mode 'go-mode) (gofmt)))
   2027 
   2028 (defun godoc--read-query ()
   2029   "Read a godoc query from the minibuffer."
   2030   (if godoc-use-completing-read
   2031       (completing-read "godoc; "
   2032                        (go-packages) nil nil nil 'go-godoc-history)
   2033     (read-from-minibuffer "godoc: " nil nil nil 'go-godoc-history)))
   2034 
   2035 (defun godoc--buffer-name (query)
   2036   "Determine the name to use for the output buffer of a given godoc QUERY."
   2037   (if godoc-reuse-buffer
   2038       "*godoc*"
   2039     (concat "*godoc " query "*")))
   2040 
   2041 (defun godoc--get-buffer (query)
   2042   "Get an empty buffer for a godoc QUERY."
   2043   (let* ((buffer-name (godoc--buffer-name query))
   2044          (buffer (get-buffer buffer-name)))
   2045     ;; Kill the existing buffer if it already exists.
   2046     (when buffer (kill-buffer buffer))
   2047     (get-buffer-create buffer-name)))
   2048 
   2049 (defun godoc--buffer-sentinel (proc event)
   2050   "Sentinel function run when godoc command completes."
   2051   (with-current-buffer (process-buffer proc)
   2052     (cond ((string= event "finished\n")  ;; Successful exit.
   2053            (goto-char (point-min))
   2054            (godoc-mode)
   2055            (display-buffer (current-buffer) t))
   2056           ((/= (process-exit-status proc) 0)  ;; Error exit.
   2057            (let ((output (buffer-string)))
   2058              (kill-buffer (current-buffer))
   2059              (message (concat "godoc: " output)))))))
   2060 
   2061 (define-derived-mode godoc-mode special-mode "Godoc"
   2062   "Major mode for showing Go documentation."
   2063   (view-mode-enter))
   2064 
   2065 ;;;###autoload
   2066 (defun godoc (query)
   2067   "Show Go documentation for QUERY, much like \\<go-mode-map>\\[man]."
   2068   (interactive (list (godoc--read-query)))
   2069   (go--godoc query godoc-command))
   2070 
   2071 (defun go--godoc (query command)
   2072   (unless (string= query "")
   2073     (set-process-sentinel
   2074      (start-process-shell-command "godoc" (godoc--get-buffer query)
   2075                                   (concat command " " query))
   2076      'godoc--buffer-sentinel)
   2077     nil))
   2078 
   2079 (defun godoc-at-point (point)
   2080   "Show Go documentation for the identifier at POINT.
   2081 
   2082 It uses `godoc-at-point-function' to look up the documentation."
   2083   (interactive "d")
   2084   (funcall godoc-at-point-function point))
   2085 
   2086 (defun go-goto-imports ()
   2087   "Move point to the block of imports.
   2088 
   2089 If using
   2090 
   2091   import (
   2092     \"foo\"
   2093     \"bar\"
   2094   )
   2095 
   2096 it will move point directly behind the last import.
   2097 
   2098 If using
   2099 
   2100   import \"foo\"
   2101   import \"bar\"
   2102 
   2103 it will move point to the next line after the last import.
   2104 
   2105 If no imports can be found, point will be moved after the package
   2106 declaration."
   2107   (interactive)
   2108   ;; FIXME if there's a block-commented import before the real
   2109   ;; imports, we'll jump to that one.
   2110 
   2111   ;; Generally, this function isn't very forgiving. it'll bark on
   2112   ;; extra whitespace. It works well for clean code.
   2113   (let ((old-point (point)))
   2114     (goto-char (point-min))
   2115     (cond
   2116      ((re-search-forward "^import ()" nil t)
   2117       (backward-char 1)
   2118       'block-empty)
   2119      ((re-search-forward "^import ([^)]+)" nil t)
   2120       (backward-char 2)
   2121       'block)
   2122      ((re-search-forward "\\(^import \\([^\"]+ \\)?\"[^\"]+\"\n?\\)+" nil t)
   2123       'single)
   2124      ((re-search-forward "^[[:space:]\n]*package .+?\n" nil t)
   2125       (message "No imports found, moving point after package declaration")
   2126       'none)
   2127      (t
   2128       (goto-char old-point)
   2129       (message "No imports or package declaration found. Is this really a Go file?")
   2130       'fail))))
   2131 
   2132 (defun go-play-buffer ()
   2133   "Like `go-play-region', but acts on the entire buffer."
   2134   (interactive)
   2135   (go-play-region (point-min) (point-max)))
   2136 
   2137 (defun go-play-region (start end)
   2138   "Send the region between START and END to the Playground.
   2139 If non-nil `go-play-browse-function' is called with the
   2140 Playground URL.
   2141 
   2142 By default this function will prompt to confirm you want to upload
   2143 code to the Playground. You can disable the confirmation by setting
   2144 `go-confirm-playground-uploads' to nil.
   2145 "
   2146   (interactive "r")
   2147   (if (and go-confirm-playground-uploads
   2148            (not (yes-or-no-p "Upload to public Go Playground? ")))
   2149       (message "Upload aborted")
   2150     (let* ((url-request-method "POST")
   2151            (url-request-extra-headers
   2152             '(("Content-Type" . "text/plain; charset=UTF-8")))
   2153            (url-request-data
   2154             (encode-coding-string
   2155              (buffer-substring-no-properties start end)
   2156              'utf-8))
   2157 
   2158            (content-buf (url-retrieve
   2159                          "https://play.golang.org/share"
   2160                          (lambda (arg)
   2161                            (cond
   2162                             ((equal :error (car arg))
   2163                              (signal 'go-play-error (cdr arg)))
   2164                             (t
   2165                              (re-search-forward "\n\n")
   2166                              (let ((url (format "https://play.golang.org/p/%s"
   2167                                                 (buffer-substring (point) (point-max)))))
   2168                                (when go-play-browse-function
   2169                                  (funcall go-play-browse-function url))))))))))))
   2170 
   2171 ;;;###autoload
   2172 (defun go-download-play (url)
   2173   "Download a paste from the playground and insert it in a Go buffer.
   2174 Tries to look for a URL at point."
   2175   (interactive (list (read-from-minibuffer "Playground URL: " (ffap-url-p (ffap-string-at-point 'url)))))
   2176   (with-current-buffer
   2177       (let ((url-request-method "GET") url-request-data url-request-extra-headers)
   2178         (url-retrieve-synchronously (concat url ".go")))
   2179     (let ((buffer (generate-new-buffer (concat (car (last (split-string url "/"))) ".go"))))
   2180       (goto-char (point-min))
   2181       (re-search-forward "\n\n")
   2182       (copy-to-buffer buffer (point) (point-max))
   2183       (kill-buffer)
   2184       (with-current-buffer buffer
   2185         (go-mode)
   2186         (switch-to-buffer buffer)))))
   2187 
   2188 (defun go-propertize-syntax (start end)
   2189   (save-excursion
   2190     (goto-char start)
   2191     (while (search-forward "\\" end t)
   2192       (put-text-property (1- (point)) (point) 'syntax-table (if (= (char-after) ?`) '(1) '(9))))))
   2193 
   2194 (defun go-import-add (arg import)
   2195   "Add a new IMPORT to the list of imports.
   2196 
   2197 When called with a prefix ARG asks for an alternative name to
   2198 import the package as.
   2199 
   2200 If no list exists yet, one will be created if possible.
   2201 
   2202 If an identical import has been commented, it will be
   2203 uncommented, otherwise a new import will be added."
   2204 
   2205   ;; - If there's a matching `// import "foo"`, uncomment it
   2206   ;; - If we're in an import() block and there's a matching `"foo"`, uncomment it
   2207   ;; - Otherwise add a new import, with the appropriate syntax
   2208   (interactive
   2209    (list
   2210     current-prefix-arg
   2211     (replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go-packages)))))
   2212   (save-excursion
   2213     (let (as line import-start)
   2214       (if arg
   2215           (setq as (read-from-minibuffer "Import as: ")))
   2216       (if as
   2217           (setq line (format "%s \"%s\"" as import))
   2218         (setq line (format "\"%s\"" import)))
   2219 
   2220       (goto-char (point-min))
   2221       (if (re-search-forward (concat "^[[:space:]]*//[[:space:]]*import " line "$") nil t)
   2222           (uncomment-region (line-beginning-position) (line-end-position))
   2223         (cl-case (go-goto-imports)
   2224           ('fail (message "Could not find a place to add import."))
   2225           ('block-empty
   2226            (insert "\n\t" line "\n"))
   2227           ('block
   2228               (save-excursion
   2229                 (re-search-backward "^import (")
   2230                 (setq import-start (point)))
   2231             (if (re-search-backward (concat "^[[:space:]]*//[[:space:]]*" line "$")  import-start t)
   2232                 (uncomment-region (line-beginning-position) (line-end-position))
   2233               (insert "\n\t" line)))
   2234           ('single (insert "import " line "\n"))
   2235           ('none (insert "\nimport (\n\t" line "\n)\n")))))))
   2236 
   2237 (defun go-root-and-paths ()
   2238   (let* ((output (process-lines go-command "env" "GOROOT" "GOPATH"))
   2239          (root (car output))
   2240          (paths (split-string (cadr output) path-separator)))
   2241     (cons root paths)))
   2242 
   2243 (defun go--string-prefix-p (s1 s2 &optional ignore-case)
   2244   "Return non-nil if S1 is a prefix of S2.
   2245 If IGNORE-CASE is non-nil, the comparison is case-insensitive."
   2246   (eq t (compare-strings s1 nil nil
   2247                          s2 0 (length s1) ignore-case)))
   2248 
   2249 (defun go--directory-dirs (dir)
   2250   "Recursively return all subdirectories in DIR."
   2251   (if (file-directory-p dir)
   2252       (let ((dir (directory-file-name dir))
   2253             (dirs '())
   2254             (files (directory-files dir nil nil t)))
   2255         (dolist (file files)
   2256           (unless (member file '("." ".."))
   2257             (let ((file (concat dir "/" file)))
   2258               (if (and (file-directory-p file)
   2259                        (not (file-symlink-p file)))
   2260                   (setq dirs (append (cons file
   2261                                            (go--directory-dirs file))
   2262                                      dirs))))))
   2263         dirs)
   2264     '()))
   2265 
   2266 
   2267 (defun go-packages ()
   2268   (funcall go-packages-function))
   2269 
   2270 (defun go-packages-native ()
   2271   "Return a list of all installed Go packages. Obsolete.
   2272 It looks for archive files in /pkg/. This strategy does not work
   2273 well with the Go build cache or Go modules.
   2274 
   2275 You should use `go-packages-go-list' instead."
   2276   (sort
   2277    (delete-dups
   2278     (cl-mapcan
   2279      (lambda (topdir)
   2280        (let ((pkgdir (concat topdir "/pkg/")))
   2281          (cl-mapcan (lambda (dir)
   2282                    (mapcar (lambda (file)
   2283                              (let ((sub (substring file (length pkgdir) -2)))
   2284                                (unless (or (go--string-prefix-p "obj/" sub) (go--string-prefix-p "tool/" sub))
   2285                                  (mapconcat #'identity (cdr (split-string sub "/")) "/"))))
   2286                            (if (file-directory-p dir)
   2287                                (directory-files dir t "\\.a$"))))
   2288                  (if (file-directory-p pkgdir)
   2289                      (go--directory-dirs pkgdir)))))
   2290      (go-root-and-paths)))
   2291    #'string<))
   2292 
   2293 (defun go-packages-go-list ()
   2294   "Return a list of all Go packages, using `go list'."
   2295   (process-lines go-command "list" "-e" "all"))
   2296 
   2297 (defun go-unused-imports-lines ()
   2298   (reverse (remove nil
   2299                    (mapcar
   2300                     (lambda (line)
   2301                       (when (string-match "^\\(.+\\):\\([[:digit:]]+\\):\\([[:digit:]]+\\): imported and not used: \".+\".*$" line)
   2302                         (let ((error-file-name (match-string 1 line))
   2303                               (error-line-num (match-string 2 line)))
   2304                           (if (string= (file-truename error-file-name) (file-truename buffer-file-name))
   2305                               (string-to-number error-line-num)))))
   2306                     (split-string (shell-command-to-string
   2307                                    (concat go-command
   2308                                            (if (string-match "_test\\.go$" buffer-file-truename)
   2309                                                " test -c"
   2310                                              (concat " build -o " null-device))
   2311                                            " -gcflags=-e"
   2312                                            " "
   2313                                            (shell-quote-argument (file-truename buffer-file-name)))) "\n")))))
   2314 
   2315 (defun go-remove-unused-imports (arg)
   2316   "Remove all unused imports.
   2317 If ARG is non-nil, unused imports will be commented, otherwise
   2318 they will be removed completely."
   2319   (interactive "P")
   2320   (save-excursion
   2321     (let ((cur-buffer (current-buffer)) flymake-state lines)
   2322       (when (boundp 'flymake-mode)
   2323         (setq flymake-state flymake-mode)
   2324         (flymake-mode -1))
   2325       (save-some-buffers nil (lambda () (equal cur-buffer (current-buffer))))
   2326       (if (buffer-modified-p)
   2327           (message "Cannot operate on unsaved buffer")
   2328         (setq lines (go-unused-imports-lines))
   2329         (dolist (import lines)
   2330           (go--goto-line import)
   2331           (beginning-of-line)
   2332           (if arg
   2333               (comment-region (line-beginning-position) (line-end-position))
   2334             (go--delete-whole-line)))
   2335         (message "Removed %d imports" (length lines)))
   2336       (if flymake-state (flymake-mode 1)))))
   2337 
   2338 (defun godef--find-file-line-column (specifier other-window)
   2339   "Given a file name in the format of `filename:line:column',
   2340 visit FILENAME and go to line LINE and column COLUMN."
   2341   (if (not (string-match "\\(.+\\):\\([0-9]+\\):\\([0-9]+\\)" specifier))
   2342       ;; We've only been given a directory name
   2343       (funcall (if other-window #'find-file-other-window #'find-file) specifier)
   2344     (let ((filename (match-string 1 specifier))
   2345           (line (string-to-number (match-string 2 specifier)))
   2346           (column (string-to-number (match-string 3 specifier))))
   2347       (funcall (if other-window #'find-file-other-window #'find-file) filename)
   2348       (go--goto-line line)
   2349       (beginning-of-line)
   2350       (forward-char (1- column))
   2351       (if (buffer-modified-p)
   2352           (message "Buffer is modified, file position might not have been correct")))))
   2353 
   2354 (defun godef--call (point)
   2355   "Call godef, acquiring definition position and expression
   2356 description at POINT."
   2357   (if (not (buffer-file-name (go--coverage-origin-buffer)))
   2358       (error "Cannot use godef on a buffer without a file name")
   2359     (let ((outbuf (generate-new-buffer "*godef*"))
   2360           (coding-system-for-read 'utf-8)
   2361           (coding-system-for-write 'utf-8))
   2362       (prog2
   2363           (call-process-region (point-min)
   2364                                (point-max)
   2365                                godef-command
   2366                                nil
   2367                                outbuf
   2368                                nil
   2369                                "-i"
   2370                                "-t"
   2371                                "-f"
   2372                                (file-truename (buffer-file-name (go--coverage-origin-buffer)))
   2373                                "-o"
   2374                                ;; Emacs point and byte positions are 1-indexed.
   2375                                (number-to-string (1- (position-bytes point))))
   2376           (with-current-buffer outbuf
   2377             (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n"))
   2378         (kill-buffer outbuf)))))
   2379 
   2380 (defun godef--successful-p (output)
   2381   (not (or (string= "-" output)
   2382            (string= "godef: no identifier found" output)
   2383            (string= "godef: no object" output)
   2384            (go--string-prefix-p "godef: no declaration found for " output)
   2385            (go--string-prefix-p "error finding import path for " output))))
   2386 
   2387 (defun godef--error (output)
   2388   (cond
   2389    ((godef--successful-p output)
   2390     nil)
   2391    ((string= "-" output)
   2392     "godef: expression is not defined anywhere")
   2393    (t
   2394     output)))
   2395 
   2396 (defun godef-describe (point)
   2397   "Describe the expression at POINT."
   2398   (interactive "d")
   2399   (condition-case nil
   2400       (let ((description (cdr (butlast (godef--call point) 1))))
   2401         (if (not description)
   2402             (message "No description found for expression at point")
   2403           (message "%s" (mapconcat #'identity description "\n"))))
   2404     (file-error (message "Could not run godef binary"))))
   2405 
   2406 (defun godef-jump (point &optional other-window)
   2407   "Jump to the definition of the expression at POINT."
   2408   (interactive "d")
   2409   (condition-case nil
   2410 	  (let ((file (car (godef--call point))))
   2411 		(if (not (godef--successful-p file))
   2412 			(message "%s" (godef--error file))
   2413 		  (push-mark)
   2414 		  ;; TODO: Integrate this facility with XRef.
   2415 		  (xref-push-marker-stack)
   2416 		  (godef--find-file-line-column file other-window)))
   2417 	(file-error (message "Could not run godef binary"))))
   2418 
   2419 (defun godef-jump-other-window (point)
   2420   (interactive "d")
   2421   (godef-jump point t))
   2422 
   2423 (defun go--goto-line (line)
   2424   (goto-char (point-min))
   2425   (forward-line (1- line)))
   2426 
   2427 (defun go--line-column-to-point (line column)
   2428   (save-excursion
   2429     (go--goto-line line)
   2430     (forward-char (1- column))
   2431     (point)))
   2432 
   2433 (cl-defstruct go--covered
   2434   start-line start-column end-line end-column covered count)
   2435 
   2436 (defun go--coverage-file ()
   2437   "Return the coverage file to use, either by reading it from the
   2438 current coverage buffer or by prompting for it."
   2439   (if (boundp 'go--coverage-current-file-name)
   2440       go--coverage-current-file-name
   2441     (read-file-name "Coverage file: " nil nil t)))
   2442 
   2443 (defun go--coverage-origin-buffer ()
   2444   "Return the buffer to base the coverage on."
   2445   (or (buffer-base-buffer) (current-buffer)))
   2446 
   2447 (defun go--coverage-face (count divisor)
   2448   "Return the intensity face for COUNT when using DIVISOR
   2449 to scale it to a range [0,10].
   2450 
   2451 DIVISOR scales the absolute cover count to values from 0 to 10.
   2452 For DIVISOR = 0 the count will always translate to 8."
   2453   (let* ((norm (cond
   2454                 ((= count 0)
   2455                  -0.1) ;; Uncovered code, set to -0.1 so n becomes 0.
   2456                 ((= divisor 0)
   2457                  0.8) ;; covermode=set, set to 0.8 so n becomes 8.
   2458                 (t
   2459                  (/ (log count) divisor))))
   2460          (n (1+ (floor (* norm 9))))) ;; Convert normalized count [0,1] to intensity [0,10]
   2461     (concat "go-coverage-" (number-to-string n))))
   2462 
   2463 (defun go--coverage-make-overlay (range divisor)
   2464   "Create a coverage overlay for a RANGE of covered/uncovered code.
   2465 Use DIVISOR to scale absolute counts to a [0,10] scale."
   2466   (let* ((count (go--covered-count range))
   2467          (face (go--coverage-face count divisor))
   2468          (ov (make-overlay (go--line-column-to-point (go--covered-start-line range)
   2469                                                      (go--covered-start-column range))
   2470                            (go--line-column-to-point (go--covered-end-line range)
   2471                                                      (go--covered-end-column range)))))
   2472 
   2473     (overlay-put ov 'face face)
   2474     (overlay-put ov 'help-echo (format "Count: %d" count))))
   2475 
   2476 (defun go--coverage-clear-overlays ()
   2477   "Remove existing overlays and put a single untracked overlay
   2478 over the entire buffer."
   2479   (remove-overlays)
   2480   (overlay-put (make-overlay (point-min) (point-max))
   2481                'face
   2482                'go-coverage-untracked))
   2483 
   2484 (defun go--coverage-parse-file (coverage-file file-name)
   2485   "Parse COVERAGE-FILE and extract coverage information and
   2486 divisor for FILE-NAME."
   2487   (let (ranges
   2488         (max-count 0))
   2489     (with-temp-buffer
   2490       (insert-file-contents coverage-file)
   2491       (go--goto-line 2) ;; Skip over mode
   2492       (while (not (eobp))
   2493         (let* ((parts (split-string (buffer-substring (point-at-bol) (point-at-eol)) ":"))
   2494                (file (car parts))
   2495                (rest (split-string (nth 1 parts) "[., ]")))
   2496 
   2497           (cl-destructuring-bind
   2498               (start-line start-column end-line end-column num count)
   2499               (mapcar #'string-to-number rest)
   2500 
   2501             (when (string= (file-name-nondirectory file) file-name)
   2502               (if (> count max-count)
   2503                   (setq max-count count))
   2504               (push (make-go--covered :start-line start-line
   2505                                       :start-column start-column
   2506                                       :end-line end-line
   2507                                       :end-column end-column
   2508                                       :covered (/= count 0)
   2509                                       :count count)
   2510                     ranges)))
   2511 
   2512           (forward-line)))
   2513 
   2514       (list ranges (if (> max-count 0) (log max-count) 0)))))
   2515 
   2516 (defun go-coverage (&optional coverage-file)
   2517   "Open a clone of the current buffer and overlay it with
   2518 coverage information gathered via go test -coverprofile=COVERAGE-FILE.
   2519 
   2520 If COVERAGE-FILE is nil, it will either be inferred from the
   2521 current buffer if it's already a coverage buffer, or be prompted
   2522 for."
   2523   (interactive)
   2524   (let* ((cur-buffer (current-buffer))
   2525          (origin-buffer (go--coverage-origin-buffer))
   2526          (gocov-buffer-name (concat (buffer-name origin-buffer) "<gocov>"))
   2527          (coverage-file (or coverage-file (go--coverage-file)))
   2528          (ranges-and-divisor (go--coverage-parse-file
   2529                               coverage-file
   2530                               (file-name-nondirectory (buffer-file-name origin-buffer))))
   2531          (cov-mtime (nth 5 (file-attributes coverage-file)))
   2532          (cur-mtime (nth 5 (file-attributes (buffer-file-name origin-buffer)))))
   2533 
   2534     (if (< (float-time cov-mtime) (float-time cur-mtime))
   2535         (message "Coverage file is older than the source file."))
   2536 
   2537     (with-current-buffer (or (get-buffer gocov-buffer-name)
   2538                              (make-indirect-buffer origin-buffer gocov-buffer-name t))
   2539       (set (make-local-variable 'go--coverage-current-file-name) coverage-file)
   2540 
   2541       (save-excursion
   2542         (go--coverage-clear-overlays)
   2543         (dolist (range (car ranges-and-divisor))
   2544           (go--coverage-make-overlay range (cadr ranges-and-divisor))))
   2545 
   2546       (if (not (eq cur-buffer (current-buffer)))
   2547           (display-buffer (current-buffer) `(,go-coverage-display-buffer-func))))))
   2548 
   2549 (defun go-goto-function (&optional arg)
   2550   "Go to the function definition (named or anonymous) surrounding point.
   2551 
   2552 If we are on a docstring, follow the docstring down.
   2553 If no function is found, assume that we are at the top of a file
   2554 and search forward instead.
   2555 
   2556 If point is looking at the func keyword of an anonymous function,
   2557 go to the surrounding function.
   2558 
   2559 If ARG is non-nil, anonymous functions are ignored."
   2560   (interactive "P")
   2561   (let ((p (point)))
   2562     (cond
   2563      ((save-excursion
   2564         (beginning-of-line)
   2565         (looking-at "^//"))
   2566       ;; In case we are looking at the docstring, move on forward until we are
   2567       ;; not anymore
   2568       (beginning-of-line)
   2569       (while (looking-at "^//")
   2570         (forward-line 1))
   2571       ;; If we are still not looking at a function, retry by calling self again.
   2572       (when (not (looking-at "\\<func\\>"))
   2573         (go-goto-function arg)))
   2574 
   2575      ;; If we're already looking at an anonymous func, look for the
   2576      ;; surrounding function.
   2577      ((and (looking-at "\\<func\\>")
   2578            (not (looking-at "^func\\>")))
   2579       (re-search-backward "\\<func\\>" nil t))
   2580 
   2581      ((not (looking-at "\\<func\\>"))
   2582       ;; If point is on the "func" keyword, step back a word and retry
   2583       (if (string= (symbol-name (symbol-at-point)) "func")
   2584           (backward-word)
   2585         ;; If we are not looking at the beginning of a function line, do a regexp
   2586         ;; search backwards
   2587         (re-search-backward "\\<func\\>" nil t))
   2588 
   2589       ;; If nothing is found, assume that we are at the top of the file and
   2590       ;; should search forward instead.
   2591       (when (not (looking-at "\\<func\\>"))
   2592         (re-search-forward "\\<func\\>" nil t)
   2593         (go--forward-word -1))
   2594 
   2595       ;; If we have landed at an anonymous function, it is possible that we
   2596       ;; were not inside it but below it. If we were not inside it, we should
   2597       ;; go to the containing function.
   2598       (while (and (not (go--in-function-p p))
   2599                   (not (looking-at "^func\\>")))
   2600         (go-goto-function arg)))))
   2601 
   2602   (cond
   2603    ((go-in-comment-p)
   2604     ;; If we are still in a comment, redo the call so that we get out of it.
   2605     (go-goto-function arg))
   2606 
   2607    ((and (looking-at "\\<func(") arg)
   2608     ;; If we are looking at an anonymous function and a prefix argument has
   2609     ;; been supplied, redo the call so that we skip the anonymous function.
   2610     (go-goto-function arg))))
   2611 
   2612 (defun go--goto-opening-curly-brace ()
   2613   ;; Find the { that starts the function, i.e., the next { that isn't
   2614   ;; preceded by struct or interface, or a comment or struct tag.  BUG:
   2615   ;; breaks if there's a comment between the struct/interface keyword and
   2616   ;; bracket, like this:
   2617   ;;
   2618   ;;     struct /* why? */ {
   2619   (go--goto-return-values)
   2620   (while (progn
   2621            (skip-chars-forward "^{")
   2622            (forward-char)
   2623            (or (go-in-string-or-comment-p)
   2624                (looking-back "\\(struct\\|interface\\)\\s-*{"
   2625                              (line-beginning-position)))))
   2626   (backward-char))
   2627 
   2628 (defun go--in-function-p (compare-point)
   2629   "Return t if COMPARE-POINT is inside the function immediately surrounding point."
   2630   (save-excursion
   2631     (when (not (looking-at "\\<func\\>"))
   2632       (go-goto-function))
   2633     (let ((start (point)))
   2634       (go--goto-opening-curly-brace)
   2635 
   2636       (unless (looking-at "{")
   2637         (error "Expected to be looking at opening curly brace"))
   2638       (forward-list 1)
   2639       (and (>= compare-point start)
   2640            (<= compare-point (point))))))
   2641 
   2642 (defun go-goto-function-name (&optional arg)
   2643   "Go to the name of the current function.
   2644 
   2645 If the function is a test, place point after 'Test'.
   2646 If the function is anonymous, place point on the 'func' keyword.
   2647 
   2648 If ARG is non-nil, anonymous functions are skipped."
   2649   (interactive "P")
   2650   (when (not (looking-at "\\<func\\>"))
   2651     (go-goto-function arg))
   2652   ;; If we are looking at func( we are on an anonymous function and
   2653   ;; nothing else should be done.
   2654   (when (not (looking-at "\\<func("))
   2655     (let ((words 1)
   2656           (chars 1))
   2657       (when (looking-at "\\<func (")
   2658         (setq words 3
   2659               chars 2))
   2660       (go--forward-word words)
   2661       (forward-char chars)
   2662       (when (looking-at "Test")
   2663         (forward-char 4)))))
   2664 
   2665 (defun go-goto-arguments (&optional arg)
   2666   "Go to the arguments of the current function.
   2667 
   2668 If ARG is non-nil, anonymous functions are skipped."
   2669   (interactive "P")
   2670   (go-goto-function-name arg)
   2671   (go--forward-word 1)
   2672   (forward-char 1))
   2673 
   2674 (defun go--goto-return-values (&optional arg)
   2675   "Go to the declaration of return values for the current function."
   2676   (go-goto-arguments arg)
   2677   (backward-char)
   2678   (forward-list)
   2679   (forward-char))
   2680 
   2681 (defun go-goto-return-values (&optional arg)
   2682   "Go to the return value declaration of the current function.
   2683 
   2684 If there are multiple ones contained in a parenthesis, enter the parenthesis.
   2685 If there is none, make space for one to be added.
   2686 
   2687 If ARG is non-nil, anonymous functions are skipped."
   2688   (interactive "P")
   2689   (go--goto-return-values arg)
   2690 
   2691   ;; Opening parenthesis, enter it
   2692   (when (looking-at "(")
   2693     (forward-char 1))
   2694 
   2695   ;; No return arguments, add space for adding
   2696   (when (looking-at "{")
   2697     (insert " ")
   2698     (backward-char 1)))
   2699 
   2700 (defun go-goto-method-receiver (&optional arg)
   2701   "Go to the receiver of the current method.
   2702 
   2703 If there is none, add parenthesis to add one.
   2704 
   2705 Anonymous functions cannot have method receivers, so when this is called
   2706 interactively anonymous functions will be skipped.  If called programmatically,
   2707 an error is raised unless ARG is non-nil."
   2708   (interactive "P")
   2709 
   2710   (when (and (not (called-interactively-p 'interactive))
   2711              (not arg)
   2712              (go--in-anonymous-funcion-p))
   2713     (error "Anonymous functions cannot have method receivers"))
   2714 
   2715   (go-goto-function t)  ; Always skip anonymous functions
   2716   (forward-char 5)
   2717   (when (not (looking-at "("))
   2718     (save-excursion
   2719       (insert "() ")))
   2720   (forward-char 1))
   2721 
   2722 (defun go-goto-docstring (&optional arg)
   2723   "Go to the top of the docstring of the current function.
   2724 
   2725 If there is none, add one beginning with the name of the current function.
   2726 
   2727 Anonymous functions do not have docstrings, so when this is called
   2728 interactively anonymous functions will be skipped.  If called programmatically,
   2729 an error is raised unless ARG is non-nil."
   2730   (interactive "P")
   2731 
   2732   (when (and (not (called-interactively-p 'interactive))
   2733              (not arg)
   2734              (go--in-anonymous-funcion-p))
   2735     (error "Anonymous functions do not have docstrings"))
   2736 
   2737   (go-goto-function t)
   2738   (forward-line -1)
   2739   (beginning-of-line)
   2740 
   2741   (while (looking-at "^//")
   2742     (forward-line -1))
   2743   (forward-line 1)
   2744   (beginning-of-line)
   2745 
   2746   (cond
   2747    ;; If we are looking at an empty comment, add a single space in front of it.
   2748    ((looking-at "^//$")
   2749     (forward-char 2)
   2750     (insert (format " %s " (go--function-name t))))
   2751    ;; If we are not looking at the function signature, we are looking at a docstring.
   2752    ;; Move to the beginning of the first word of it.
   2753    ((not (looking-at "^func"))
   2754     (forward-char 3))
   2755    ;; If we are still at the function signature, we should add a new docstring.
   2756    (t
   2757     (forward-line -1)
   2758     (newline)
   2759     (insert "// ")
   2760     (insert (go--function-name t)))))
   2761 
   2762 (defun go--function-name (&optional arg)
   2763   "Return the name of the surrounding function.
   2764 
   2765 If ARG is non-nil, anonymous functions will be ignored and the
   2766 name returned will be that of the top-level function.  If ARG is
   2767 nil and the surrounding function is anonymous, nil will be
   2768 returned."
   2769   (when (or (not (go--in-anonymous-funcion-p))
   2770             arg)
   2771     (save-excursion
   2772       (go-goto-function-name t)
   2773       (symbol-name (symbol-at-point)))))
   2774 
   2775 (defun go--in-anonymous-funcion-p ()
   2776   "Return t if point is inside an anonymous function, nil otherwise."
   2777   (save-excursion
   2778     (go-goto-function)
   2779     (looking-at "\\<func(")))
   2780 
   2781 (defun go-guess-gopath (&optional buffer)
   2782   "Determine a suitable GOPATH for BUFFER, or the current buffer if BUFFER is nil."
   2783   (with-current-buffer (or buffer (current-buffer))
   2784     (let ((gopath (cl-some (lambda (el) (funcall el))
   2785                            go-guess-gopath-functions)))
   2786       (if gopath
   2787           (mapconcat
   2788            (lambda (el) (file-truename el))
   2789            gopath
   2790            path-separator)))))
   2791 
   2792 (defun go-plain-gopath ()
   2793   "Detect a normal GOPATH, by looking for the first `src'
   2794 directory up the directory tree."
   2795   (let ((d (locate-dominating-file buffer-file-name "src")))
   2796     (if d
   2797         (list d))))
   2798 
   2799 (defun go-set-project (&optional buffer)
   2800   "Set GOPATH based on `go-guess-gopath' for BUFFER.
   2801 Set it to the current buffer if BUFFER is nil.
   2802 
   2803 If go-guess-gopath returns nil, that is if it couldn't determine
   2804 a valid value for GOPATH, GOPATH will be set to the initial value
   2805 of when Emacs was started.
   2806 
   2807 This function can for example be used as a
   2808 projectile-switch-project-hook, or simply be called manually when
   2809 switching projects."
   2810   (interactive)
   2811   (let ((gopath (or (go-guess-gopath buffer)
   2812                     (go-original-gopath))))
   2813     (setenv "GOPATH" gopath)
   2814     (message "Set GOPATH to %s" gopath)))
   2815 
   2816 (defun go-reset-gopath ()
   2817   "Reset GOPATH to the value it had when Emacs started."
   2818   (interactive)
   2819   (let ((gopath (go-original-gopath)))
   2820     (setenv "GOPATH" gopath)
   2821     (message "Set GOPATH to %s" gopath)))
   2822 
   2823 (defun go-original-gopath ()
   2824   "Return the original value of GOPATH from when Emacs was started."
   2825   (let ((process-environment initial-environment)) (getenv "GOPATH")))
   2826 
   2827 (defun go--insert-modified-files ()
   2828   "Insert the contents of each modified Go buffer into the
   2829 current buffer in the format specified by guru's -modified flag."
   2830   (mapc #'(lambda (b)
   2831             (and (buffer-modified-p b)
   2832                  (buffer-file-name b)
   2833                  (string= (file-name-extension (buffer-file-name b)) "go")
   2834                  (go--insert-modified-file (buffer-file-name b) b)))
   2835         (buffer-list)))
   2836 
   2837 (defun go--insert-modified-file (name buffer)
   2838   (insert (format "%s\n%d\n" name (go--buffer-size-bytes buffer)))
   2839   (insert-buffer-substring buffer))
   2840 
   2841 (defun go--buffer-size-bytes (&optional buffer)
   2842   (message "buffer; %s" buffer)
   2843   "Return the number of bytes in the current buffer.
   2844 If BUFFER, return the number of characters in that buffer instead."
   2845   (with-current-buffer (or buffer (current-buffer))
   2846     (1- (position-bytes (point-max)))))
   2847 
   2848 (defvar go-dot-mod-mode-map
   2849   (let ((map (make-sparse-keymap)))
   2850     map)
   2851   "Keymap for `go-dot-mod-mode'.")
   2852 
   2853 (defvar go-dot-mod-mode-syntax-table
   2854   (let ((st (make-syntax-table)))
   2855     ;; handle '//' comment syntax
   2856     (modify-syntax-entry ?/ ". 124b" st)
   2857     (modify-syntax-entry ?\n "> b" st)
   2858     st)
   2859   "Syntax table for `go-dot-mod-mode'.")
   2860 
   2861 (defconst go-dot-mod-mode-keywords
   2862   '("module" "go" "require" "replace" "exclude")
   2863   "All keywords for go.mod files.  Used for font locking.")
   2864 
   2865 (defgroup go-dot-mod nil
   2866   "Options specific to `go-dot-mod-mode`."
   2867   :group 'go)
   2868 
   2869 (defface go-dot-mod-module-name '((t :inherit default))
   2870   "Face for module name in \"require\" list."
   2871   :group 'go-dot-mod)
   2872 
   2873 (defface go-dot-mod-module-version '((t :inherit default))
   2874   "Face for module version in \"require\" list."
   2875   :group 'go-dot-mod)
   2876 
   2877 (defface go-dot-mod-module-semver '((t :inherit go-dot-mod-module-version))
   2878   "Face for module semver in \"require\" list."
   2879   :group 'go-dot-mod)
   2880 
   2881 
   2882 (defvar go-dot-mod-font-lock-keywords
   2883   `(
   2884     (,(concat "^\\s-*\\(" (regexp-opt go-dot-mod-mode-keywords t) "\\)\\s-") 1 font-lock-keyword-face)
   2885     ("\\(?:^\\|=>\\)\\s-*\\([^[:space:]\n()]+\\)\\(?:\\s-+\\(v[0-9]+\\.[0-9]+\\.[0-9]+\\)\\([^[:space:]\n]*\\)\\)?" (1 'go-dot-mod-module-name) (2 'go-dot-mod-module-semver nil t) (3 'go-dot-mod-module-version nil t)))
   2886   "Keyword highlighting specification for `go-dot-mod-mode'.")
   2887 
   2888 ;;;###autoload
   2889 (define-derived-mode go-dot-mod-mode fundamental-mode "Go Mod"
   2890   "A major mode for editing go.mod files."
   2891   :syntax-table go-dot-mod-mode-syntax-table
   2892   (set (make-local-variable 'comment-start) "// ")
   2893   (set (make-local-variable 'comment-end)   "")
   2894   (set (make-local-variable 'comment-use-syntax) t)
   2895   (set (make-local-variable 'comment-start-skip) "\\(//+\\)\\s *")
   2896 
   2897   (set (make-local-variable 'font-lock-defaults)
   2898        '(go-dot-mod-font-lock-keywords))
   2899   (set (make-local-variable 'indent-line-function) 'go-mode-indent-line)
   2900 
   2901   ;; Go style
   2902   (setq indent-tabs-mode t)
   2903 
   2904   ;; we borrow the go-mode-indent function so we need this buffer cache
   2905   (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql))
   2906   (add-hook 'before-change-functions #'go--reset-dangling-cache-before-change t t))
   2907 
   2908 ;;;###autoload
   2909 (add-to-list 'auto-mode-alist '("go\\.mod\\'" . go-dot-mod-mode))
   2910 
   2911 ;; The following functions were copied (and modified) from rust-mode.el.
   2912 ;;
   2913 ;; Copyright (c) 2015 The Rust Project Developers
   2914 ;;
   2915 ;; Permission is hereby granted, free of charge, to any
   2916 ;; person obtaining a copy of this software and associated
   2917 ;; documentation files (the "Software"), to deal in the
   2918 ;; Software without restriction, including without
   2919 ;; limitation the rights to use, copy, modify, merge,
   2920 ;; publish, distribute, sublicense, and/or sell copies of
   2921 ;; the Software, and to permit persons to whom the Software
   2922 ;; is furnished to do so, subject to the following
   2923 ;; conditions:
   2924 ;;
   2925 ;; The above copyright notice and this permission notice
   2926 ;; shall be included in all copies or substantial portions
   2927 ;; of the Software.
   2928 
   2929 (defun go--fill-prefix-for-comment-start (line-start)
   2930   "Determine what to use for `fill-prefix' based on the text at LINE-START."
   2931   (let ((result
   2932          ;; Replace /* with same number of spaces
   2933          (replace-regexp-in-string
   2934           "\\(?:/\\*+?\\)[!*]?"
   2935           (lambda (s)
   2936             (let ((offset (if (eq t
   2937                                   (compare-strings "/*" nil nil
   2938                                                    s
   2939                                                    (- (length s) 2)
   2940                                                    (length s)))
   2941                               1 2)))
   2942               (make-string (1+ (- (length s) offset)) ?\x20)))
   2943           line-start)))
   2944     ;; Make sure we've got at least one space at the end
   2945     (if (not (= (aref result (- (length result) 1)) ?\x20))
   2946         (setq result (concat result " ")))
   2947     result))
   2948 
   2949 (defun go--in-comment-paragraph (body)
   2950   ;; We might move the point to fill the next comment, but we don't want it
   2951   ;; seeming to jump around on the user
   2952   (save-excursion
   2953     ;; If we're outside of a comment, with only whitespace and then a comment
   2954     ;; in front, jump to the comment and prepare to fill it.
   2955     (when (not (go-in-comment-p))
   2956       (beginning-of-line)
   2957       (when (looking-at (concat "[[:space:]\n]*" comment-start-skip))
   2958         (goto-char (match-end 0))))
   2959 
   2960     ;; If we're at the beginning of a comment paragraph with nothing but
   2961     ;; whitespace til the next line, jump to the next line so that we use the
   2962     ;; existing prefix to figure out what the new prefix should be, rather than
   2963     ;; inferring it from the comment start.
   2964     (while (save-excursion
   2965              (end-of-line)
   2966              (and (go-in-comment-p)
   2967                   (save-excursion
   2968                     (beginning-of-line)
   2969                     (looking-at paragraph-start))
   2970                   (looking-at "[[:space:]]*$")
   2971                   (nth 4 (syntax-ppss (line-beginning-position 2)))))
   2972       (goto-char (line-beginning-position 2)))
   2973 
   2974     ;; If we're on the last line of a multiline-style comment that started
   2975     ;; above, back up one line so we don't mistake the * of the */ that ends
   2976     ;; the comment for a prefix.
   2977     (when (save-excursion
   2978             (and (nth 4 (syntax-ppss (line-beginning-position 1)))
   2979                  (looking-at "[[:space:]]*\\*/")))
   2980       (goto-char (line-end-position 0)))
   2981     (funcall body)))
   2982 
   2983 (defun go--with-comment-fill-prefix (body)
   2984   (let*
   2985       ((line-string (buffer-substring-no-properties
   2986                      (line-beginning-position) (line-end-position)))
   2987        (line-comment-start
   2988         (when (go-in-comment-p)
   2989           (cond
   2990            ;; If we're inside the comment and see a * prefix, use it
   2991            ((string-match "^\\([[:space:]]*\\*+[[:space:]]*\\)"
   2992                           line-string)
   2993             (match-string 1 line-string))
   2994            ;; If we're at the start of a comment, figure out what prefix
   2995            ;; to use for the subsequent lines after it
   2996            ((string-match (concat "[[:space:]]*" comment-start-skip) line-string)
   2997             (go--fill-prefix-for-comment-start
   2998              (match-string 0 line-string))))))
   2999        (fill-prefix
   3000         (or line-comment-start
   3001             fill-prefix)))
   3002     (funcall body)))
   3003 
   3004 (defun go--find-fill-prefix ()
   3005   (go--in-comment-paragraph
   3006    (lambda ()
   3007      (go--with-comment-fill-prefix
   3008       (lambda ()
   3009         fill-prefix)))))
   3010 
   3011 (defun go-fill-paragraph (&rest args)
   3012   "Special wrapping for `fill-paragraph'.
   3013 This handles multi-line comments with a * prefix on each line."
   3014   (go--in-comment-paragraph
   3015    (lambda ()
   3016      (go--with-comment-fill-prefix
   3017       (lambda ()
   3018         (let
   3019             ((fill-paragraph-function
   3020               (if (not (eq fill-paragraph-function 'go-fill-paragraph))
   3021                   fill-paragraph-function))
   3022              (fill-paragraph-handle-comment t))
   3023           (apply 'fill-paragraph args)
   3024           t))))))
   3025 
   3026 (defun go--do-auto-fill (&rest args)
   3027   "Special wrapping for `do-auto-fill'.
   3028 This handles multi-line comments with a * prefix on each line."
   3029   (go--with-comment-fill-prefix
   3030    (lambda ()
   3031      (apply 'do-auto-fill args)
   3032      t)))
   3033 
   3034 (defun go--fill-forward-paragraph (arg)
   3035   ;; This is to work around some funny behavior when a paragraph separator is
   3036   ;; at the very top of the file and there is a fill prefix.
   3037   (let ((fill-prefix nil)) (forward-paragraph arg)))
   3038 
   3039 (defun go--comment-indent-new-line (&optional arg)
   3040   (go--with-comment-fill-prefix
   3041    (lambda () (comment-indent-new-line arg))))
   3042 
   3043 
   3044 
   3045 (provide 'go-mode)
   3046 
   3047 ;;; go-mode.el ends here