config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

go-mode.el (113771B)


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