Initial Commit
[packages] / xemacs-packages / prog-modes / go-mode.el
1 ;;; go-mode.el --- Major mode for the Go programming language
2
3 ;; Copyright 2013 The go-mode Authors. All rights reserved.
4 ;; Use of this source code is governed by a BSD-style
5 ;; license that can be found in the LICENSE file.
6
7 ;; Author: The go-mode Authors
8 ;; Version: 1.3.1
9 ;; Keywords: languages go
10 ;; URL: https://github.com/dominikh/go-mode.el
11 ;;
12 ;; This file is not part of GNU Emacs.
13
14 ;;; Code:
15
16 (require 'cl)
17 (require 'etags)
18 (require 'ffap)
19 (require 'find-file)
20 (require 'ring)
21 (require 'url)
22
23 ;; XEmacs compatibility guidelines
24 ;; - Minimum required version of XEmacs: 21.5.32
25 ;;   - Feature that cannot be backported: POSIX character classes in
26 ;;     regular expressions
27 ;;   - Functions that could be backported but won't because 21.5.32
28 ;;     covers them: plenty.
29 ;;   - Features that are still partly broken:
30 ;;     - godef will not work correctly if multibyte characters are
31 ;;       being used
32 ;;     - Fontification will not handle unicode correctly
33 ;;
34 ;; - Do not use \_< and \_> regexp delimiters directly; use
35 ;;   go--regexp-enclose-in-symbol
36 ;;
37 ;; - The character `_` must not be a symbol constituent but a
38 ;;   character constituent
39 ;;
40 ;; - Do not use process-lines
41 ;;
42 ;; - Use go--old-completion-list-style when using a plain list as the
43 ;;   collection for completing-read
44 ;;
45 ;; - Use go--position-bytes instead of position-bytes
46 (defmacro go--xemacs-p ()
47   `(featurep 'xemacs))
48
49 (defun go--delete-whole-line (&optional arg)
50   "Delete the current line without putting it in the `kill-ring'.
51 Derived from function `kill-whole-line'.  ARG is defined as for that
52 function."
53   (setq arg (or arg 1))
54   (if (and (> arg 0)
55            (eobp)
56            (save-excursion (forward-visible-line 0) (eobp)))
57       (signal 'end-of-buffer nil))
58   (if (and (< arg 0)
59            (bobp)
60            (save-excursion (end-of-visible-line) (bobp)))
61       (signal 'beginning-of-buffer nil))
62   (cond ((zerop arg)
63          (delete-region (progn (forward-visible-line 0) (point))
64                         (progn (end-of-visible-line) (point))))
65         ((< arg 0)
66          (delete-region (progn (end-of-visible-line) (point))
67                         (progn (forward-visible-line (1+ arg))
68                                (unless (bobp)
69                                  (backward-char))
70                                (point))))
71         (t
72          (delete-region (progn (forward-visible-line 0) (point))
73                         (progn (forward-visible-line arg) (point))))))
74
75 ;; declare-function is an empty macro that only byte-compile cares
76 ;; about. Wrap in always false if to satisfy Emacsen without that
77 ;; macro.
78 (if nil
79     (declare-function go--position-bytes "go-mode" (point)))
80
81 ;; XEmacs unfortunately does not offer position-bytes. We can fall
82 ;; back to just using (point), but it will be incorrect as soon as
83 ;; multibyte characters are being used.
84 (if (fboundp 'position-bytes)
85     (defalias 'go--position-bytes #'position-bytes)
86   (defun go--position-bytes (point) point))
87
88 (defun go--old-completion-list-style (list)
89   (mapcar (lambda (x) (cons x nil)) list))
90
91 ;; GNU Emacs 24 has prog-mode, older GNU Emacs and XEmacs do not, so
92 ;; copy its definition for those.
93 (if (not (fboundp 'prog-mode))
94     (define-derived-mode prog-mode fundamental-mode "Prog"
95       "Major mode for editing source code."
96       (set (make-local-variable 'require-final-newline) mode-require-final-newline)
97       (set (make-local-variable 'parse-sexp-ignore-comments) t)
98       (setq bidi-paragraph-direction 'left-to-right)))
99
100 (defun go--regexp-enclose-in-symbol (s)
101   "Enclose S as regexp symbol.
102 XEmacs does not support \\_<, GNU Emacs does.  In GNU Emacs we
103 make extensive use of \\_< to support unicode in identifiers.
104 Until we come up with a better solution for XEmacs, this solution
105 will break fontification in XEmacs for identifiers such as
106 \"typeµ\".  XEmacs will consider \"type\" a keyword, GNU Emacs
107 won't."
108   (if (go--xemacs-p)
109       (concat "\\<" s "\\>")
110     (concat "\\_<" s "\\_>")))
111
112 (defun go-goto-opening-parenthesis (&optional _legacy-unused)
113   "Move up one level of parentheses."
114   ;; The old implementation of go-goto-opening-parenthesis had an
115   ;; optional argument to speed up the function.  It didn't change the
116   ;; function's outcome.
117
118   ;; Silently fail if there's no matching opening parenthesis.
119   (condition-case nil
120       (backward-up-list)
121     (scan-error nil)))
122
123
124 (defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]")
125 (defconst go-identifier-regexp "[[:word:][:multibyte:]]+")
126 (defconst go-type-name-no-prefix-regexp "\\(?:[[:word:][:multibyte:]]+\\.\\)?[[:word:][:multibyte:]]+")
127 (defconst go-qualified-identifier-regexp (concat go-identifier-regexp "\\." go-identifier-regexp))
128 (defconst go-label-regexp go-identifier-regexp)
129 (defconst go-type-regexp "[[:word:][:multibyte:]*]+")
130 (defconst go-func-regexp (concat (go--regexp-enclose-in-symbol "func") "\\s *\\(" go-identifier-regexp "\\)"))
131 (defconst go-func-meth-regexp (concat
132                                (go--regexp-enclose-in-symbol "func") "\\s *\\(?:(\\s *"
133                                "\\(" go-identifier-regexp "\\s +\\)?" go-type-regexp
134                                "\\s *)\\s *\\)?\\("
135                                go-identifier-regexp
136                                "\\)("))
137
138 (defconst go-builtins
139   '("append" "cap"   "close"   "complex" "copy"
140     "delete" "imag"  "len"     "make"    "new"
141     "panic"  "print" "println" "real"    "recover")
142   "All built-in functions in the Go language.  Used for font locking.")
143
144 (defconst go-mode-keywords
145   '("break"    "default"     "func"   "interface" "select"
146     "case"     "defer"       "go"     "map"       "struct"
147     "chan"     "else"        "goto"   "package"   "switch"
148     "const"    "fallthrough" "if"     "range"     "type"
149     "continue" "for"         "import" "return"    "var")
150   "All keywords in the Go language.  Used for font locking.")
151
152 (defconst go-constants '("nil" "true" "false" "iota"))
153 (defconst go-type-name-regexp (concat "\\(?:[*(]\\)*\\(\\(?:" go-identifier-regexp "\\.\\)?" go-identifier-regexp "\\)"))
154
155 ;; Maximum number of identifiers that can be highlighted as type names
156 ;; in one function type/declaration.
157 (defconst go--font-lock-func-param-num-groups 16)
158
159 (defvar go-dangling-cache)
160 (defvar go-godoc-history nil)
161 (defvar go--coverage-current-file-name)
162
163 (defgroup go nil
164   "Major mode for editing Go code."
165   :link '(url-link "https://github.com/dominikh/go-mode.el")
166   :group 'languages)
167
168 (defgroup go-cover nil
169   "Options specific to `cover`."
170   :group 'go)
171
172 (defcustom go-fontify-function-calls t
173   "Fontify function and method calls if this is non-nil."
174   :type 'boolean
175   :group 'go)
176
177 (defcustom go-mode-hook nil
178   "Hook called by `go-mode'."
179   :type 'hook
180   :group 'go)
181
182 (defcustom go-command "go"
183   "The 'go' command.
184 Some users have multiple Go development trees and invoke the 'go'
185 tool via a wrapper that sets GOROOT and GOPATH based on the
186 current directory.  Such users should customize this variable to
187 point to the wrapper script."
188   :type 'string
189   :group 'go)
190
191 (defcustom gofmt-command "gofmt"
192   "The 'gofmt' command.
193 Some users may replace this with 'goimports'
194 from https://github.com/bradfitz/goimports."
195   :type 'string
196   :group 'go)
197
198 (defcustom gofmt-show-errors 'buffer
199   "Where to display gofmt error output.
200 It can either be displayed in its own buffer, in the echo area, or not at all.
201
202 Please note that Emacs outputs to the echo area when writing
203 files and will overwrite gofmt's echo output if used from inside
204 a `before-save-hook'."
205   :type '(choice
206           (const :tag "Own buffer" buffer)
207           (const :tag "Echo area" echo)
208           (const :tag "None" nil))
209   :group 'go)
210
211 (defcustom godef-command "godef"
212   "The 'godef' command."
213   :type 'string
214   :group 'go)
215
216 (defcustom go-other-file-alist
217   '(("_test\\.go\\'" (".go"))
218     ("\\.go\\'" ("_test.go")))
219   "See the documentation of `ff-other-file-alist' for details."
220   :type '(repeat (list regexp (choice (repeat string) function)))
221   :group 'go)
222
223 (defun go--kill-new-message (url)
224   "Make URL the latest kill and print a message."
225   (kill-new url)
226   (message "%s" url))
227
228 (defcustom go-play-browse-function 'go--kill-new-message
229   "Function to call with the Playground URL.
230 See `go-play-region' for more details."
231   :type '(choice
232           (const :tag "Nothing" nil)
233           (const :tag "Kill + Message" go--kill-new-message)
234           (const :tag "Browse URL" browse-url)
235           (function :tag "Call function"))
236   :group 'go)
237
238 (defcustom go-coverage-display-buffer-func 'display-buffer-reuse-window
239   "How `go-coverage' should display the coverage buffer.
240 See `display-buffer' for a list of possible functions."
241   :type 'function
242   :group 'go-cover)
243
244 (defface go-coverage-untracked
245   '((t (:foreground "#505050")))
246   "Coverage color of untracked code."
247   :group 'go-cover)
248
249 (defface go-coverage-0
250   '((t (:foreground "#c00000")))
251   "Coverage color for uncovered code."
252   :group 'go-cover)
253 (defface go-coverage-1
254   '((t (:foreground "#808080")))
255   "Coverage color for covered code with weight 1."
256   :group 'go-cover)
257 (defface go-coverage-2
258   '((t (:foreground "#748c83")))
259   "Coverage color for covered code with weight 2."
260   :group 'go-cover)
261 (defface go-coverage-3
262   '((t (:foreground "#689886")))
263   "Coverage color for covered code with weight 3."
264   :group 'go-cover)
265 (defface go-coverage-4
266   '((t (:foreground "#5ca489")))
267   "Coverage color for covered code with weight 4."
268   :group 'go-cover)
269 (defface go-coverage-5
270   '((t (:foreground "#50b08c")))
271   "Coverage color for covered code with weight 5."
272   :group 'go-cover)
273 (defface go-coverage-6
274   '((t (:foreground "#44bc8f")))
275   "Coverage color for covered code with weight 6."
276   :group 'go-cover)
277 (defface go-coverage-7
278   '((t (:foreground "#38c892")))
279   "Coverage color for covered code with weight 7."
280   :group 'go-cover)
281 (defface go-coverage-8
282   '((t (:foreground "#2cd495")))
283   "Coverage color for covered code with weight 8.
284 For mode=set, all covered lines will have this weight."
285   :group 'go-cover)
286 (defface go-coverage-9
287   '((t (:foreground "#20e098")))
288   "Coverage color for covered code with weight 9."
289   :group 'go-cover)
290 (defface go-coverage-10
291   '((t (:foreground "#14ec9b")))
292   "Coverage color for covered code with weight 10."
293   :group 'go-cover)
294 (defface go-coverage-covered
295   '((t (:foreground "#2cd495")))
296   "Coverage color of covered code."
297   :group 'go-cover)
298
299 (defvar go-mode-syntax-table
300   (let ((st (make-syntax-table)))
301     (modify-syntax-entry ?+  "." st)
302     (modify-syntax-entry ?-  "." st)
303     (modify-syntax-entry ?%  "." st)
304     (modify-syntax-entry ?&  "." st)
305     (modify-syntax-entry ?|  "." st)
306     (modify-syntax-entry ?^  "." st)
307     (modify-syntax-entry ?!  "." st)
308     (modify-syntax-entry ?=  "." st)
309     (modify-syntax-entry ?<  "." st)
310     (modify-syntax-entry ?>  "." st)
311     (modify-syntax-entry ?/ (if (go--xemacs-p) ". 1456" ". 124b") st)
312     (modify-syntax-entry ?*  ". 23" st)
313     (modify-syntax-entry ?\n "> b" st)
314     (modify-syntax-entry ?\" "\"" st)
315     (modify-syntax-entry ?\' "\"" st)
316     (modify-syntax-entry ?`  "\"" st)
317     (modify-syntax-entry ?\\ "\\" st)
318     ;; It would be nicer to have _ as a symbol constituent, but that
319     ;; would trip up XEmacs, which does not support the \_< anchor
320     (modify-syntax-entry ?_  "w" st)
321
322     st)
323   "Syntax table for Go mode.")
324
325 (defun go--build-font-lock-keywords ()
326   ;; we cannot use 'symbols in regexp-opt because GNU Emacs <24
327   ;; doesn't understand that
328   (append
329    `((go--match-func
330       ,@(mapcar (lambda (x) `(,x font-lock-type-face))
331                 (number-sequence 1 go--font-lock-func-param-num-groups)))
332      (,(go--regexp-enclose-in-symbol (regexp-opt go-mode-keywords t)) . font-lock-keyword-face)
333      (,(concat "\\(" (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) "\\)[[:space:]]*(") 1 font-lock-builtin-face)
334      (,(go--regexp-enclose-in-symbol (regexp-opt go-constants t)) . font-lock-constant-face)
335      (,go-func-regexp 1 font-lock-function-name-face)) ;; function (not method) name
336
337    (if go-fontify-function-calls
338        `((,(concat "\\(" go-identifier-regexp "\\)[[:space:]]*(") 1 font-lock-function-name-face) ;; function call/method name
339          (,(concat "[^[:word:][:multibyte:]](\\(" go-identifier-regexp "\\))[[:space:]]*(") 1 font-lock-function-name-face)) ;; bracketed function call
340      `((,go-func-meth-regexp 2 font-lock-function-name-face))) ;; method name
341
342    `(
343      ("\\(`[^`]*`\\)" 1 font-lock-multiline) ;; raw string literal, needed for font-lock-syntactic-keywords
344      (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+\\([^[:space:]]+\\)") 1 font-lock-type-face) ;; types
345      (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types
346      (,(concat "[^[:word:][:multibyte:]]\\[\\([[:digit:]]+\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 2 font-lock-type-face) ;; Arrays/slices
347      (,(concat "\\(" go-identifier-regexp "\\)" "{") 1 font-lock-type-face)
348      (,(concat (go--regexp-enclose-in-symbol "map") "\\[[^]]+\\]" go-type-name-regexp) 1 font-lock-type-face) ;; map value type
349      (,(concat (go--regexp-enclose-in-symbol "map") "\\[" go-type-name-regexp) 1 font-lock-type-face) ;; map key type
350      (,(concat (go--regexp-enclose-in-symbol "chan") "[[:space:]]*\\(?:<-[[:space:]]*\\)?" go-type-name-regexp) 1 font-lock-type-face) ;; channel type
351      (,(concat (go--regexp-enclose-in-symbol "\\(?:new\\|make\\)") "\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) ;; new/make type
352      ;; TODO do we actually need this one or isn't it just a function call?
353      (,(concat "\\.\\s *(" go-type-name-regexp) 1 font-lock-type-face) ;; Type conversion
354      ;; Like the original go-mode this also marks compound literal
355      ;; fields. There, it was marked as to fix, but I grew quite
356      ;; accustomed to it, so it'll stay for now.
357      (,(concat "^[[:space:]]*\\(" go-label-regexp "\\)[[:space:]]*:\\(\\S.\\|$\\)") 1 font-lock-constant-face) ;; Labels and compound literal fields
358      (,(concat (go--regexp-enclose-in-symbol "\\(goto\\|break\\|continue\\)") "[[:space:]]*\\(" go-label-regexp "\\)") 2 font-lock-constant-face)))) ;; labels in goto/break/continue
359
360 (defconst go--font-lock-syntactic-keywords
361   ;; Override syntax property of raw string literal contents, so that
362   ;; backslashes have no special meaning in ``. Used in Emacs 23 or older.
363   '((go--match-raw-string-literal
364      (1 (7 . ?`))
365      (2 (15 . nil))  ;; 15 = "generic string"
366      (3 (7 . ?`)))))
367
368 (defvar go-mode-map
369   (let ((m (make-sparse-keymap)))
370     (define-key m "}" #'go-mode-insert-and-indent)
371     (define-key m ")" #'go-mode-insert-and-indent)
372     (define-key m "," #'go-mode-insert-and-indent)
373     (define-key m ":" #'go-mode-insert-and-indent)
374     (define-key m "=" #'go-mode-insert-and-indent)
375     (define-key m (kbd "C-c C-a") #'go-import-add)
376     (define-key m (kbd "C-c C-j") #'godef-jump)
377     (define-key m (kbd "C-x 4 C-c C-j") #'godef-jump-other-window)
378     (define-key m (kbd "C-c C-d") #'godef-describe)
379     m)
380   "Keymap used by Go mode to implement electric keys.")
381
382 (easy-menu-define go-mode-menu go-mode-map
383   "Menu for Go mode."
384   '("Go"
385     ["Describe Expression"   godef-describe t]
386     ["Jump to Definition"    godef-jump t]
387     "---"
388     ["Add Import"            go-import-add t]
389     ["Remove Unused Imports" go-remove-unused-imports t]
390     ["Go to Imports"         go-goto-imports t]
391     "---"
392     ("Playground"
393      ["Send Buffer"          go-play-buffer t]
394      ["Send Region"          go-play-region t]
395      ["Download"             go-download-play t])
396     "---"
397     ["Coverage"              go-coverage t]
398     ["Gofmt"                 gofmt t]
399     ["Godoc"                 godoc t]
400     "---"
401     ["Customize Mode"        (customize-group 'go) t]))
402
403 (defun go-mode-insert-and-indent (key)
404   "Invoke the global binding of KEY, then reindent the line."
405
406   (interactive (list (this-command-keys)))
407   (call-interactively (lookup-key (current-global-map) key))
408   (indent-according-to-mode))
409
410 (defmacro go-paren-level ()
411   `(car (syntax-ppss)))
412
413 (defmacro go-in-string-or-comment-p ()
414   `(nth 8 (syntax-ppss)))
415
416 (defmacro go-in-string-p ()
417   `(nth 3 (syntax-ppss)))
418
419 (defmacro go-in-comment-p ()
420   `(nth 4 (syntax-ppss)))
421
422 (defmacro go-goto-beginning-of-string-or-comment ()
423   `(goto-char (nth 8 (syntax-ppss))))
424
425 (defun go--backward-irrelevant (&optional stop-at-string)
426   "Skip backwards over any characters that are irrelevant for
427 indentation and related tasks.
428
429 It skips over whitespace, comments, cases and labels and, if
430 STOP-AT-STRING is not true, over strings."
431
432   (let (pos (start-pos (point)))
433     (skip-chars-backward "\n\s\t")
434     (if (and (save-excursion (beginning-of-line) (go-in-string-p)) (looking-back "`") (not stop-at-string))
435         (backward-char))
436     (if (and (go-in-string-p) (not stop-at-string))
437         (go-goto-beginning-of-string-or-comment))
438     (if (looking-back "\\*/")
439         (backward-char))
440     (if (go-in-comment-p)
441         (go-goto-beginning-of-string-or-comment))
442     (setq pos (point))
443     (beginning-of-line)
444     (if (or (looking-at (concat "^" go-label-regexp ":")) (looking-at "^[[:space:]]*\\(case .+\\|default\\):"))
445         (end-of-line 0)
446       (goto-char pos))
447     (if (/= start-pos (point))
448         (go--backward-irrelevant stop-at-string))
449     (/= start-pos (point))))
450
451 (defun go--buffer-narrowed-p ()
452   "Return non-nil if the current buffer is narrowed."
453   (/= (buffer-size)
454       (- (point-max)
455          (point-min))))
456
457 (defun go--match-raw-string-literal (end)
458   "Search for a raw string literal.
459 Set point to the end of the occurence found on success.  Return nil on failure."
460   (unless (go-in-string-or-comment-p)
461     (when (search-forward "`" end t)
462       (goto-char (match-beginning 0))
463       (if (go-in-string-or-comment-p)
464           (progn (goto-char (match-end 0))
465                  (go--match-raw-string-literal end))
466         (when (looking-at "\\(`\\)\\([^`]*\\)\\(`\\)")
467           (goto-char (match-end 0))
468           t)))))
469
470 (defun go-previous-line-has-dangling-op-p ()
471   "Return non-nil if the current line is a continuation line."
472   (let* ((cur-line (line-number-at-pos))
473          (val (gethash cur-line go-dangling-cache 'nope)))
474     (if (or (go--buffer-narrowed-p) (equal val 'nope))
475         (save-excursion
476           (beginning-of-line)
477           (go--backward-irrelevant t)
478           (setq val (looking-back go-dangling-operators-regexp))
479           (if (not (go--buffer-narrowed-p))
480               (puthash cur-line val go-dangling-cache))))
481     val))
482
483 (defun go--at-function-definition ()
484   "Return non-nil if point is on the opening curly brace of a
485 function definition.
486
487 We do this by first calling (beginning-of-defun), which will take
488 us to the start of *some* function. We then look for the opening
489 curly brace of that function and compare its position against the
490 curly brace we are checking. If they match, we return non-nil."
491   (if (= (char-after) ?\{)
492       (save-excursion
493         (let ((old-point (point))
494               start-nesting)
495           (beginning-of-defun)
496           (when (looking-at "func ")
497             (setq start-nesting (go-paren-level))
498             (skip-chars-forward "^{")
499             (while (> (go-paren-level) start-nesting)
500               (forward-char)
501               (skip-chars-forward "^{") 0)
502             (if (and (= (go-paren-level) start-nesting) (= old-point (point)))
503                 t))))))
504
505 (defun go--indentation-for-opening-parenthesis ()
506   "Return the semantic indentation for the current opening parenthesis.
507
508 If point is on an opening curly brace and said curly brace
509 belongs to a function declaration, the indentation of the func
510 keyword will be returned.  Otherwise the indentation of the
511 current line will be returned."
512   (save-excursion
513     (if (go--at-function-definition)
514         (progn
515           (beginning-of-defun)
516           (current-indentation))
517       (current-indentation))))
518
519 (defun go-indentation-at-point ()
520   (save-excursion
521     (let (start-nesting)
522       (back-to-indentation)
523       (setq start-nesting (go-paren-level))
524
525       (cond
526        ((go-in-string-p)
527         (current-indentation))
528        ((looking-at "[])}]")
529         (go-goto-opening-parenthesis)
530         (if (go-previous-line-has-dangling-op-p)
531             (- (current-indentation) tab-width)
532           (go--indentation-for-opening-parenthesis)))
533        ((progn (go--backward-irrelevant t) (looking-back go-dangling-operators-regexp))
534         ;; only one nesting for all dangling operators in one operation
535         (if (go-previous-line-has-dangling-op-p)
536             (current-indentation)
537           (+ (current-indentation) tab-width)))
538        ((zerop (go-paren-level))
539         0)
540        ((progn (go-goto-opening-parenthesis) (< (go-paren-level) start-nesting))
541         (if (go-previous-line-has-dangling-op-p)
542             (current-indentation)
543           (+ (go--indentation-for-opening-parenthesis) tab-width)))
544        (t
545         (current-indentation))))))
546
547 (defun go-mode-indent-line ()
548   (interactive)
549   (let (indent
550         shift-amt
551         (pos (- (point-max) (point)))
552         (point (point))
553         (beg (line-beginning-position)))
554     (back-to-indentation)
555     (if (go-in-string-or-comment-p)
556         (goto-char point)
557       (setq indent (go-indentation-at-point))
558       (if (looking-at (concat go-label-regexp ":\\([[:space:]]*/.+\\)?$\\|case .+:\\|default:"))
559           (decf indent tab-width))
560       (setq shift-amt (- indent (current-column)))
561       (if (zerop shift-amt)
562           nil
563         (delete-region beg (point))
564         (indent-to indent))
565       ;; If initial point was within line's indentation,
566       ;; position after the indentation.  Else stay at same point in text.
567       (if (> (- (point-max) pos) (point))
568           (goto-char (- (point-max) pos))))))
569
570 (defun go-beginning-of-defun (&optional count)
571   (unless (bolp)
572     (end-of-line))
573   (setq count (or count 1))
574   (let (first failure)
575     (dotimes (i (abs count))
576       (setq first t)
577       (while (and (not failure)
578                   (or first (go-in-string-or-comment-p)))
579         (if (>= count 0)
580             (progn
581               (go--backward-irrelevant)
582               (if (not (re-search-backward go-func-meth-regexp nil t))
583                   (setq failure t)))
584           (if (looking-at go-func-meth-regexp)
585               (forward-char))
586           (if (not (re-search-forward go-func-meth-regexp nil t))
587               (setq failure t)))
588         (setq first nil)))
589     (if (< count 0)
590         (beginning-of-line))
591     (not failure)))
592
593 (defun go-end-of-defun ()
594   (let (orig-level)
595     ;; It can happen that we're not placed before a function by emacs
596     (if (not (looking-at "func"))
597         (go-beginning-of-defun -1))
598     ;; Find the { that starts the function, i.e., the next { that isn't
599     ;; preceded by struct or interface, or a comment or struct tag.  BUG:
600     ;; breaks if there's a comment between the struct/interface keyword and
601     ;; bracket, like this:
602     ;;
603     ;;     struct /* why? */ { 
604     (while (progn
605       (skip-chars-forward "^{")
606       (forward-char)
607       (or (go-in-string-or-comment-p)
608           (looking-back "\\(struct\\|interface\\)\\s-*{"))))
609     (setq orig-level (go-paren-level))
610     (while (>= (go-paren-level) orig-level)
611       (skip-chars-forward "^}")
612       (forward-char))))
613
614 (defun go--find-enclosing-parentheses (position)
615   "Return points of outermost '(' and ')' surrounding POSITION if
616 such parentheses exist.
617
618 If outermost '(' exists but ')' does not, it returns the next blank
619 line or end-of-buffer position instead of the position of the closing
620 parenthesis.
621
622 If the starting parenthesis is not found, it returns (POSITION
623 POSITION)."
624   (save-excursion
625     (let (beg end)
626       (goto-char position)
627       (while (> (go-paren-level) 0)
628         (re-search-backward "[(\\[{]" nil t)
629         (when (looking-at "(")
630           (setq beg (point))))
631       (if (null beg)
632           (list position position)
633         (goto-char position)
634         (while (and (> (go-paren-level) 0)
635                     (search-forward ")" nil t)))
636         (when (> (go-paren-level) 0)
637           (unless (re-search-forward "^[[:space:]]*$" nil t)
638             (goto-char (point-max))))
639         (list beg (point))))))
640
641 (defun go--search-next-comma (end)
642   "Search forward from point for a comma whose nesting level is
643 the same as point.  If it reaches the end of line or a closing
644 parenthesis before a comma, it stops at it."
645   (let ((orig-level (go-paren-level)))
646     (while (and (< (point) end)
647                 (or (looking-at "[^,)\n]")
648                     (> (go-paren-level) orig-level)))
649       (forward-char))
650     (when (and (looking-at ",")
651                (< (point) (1- end)))
652       (forward-char))))
653
654 (defun go--looking-at-keyword ()
655   (and (looking-at (concat "\\(" go-identifier-regexp "\\)"))
656        (member (match-string 1) go-mode-keywords)))
657
658 (defun go--match-func (end)
659   "Search for identifiers used as type names from a function
660 parameter list, and set the identifier positions as the results
661 of last search.  Return t if search succeeded."
662   (when (re-search-forward (go--regexp-enclose-in-symbol "func") end t)
663     (let ((regions (go--match-func-type-names end)))
664       (if (null regions)
665           ;; Nothing to highlight. This can happen if the current func
666           ;; is "func()". Try next one.
667           (go--match-func end)
668         ;; There are something to highlight. Set those positions as
669         ;; last search results.
670         (setq regions (go--filter-match-data regions end))
671         (when regions
672           (set-match-data (go--make-match-data regions))
673           t)))))
674
675 (defun go--match-func-type-names (end)
676   (cond
677    ;; Function declaration (e.g. "func foo(")
678    ((looking-at (concat "[[:space:]\n]*" go-identifier-regexp "[[:space:]\n]*("))
679     (goto-char (match-end 0))
680     (nconc (go--match-parameter-list end)
681            (go--match-function-result end)))
682    ;; Method declaration, function literal, or function type
683    ((looking-at "[[:space:]]*(")
684     (goto-char (match-end 0))
685     (let ((regions (go--match-parameter-list end)))
686       ;; Method declaration (e.g. "func (x y) foo(")
687       (when (looking-at (concat "[[:space:]]*" go-identifier-regexp "[[:space:]\n]*("))
688         (goto-char (match-end 0))
689         (setq regions (nconc regions (go--match-parameter-list end))))
690       (nconc regions (go--match-function-result end))))))
691
692 (defun go--parameter-list-type (end)
693   "Return `present' if the parameter list has names, or `absent' if
694 not, assuming point is at the beginning of a parameter list, just
695 after '('."
696   (save-excursion
697     (skip-chars-forward "[:space:]\n" end)
698     (cond ((> (point) end)
699            nil)
700           ((looking-at (concat go-identifier-regexp "[[:space:]\n]*,"))
701            (goto-char (match-end 0))
702            (go--parameter-list-type end))
703           ((or (looking-at go-qualified-identifier-regexp)
704                (looking-at (concat go-type-name-no-prefix-regexp "[[:space:]\n]*\\(?:)\\|\\'\\)"))
705                (go--looking-at-keyword)
706                (looking-at "[*\\[]\\|\\.\\.\\.\\|\\'"))
707            'absent)
708           (t 'present))))
709
710 (defconst go--opt-dotdotdot-regexp "\\(?:\\.\\.\\.\\)?")
711 (defconst go--parameter-type-regexp
712   (concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(" go-type-name-no-prefix-regexp "\\)[[:space:]\n]*\\([,)]\\|\\'\\)"))
713 (defconst go--func-type-in-parameter-list-regexp
714   (concat go--opt-dotdotdot-regexp "[[:space:]*\n]*\\(" (go--regexp-enclose-in-symbol "func") "\\)"))
715
716 (defun go--match-parameters-common (identifier-regexp end)
717   (let ((acc ())
718         (start -1))
719     (while (progn (skip-chars-forward "[:space:]\n" end)
720                   (and (not (looking-at "\\(?:)\\|\\'\\)"))
721                        (< start (point))
722                        (<= (point) end)))
723       (setq start (point))
724       (cond
725        ((looking-at (concat identifier-regexp go--parameter-type-regexp))
726         (setq acc (nconc acc (list (match-beginning 1) (match-end 1))))
727         (goto-char (match-beginning 2)))
728        ((looking-at (concat identifier-regexp go--func-type-in-parameter-list-regexp))
729         (goto-char (match-beginning 1))
730         (setq acc (nconc acc (go--match-func-type-names end)))
731         (go--search-next-comma end))
732        (t
733         (go--search-next-comma end))))
734     (when (and (looking-at ")")
735                (< (point) end))
736       (forward-char))
737     acc))
738
739 (defun go--match-parameters-with-identifier-list (end)
740   (go--match-parameters-common
741    (concat go-identifier-regexp "[[:space:]\n]+")
742    end))
743
744 (defun go--match-parameters-without-identifier-list (end)
745   (go--match-parameters-common "" end))
746
747 (defun go--filter-match-data (regions end)
748   "Remove points from REGIONS if they are beyond END.
749 REGIONS are a list whose size is multiple of 2.  Element 2n is beginning of a
750 region and 2n+1 is end of it.
751
752 This function is used to make sure we don't override end point
753 that `font-lock-mode' gave to us."
754   (when regions
755     (let* ((vec (vconcat regions))
756            (i 0)
757            (len (length vec)))
758       (while (and (< i len)
759                   (<= (nth i regions) end)
760                   (<= (nth (1+ i) regions) end))
761         (setq i (+ i 2)))
762       (cond ((= i len)
763              regions)
764             ((zerop i)
765              nil)
766             (t
767              (butlast regions (- (length regions) i)))))))
768
769 (defun go--make-match-data (regions)
770   (let ((deficit (- (* 2 go--font-lock-func-param-num-groups)
771                     (length regions))))
772     (when (> deficit 0)
773       (let ((last (car (last regions))))
774         (setq regions (nconc regions (make-list deficit last))))))
775   `(,(car regions) ,@(last regions) ,@regions))
776
777 (defun go--match-parameter-list (end)
778   "Return a list of identifier positions that are used as type
779 names in a function parameter list, assuming point is at the
780 beginning of a parameter list.  Return nil if the text after
781 point does not look like a parameter list.
782
783 Set point to end of closing parenthesis on success.
784
785 In Go, the names must either all be present or all be absent
786 within a list of parameters.
787
788 Parsing a parameter list is a little bit complicated because we
789 have to scan through the parameter list to determine whether or
790 not the list has names. Until a type name is found or reaching
791 end of a parameter list, we are not sure which form the parameter
792 list is.
793
794 For example, X and Y are type names in a parameter list \"(X,
795 Y)\" but are parameter names in \"(X, Y int)\". We cannot say if
796 X is a type name until we see int after Y.
797
798 Note that even \"(int, float T)\" is a valid parameter
799 list. Builtin type names are not reserved words. In this example,
800 int and float are parameter names and only T is a type name.
801
802 In this function, we first scan the parameter list to see if the
803 list has names, and then handle it accordingly."
804   (let ((name (go--parameter-list-type end)))
805     (cond ((eq name 'present)
806            (go--match-parameters-with-identifier-list end))
807           ((eq name 'absent)
808            (go--match-parameters-without-identifier-list end))
809           (t nil))))
810
811 (defun go--match-function-result (end)
812   "Return a list of identifier positions that are used as type
813 names in a function result, assuming point is at the beginning of
814 a result.
815
816 Function result is a unparenthesized type or a parameter list."
817   (cond ((and (looking-at (concat "[[:space:]*]*\\(" go-type-name-no-prefix-regexp "\\)"))
818               (not (member (match-string 1) go-mode-keywords)))
819          (list (match-beginning 1) (match-end 1)))
820         ((looking-at "[[:space:]]*(")
821          (goto-char (match-end 0))
822          (go--match-parameter-list end))
823         (t nil)))
824
825 ;;;###autoload
826 (define-derived-mode go-mode prog-mode "Go"
827   "Major mode for editing Go source text.
828
829 This mode provides (not just) basic editing capabilities for
830 working with Go code. It offers almost complete syntax
831 highlighting, indentation that is almost identical to gofmt and
832 proper parsing of the buffer content to allow features such as
833 navigation by function, manipulation of comments or detection of
834 strings.
835
836 In addition to these core features, it offers various features to
837 help with writing Go code. You can directly run buffer content
838 through gofmt, read godoc documentation from within Emacs, modify
839 and clean up the list of package imports or interact with the
840 Playground (uploading and downloading pastes).
841
842 The following extra functions are defined:
843
844 - `gofmt'
845 - `godoc'
846 - `go-import-add'
847 - `go-remove-unused-imports'
848 - `go-goto-imports'
849 - `go-play-buffer' and `go-play-region'
850 - `go-download-play'
851 - `godef-describe' and `godef-jump'
852 - `go-coverage'
853
854 If you want to automatically run `gofmt' before saving a file,
855 add the following hook to your emacs configuration:
856
857 \(add-hook 'before-save-hook #'gofmt-before-save)
858
859 If you want to use `godef-jump' instead of etags (or similar),
860 consider binding godef-jump to `M-.', which is the default key
861 for `find-tag':
862
863 \(add-hook 'go-mode-hook (lambda ()
864                           (local-set-key (kbd \"M-.\") #'godef-jump)))
865
866 Please note that godef is an external dependency. You can install
867 it with
868
869 go get github.com/rogpeppe/godef
870
871
872 If you're looking for even more integration with Go, namely
873 on-the-fly syntax checking, auto-completion and snippets, it is
874 recommended that you look at flycheck
875 \(see URL `https://github.com/flycheck/flycheck') or flymake in combination
876 with goflymake \(see URL `https://github.com/dougm/goflymake'), gocode
877 \(see URL `https://github.com/nsf/gocode'), go-eldoc
878 \(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go
879 \(see URL `https://github.com/dominikh/yasnippet-go')"
880
881   ;; Font lock
882   (set (make-local-variable 'font-lock-defaults)
883        '(go--build-font-lock-keywords))
884
885   ;; Indentation
886   (set (make-local-variable 'indent-line-function) #'go-mode-indent-line)
887
888   ;; Comments
889   (set (make-local-variable 'comment-start) "// ")
890   (set (make-local-variable 'comment-end)   "")
891   (set (make-local-variable 'comment-use-syntax) t)
892   (set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *")
893
894   (set (make-local-variable 'beginning-of-defun-function) #'go-beginning-of-defun)
895   (set (make-local-variable 'end-of-defun-function) #'go-end-of-defun)
896
897   (set (make-local-variable 'parse-sexp-lookup-properties) t)
898   (if (boundp 'syntax-propertize-function)
899       (set (make-local-variable 'syntax-propertize-function) #'go-propertize-syntax)
900     (set (make-local-variable 'font-lock-syntactic-keywords)
901          go--font-lock-syntactic-keywords)
902     (set (make-local-variable 'font-lock-multiline) t))
903
904   (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql))
905   (add-hook 'before-change-functions (lambda (x y) (setq go-dangling-cache (make-hash-table :test 'eql))) t t)
906
907   ;; ff-find-other-file
908   (setq ff-other-file-alist 'go-other-file-alist)
909
910   (setq imenu-generic-expression
911         '(("type" "^type *\\([^ \t\n\r\f]*\\)" 1)
912           ("func" "^func *\\(.*\\) {" 1)))
913   (imenu-add-to-menubar "Index")
914
915   ;; Go style
916   (setq indent-tabs-mode t)
917
918   ;; Handle unit test failure output in compilation-mode
919   ;;
920   ;; Note the final t argument to add-to-list for append, ie put these at the
921   ;; *ends* of compilation-error-regexp-alist[-alist]. We want go-test to be
922   ;; handled first, otherwise other elements will match that don't work, and
923   ;; those alists are traversed in *reverse* order:
924   ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2001-12/msg00674.html
925   (when (and (boundp 'compilation-error-regexp-alist)
926              (boundp 'compilation-error-regexp-alist-alist))
927     (add-to-list 'compilation-error-regexp-alist 'go-test t)
928     (add-to-list 'compilation-error-regexp-alist-alist
929                  '(go-test . ("^\t+\\([^()\t\n]+\\):\\([0-9]+\\):? .*$" 1 2)) t)))
930
931 ;;;###autoload
932 (add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode))
933
934 (defun go--apply-rcs-patch (patch-buffer)
935   "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer."
936   (let ((target-buffer (current-buffer))
937         ;; Relative offset between buffer line numbers and line numbers
938         ;; in patch.
939         ;;
940         ;; Line numbers in the patch are based on the source file, so
941         ;; we have to keep an offset when making changes to the
942         ;; buffer.
943         ;;
944         ;; Appending lines decrements the offset (possibly making it
945         ;; negative), deleting lines increments it. This order
946         ;; simplifies the forward-line invocations.
947         (line-offset 0))
948     (save-excursion
949       (with-current-buffer patch-buffer
950         (goto-char (point-min))
951         (while (not (eobp))
952           (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)")
953             (error "invalid rcs patch or internal error in go--apply-rcs-patch"))
954           (forward-line)
955           (let ((action (match-string 1))
956                 (from (string-to-number (match-string 2)))
957                 (len  (string-to-number (match-string 3))))
958             (cond
959              ((equal action "a")
960               (let ((start (point)))
961                 (forward-line len)
962                 (let ((text (buffer-substring start (point))))
963                   (with-current-buffer target-buffer
964                     (decf line-offset len)
965                     (goto-char (point-min))
966                     (forward-line (- from len line-offset))
967                     (insert text)))))
968              ((equal action "d")
969               (with-current-buffer target-buffer
970                 (go--goto-line (- from line-offset))
971                 (incf line-offset len)
972                 (go--delete-whole-line len)))
973              (t
974               (error "invalid rcs patch or internal error in go--apply-rcs-patch")))))))))
975
976 (defun gofmt ()
977   "Format the current buffer according to the gofmt tool."
978   (interactive)
979   (let ((tmpfile (make-temp-file "gofmt" nil ".go"))
980         (patchbuf (get-buffer-create "*Gofmt patch*"))
981         (errbuf (if gofmt-show-errors (get-buffer-create "*Gofmt Errors*")))
982         (coding-system-for-read 'utf-8)
983         (coding-system-for-write 'utf-8))
984
985     (save-restriction
986       (widen)
987       (if errbuf
988           (with-current-buffer errbuf
989             (setq buffer-read-only nil)
990             (erase-buffer)))
991       (with-current-buffer patchbuf
992         (erase-buffer))
993
994       (write-region nil nil tmpfile)
995
996       ;; We're using errbuf for the mixed stdout and stderr output. This
997       ;; is not an issue because gofmt -w does not produce any stdout
998       ;; output in case of success.
999       (if (zerop (call-process gofmt-command nil errbuf nil "-w" tmpfile))
1000           (progn
1001             (if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile))
1002                 (message "Buffer is already gofmted")
1003               (go--apply-rcs-patch patchbuf)
1004               (message "Applied gofmt"))
1005             (if errbuf (gofmt--kill-error-buffer errbuf)))
1006         (message "Could not apply gofmt")
1007         (if errbuf (gofmt--process-errors (buffer-file-name) tmpfile errbuf)))
1008
1009       (kill-buffer patchbuf)
1010       (delete-file tmpfile))))
1011
1012
1013 (defun gofmt--process-errors (filename tmpfile errbuf)
1014   (with-current-buffer errbuf
1015     (if (eq gofmt-show-errors 'echo)
1016         (progn
1017           (message "%s" (buffer-string))
1018           (gofmt--kill-error-buffer errbuf))
1019       ;; Convert the gofmt stderr to something understood by the compilation mode.
1020       (goto-char (point-min))
1021       (insert "gofmt errors:\n")
1022       (while (search-forward-regexp (concat "^\\(" (regexp-quote tmpfile) "\\):") nil t)
1023         (replace-match (file-name-nondirectory filename) t t nil 1))
1024       (compilation-mode)
1025       (display-buffer errbuf))))
1026
1027 (defun gofmt--kill-error-buffer (errbuf)
1028   (let ((win (get-buffer-window errbuf)))
1029     (if win
1030         (quit-window t win)
1031       (kill-buffer errbuf))))
1032
1033 ;;;###autoload
1034 (defun gofmt-before-save ()
1035   "Add this to .emacs to run gofmt on the current buffer when saving:
1036  (add-hook 'before-save-hook 'gofmt-before-save).
1037
1038 Note that this will cause go-mode to get loaded the first time
1039 you save any file, kind of defeating the point of autoloading."
1040
1041   (interactive)
1042   (when (eq major-mode 'go-mode) (gofmt)))
1043
1044 (defun godoc--read-query ()
1045   "Read a godoc query from the minibuffer."
1046   ;; Compute the default query as the symbol under the cursor.
1047   ;; TODO: This does the wrong thing for e.g. multipart.NewReader (it only grabs
1048   ;; half) but I see no way to disambiguate that from e.g. foobar.SomeMethod.
1049   (let* ((bounds (bounds-of-thing-at-point 'symbol))
1050          (symbol (if bounds
1051                      (buffer-substring-no-properties (car bounds)
1052                                                      (cdr bounds)))))
1053     (completing-read (if symbol
1054                          (format "godoc (default %s): " symbol)
1055                        "godoc: ")
1056                      (go--old-completion-list-style (go-packages)) nil nil nil 'go-godoc-history symbol)))
1057
1058 (defun godoc--get-buffer (query)
1059   "Get an empty buffer for a godoc query."
1060   (let* ((buffer-name (concat "*godoc " query "*"))
1061          (buffer (get-buffer buffer-name)))
1062     ;; Kill the existing buffer if it already exists.
1063     (when buffer (kill-buffer buffer))
1064     (get-buffer-create buffer-name)))
1065
1066 (defun godoc--buffer-sentinel (proc event)
1067   "Sentinel function run when godoc command completes."
1068   (with-current-buffer (process-buffer proc)
1069     (cond ((string= event "finished\n")  ;; Successful exit.
1070            (goto-char (point-min))
1071            (godoc-mode)
1072            (display-buffer (current-buffer) t))
1073           ((/= (process-exit-status proc) 0)  ;; Error exit.
1074            (let ((output (buffer-string)))
1075              (kill-buffer (current-buffer))
1076              (message (concat "godoc: " output)))))))
1077
1078 (define-derived-mode godoc-mode special-mode "Godoc"
1079   "Major mode for showing Go documentation."
1080   (view-mode-enter))
1081
1082 ;;;###autoload
1083 (defun godoc (query)
1084   "Show Go documentation for QUERY, much like M-x man."
1085   (interactive (list (godoc--read-query)))
1086   (unless (string= query "")
1087     (set-process-sentinel
1088      (start-process-shell-command "godoc" (godoc--get-buffer query)
1089                                   (concat "godoc " query))
1090      'godoc--buffer-sentinel)
1091     nil))
1092
1093 (defun godoc-at-point (point)
1094   "Show Go documentation for the identifier at POINT.
1095
1096 `godoc-at-point' requires godef to work.
1097
1098 Due to a limitation in godoc, it is not possible to differentiate
1099 between functions and methods, which may cause `godoc-at-point'
1100 to display more documentation than desired."
1101   ;; TODO(dominikh): Support executing godoc-at-point on a package
1102   ;; name.
1103   (interactive "d")
1104   (condition-case nil
1105       (let* ((output (godef--call point))
1106              (file (car output))
1107              (name-parts (split-string (cadr output) " "))
1108              (first (car name-parts)))
1109         (if (not (godef--successful-p file))
1110             (message "%s" (godef--error file))
1111           (godoc (format "%s %s"
1112                          (file-name-directory file)
1113                          (if (or (string= first "type") (string= first "const"))
1114                              (cadr name-parts)
1115                            (car name-parts))))))
1116     (file-error (message "Could not run godef binary"))))
1117
1118 (defun go-goto-imports ()
1119   "Move point to the block of imports.
1120
1121 If using
1122
1123   import (
1124     \"foo\"
1125     \"bar\"
1126   )
1127
1128 it will move point directly behind the last import.
1129
1130 If using
1131
1132   import \"foo\"
1133   import \"bar\"
1134
1135 it will move point to the next line after the last import.
1136
1137 If no imports can be found, point will be moved after the package
1138 declaration."
1139   (interactive)
1140   ;; FIXME if there's a block-commented import before the real
1141   ;; imports, we'll jump to that one.
1142
1143   ;; Generally, this function isn't very forgiving. it'll bark on
1144   ;; extra whitespace. It works well for clean code.
1145   (let ((old-point (point)))
1146     (goto-char (point-min))
1147     (cond
1148      ((re-search-forward "^import ()" nil t)
1149       (backward-char 1)
1150       'block-empty)
1151      ((re-search-forward "^import ([^)]+)" nil t)
1152       (backward-char 2)
1153       'block)
1154      ((re-search-forward "\\(^import \\([^\"]+ \\)?\"[^\"]+\"\n?\\)+" nil t)
1155       'single)
1156      ((re-search-forward "^[[:space:]\n]*package .+?\n" nil t)
1157       (message "No imports found, moving point after package declaration")
1158       'none)
1159      (t
1160       (goto-char old-point)
1161       (message "No imports or package declaration found. Is this really a Go file?")
1162       'fail))))
1163
1164 (defun go-play-buffer ()
1165   "Like `go-play-region', but acts on the entire buffer."
1166   (interactive)
1167   (go-play-region (point-min) (point-max)))
1168
1169 (defun go-play-region (start end)
1170   "Send the region to the Playground.
1171 If non-nil `go-play-browse-function' is called with the
1172 Playground URL."
1173   (interactive "r")
1174   (let* ((url-request-method "POST")
1175          (url-request-extra-headers
1176           '(("Content-Type" . "application/x-www-form-urlencoded")))
1177          (url-request-data
1178           (encode-coding-string
1179            (buffer-substring-no-properties start end)
1180            'utf-8))
1181          (content-buf (url-retrieve
1182                        "http://play.golang.org/share"
1183                        (lambda (arg)
1184                          (cond
1185                           ((equal :error (car arg))
1186                            (signal 'go-play-error (cdr arg)))
1187                           (t
1188                            (re-search-forward "\n\n")
1189                            (let ((url (format "http://play.golang.org/p/%s"
1190                                               (buffer-substring (point) (point-max)))))
1191                              (when go-play-browse-function
1192                                (funcall go-play-browse-function url)))))))))))
1193
1194 ;;;###autoload
1195 (defun go-download-play (url)
1196   "Download a paste from the playground and insert it in a Go buffer.
1197 Tries to look for a URL at point."
1198   (interactive (list (read-from-minibuffer "Playground URL: " (ffap-url-p (ffap-string-at-point 'url)))))
1199   (with-current-buffer
1200       (let ((url-request-method "GET") url-request-data url-request-extra-headers)
1201         (url-retrieve-synchronously (concat url ".go")))
1202     (let ((buffer (generate-new-buffer (concat (car (last (split-string url "/"))) ".go"))))
1203       (goto-char (point-min))
1204       (re-search-forward "\n\n")
1205       (copy-to-buffer buffer (point) (point-max))
1206       (kill-buffer)
1207       (with-current-buffer buffer
1208         (go-mode)
1209         (switch-to-buffer buffer)))))
1210
1211 (defun go-propertize-syntax (start end)
1212   (save-excursion
1213     (goto-char start)
1214     (while (search-forward "\\" end t)
1215       (put-text-property (1- (point)) (point) 'syntax-table (if (= (char-after) ?`) '(1) '(9))))))
1216
1217 (defun go-import-add (arg import)
1218   "Add a new IMPORT to the list of imports.
1219
1220 When called with a prefix ARG asks for an alternative name to
1221 import the package as.
1222
1223 If no list exists yet, one will be created if possible.
1224
1225 If an identical import has been commented, it will be
1226 uncommented, otherwise a new import will be added."
1227
1228   ;; - If there's a matching `// import "foo"`, uncomment it
1229   ;; - If we're in an import() block and there's a matching `"foo"`, uncomment it
1230   ;; - Otherwise add a new import, with the appropriate syntax
1231   (interactive
1232    (list
1233     current-prefix-arg
1234     (replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go--old-completion-list-style (go-packages))))))
1235   (save-excursion
1236     (let (as line import-start)
1237       (if arg
1238           (setq as (read-from-minibuffer "Import as: ")))
1239       (if as
1240           (setq line (format "%s \"%s\"" as import))
1241         (setq line (format "\"%s\"" import)))
1242
1243       (goto-char (point-min))
1244       (if (re-search-forward (concat "^[[:space:]]*//[[:space:]]*import " line "$") nil t)
1245           (uncomment-region (line-beginning-position) (line-end-position))
1246         (case (go-goto-imports)
1247           ('fail (message "Could not find a place to add import."))
1248           ('block-empty
1249            (insert "\n\t" line "\n"))
1250           ('block
1251               (save-excursion
1252                 (re-search-backward "^import (")
1253                 (setq import-start (point)))
1254             (if (re-search-backward (concat "^[[:space:]]*//[[:space:]]*" line "$")  import-start t)
1255                 (uncomment-region (line-beginning-position) (line-end-position))
1256               (insert "\n\t" line)))
1257           ('single (insert "import " line "\n"))
1258           ('none (insert "\nimport (\n\t" line "\n)\n")))))))
1259
1260 (defun go-root-and-paths ()
1261   (let* ((output (split-string (shell-command-to-string (concat go-command " env GOROOT GOPATH"))
1262                                "\n"))
1263          (root (car output))
1264          (paths (split-string (cadr output) path-separator)))
1265     (append (list root) paths)))
1266
1267 (defun go--string-prefix-p (s1 s2 &optional ignore-case)
1268   "Return non-nil if S1 is a prefix of S2.
1269 If IGNORE-CASE is non-nil, the comparison is case-insensitive."
1270   (eq t (compare-strings s1 nil nil
1271                          s2 0 (length s1) ignore-case)))
1272
1273 (defun go--directory-dirs (dir)
1274   "Recursively return all subdirectories in DIR."
1275   (if (file-directory-p dir)
1276       (let ((dir (directory-file-name dir))
1277             (dirs '())
1278             (files (directory-files dir nil nil t)))
1279         (dolist (file files)
1280           (unless (member file '("." ".."))
1281             (let ((file (concat dir "/" file)))
1282               (if (file-directory-p file)
1283                   (setq dirs (append (cons file
1284                                            (go--directory-dirs file))
1285                                      dirs))))))
1286         dirs)
1287     '()))
1288
1289
1290 (defun go-packages ()
1291   (sort
1292    (delete-dups
1293     (mapcan
1294      (lambda (topdir)
1295        (let ((pkgdir (concat topdir "/pkg/")))
1296          (mapcan (lambda (dir)
1297                    (mapcar (lambda (file)
1298                              (let ((sub (substring file (length pkgdir) -2)))
1299                                (unless (or (go--string-prefix-p "obj/" sub) (go--string-prefix-p "tool/" sub))
1300                                  (mapconcat #'identity (cdr (split-string sub "/")) "/"))))
1301                            (if (file-directory-p dir)
1302                                (directory-files dir t "\\.a$"))))
1303                  (if (file-directory-p pkgdir)
1304                      (go--directory-dirs pkgdir)))))
1305      (go-root-and-paths)))
1306    #'string<))
1307
1308 (defun go-unused-imports-lines ()
1309   ;; FIXME Technically, -o /dev/null fails in quite some cases (on
1310   ;; Windows, when compiling from within GOPATH). Practically,
1311   ;; however, it has the same end result: There won't be a
1312   ;; compiled binary/archive, and we'll get our import errors when
1313   ;; there are any.
1314   (reverse (remove nil
1315                    (mapcar
1316                     (lambda (line)
1317                       (when (string-match "^\\(.+\\):\\([[:digit:]]+\\): imported and not used: \".+\".*$" line)
1318                         (let ((error-file-name (match-string 1 line))
1319                               (error-line-num (match-string 2 line)))
1320                           (if (string= (file-truename error-file-name) (file-truename buffer-file-name))
1321                               (string-to-number error-line-num)))))
1322                     (split-string (shell-command-to-string
1323                                    (concat go-command
1324                                            (if (string-match "_test\.go$" buffer-file-truename)
1325                                                " test -c"
1326                                              " build -o /dev/null"))) "\n")))))
1327
1328 (defun go-remove-unused-imports (arg)
1329   "Remove all unused imports.
1330 If ARG is non-nil, unused imports will be commented, otherwise
1331 they will be removed completely."
1332   (interactive "P")
1333   (save-excursion
1334     (let ((cur-buffer (current-buffer)) flymake-state lines)
1335       (when (boundp 'flymake-mode)
1336         (setq flymake-state flymake-mode)
1337         (flymake-mode-off))
1338       (save-some-buffers nil (lambda () (equal cur-buffer (current-buffer))))
1339       (if (buffer-modified-p)
1340           (message "Cannot operate on unsaved buffer")
1341         (setq lines (go-unused-imports-lines))
1342         (dolist (import lines)
1343           (go--goto-line import)
1344           (beginning-of-line)
1345           (if arg
1346               (comment-region (line-beginning-position) (line-end-position))
1347             (go--delete-whole-line)))
1348         (message "Removed %d imports" (length lines)))
1349       (if flymake-state (flymake-mode-on)))))
1350
1351 (defun godef--find-file-line-column (specifier other-window)
1352   "Given a file name in the format of `filename:line:column',
1353 visit FILENAME and go to line LINE and column COLUMN."
1354   (if (not (string-match "\\(.+\\):\\([0-9]+\\):\\([0-9]+\\)" specifier))
1355       ;; We've only been given a directory name
1356       (funcall (if other-window #'find-file-other-window #'find-file) specifier)
1357     (let ((filename (match-string 1 specifier))
1358           (line (string-to-number (match-string 2 specifier)))
1359           (column (string-to-number (match-string 3 specifier))))
1360       (funcall (if other-window #'find-file-other-window #'find-file) filename)
1361       (go--goto-line line)
1362       (beginning-of-line)
1363       (forward-char (1- column))
1364       (if (buffer-modified-p)
1365           (message "Buffer is modified, file position might not have been correct")))))
1366
1367 (defun godef--call (point)
1368   "Call godef, acquiring definition position and expression
1369 description at POINT."
1370   (if (go--xemacs-p)
1371       (error "godef does not reliably work in XEmacs, expect bad results"))
1372   (if (not (buffer-file-name (go--coverage-origin-buffer)))
1373       (error "Cannot use godef on a buffer without a file name")
1374     (let ((outbuf (get-buffer-create "*godef*"))
1375           (coding-system-for-read 'utf-8)
1376           (coding-system-for-write 'utf-8))
1377       (with-current-buffer outbuf
1378         (erase-buffer))
1379       (call-process-region (point-min)
1380                            (point-max)
1381                            godef-command
1382                            nil
1383                            outbuf
1384                            nil
1385                            "-i"
1386                            "-t"
1387                            "-f"
1388                            (file-truename (buffer-file-name (go--coverage-origin-buffer)))
1389                            "-o"
1390                            (number-to-string (go--position-bytes point)))
1391       (with-current-buffer outbuf
1392         (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n")))))
1393
1394 (defun godef--successful-p (output)
1395   (not (or (string= "-" output)
1396            (string= "godef: no identifier found" output)
1397            (go--string-prefix-p "godef: no declaration found for " output)
1398            (go--string-prefix-p "error finding import path for " output))))
1399
1400 (defun godef--error (output)
1401   (cond
1402    ((godef--successful-p output)
1403     nil)
1404    ((string= "-" output)
1405     "godef: expression is not defined anywhere")
1406    (t
1407     output)))
1408
1409 (defun godef-describe (point)
1410   "Describe the expression at POINT."
1411   (interactive "d")
1412   (condition-case nil
1413       (let ((description (cdr (butlast (godef--call point) 1))))
1414         (if (not description)
1415             (message "No description found for expression at point")
1416           (message "%s" (mapconcat #'identity description "\n"))))
1417     (file-error (message "Could not run godef binary"))))
1418
1419 (defun godef-jump (point &optional other-window)
1420   "Jump to the definition of the expression at POINT."
1421   (interactive "d")
1422   (condition-case nil
1423       (let ((file (car (godef--call point))))
1424         (if (not (godef--successful-p file))
1425             (message "%s" (godef--error file))
1426           (push-mark)
1427           (ring-insert find-tag-marker-ring (point-marker))
1428           (godef--find-file-line-column file other-window)))
1429     (file-error (message "Could not run godef binary"))))
1430
1431 (defun godef-jump-other-window (point)
1432   (interactive "d")
1433   (godef-jump point t))
1434
1435 (defun go--goto-line (line)
1436   (goto-char (point-min))
1437   (forward-line (1- line)))
1438
1439 (defun go--line-column-to-point (line column)
1440   (save-excursion
1441     (go--goto-line line)
1442     (forward-char (1- column))
1443     (point)))
1444
1445 (defstruct go--covered
1446   start-line start-column end-line end-column covered count)
1447
1448 (defun go--coverage-file ()
1449   "Return the coverage file to use, either by reading it from the
1450 current coverage buffer or by prompting for it."
1451   (if (boundp 'go--coverage-current-file-name)
1452       go--coverage-current-file-name
1453     (read-file-name "Coverage file: " nil nil t)))
1454
1455 (defun go--coverage-origin-buffer ()
1456   "Return the buffer to base the coverage on."
1457   (or (buffer-base-buffer) (current-buffer)))
1458
1459 (defun go--coverage-face (count divisor)
1460   "Return the intensity face for COUNT when using DIVISOR
1461 to scale it to a range [0,10].
1462
1463 DIVISOR scales the absolute cover count to values from 0 to 10.
1464 For DIVISOR = 0 the count will always translate to 8."
1465   (let* ((norm (cond
1466                 ((= count 0)
1467                  -0.1) ;; Uncovered code, set to -0.1 so n becomes 0.
1468                 ((= divisor 0)
1469                  0.8) ;; covermode=set, set to 0.8 so n becomes 8.
1470                 (t
1471                  (/ (log count) divisor))))
1472          (n (1+ (floor (* norm 9))))) ;; Convert normalized count [0,1] to intensity [0,10]
1473     (concat "go-coverage-" (number-to-string n))))
1474
1475 (defun go--coverage-make-overlay (range divisor)
1476   "Create a coverage overlay for a RANGE of covered/uncovered code.
1477 Use DIVISOR to scale absolute counts to a [0,10] scale."
1478   (let* ((count (go--covered-count range))
1479          (face (go--coverage-face count divisor))
1480          (ov (make-overlay (go--line-column-to-point (go--covered-start-line range)
1481                                                      (go--covered-start-column range))
1482                            (go--line-column-to-point (go--covered-end-line range)
1483                                                      (go--covered-end-column range)))))
1484
1485     (overlay-put ov 'face face)
1486     (overlay-put ov 'help-echo (format "Count: %d" count))))
1487
1488 (defun go--coverage-clear-overlays ()
1489   "Remove existing overlays and put a single untracked overlay
1490 over the entire buffer."
1491   (remove-overlays)
1492   (overlay-put (make-overlay (point-min) (point-max))
1493                'face
1494                'go-coverage-untracked))
1495
1496 (defun go--coverage-parse-file (coverage-file file-name)
1497   "Parse COVERAGE-FILE and extract coverage information and
1498 divisor for FILE-NAME."
1499   (let (ranges
1500         (max-count 0))
1501     (with-temp-buffer
1502       (insert-file-contents coverage-file)
1503       (go--goto-line 2) ;; Skip over mode
1504       (while (not (eobp))
1505         (let* ((parts (split-string (buffer-substring (point-at-bol) (point-at-eol)) ":"))
1506                (file (car parts))
1507                (rest (split-string (nth 1 parts) "[., ]")))
1508
1509           (destructuring-bind
1510               (start-line start-column end-line end-column num count)
1511               (mapcar #'string-to-number rest)
1512
1513             (when (string= (file-name-nondirectory file) file-name)
1514               (if (> count max-count)
1515                   (setq max-count count))
1516               (push (make-go--covered :start-line start-line
1517                                       :start-column start-column
1518                                       :end-line end-line
1519                                       :end-column end-column
1520                                       :covered (/= count 0)
1521                                       :count count)
1522                     ranges)))
1523
1524           (forward-line)))
1525
1526       (list ranges (if (> max-count 0) (log max-count) 0)))))
1527
1528 (defun go-coverage (&optional coverage-file)
1529   "Open a clone of the current buffer and overlay it with
1530 coverage information gathered via go test -coverprofile=COVERAGE-FILE.
1531
1532 If COVERAGE-FILE is nil, it will either be inferred from the
1533 current buffer if it's already a coverage buffer, or be prompted
1534 for."
1535   (interactive)
1536   (let* ((cur-buffer (current-buffer))
1537          (origin-buffer (go--coverage-origin-buffer))
1538          (gocov-buffer-name (concat (buffer-name origin-buffer) "<gocov>"))
1539          (coverage-file (or coverage-file (go--coverage-file)))
1540          (ranges-and-divisor (go--coverage-parse-file
1541                               coverage-file
1542                               (file-name-nondirectory (buffer-file-name origin-buffer))))
1543          (cov-mtime (nth 5 (file-attributes coverage-file)))
1544          (cur-mtime (nth 5 (file-attributes (buffer-file-name origin-buffer)))))
1545
1546     (if (< (float-time cov-mtime) (float-time cur-mtime))
1547         (message "Coverage file is older than the source file."))
1548
1549     (with-current-buffer (or (get-buffer gocov-buffer-name)
1550                              (make-indirect-buffer origin-buffer gocov-buffer-name t))
1551       (set (make-local-variable 'go--coverage-current-file-name) coverage-file)
1552
1553       (save-excursion
1554         (go--coverage-clear-overlays)
1555         (dolist (range (car ranges-and-divisor))
1556           (go--coverage-make-overlay range (cadr ranges-and-divisor))))
1557
1558       (if (not (eq cur-buffer (current-buffer)))
1559           (display-buffer (current-buffer) `(,go-coverage-display-buffer-func))))))
1560
1561 (provide 'go-mode)
1562
1563 ;;; go-mode.el ends here