1 new commit in prog-modes:
https://bitbucket.org/xemacs/prog-modes/commits/226ae620b716/
Changeset:   226ae620b716
User:        matsl
Date:        2013-04-14 13:44:44
Summary:     go-mode.el: Sync with upstream version 16503.
Affected #:  3 files
diff -r e5d5caa0c9c68f304f2ed904106843ce5cb1d535 -r
226ae620b7169243c2f8a508eda870fd3cb16f8a ChangeLog
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2013-04-14  Mats Lidell  <matsl(a)xemacs.org>
+
+	* Makefile (REQUIRES): Added gnus and w3 for new go-mode.
+	* go-mode.el: Sync with upstream version 16503.
+
 2012-12-16  Norbert Koch  <viteno(a)xemacs.org>
 
 	* Makefile (VERSION): XEmacs package 2.28 released.
diff -r e5d5caa0c9c68f304f2ed904106843ce5cb1d535 -r
226ae620b7169243c2f8a508eda870fd3cb16f8a Makefile
--- a/Makefile
+++ b/Makefile
@@ -24,7 +24,7 @@
 PKG_TYPE = single-file
 REQUIRES = mail-lib xemacs-devel xemacs-base cc-mode \
 	fsf-compat edit-utils ediff emerge efs vc speedbar dired \
-	ilisp sh-script cedet-common
+	ilisp sh-script cedet-common w3 gnus
 CATEGORY = standard
 
 ELCS = asm-mode.elc autoconf-mode.elc awk-mode.elc cl-indent.elc \
diff -r e5d5caa0c9c68f304f2ed904106843ce5cb1d535 -r
226ae620b7169243c2f8a508eda870fd3cb16f8a go-mode.el
--- a/go-mode.el
+++ b/go-mode.el
@@ -1,6 +1,6 @@
 ;;; go-mode.el --- Major mode for the Go programming language
 
-;; Copyright (c) 2012 The Go Authors. All rights reserved.
+;; Copyright (c) 2013 The Go Authors. All rights reserved.
 
 ;; Redistribution and use in source and binary forms, with or without
 ;; modification, are permitted provided that the following conditions are
@@ -28,43 +28,115 @@
 ;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-;;; Synced up with: upstream go-mode.el 14989:a522fb590152
+;;; Synced up with: upstream go-mode.el 16503:45a52c375f4f
 
-;;; Commentary:
+(require 'cl)
+(require 'ffap)
+(require 'url)
 
-;; For installation instructions, see go-mode-load.el
+;; XEmacs compatibility guidelines
+;; - Minimum required version of XEmacs: 21.5.32
+;;   - Feature that cannot be backported: POSIX character classes in
+;;     regular expressions
+;;   - Functions that could be backported but won't because 21.5.32
+;;     covers them: plenty.
+;;   - Features that are still partly broken:
+;;     - godef will not work correctly if multibyte characters are
+;;       being used
+;;     - Fontification will not handle unicode correctly
+;;
+;; - Do not use \_< and \_> regexp delimiters directly; use
+;;   go--regexp-enclose-in-symbol
+;;
+;; - The character `_` must not be a symbol constituent but a
+;;   character constituent
+;;
+;; - Do not use process-lines
+;;
+;; - Use go--old-completion-list-style when using a plain list as the
+;;   collection for completing-read
+;;
+;; - Use go--kill-whole-line instead of kill-whole-line (called
+;;   kill-entire-line in XEmacs)
+;;
+;; - Use go--position-bytes instead of position-bytes
+(defmacro go--xemacs-p ()
+  `(featurep 'xemacs))
 
-;;; To do:
+(defalias 'go--kill-whole-line
+  (if (fboundp 'kill-whole-line)
+      'kill-whole-line
+    'kill-entire-line))
 
-;; * Indentation is *almost* identical to gofmt
-;; ** We think struct literal keys are labels and outdent them
-;; ** We disagree on the indentation of function literals in arguments
-;; ** There are bugs with the close brace of struct literals
-;; * Highlight identifiers according to their syntactic context: type,
-;;   variable, function call, or tag
-;; * Command for adding an import
-;; ** Check if it's already there
-;; ** Factor/unfactor the import line
-;; ** Alphabetize
-;; * Remove unused imports
-;; ** This is hard, since I have to be aware of shadowing to do it
-;;    right
-;; * Format region using gofmt
+;; XEmacs unfortunately does not offer position-bytes. We can fall
+;; back to just using (point), but it will be incorrect as soon as
+;; multibyte characters are being used.
+(if (fboundp 'position-bytes)
+    (defalias 'go--position-bytes 'position-bytes)
+  (defun go--position-bytes (point) point))
 
-;;; Code:
+(defun go--old-completion-list-style (list)
+  (mapcar (lambda (x) (cons x nil)) list))
 
-(eval-when-compile (require 'cl))
+;; GNU Emacs 24 has prog-mode, older GNU Emacs and XEmacs do not.
+;; Ideally we'd use defalias instead, but that breaks in XEmacs.
+;;
+;; TODO: If XEmacs decides to add prog-mode, change this to use
+;; defalias to alias prog-mode or fundamental-mode to go--prog-mode
+;; and use that in define-derived-mode.
+(if (not (fboundp 'prog-mode))
+    (define-derived-mode prog-mode fundamental-mode "" ""))
+
+(defun go--regexp-enclose-in-symbol (s)
+  ;; XEmacs does not support \_<, GNU Emacs does. In GNU Emacs we make
+  ;; extensive use of \_< to support unicode in identifiers. Until we
+  ;; come up with a better solution for XEmacs, this solution will
+  ;; break fontification in XEmacs for identifiers such as "typeµ".
+  ;; XEmacs will consider "type" a keyword, GNU Emacs won't.
+
+  (if (go--xemacs-p)
+      (concat "\\<" s "\\>")
+    (concat "\\_<" s "\\_>")))
+
+(defconst go-dangling-operators-regexp
"[^-]-\\|[^+]\\+\\|[/*&><.=|^]")
+(defconst go-identifier-regexp "[[:word:][:multibyte:]]+")
+(defconst go-label-regexp go-identifier-regexp)
+(defconst go-type-regexp "[[:word:][:multibyte:]*]+")
+(defconst go-func-regexp (concat (go--regexp-enclose-in-symbol "func")
"\\s *\\(" go-identifier-regexp "\\)"))
+(defconst go-func-meth-regexp (concat (go--regexp-enclose-in-symbol "func")
"\\s *\\(?:(\\s *" go-identifier-regexp "\\s +" go-type-regexp
"\\s *)\\s *\\)?\\(" go-identifier-regexp "\\)("))
+(defconst go-builtins
+  '("append" "cap"   "close"   "complex"
"copy"
+    "delete" "imag"  "len"     "make"   
"new"
+    "panic"  "print" "println" "real"   
"recover")
+  "All built-in functions in the Go language. Used for font locking.")
+
+(defconst go-mode-keywords
+  '("break"    "default"     "func"  
"interface" "select"
+    "case"     "defer"       "go"     "map"      
"struct"
+    "chan"     "else"        "goto"   "package"  
"switch"
+    "const"    "fallthrough" "if"     "range"    
"type"
+    "continue" "for"         "import" "return"   
"var")
+  "All keywords in the Go language.  Used for font locking.")
+
+(defconst go-constants '("nil" "true" "false"
"iota"))
+(defconst go-type-name-regexp (concat "\\(?:[*(]\\)*\\(?:" go-identifier-regexp
"\\.\\)?\\(" go-identifier-regexp "\\)"))
+
+(defvar go-dangling-cache)
+(defvar go-godoc-history nil)
+
+(defgroup go nil
+  "Major mode for editing Go code"
+:group 'languages)
+
+(defcustom go-fontify-function-calls t
+  "Fontify function and method calls if this is non-nil."
+:type 'boolean
+:group 'go)
 
 (defvar go-mode-syntax-table
   (let ((st (make-syntax-table)))
-    ;; Add _ to :word: character class
-    (modify-syntax-entry ?_  "w" st)
-
-    ;; Operators (punctuation)
     (modify-syntax-entry ?+  "." st)
     (modify-syntax-entry ?-  "." st)
-    (modify-syntax-entry ?*  ". 23" st)                                    ;
also part of comments
-    (modify-syntax-entry ?/ (if (featurep 'xemacs) ". 1456" ".
124b") st)  ; ditto
     (modify-syntax-entry ?%  "." st)
     (modify-syntax-entry ?&  "." st)
     (modify-syntax-entry ?|  "." st)
@@ -73,80 +145,62 @@
     (modify-syntax-entry ?=  "." st)
     (modify-syntax-entry ?<  "." st)
     (modify-syntax-entry ?>  "." st)
-
-    ;; Strings and comments are font-locked separately.
-    (modify-syntax-entry ?\" "." st)
-    (modify-syntax-entry ?\' "." st)
-    (modify-syntax-entry ?`  "." st)
-    (modify-syntax-entry ?\\ "." st)
-
-    ;; Newline is a comment-ender.
+    (modify-syntax-entry ?/ (if (go--xemacs-p) ". 1456" ". 124b")
st)
+    (modify-syntax-entry ?*  ". 23" st)
     (modify-syntax-entry ?\n "> b" st)
+    (modify-syntax-entry ?\" "\"" st)
+    (modify-syntax-entry ?\' "\"" st)
+    (modify-syntax-entry ?`  "\"" st)
+    (modify-syntax-entry ?\\ "\\" st)
+    ;; It would be nicer to have _ as a symbol constituent, but that
+    ;; would trip up XEmacs, which does not support the \_< anchor
+    (modify-syntax-entry ?_  "w" st)
 
     st)
   "Syntax table for Go mode.")
 
-(defvar go-mode-keywords
-  '("break"    "default"     "func"  
"interface" "select"
-    "case"     "defer"       "go"     "map"      
"struct"
-    "chan"     "else"        "goto"   "package"  
"switch"
-    "const"    "fallthrough" "if"     "range"    
"type"
-    "continue" "for"         "import" "return"   
"var")
-  "All keywords in the Go language.  Used for font locking and
-some syntax analysis.")
+(defun go--build-font-lock-keywords ()
+  ;; we cannot use 'symbols in regexp-opt because emacs <24 doesn't
+  ;; understand that
+  (append
+   `((,(go--regexp-enclose-in-symbol (regexp-opt go-mode-keywords t)) .
font-lock-keyword-face)
+     (,(go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) .
font-lock-builtin-face)
+     (,(go--regexp-enclose-in-symbol (regexp-opt go-constants t)) .
font-lock-constant-face)
+     (,go-func-regexp 1 font-lock-function-name-face)) ;; function (not method) name
 
-(defvar go-mode-font-lock-keywords
-  (let ((builtins '("append" "cap" "close"
"complex" "copy" "delete" "imag" "len"
-                    "make" "new" "panic" "print"
"println" "real" "recover"))
-        (constants '("nil" "true" "false"
"iota"))
-        (type-name "\\s *\\(?:[*(]\\s *\\)*\\(?:\\w+\\s *\\.\\s
*\\)?\\(\\w+\\)")
-        )
-    `((go-mode-font-lock-cs-comment 0 font-lock-comment-face t)
-      (go-mode-font-lock-cs-string 0 font-lock-string-face t)
-      (,(regexp-opt go-mode-keywords 'words) . font-lock-keyword-face)
-      (,(regexp-opt builtins 'words) . font-lock-builtin-face)
-      (,(regexp-opt constants 'words) . font-lock-constant-face)
-      ;; Function names in declarations
-      ("\\<func\\>\\s *\\(\\w+\\)" 1 font-lock-function-name-face)
-      ;; Function names in methods are handled by function call pattern
-      ;; Function names in calls
-      ;; XXX Doesn't match if function name is surrounded by parens
-      ("\\(\\w+\\)\\s *(" 1 font-lock-function-name-face)
-      ;; Type names
-      ("\\<type\\>\\s *\\(\\w+\\)" 1 font-lock-type-face)
-      (,(concat "\\<type\\>\\s *\\w+\\s *" type-name) 1
font-lock-type-face)
-      ;; Arrays/slices/map value type
-      ;; XXX Wrong.  Marks 0 in expression "foo[0] * x"
-      ;;      (,(concat "]" type-name) 1 font-lock-type-face)
-      ;; Map key type
-      (,(concat "\\<map\\s *\\[" type-name) 1 font-lock-type-face)
-      ;; Channel value type
-      (,(concat "\\<chan\\>\\s *\\(?:<-\\)?" type-name) 1
font-lock-type-face)
-      ;; new/make type
-      (,(concat "\\<\\(?:new\\|make\\)\\>\\(?:\\s \\|)\\)*(" type-name) 1
font-lock-type-face)
-      ;; Type conversion
-      (,(concat "\\.\\s *(" type-name) 1 font-lock-type-face)
-      ;; Method receiver type
-      (,(concat "\\<func\\>\\s *(\\w+\\s +" type-name) 1
font-lock-type-face)
-      ;; Labels
-      ;; XXX Not quite right.  Also marks compound literal fields.
-      ("^\\s *\\(\\w+\\)\\s *:\\(\\S.\\|$\\)" 1 font-lock-constant-face)
-      ("\\<\\(goto\\|break\\|continue\\)\\>\\s *\\(\\w+\\)" 2
font-lock-constant-face)))
-  "Basic font lock keywords for Go mode.  Highlights keywords,
-built-ins, functions, and some types.")
+   (if go-fontify-function-calls
+       `((,(concat "\\(" go-identifier-regexp "\\)[[:space:]]*(") 1
font-lock-function-name-face) ;; function call/method name
+         (,(concat "(\\(" go-identifier-regexp "\\))[[:space:]]*(") 1
font-lock-function-name-face)) ;; bracketed function call
+     `((,go-func-meth-regexp 1 font-lock-function-name-face))) ;; method name
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Key map
-;;
+   `(
+     (,(concat (go--regexp-enclose-in-symbol "type")
"[[:space:]]*\\([^[:space:]]+\\)") 1 font-lock-type-face) ;; types
+     (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]*"
go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face)
;; types
+     (,(concat "[^[:word:][:multibyte:]]\\[\\([[:digit:]]+\\|\\.\\.\\.\\)?\\]"
go-type-name-regexp) 2 font-lock-type-face) ;; Arrays/slices
+     (,(concat "\\(" go-identifier-regexp "\\)" "{") 1
font-lock-type-face)
+     (,(concat (go--regexp-enclose-in-symbol "map") "\\[[^]]+\\]"
go-type-name-regexp) 1 font-lock-type-face) ;; map value type
+     (,(concat (go--regexp-enclose-in-symbol "map") "\\["
go-type-name-regexp) 1 font-lock-type-face) ;; map key type
+     (,(concat (go--regexp-enclose-in-symbol "chan")
"[[:space:]]*\\(?:<-\\)?" go-type-name-regexp) 1 font-lock-type-face) ;;
channel type
+     (,(concat (go--regexp-enclose-in-symbol "\\(?:new\\|make\\)")
"\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) ;;
new/make type
+     ;; TODO do we actually need this one or isn't it just a function call?
+     (,(concat "\\.\\s *(" go-type-name-regexp) 1 font-lock-type-face) ;; Type
conversion
+     (,(concat (go--regexp-enclose-in-symbol "func") "[[:space:]]+("
go-identifier-regexp "[[:space:]]+" go-type-name-regexp ")") 1
font-lock-type-face) ;; Method receiver
+     ;; Like the original go-mode this also marks compound literal
+     ;; fields. There, it was marked as to fix, but I grew quite
+     ;; accustomed to it, so it'll stay for now.
+     (,(concat "^[[:space:]]*\\(" go-label-regexp
"\\)[[:space:]]*:\\(\\S.\\|$\\)") 1 font-lock-constant-face) ;; Labels and
compound literal fields
+     (,(concat (go--regexp-enclose-in-symbol "\\(goto\\|break\\|continue\\)")
"[[:space:]]*\\(" go-label-regexp "\\)") 2 font-lock-constant-face))))
;; labels in goto/break/continue
 
 (defvar go-mode-map
   (let ((m (make-sparse-keymap)))
-    (define-key m "}" #'go-mode-insert-and-indent)
-    (define-key m ")" #'go-mode-insert-and-indent)
-    (define-key m "," #'go-mode-insert-and-indent)
-    (define-key m ":" #'go-mode-delayed-electric)
-    ;; In case we get : indentation wrong, correct ourselves
-    (define-key m "=" #'go-mode-insert-and-indent)
+    (define-key m "}" 'go-mode-insert-and-indent)
+    (define-key m ")" 'go-mode-insert-and-indent)
+    (define-key m "," 'go-mode-insert-and-indent)
+    (define-key m ":" 'go-mode-insert-and-indent)
+    (define-key m "=" 'go-mode-insert-and-indent)
+    (define-key m (kbd "C-c C-a") 'go-import-add)
+    (define-key m (kbd "C-c C-j") 'godef-jump)
+    (define-key m (kbd "C-c C-d") 'godef-describe)
     m)
   "Keymap used by Go mode to implement electric keys.")
 
@@ -157,606 +211,279 @@
   (call-interactively (lookup-key (current-global-map) key))
   (indent-according-to-mode))
 
-(defvar go-mode-delayed-point nil
-  "The point following the previous insertion if the insertion
-was a delayed electric key.  Used to communicate between
-`go-mode-delayed-electric' and `go-mode-delayed-electric-hook'.")
-(make-variable-buffer-local 'go-mode-delayed-point)
+(defmacro go-paren-level ()
+  `(car (syntax-ppss)))
 
-(defun go-mode-delayed-electric (p)
-  "Perform electric insertion, but delayed by one event.
+(defmacro go-in-string-or-comment-p ()
+  `(nth 8 (syntax-ppss)))
 
-This inserts P into the buffer, as usual, then waits for another key.
-If that second key causes a buffer modification starting at the
-point after the insertion of P, reindents the line containing P."
+(defmacro go-in-string-p ()
+  `(nth 3 (syntax-ppss)))
 
-  (interactive "p")
-  (self-insert-command p)
-  (setq go-mode-delayed-point (point)))
+(defmacro go-in-comment-p ()
+  `(nth 4 (syntax-ppss)))
 
-(defun go-mode-delayed-electric-hook (b e l)
-  "An after-change-function that implements `go-mode-delayed-electric'."
+(defmacro go-goto-beginning-of-string-or-comment ()
+  `(goto-char (nth 8 (syntax-ppss))))
 
-  (when (and go-mode-delayed-point
-             (= go-mode-delayed-point b))
-    (save-excursion
-      (save-match-data
-        (goto-char go-mode-delayed-point)
-        (indent-according-to-mode))))
-  (setq go-mode-delayed-point nil))
+(defun go--backward-irrelevant (&optional stop-at-string)
+  "Skips backwards over any characters that are irrelevant for
+indentation and related tasks.
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Parser
-;;
+It skips over whitespace, comments, cases and labels and, if
+STOP-AT-STRING is not true, over strings."
 
-(defvar go-mode-mark-cs-end 1
-  "The point at which the comment/string cache ends.  The buffer
-will be marked from the beginning up to this point (that is, up
-to and including character (1- go-mode-mark-cs-end)).")
-(make-variable-buffer-local 'go-mode-mark-cs-end)
+  (let (pos (start-pos (point)))
+    (skip-chars-backward "\n\s\t")
+    (if (and (save-excursion (beginning-of-line) (go-in-string-p)) (looking-back
"`") (not stop-at-string))
+        (backward-char))
+    (if (and (go-in-string-p) (not stop-at-string))
+        (go-goto-beginning-of-string-or-comment))
+    (if (looking-back "\\*/")
+        (backward-char))
+    (if (go-in-comment-p)
+        (go-goto-beginning-of-string-or-comment))
+    (setq pos (point))
+    (beginning-of-line)
+    (if (or (looking-at (concat "^" go-label-regexp ":")) (looking-at
"^[[:space:]]*\\(case .+\\|default\\):"))
+        (end-of-line 0)
+      (goto-char pos))
+    (if (/= start-pos (point))
+        (go--backward-irrelevant stop-at-string))
+    (/= start-pos (point))))
 
-(defvar go-mode-mark-string-end 1
-  "The point at which the string cache ends.  The buffer
-will be marked from the beginning up to this point (that is, up
-to and including character (1- go-mode-mark-string-end)).")
-(make-variable-buffer-local 'go-mode-mark-string-end)
+(defun go--buffer-narrowed-p ()
+  "Return non-nil if the current buffer is narrowed."
+  (/= (buffer-size)
+      (- (point-max)
+         (point-min))))
 
-(defvar go-mode-mark-comment-end 1
-  "The point at which the comment cache ends.  The buffer
-will be marked from the beginning up to this point (that is, up
-to and including character (1- go-mode-mark-comment-end)).")
-(make-variable-buffer-local 'go-mode-mark-comment-end)
+(defun go-previous-line-has-dangling-op-p ()
+  "Returns non-nil if the current line is a continuation line."
+  (let* ((cur-line (line-number-at-pos))
+         (val (gethash cur-line go-dangling-cache 'nope)))
+    (if (or (go--buffer-narrowed-p) (equal val 'nope))
+        (save-excursion
+          (beginning-of-line)
+          (go--backward-irrelevant t)
+          (setq val (looking-back go-dangling-operators-regexp))
+          (if (not (go--buffer-narrowed-p))
+              (puthash cur-line val go-dangling-cache))))
+    val))
 
-(defvar go-mode-mark-nesting-end 1
-  "The point at which the nesting cache ends.  The buffer will be
-marked from the beginning up to this point.")
-(make-variable-buffer-local 'go-mode-mark-nesting-end)
+(defun go--at-function-definition ()
+  "Return non-nil if point is on the opening curly brace of a
+function definition.
 
-(defun go-mode-mark-clear-cs (b e l)
-  "An after-change-function that removes the go-mode-cs text property"
-  (remove-text-properties b e '(go-mode-cs)))
+We do this by first calling (beginning-of-defun), which will take
+us to the start of *some* function. We then look for the opening
+curly brace of that function and compare its position against the
+curly brace we are checking. If they match, we return non-nil."
+  (if (= (char-after) ?\{)
+      (save-excursion
+        (let ((old-point (point))
+              start-nesting)
+          (beginning-of-defun)
+          (when (looking-at "func ")
+            (setq start-nesting (go-paren-level))
+            (skip-chars-forward "^{")
+            (while (> (go-paren-level) start-nesting)
+              (forward-char)
+              (skip-chars-forward "^{") 0)
+            (if (and (= (go-paren-level) start-nesting) (= old-point (point)))
+                t))))))
 
-(defun go-mode-mark-clear-cache (b e)
-  "A before-change-function that clears the comment/string and
-nesting caches from the modified point on."
+(defun go-goto-opening-parenthesis (&optional char)
+  (let ((start-nesting (go-paren-level)))
+    (while (and (not (bobp))
+                (>= (go-paren-level) start-nesting))
+      (if (zerop (skip-chars-backward
+                  (if char
+                      (case char (?\] "^[") (?\} "^{") (?\)
"^("))
+                    "^[{(")))
+          (if (go-in-string-or-comment-p)
+              (go-goto-beginning-of-string-or-comment)
+            (backward-char))))))
 
-  (save-restriction
-    (widen)
-    (when (<= b go-mode-mark-cs-end)
-      ;; Remove the property adjacent to the change position.
-      ;; It may contain positions pointing beyond the new end mark.
-      (let ((b (let ((cs (get-text-property (max 1 (1- b)) 'go-mode-cs)))
-		 (if cs (car cs) b))))
-	(remove-text-properties
-	 b (min go-mode-mark-cs-end (point-max)) '(go-mode-cs nil))
-	(setq go-mode-mark-cs-end b)))
+(defun go--indentation-for-opening-parenthesis ()
+  "Return the semantic indentation for the current opening parenthesis.
 
-    (when (<= b go-mode-mark-string-end)
-      ;; Remove the property adjacent to the change position.
-      ;; It may contain positions pointing beyond the new end mark.
-      (let ((b (let ((cs (get-text-property (max 1 (1- b)) 'go-mode-string)))
-		 (if cs (car cs) b))))
-	(remove-text-properties
-	 b (min go-mode-mark-string-end (point-max)) '(go-mode-string nil))
-	(setq go-mode-mark-string-end b)))
-    (when (<= b go-mode-mark-comment-end)
-      ;; Remove the property adjacent to the change position.
-      ;; It may contain positions pointing beyond the new end mark.
-      (let ((b (let ((cs (get-text-property (max 1 (1- b)) 'go-mode-comment)))
-		 (if cs (car cs) b))))
-	(remove-text-properties
-	 b (min go-mode-mark-string-end (point-max)) '(go-mode-comment nil))
-	(setq go-mode-mark-comment-end b)))
+If point is on an opening curly brace and said curly brace
+belongs to a function declaration, the indentation of the func
+keyword will be returned. Otherwise the indentation of the
+current line will be returned."
+  (save-excursion
+    (if (go--at-function-definition)
+        (progn
+          (beginning-of-defun)
+          (current-indentation))
+      (current-indentation))))
 
-    (when (< b go-mode-mark-nesting-end)
-      (remove-text-properties b (min go-mode-mark-nesting-end (point-max))
'(go-mode-nesting nil))
-      (setq go-mode-mark-nesting-end b))))
+(defun go-indentation-at-point ()
+  (save-excursion
+    (let (start-nesting (outindent 0))
+      (back-to-indentation)
+      (setq start-nesting (go-paren-level))
 
-(defmacro go-mode-parser (&rest body)
-  "Evaluate BODY in an environment set up for parsers that use
-text properties to mark text.  This inhibits changes to the undo
-list or the buffer's modification status and inhibits calls to
-the modification hooks.  It also saves the excursion and
-restriction and widens the buffer, since most parsers are
-context-sensitive."
-
-  (let ((modified-var (make-symbol "modified")))
-    `(let ((buffer-undo-list t)
-           (,modified-var (buffer-modified-p))
-           (inhibit-modification-hooks t)
-           (inhibit-read-only t))
-       (save-excursion
-         (save-restriction
-           (widen)
-           (unwind-protect
-               (progn ,@body)
-             (set-buffer-modified-p ,modified-var)))))))
-
-(defun go-mode-cs (&optional pos)
-  "Return the comment/string state at point POS.  If point is
-inside a comment or string (including the delimiters), this
-returns a pair (START . END) indicating the extents of the
-comment or string."
-
-  (unless pos
-    (setq pos (point)))
-  (when (>= pos go-mode-mark-cs-end)
-    (go-mode-mark-cs (1+ pos)))
-  (get-text-property pos 'go-mode-cs))
-
-(defun go-mode-mark-cs (end)
-  "Mark comments and strings up to point END.  Don't call this
-directly; use `go-mode-cs'."
-  (setq end (min end (point-max)))
-  (go-mode-parser
-   (save-match-data
-     (let ((pos
-	    ;; Back up to the last known state.
-	    (let ((last-cs
-		   (and (> go-mode-mark-cs-end 1)
-			(get-text-property (1- go-mode-mark-cs-end)
-					   'go-mode-cs))))
-	      (if last-cs
-		  (car last-cs)
-		(max 1 (1- go-mode-mark-cs-end))))))
-       (while (< pos end)
-	 (goto-char pos)
-	 (let ((cs-end			; end of the text property
-		(cond
-		 ((looking-at "//")
-		  (end-of-line)
-		  (1+ (point)))
-		 ((looking-at "/\\*")
-		  (goto-char (+ pos 2))
-		  (if (search-forward "*/" (1+ end) t)
-		      (point)
-		    end))
-		 ((looking-at "\"")
-		  (goto-char (1+ pos))
-		  (if (looking-at "[^\"\n\\\\]*\\(\\\\.[^\"\n\\\\]*\\)*\"")
-		      (match-end 0)
-		    (end-of-line)
-		    (point)))
-		 ((looking-at "'")
-		  (goto-char (1+ pos))
-		  (if (looking-at "[^'\n\\\\]*\\(\\\\.[^'\n\\\\]*\\)*'")
-		      (match-end 0)
-		    (end-of-line)
-		    (point)))
-		 ((looking-at "`")
-		  (goto-char (1+ pos))
-		  (while (if (search-forward "`" end t)
-			     (if (eq (char-after) ?`)
-				 (goto-char (1+ (point))))
-			   (goto-char end)
-			   nil))
-		  (point)))))
-	   (cond
-	    (cs-end
-	     (put-text-property pos cs-end 'go-mode-cs (cons pos cs-end))
-	     (setq pos cs-end))
-	    ((re-search-forward "[\"'`]\\|/[/*]" end t)
-	     (setq pos (match-beginning 0)))
-	    (t
-	     (setq pos end)))))
-       (setq go-mode-mark-cs-end pos)))))
-
-(defun go-mode-in-comment (&optional pos)
-  "Return the comment/string state at point POS.  If point is
-inside a comment (including the delimiters), this
-returns a pair (START . END) indicating the extents of the
-comment or string."
-
-  (unless pos
-    (setq pos (point)))
-  (when (> pos go-mode-mark-comment-end)
-    (go-mode-mark-comment pos))
-  (get-text-property pos 'go-mode-comment))
-
-(defun go-mode-mark-comment (end)
-  "Mark comments up to point END.  Don't call this directly; use
`go-mode-in-comment'."
-  (setq end (min end (point-max)))
-  (go-mode-parser
-   (save-match-data
-     (let ((pos
-	    ;; Back up to the last known state.
-	    (let ((last-comment
-		   (and (> go-mode-mark-comment-end 1)
-			(get-text-property (1- go-mode-mark-comment-end)
-					   'go-mode-comment))))
-	      (if last-comment
-		  (car last-comment)
-		(max 1 (1- go-mode-mark-comment-end))))))
-       (while (< pos end)
-	 (goto-char pos)
-	 (let ((comment-end			; end of the text property
-		(cond
-		 ((looking-at "//")
-		  (end-of-line)
-		  (1+ (point)))
-		 ((looking-at "/\\*")
-		  (goto-char (+ pos 2))
-		  (if (search-forward "*/" (1+ end) t)
-		      (point)
-		    end)))))
-	   (cond
-	    (comment-end
-	     (put-text-property pos comment-end 'go-mode-comment (cons pos comment-end))
-	     (setq pos comment-end))
-	    ((re-search-forward "/[/*]" end t)
-	     (setq pos (match-beginning 0)))
-	    (t
-	     (setq pos end)))))
-       (setq go-mode-mark-comment-end pos)))))
-
-(defun go-mode-in-string (&optional pos)
-  "Return the string state at point POS.  If point is
-inside a string (including the delimiters), this
-returns a pair (START . END) indicating the extents of the
-comment or string."
-
-  (unless pos
-    (setq pos (point)))
-  (when (> pos go-mode-mark-string-end)
-    (go-mode-mark-string pos))
-  (get-text-property pos 'go-mode-string))
-
-(defun go-mode-mark-string (end)
-  "Mark strings up to point END.  Don't call this
-directly; use `go-mode-in-string'."
-  (setq end (min end (point-max)))
-  (go-mode-parser
-   (save-match-data
-     (let ((pos
-	    ;; Back up to the last known state.
-	    (let ((last-cs
-		   (and (> go-mode-mark-string-end 1)
-			(get-text-property (1- go-mode-mark-string-end)
-					   'go-mode-string))))
-	      (if last-cs
-		  (car last-cs)
-		(max 1 (1- go-mode-mark-string-end))))))
-       (while (< pos end)
-	 (goto-char pos)
-	 (let ((cs-end			; end of the text property
-		(cond
-		 ((looking-at "\"")
-		  (goto-char (1+ pos))
-		  (if (looking-at "[^\"\n\\\\]*\\(\\\\.[^\"\n\\\\]*\\)*\"")
-		      (match-end 0)
-		    (end-of-line)
-		    (point)))
-		 ((looking-at "'")
-		  (goto-char (1+ pos))
-		  (if (looking-at "[^'\n\\\\]*\\(\\\\.[^'\n\\\\]*\\)*'")
-		      (match-end 0)
-		    (end-of-line)
-		    (point)))
-		 ((looking-at "`")
-		  (goto-char (1+ pos))
-		  (while (if (search-forward "`" end t)
-			     (if (eq (char-after) ?`)
-				 (goto-char (1+ (point))))
-			   (goto-char end)
-			   nil))
-		  (point)))))
-	   (cond
-	    (cs-end
-	     (put-text-property pos cs-end 'go-mode-string (cons pos cs-end))
-	     (setq pos cs-end))
-	    ((re-search-forward "[\"'`]" end t)
-	     (setq pos (match-beginning 0)))
-	    (t
-	     (setq pos end)))))
-       (setq go-mode-mark-string-end pos)))))
-
-(defun go-mode-font-lock-cs (limit comment)
-  "Helper function for highlighting comment/strings.  If COMMENT is t,
-set match data to the next comment after point, and advance point
-after it.  If COMMENT is nil, use the next string.  Returns nil
-if no further tokens of the type exist."
-  ;; Ensures that `next-single-property-change' below will work properly.
-  (go-mode-cs limit)
-  (let (cs next (result 'scan))
-    (while (eq result 'scan)
-      (if (or (>= (point) limit) (eobp))
-	  (setq result nil)
-	(setq cs (go-mode-cs))
-	(if (and cs (>= (car cs) (point)))
-	    (if (eq (= (char-after (car cs)) ?/) comment)
-		;; If inside the expected comment/string, highlight it.
-		(progn
-		  ;; If the match includes a "\n", we have a
-		  ;; multi-line construct.  Mark it as such.
-		  (goto-char (car cs))
-		  (when (search-forward "\n" (cdr cs) t)
-		    (put-text-property
-		     (car cs) (cdr cs) 'font-lock-multline t))
-		  (set-match-data (list (car cs) (copy-marker (cdr cs))))
-		  (goto-char (cdr cs))
-		  (setq result t))
-	      ;; Wrong type.  Look for next comment/string after this one.
-	      (goto-char (cdr cs)))
-	  ;; Not inside comment/string.  Search for next comment/string.
-	  (setq next (next-single-property-change
-		      (point) 'go-mode-cs nil limit))
-	  (if (and next (< next limit))
-	      (goto-char next)
-	    (setq result nil)))))
-    result))
-
-(defun go-mode-font-lock-cs-string (limit)
-  "Font-lock iterator for strings."
-  (go-mode-font-lock-cs limit nil))
-
-(defun go-mode-font-lock-cs-comment (limit)
-  "Font-lock iterator for comments."
-  (go-mode-font-lock-cs limit t))
-
-(defsubst go-mode-nesting (&optional pos)
-  "Return the nesting at point POS.  The nesting is a list
-of (START . END) pairs for all braces, parens, and brackets
-surrounding POS, starting at the inner-most nesting.  START is
-the location of the open character.  END is the location of the
-close character or nil if the nesting scanner has not yet
-encountered the close character."
-
-  (unless pos
-    (setq pos (point)))
-  (if (= pos 1)
-      '()
-    (when (> pos go-mode-mark-nesting-end)
-      (go-mode-mark-nesting pos))
-    (get-text-property (- pos 1) 'go-mode-nesting)))
-
-(defun go-mode-mark-nesting (pos)
-  "Mark nesting up to point END.  Don't call this directly; use
-`go-mode-nesting'."
-
-  (go-mode-cs pos)
-  (go-mode-parser
-   ;; Mark depth
-   (goto-char go-mode-mark-nesting-end)
-   (let ((nesting (go-mode-nesting))
-         (last (point)))
-     (while (< last pos)
-       ;; Find the next depth-changing character
-       (skip-chars-forward "^(){}[]" pos)
-       ;; Mark everything up to this character with the current
-       ;; nesting
-       (put-text-property last (point) 'go-mode-nesting nesting)
-       (when nil
-         (let ((depth (length nesting)))
-           (put-text-property last (point) 'face
-                              `((:background
-                                 ,(format "gray%d" (* depth 10)))))))
-       (setq last (point))
-       ;; Update nesting
-       (unless (eobp)
-         (let ((ch (unless (go-mode-cs) (char-after))))
-           (forward-char 1)
-           (case ch
-             ((?\( ?\{ ?\[)
-              (setq nesting (cons (cons (- (point) 1) nil)
-                                  nesting)))
-             ((?\) ?\} ?\])
-              (when nesting
-                (setcdr (car nesting) (- (point) 1))
-                (setq nesting (cdr nesting))))))))
-     ;; Update state
-     (setq go-mode-mark-nesting-end last))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Indentation
-;;
-
-(defvar go-mode-non-terminating-keywords-regexp
-  (let* ((kws go-mode-keywords)
-         (kws (remove "break" kws))
-         (kws (remove "continue" kws))
-         (kws (remove "fallthrough" kws))
-         (kws (remove "return" kws)))
-    (regexp-opt kws 'words))
-  "Regular expression matching all Go keywords that *do not*
-implicitly terminate a statement.")
-
-(defun go-mode-semicolon-p ()
-  "True iff point immediately follows either an explicit or
-implicit semicolon.  Point should immediately follow the last
-token on the line."
-
-  ;; #Semicolons
-  (case (char-before)
-    ((?\;) t)
-    ;; String literal
-    ((?' ?\" ?`) t)
-    ;; One of the operators and delimiters ++, --, ), ], or }
-    ((?+) (eq (char-before (1- (point))) ?+))
-    ((?-) (eq (char-before (1- (point))) ?-))
-    ((?\) ?\] ?\}) t)
-    ;; An identifier or one of the keywords break, continue,
-    ;; fallthrough, or return or a numeric literal
-    (otherwise
-     (save-excursion
-       (when (/= (skip-chars-backward "[:word:]_") 0)
-         (not (looking-at go-mode-non-terminating-keywords-regexp)))))))
-
-(defun go-mode-whitespace-p (char)
-  "Is newline, or char whitespace in the syntax table for go."
-  (or (eq char ?\n)
-      (= (char-syntax char) ?\ )))
-
-(defun go-mode-backward-skip-comments ()
-  "Skip backward over comments and whitespace."
-  ;; only proceed if point is in a comment or white space
-  (if (or (go-mode-in-comment)
-	  (go-mode-whitespace-p (char-after (point))))
-      (let ((loop-guard t))
-	(while (and
-		loop-guard
-		(not (bobp)))
-
-	  (cond ((go-mode-whitespace-p (char-after (point)))
-		 ;; moves point back over any whitespace
-		 (re-search-backward "[^[:space:]]"))
-
-		((go-mode-in-comment)
-		 ;; move point to char preceeding current comment
-		 (goto-char (1- (car (go-mode-in-comment)))))
-		
-		;; not in a comment or whitespace? we must be done.
-		(t (setq loop-guard nil)
-		   (forward-char 1)))))))
-
-(defun go-mode-indentation ()
-  "Compute the ideal indentation level of the current line.
-
-To the first order, this is the brace depth of the current line,
-plus parens that follow certain keywords.  case, default, and
-labels are outdented one level, and continuation lines are
-indented one level."
-
-  (save-excursion
-    (back-to-indentation)
-    (let ((cs (go-mode-cs)))
-      ;; Treat comments and strings differently only if the beginning
-      ;; of the line is contained within them
-      (when (and cs (= (point) (car cs)))
-        (setq cs nil))
-      ;; What type of context am I in?
       (cond
-       ((and cs (save-excursion
-                  (goto-char (car cs))
-                  (looking-at "`")))
-        ;; Inside a multi-line string.  Don't mess with indentation.
-        nil)
-       (cs
-        ;; Inside a general comment
-        (goto-char (car cs))
-        (forward-char 1)
-        (current-column))
+       ((go-in-string-p)
+        (current-indentation))
+       ((looking-at "[])}]")
+        (go-goto-opening-parenthesis (char-after))
+        (if (go-previous-line-has-dangling-op-p)
+            (- (current-indentation) tab-width)
+          (go--indentation-for-opening-parenthesis)))
+       ((progn (go--backward-irrelevant t) (looking-back go-dangling-operators-regexp))
+        ;; only one nesting for all dangling operators in one operation
+        (if (go-previous-line-has-dangling-op-p)
+            (current-indentation)
+          (+ (current-indentation) tab-width)))
+       ((zerop (go-paren-level))
+        0)
+       ((progn (go-goto-opening-parenthesis) (< (go-paren-level) start-nesting))
+        (if (go-previous-line-has-dangling-op-p)
+            (current-indentation)
+          (+ (go--indentation-for-opening-parenthesis) tab-width)))
        (t
-        ;; Not in a multi-line string or comment
-        (let ((indent 0)
-              (inside-indenting-paren nil))
-          ;; Count every enclosing brace, plus parens that follow
-          ;; import, const, var, or type and indent according to
-          ;; depth.  This simple rule does quite well, but also has a
-          ;; very large extent.  It would be better if we could mimic
-          ;; some nearby indentation.
-          (save-excursion
-            (skip-chars-forward "})")
-            (let ((first t))
-              (dolist (nest (go-mode-nesting))
-                (case (char-after (car nest))
-                  ((?\{)
-                   (incf indent tab-width))
-                  ((?\()
-                   (goto-char (car nest))
-                   (go-mode-backward-skip-comments)
-                   (backward-char)
-                   ;; Really just want the token before
-                   (when (looking-back
"\\<import\\|const\\|var\\|type\\|package"
-                                       (max (- (point) 7) (point-min)))
-                     (incf indent tab-width)
-                     (when first
-                       (setq inside-indenting-paren t)))))
-                (setq first nil))))
-
-          ;; case, default, and labels are outdented 1 level
-          (when (looking-at "\\<case\\>\\|\\<default\\>\\|\\w+\\s
*:\\(\\S.\\|$\\)")
-            (decf indent tab-width))
-
-	  (when (looking-at "\\w+\\s *:.+,\\s *$")
-	    (incf indent tab-width))
-
-          ;; Continuation lines are indented 1 level
-          (beginning-of-line)		; back up to end of previous line
-	  (backward-char)
-          (go-mode-backward-skip-comments) ; back up past any comments
-          (when (case (char-before)
-                  ((nil ?\{ ?:)
-                   ;; At the beginning of a block or the statement
-                   ;; following a label.
-                   nil)
-                  ((?\()
-                   ;; Usually a continuation line in an expression,
-                   ;; unless this paren is part of a factored
-                   ;; declaration.
-                   (not inside-indenting-paren))
-                  ((?,)
-                   ;; Could be inside a literal.  We're a little
-                   ;; conservative here and consider any comma within
-                   ;; curly braces (as opposed to parens) to be a
-                   ;; literal separator.  This will fail to recognize
-                   ;; line-breaks in parallel assignments as
-                   ;; continuation lines.
-                   (let ((depth (go-mode-nesting)))
-                     (and depth
-                          (not (eq (char-after (caar depth)) ?\{)))))
-                  (t
-                   ;; We're in the middle of a block.  Did the
-                   ;; previous line end with an implicit or explicit
-                   ;; semicolon?
-                   (not (go-mode-semicolon-p))))
-            (incf indent tab-width))
-
-          (max indent 0)))))))
+        (current-indentation))))))
 
 (defun go-mode-indent-line ()
-  "Indent the current line according to `go-mode-indentation'."
   (interactive)
+  (let (indent
+        shift-amt
+        end
+        (pos (- (point-max) (point)))
+        (point (point))
+        (beg (line-beginning-position)))
+    (back-to-indentation)
+    (if (go-in-string-or-comment-p)
+        (goto-char point)
+      (setq indent (go-indentation-at-point))
+      (if (looking-at (concat go-label-regexp ":\\([[:space:]]*/.+\\)?$\\|case
.+:\\|default:"))
+          (decf indent tab-width))
+      (setq shift-amt (- indent (current-column)))
+      (if (zerop shift-amt)
+          nil
+        (delete-region beg (point))
+        (indent-to indent))
+      ;; If initial point was within line's indentation,
+      ;; position after the indentation.  Else stay at same point in text.
+      (if (> (- (point-max) pos) (point))
+          (goto-char (- (point-max) pos))))))
 
-  ;; turn off case folding to distinguish keywords from identifiers
-  ;; e.g. "default" is a keyword; "Default" can be a variable name.
-  (let ((case-fold-search nil))
-    (let ((col (go-mode-indentation)))
-      (when col
-	(let ((offset (- (current-column) (current-indentation))))
-	  (indent-line-to col)
-	  (when (> offset 0)
-	    (forward-char offset)))))))
+(defun go-beginning-of-defun (&optional count)
+  (unless count (setq count 1))
+  (let ((first t) failure)
+    (dotimes (i (abs count))
+      (while (and (not failure)
+                  (or first (go-in-string-or-comment-p)))
+        (if (>= count 0)
+            (progn
+              (go--backward-irrelevant)
+              (if (not (re-search-backward go-func-meth-regexp nil t))
+                  (setq failure t)))
+          (if (looking-at go-func-meth-regexp)
+              (forward-char))
+          (if (not (re-search-forward go-func-meth-regexp nil t))
+              (setq failure t)))
+        (setq first nil)))
+    (if (< count 0)
+        (beginning-of-line))
+    (not failure)))
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Go mode
-;;
+(defun go-end-of-defun ()
+  (let (orig-level)
+    ;; It can happen that we're not placed before a function by emacs
+    (if (not (looking-at "func"))
+        (go-beginning-of-defun -1))
+    (skip-chars-forward "^{")
+    (forward-char)
+    (setq orig-level (go-paren-level))
+    (while (>= (go-paren-level) orig-level)
+      (skip-chars-forward "^}")
+      (forward-char))))
 
 ;;;###autoload
-(define-derived-mode go-mode nil "Go"
+(define-derived-mode go-mode prog-mode "Go"
   "Major mode for editing Go source text.
 
-This provides basic syntax highlighting for keywords, built-ins,
-functions, and some types.  It also provides indentation that is
-\(almost) identical to gofmt."
+This mode provides (not just) basic editing capabilities for
+working with Go code. It offers almost complete syntax
+highlighting, indentation that is almost identical to gofmt and
+proper parsing of the buffer content to allow features such as
+navigation by function, manipulation of comments or detection of
+strings.
+
+In addition to these core features, it offers various features to
+help with writing Go code. You can directly run buffer content
+through gofmt, read godoc documentation from within Emacs, modify
+and clean up the list of package imports or interact with the
+Playground (uploading and downloading pastes).
+
+The following extra functions are defined:
+
+- `gofmt'
+- `godoc'
+- `go-import-add'
+- `go-remove-unused-imports'
+- `go-goto-imports'
+- `go-play-buffer' and `go-play-region'
+- `go-download-play'
+- `godef-describe' and `godef-jump'
+
+If you want to automatically run `gofmt' before saving a file,
+add the following hook to your emacs configuration:
+
+\(add-hook 'before-save-hook 'gofmt-before-save)
+
+If you want to use `godef-jump' instead of etags (or similar),
+consider binding godef-jump to `M-.', which is the default key
+for `find-tag':
+
+\(add-hook 'go-mode-hook (lambda ()
+                          (local-set-key (kbd \"M-.\") 'godef-jump)))
+
+Please note that godef is an external dependency. You can install
+it with
+
+go get 
code.google.com/p/rog-go/exp/cmd/godef
+
+
+If you're looking for even more integration with Go, namely
+on-the-fly syntax checking, auto-completion and snippets, it is
+recommended that you look at goflymake
+\(https://github.com/dougm/goflymake), gocode
+\(https://github.com/nsf/gocode) and yasnippet-go
+\(https://github.com/dominikh/yasnippet-go)"
 
   ;; Font lock
   (set (make-local-variable 'font-lock-defaults)
-       '(go-mode-font-lock-keywords nil nil nil nil))
-
-  ;; Remove stale text properties
-  (save-restriction
-    (widen)
-    (let ((modified (buffer-modified-p)))
-      (remove-text-properties 1 (point-max)
-                              '(go-mode-cs nil go-mode-nesting nil))
-      ;; remove-text-properties marks the buffer modified. undo that if it
-      ;; wasn't originally marked modified.
-      (set-buffer-modified-p modified)))
-
-  ;; Reset the syntax mark caches
-  (setq go-mode-mark-cs-end      1
-        go-mode-mark-nesting-end 1)
-  (add-hook 'before-change-functions #'go-mode-mark-clear-cache nil t)
-  (add-hook 'after-change-functions #'go-mode-mark-clear-cs nil t)
+       '(go--build-font-lock-keywords))
 
   ;; Indentation
-  (set (make-local-variable 'indent-line-function)
-       #'go-mode-indent-line)
-  (add-hook 'after-change-functions #'go-mode-delayed-electric-hook nil t)
+  (set (make-local-variable 'indent-line-function) 'go-mode-indent-line)
 
   ;; Comments
   (set (make-local-variable 'comment-start) "// ")
   (set (make-local-variable 'comment-end)   "")
+  (set (make-local-variable 'comment-use-syntax) t)
+  (set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *")
+
+  (set (make-local-variable 'beginning-of-defun-function)
'go-beginning-of-defun)
+  (set (make-local-variable 'end-of-defun-function) 'go-end-of-defun)
+
+  (set (make-local-variable 'parse-sexp-lookup-properties) t)
+  (if (boundp 'syntax-propertize-function)
+      (set (make-local-variable 'syntax-propertize-function)
'go-propertize-syntax))
+
+  (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql))
+  (add-hook 'before-change-functions (lambda (x y) (setq go-dangling-cache
(make-hash-table :test 'eql))) t t)
+
+
+  (setq imenu-generic-expression
+        '(("type" "^type *\\([^ \t\n\r\f]*\\)" 1)
+          ("func" "^func *\\(.*\\) {" 1)))
+  (imenu-add-to-menubar "Index")
 
   ;; Go style
   (setq indent-tabs-mode t)
@@ -769,118 +496,116 @@
   ;; those alists are traversed in *reverse* order:
   ;; 
http://lists.gnu.org/archive/html/bug-gnu-emacs/2001-12/msg00674.html
   (when (and (boundp 'compilation-error-regexp-alist)
-           (boundp 'compilation-error-regexp-alist-alist))
-      (add-to-list 'compilation-error-regexp-alist 'go-test t)
-      (add-to-list 'compilation-error-regexp-alist-alist
-                   '(go-test . ("^\t+\\([^()\t\n]+\\):\\([0-9]+\\):? .*$" 1
2)) t)))
+             (boundp 'compilation-error-regexp-alist-alist))
+    (add-to-list 'compilation-error-regexp-alist 'go-test t)
+    (add-to-list 'compilation-error-regexp-alist-alist
+                 '(go-test . ("^\t+\\([^()\t\n]+\\):\\([0-9]+\\):? .*$" 1
2)) t)))
 
 ;;;###autoload
-(add-to-list 'auto-mode-alist (cons "\\.go$" #'go-mode))
+(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode))
 
-(defun go-mode-reload ()
-  "Reload go-mode.el and put the current buffer into Go mode.
-Useful for development work."
+(defun go--apply-rcs-patch (patch-buffer)
+  "Apply an RCS-formatted diff from PATCH-BUFFER to the current
+buffer."
+  (let ((target-buffer (current-buffer))
+        ;; Relative offset between buffer line numbers and line numbers
+        ;; in patch.
+        ;;
+        ;; Line numbers in the patch are based on the source file, so
+        ;; we have to keep an offset when making changes to the
+        ;; buffer.
+        ;;
+        ;; Appending lines decrements the offset (possibly making it
+        ;; negative), deleting lines increments it. This order
+        ;; simplifies the forward-line invocations.
+        (line-offset 0))
+    (save-excursion
+      (with-current-buffer patch-buffer
+        (goto-char (point-min))
+        (while (not (eobp))
+          (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)")
+            (error "invalid rcs patch or internal error in
go--apply-rcs-patch"))
+          (forward-line)
+          (let ((action (match-string 1))
+                (from (string-to-number (match-string 2)))
+                (len  (string-to-number (match-string 3))))
+            (cond
+             ((equal action "a")
+              (let ((start (point)))
+                (forward-line len)
+                (let ((text (buffer-substring start (point))))
+                  (with-current-buffer target-buffer
+                    (decf line-offset len)
+                    (goto-char (point-min))
+                    (forward-line (- from len line-offset))
+                    (insert text)))))
+             ((equal action "d")
+              (with-current-buffer target-buffer
+                (goto-char (point-min))
+                (forward-line (- from line-offset 1))
+                (incf line-offset len)
+                (go--kill-whole-line len)))
+             (t
+              (error "invalid rcs patch or internal error in
go--apply-rcs-patch")))))))))
+
+(defun gofmt ()
+  "Formats the current buffer according to the gofmt tool."
 
   (interactive)
-  (unload-feature 'go-mode)
-  (require 'go-mode)
-  (go-mode))
+  (let ((tmpfile (make-temp-file "gofmt" nil ".go"))
+        (patchbuf (get-buffer-create "*Gofmt patch*"))
+        (errbuf (get-buffer-create "*Gofmt Errors*"))
+        (coding-system-for-read 'utf-8)
+        (coding-system-for-write 'utf-8))
 
-;;;###autoload
-(defun gofmt ()
-  "Pipe the current buffer through the external tool `gofmt`.
-Replace the current buffer on success; display errors on failure."
+    (with-current-buffer errbuf
+      (setq buffer-read-only nil)
+      (erase-buffer))
+    (with-current-buffer patchbuf
+      (erase-buffer))
 
-  (interactive)
-  (let ((currconf (current-window-configuration)))
-    (let ((srcbuf (current-buffer))
-          (filename buffer-file-name)
-          (patchbuf (get-buffer-create "*Gofmt patch*")))
-      (with-current-buffer patchbuf
-        (let ((errbuf (get-buffer-create "*Gofmt Errors*"))
-              (coding-system-for-read 'utf-8)    ;; use utf-8 with subprocesses
-              (coding-system-for-write 'utf-8))
-          (with-current-buffer errbuf
-            (toggle-read-only 0)
-            (erase-buffer))
-          (with-current-buffer srcbuf
-            (save-restriction
-              (let (deactivate-mark)
-                (widen)
-                ; If this is a new file, diff-mode can't apply a
-                ; patch to a non-exisiting file, so replace the buffer
-                ; completely with the output of 'gofmt'.
-                ; If the file exists, patch it to keep the 'undo' list happy.
-                (let* ((newfile (not (file-exists-p filename)))
-                      (flag (if newfile "" " -d")))
-                  (if (= 0 (shell-command-on-region (point-min) (point-max)
-                                                    (concat "gofmt" flag)
-                                                    patchbuf nil errbuf))
-                      ; gofmt succeeded: replace buffer or apply patch hunks.
-                      (let ((old-point (point))
-                            (old-mark (mark t)))
-                        (kill-buffer errbuf)
-                        (if newfile
-                            ; New file, replace it (diff-mode won't work)
-                            (gofmt-replace-buffer srcbuf patchbuf)
-                          ; Existing file, patch it
-                          (gofmt-apply-patch filename srcbuf patchbuf))
-                        (goto-char (min old-point (point-max)))
-                        ;; Restore the mark and point
-                        (if old-mark (push-mark (min old-mark (point-max)) t))
-                        (set-window-configuration currconf))
+    (write-region nil nil tmpfile)
 
-                  ;; gofmt failed: display the errors
-                  (gofmt-process-errors filename errbuf))))))
+    ;; We're using errbuf for the mixed stdout and stderr output. This
+    ;; is not an issue because gofmt -w does not produce any stdout
+    ;; output in case of success.
+    (if (zerop (call-process "gofmt" nil errbuf nil "-w" tmpfile))
+        (if (zerop (call-process-region (point-min) (point-max) "diff" nil
patchbuf nil "-n" "-" tmpfile))
+            (progn
+              (kill-buffer errbuf)
+              (message "Buffer is already gofmted"))
+          (go--apply-rcs-patch patchbuf)
+          (kill-buffer errbuf)
+          (message "Applied gofmt"))
+      (message "Could not apply gofmt. Check errors for details")
+      (gofmt--process-errors (buffer-file-name) tmpfile errbuf))
 
-          ;; Collapse any window opened on outbuf if shell-command-on-region
-          ;; displayed it.
-          (delete-windows-on patchbuf)))
-      (kill-buffer patchbuf))))
+    (kill-buffer patchbuf)
+    (delete-file tmpfile)))
 
-(defun gofmt-replace-buffer (srcbuf patchbuf)
-  (with-current-buffer srcbuf
-    (erase-buffer)
-    (insert-buffer-substring patchbuf)))
 
-(defconst gofmt-stdin-tag "<standard input>")
-
-(defun gofmt-apply-patch (filename srcbuf patchbuf)
-  (require 'diff-mode)
-  ;; apply all the patch hunks
-  (with-current-buffer patchbuf
-    (goto-char (point-min))
-    ;; The .* is for TMPDIR, but to avoid dealing with TMPDIR
-    ;; having a trailing / or not, it's easier to just search for .*
-    ;; especially as we're only replacing the first instance.
-    (if (re-search-forward "^--- \\(.*/gofmt[0-9]*\\)" nil t)
-      (replace-match filename nil nil nil 1))
-    (condition-case nil
-        (while t
-          (diff-hunk-next)
-          (diff-apply-hunk))
-      ;; When there's no more hunks, diff-hunk-next signals an error, ignore it
-      (error nil))))
-
-(defun gofmt-process-errors (filename errbuf)
+(defun gofmt--process-errors (filename tmpfile errbuf)
   ;; Convert the gofmt stderr to something understood by the compilation mode.
   (with-current-buffer errbuf
     (goto-char (point-min))
     (insert "gofmt errors:\n")
-    (if (search-forward gofmt-stdin-tag nil t)
-      (replace-match (file-name-nondirectory filename) nil t))
-    (display-buffer errbuf)
-    (compilation-mode)))
+    (while (search-forward-regexp (concat "^\\(" (regexp-quote tmpfile)
"\\):") nil t)
+      (replace-match (file-name-nondirectory filename) t t nil 1))
+    (compilation-mode)
+    (display-buffer errbuf)))
 
 ;;;###autoload
 (defun gofmt-before-save ()
   "Add this to .emacs to run gofmt on the current buffer when saving:
- (add-hook 'before-save-hook #'gofmt-before-save)"
+ (add-hook 'before-save-hook 'gofmt-before-save).
+
+Note that this will cause go-mode to get loaded the first time
+you save any file, kind of defeating the point of autoloading."
 
   (interactive)
   (when (eq major-mode 'go-mode) (gofmt)))
 
-(defun godoc-read-query ()
+(defun godoc--read-query ()
   "Read a godoc query from the minibuffer."
   ;; Compute the default query as the symbol under the cursor.
   ;; TODO: This does the wrong thing for e.g. multipart.NewReader (it only grabs
@@ -889,12 +614,12 @@
          (symbol (if bounds
                      (buffer-substring-no-properties (car bounds)
                                                      (cdr bounds)))))
-    (read-string (if symbol
-                     (format "godoc (default %s): " symbol)
-                   "godoc: ")
-                 nil nil symbol)))
+    (completing-read (if symbol
+                         (format "godoc (default %s): " symbol)
+                       "godoc: ")
+                     (go--old-completion-list-style (go-packages)) nil nil nil
'go-godoc-history symbol)))
 
-(defun godoc-get-buffer (query)
+(defun godoc--get-buffer (query)
   "Get an empty buffer for a godoc query."
   (let* ((buffer-name (concat "*godoc " query "*"))
          (buffer (get-buffer buffer-name)))
@@ -902,13 +627,14 @@
     (when buffer (kill-buffer buffer))
     (get-buffer-create buffer-name)))
 
-(defun godoc-buffer-sentinel (proc event)
+(defun godoc--buffer-sentinel (proc event)
   "Sentinel function run when godoc command completes."
   (with-current-buffer (process-buffer proc)
     (cond ((string= event "finished\n")  ;; Successful exit.
            (goto-char (point-min))
-           (display-buffer (current-buffer) 'not-this-window))
-          ((not (= (process-exit-status proc) 0))  ;; Error exit.
+           (view-mode 1)
+           (display-buffer (current-buffer) t))
+          ((/= (process-exit-status proc) 0)  ;; Error exit.
            (let ((output (buffer-string)))
              (kill-buffer (current-buffer))
              (message (concat "godoc: " output)))))))
@@ -916,12 +642,294 @@
 ;;;###autoload
 (defun godoc (query)
   "Show go documentation for a query, much like M-x man."
-  (interactive (list (godoc-read-query)))
+  (interactive (list (godoc--read-query)))
   (unless (string= query "")
     (set-process-sentinel
-     (start-process-shell-command "godoc" (godoc-get-buffer query)
+     (start-process-shell-command "godoc" (godoc--get-buffer query)
                                   (concat "godoc " query))
-     'godoc-buffer-sentinel)
+     'godoc--buffer-sentinel)
     nil))
 
+(defun go-goto-imports ()
+  "Move point to the block of imports.
+
+If using
+
+  import (
+    \"foo\"
+    \"bar\"
+  )
+
+it will move point directly behind the last import.
+
+If using
+
+  import \"foo\"
+  import \"bar\"
+
+it will move point to the next line after the last import.
+
+If no imports can be found, point will be moved after the package
+declaration."
+  (interactive)
+  ;; FIXME if there's a block-commented import before the real
+  ;; imports, we'll jump to that one.
+
+  ;; Generally, this function isn't very forgiving. it'll bark on
+  ;; extra whitespace. It works well for clean code.
+  (let ((old-point (point)))
+    (goto-char (point-min))
+    (cond
+     ((re-search-forward "^import ([^)]+)" nil t)
+      (backward-char 2)
+      'block)
+     ((re-search-forward "\\(^import \\([^\"]+
\\)?\"[^\"]+\"\n?\\)+" nil t)
+      'single)
+     ((re-search-forward "^[[:space:]\n]*package .+?\n" nil t)
+      (message "No imports found, moving point after package declaration")
+      'none)
+     (t
+      (goto-char old-point)
+      (message "No imports or package declaration found. Is this really a Go
file?")
+      'fail))))
+
+(defun go-play-buffer ()
+  "Like `go-play-region', but acts on the entire buffer."
+  (interactive)
+  (go-play-region (point-min) (point-max)))
+
+(defun go-play-region (start end)
+  "Send the region to the Playground and stores the resulting
+link in the kill ring."
+  (interactive "r")
+  (let* ((url-request-method "POST")
+         (url-request-extra-headers
+          '(("Content-Type" .
"application/x-www-form-urlencoded")))
+         (url-request-data (buffer-substring-no-properties start end))
+         (content-buf (url-retrieve
+                       "http://play.golang.org/share"
+                       (lambda (arg)
+                         (cond
+                          ((equal :error (car arg))
+                           (signal 'go-play-error (cdr arg)))
+                          (t
+                           (re-search-forward "\n\n")
+                           (kill-new (format "http://play.golang.org/p/%s"
(buffer-substring (point) (point-max))))
+                           (message "http://play.golang.org/p/%s"
(buffer-substring (point) (point-max)))))))))))
+
+;;;###autoload
+(defun go-download-play (url)
+  "Downloads a paste from the playground and inserts it in a Go
+buffer. Tries to look for a URL at point."
+  (interactive (list (read-from-minibuffer "Playground URL: " (ffap-url-p
(ffap-string-at-point 'url)))))
+  (with-current-buffer
+      (let ((url-request-method "GET") url-request-data
url-request-extra-headers)
+        (url-retrieve-synchronously (concat url ".go")))
+    (let ((buffer (generate-new-buffer (concat (car (last (split-string url
"/"))) ".go"))))
+      (goto-char (point-min))
+      (re-search-forward "\n\n")
+      (copy-to-buffer buffer (point) (point-max))
+      (kill-buffer)
+      (with-current-buffer buffer
+        (go-mode)
+        (switch-to-buffer buffer)))))
+
+(defun go-propertize-syntax (start end)
+  (save-excursion
+    (goto-char start)
+    (while (search-forward "\\" end t)
+      (put-text-property (1- (point)) (point) 'syntax-table (if (= (char-after) ?`)
'(1) '(9))))))
+
+;; ;; Commented until we actually make use of this function
+;; (defun go--common-prefix (sequences)
+;;   ;; mismatch and reduce are cl
+;;   (assert sequences)
+;;   (flet ((common-prefix (s1 s2)
+;;                         (let ((diff-pos (mismatch s1 s2)))
+;;                           (if diff-pos (subseq s1 0 diff-pos) s1))))
+;;     (reduce #'common-prefix sequences)))
+
+(defun go-import-add (arg import)
+  "Add a new import to the list of imports.
+
+When called with a prefix argument asks for an alternative name
+to import the package as.
+
+If no list exists yet, one will be created if possible.
+
+If an identical import has been commented, it will be
+uncommented, otherwise a new import will be added."
+
+  ;; - If there's a matching `// import "foo"`, uncomment it
+  ;; - If we're in an import() block and there's a matching `"foo"`,
uncomment it
+  ;; - Otherwise add a new import, with the appropriate syntax
+  (interactive
+   (list
+    current-prefix-arg
+    (replace-regexp-in-string "^[\"']\\|[\"']$" ""
(completing-read "Package: " (go--old-completion-list-style (go-packages))))))
+  (save-excursion
+    (let (as line import-start)
+      (if arg
+          (setq as (read-from-minibuffer "Import as: ")))
+      (if as
+          (setq line (format "%s \"%s\"" as import))
+        (setq line (format "\"%s\"" import)))
+
+      (goto-char (point-min))
+      (if (re-search-forward (concat "^[[:space:]]*//[[:space:]]*import " line
"$") nil t)
+          (uncomment-region (line-beginning-position) (line-end-position))
+        (case (go-goto-imports)
+          ('fail (message "Could not find a place to add import."))
+          ('block
+              (save-excursion
+                (re-search-backward "^import (")
+                (setq import-start (point)))
+            (if (re-search-backward (concat "^[[:space:]]*//[[:space:]]*" line
"$")  import-start t)
+                (uncomment-region (line-beginning-position) (line-end-position))
+              (insert "\n\t" line)))
+          ('single (insert "import " line "\n"))
+          ('none (insert "\nimport (\n\t" line "\n)\n")))))))
+
+(defun go-root-and-paths ()
+  (let* ((output (split-string (shell-command-to-string "go env GOROOT GOPATH")
"\n"))
+         (root (car output))
+         (paths (split-string (cadr output) ":")))
+    (append (list root) paths)))
+
+(defun go--string-prefix-p (s1 s2 &optional ignore-case)
+  "Return non-nil if S1 is a prefix of S2.
+If IGNORE-CASE is non-nil, the comparison is case-insensitive."
+  (eq t (compare-strings s1 nil nil
+                         s2 0 (length s1) ignore-case)))
+
+(defun go--directory-dirs (dir)
+  "Recursively return all subdirectories in DIR."
+  (if (file-directory-p dir)
+      (let ((dir (directory-file-name dir))
+            (dirs '())
+            (files (directory-files dir nil nil t)))
+        (dolist (file files)
+          (unless (member file '("." ".."))
+            (let ((file (concat dir "/" file)))
+              (if (file-directory-p file)
+                  (setq dirs (append (cons file
+                                           (go--directory-dirs file))
+                                     dirs))))))
+        dirs)
+    '()))
+
+
+(defun go-packages ()
+  (sort
+   (delete-dups
+    (mapcan
+     (lambda (topdir)
+       (let ((pkgdir (concat topdir "/pkg/")))
+         (mapcan (lambda (dir)
+                   (mapcar (lambda (file)
+                             (let ((sub (substring file (length pkgdir) -2)))
+                               (unless (or (go--string-prefix-p "obj/" sub)
(go--string-prefix-p "tool/" sub))
+                                 (mapconcat 'identity (cdr (split-string sub
"/")) "/"))))
+                           (if (file-directory-p dir)
+                               (directory-files dir t "\\.a$"))))
+                 (if (file-directory-p pkgdir)
+                     (go--directory-dirs pkgdir)))))
+     (go-root-and-paths)))
+   'string<))
+
+(defun go-unused-imports-lines ()
+  ;; FIXME Technically, -o /dev/null fails in quite some cases (on
+  ;; Windows, when compiling from within GOPATH). Practically,
+  ;; however, it has the same end result: There won't be a
+  ;; compiled binary/archive, and we'll get our import errors when
+  ;; there are any.
+  (reverse (remove nil
+                   (mapcar
+                    (lambda (line)
+                      (if (string-match "^\\(.+\\):\\([[:digit:]]+\\): imported and
not used: \".+\"$" line)
+                          (if (string= (file-truename (match-string 1 line))
(file-truename buffer-file-name))
+                              (string-to-number (match-string 2 line)))))
+                    (split-string (shell-command-to-string
+                                   (if (string-match "_test\.go$"
buffer-file-truename)
+                                       "go test -c"
+                                     "go build -o /dev/null"))
"\n")))))
+
+(defun go-remove-unused-imports (arg)
+  "Removes all unused imports. If ARG is non-nil, unused imports
+will be commented, otherwise they will be removed completely."
+  (interactive "P")
+  (save-excursion
+    (let ((cur-buffer (current-buffer)) flymake-state lines)
+      (when (boundp 'flymake-mode)
+        (setq flymake-state flymake-mode)
+        (flymake-mode-off))
+      (save-some-buffers nil (lambda () (equal cur-buffer (current-buffer))))
+      (if (buffer-modified-p)
+          (message "Cannot operate on unsaved buffer")
+        (setq lines (go-unused-imports-lines))
+        (dolist (import lines)
+          (goto-char (point-min))
+          (forward-line (1- import))
+          (beginning-of-line)
+          (if arg
+              (comment-region (line-beginning-position) (line-end-position))
+            (go--kill-whole-line)))
+        (message "Removed %d imports" (length lines)))
+      (if flymake-state (flymake-mode-on)))))
+
+(defun godef--find-file-line-column (specifier)
+  "Given a file name in the format of `filename:line:column',
+visit FILENAME and go to line LINE and column COLUMN."
+  (let* ((components (split-string specifier ":"))
+         (line (string-to-number (nth 1 components)))
+         (column (string-to-number (nth 2 components))))
+    (with-current-buffer (find-file (car components))
+      (goto-char (point-min))
+      (forward-line (1- line))
+      (beginning-of-line)
+      (forward-char (1- column))
+      (if (buffer-modified-p)
+          (message "Buffer is modified, file position might not have been
correct")))))
+
+(defun godef--call (point)
+  "Call godef, acquiring definition position and expression
+description at POINT."
+  (if (go--xemacs-p)
+      (message "godef does not reliably work in XEmacs, expect bad results"))
+  (if (not buffer-file-name)
+      (message "Cannot use godef on a buffer without a file name")
+    (let ((outbuf (get-buffer-create "*godef*")))
+      (with-current-buffer outbuf
+        (erase-buffer))
+      (call-process-region (point-min) (point-max) "godef" nil outbuf nil
"-i" "-t" "-f" (file-truename buffer-file-name)
"-o" (number-to-string (go--position-bytes (point))))
+      (with-current-buffer outbuf
+        (split-string (buffer-substring-no-properties (point-min) (point-max))
"\n")))))
+
+(defun godef-describe (point)
+  "Describe the expression at POINT."
+  (interactive "d")
+  (condition-case nil
+      (let ((description (nth 1 (godef--call point))))
+        (if (string= "" description)
+            (message "No description found for expression at point")
+          (message "%s" description)))
+    (file-error (message "Could not run godef binary"))))
+
+(defun godef-jump (point)
+  "Jump to the definition of the expression at POINT."
+  (interactive "d")
+  (condition-case nil
+      (let ((file (car (godef--call point))))
+        (cond
+         ((string= "-" file)
+          (message "godef: expression is not defined anywhere"))
+         ((string= "godef: no identifier found" file)
+          (message "%s" file))
+         ((go--string-prefix-p "godef: no declaration found for " file)
+          (message "%s" file))
+         (t
+          (push-mark)
+          (godef--find-file-line-column file))))
+    (file-error (message "Could not run godef binary"))))
+
 (provide 'go-mode)
Repository URL: 
https://bitbucket.org/xemacs/prog-modes/
--
This is a commit notification from 
bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
_______________________________________________
XEmacs-Patches mailing list
XEmacs-Patches(a)xemacs.org
http://lists.xemacs.org/mailman/listinfo/xemacs-patches