APPROVE COMMIT
NOTE: This patch has been committed.
# HG changeset patch
# User Mats Lidell <matsl(a)xemacs.org>
# Date 1336937625 -7200
# Node ID a246dff1f05567a1054fc4d870c9f7ea6db09705
# Parent 9a5e5e7332618c86410233a50441d7a58cc62848
Added go-mode.el
diff -r 9a5e5e733261 -r a246dff1f055 ChangeLog
--- a/ChangeLog Fri Dec 23 11:41:10 2011 -0500
+++ b/ChangeLog Sun May 13 21:33:45 2012 +0200
@@ -1,3 +1,8 @@
+2012-05-13 Mats Lidell <matsl(a)xemacs.org>
+
+ * prog-modes.texi (go-mode): New mode for Go source.
+ * go-mode.el: New.
+
2011-12-22 Vin Shelton <acs(a)xemacs.org>
* verilog-mode.el (verilog-mode-version):
diff -r 9a5e5e733261 -r a246dff1f055 Makefile
--- a/Makefile Fri Dec 23 11:41:10 2011 -0500
+++ b/Makefile Sun May 13 21:33:45 2012 +0200
@@ -34,7 +34,7 @@
postscript.elc prolog.elc rexx-mode.elc simula.elc \
sql.elc tcl.elc teco.elc verilog-mode.elc vrml-mode.elc p4.elc \
old-c-mode.elc php-mode.elc javascript-mode.elc \
- rpm-spec-mode.elc uil-mode.elc
+ rpm-spec-mode.elc uil-mode.elc go-mode.elc
STANDARD_DOCS = t
diff -r 9a5e5e733261 -r a246dff1f055 go-mode.el
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/go-mode.el Sun May 13 21:33:45 2012 +0200
@@ -0,0 +1,860 @@
+;;; go-mode.el --- Major mode for the Go programming language
+
+;; Copyright (c) 2012 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
+;; met:
+
+;; * Redistributions of source code must retain the above copyright
+;; notice, this list of conditions and the following disclaimer.
+;; * Redistributions in binary form must reproduce the above
+;; copyright notice, this list of conditions and the following disclaimer
+;; in the documentation and/or other materials provided with the
+;; distribution.
+;; * Neither the name of Google Inc. nor the names of its
+;; contributors may be used to endorse or promote products derived from
+;; this software without specific prior written permission.
+
+;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+;; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+;; OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+;; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+;; (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 13182:9d6693456f93
+
+;;; Commentary:
+
+;; For installation instructions, see go-mode-load.el
+
+;;; To do:
+
+;; * 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
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+
+(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)
+ (modify-syntax-entry ?^ "." st)
+ (modify-syntax-entry ?! "." st)
+ (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 ?\n "> b" 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.")
+
+(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.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Key map
+;;
+
+(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)
+ m)
+ "Keymap used by Go mode to implement electric keys.")
+
+(defun go-mode-insert-and-indent (key)
+ "Invoke the global binding of KEY, then reindent the line."
+
+ (interactive (list (this-command-keys)))
+ (call-interactively (lookup-key (current-global-map) key))
+ (indent-according-to-mode))
+
+(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)
+
+(defun go-mode-delayed-electric (p)
+ "Perform electric insertion, but delayed by one event.
+
+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."
+
+ (interactive "p")
+ (self-insert-command p)
+ (setq go-mode-delayed-point (point)))
+
+(defun go-mode-delayed-electric-hook (b e l)
+ "An after-change-function that implements `go-mode-delayed-electric'."
+
+ (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))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Parser
+;;
+
+(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)
+
+(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)
+
+(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)
+
+(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-mode-mark-clear-cache (b e)
+ "A before-change-function that clears the comment/string and
+nesting caches from the modified point on."
+
+ (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)))
+
+ (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)))
+
+ (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))))
+
+(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 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 cs
+ (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 "\\s\"")))
+ ;; 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))
+ (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)))))))
+
+(defun go-mode-indent-line ()
+ "Indent the current line according to `go-mode-indentation'."
+ (interactive)
+
+ ;; 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)))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Go mode
+;;
+
+;;;###autoload
+(define-derived-mode go-mode nil "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."
+
+ ;; 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)
+ (remove-text-properties 1 (point-max)
+ '(go-mode-cs nil go-mode-nesting nil)))
+
+ ;; 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)
+
+ ;; Indentation
+ (set (make-local-variable 'indent-line-function)
+ #'go-mode-indent-line)
+ (add-hook 'after-change-functions #'go-mode-delayed-electric-hook nil t)
+
+ ;; Comments
+ (set (make-local-variable 'comment-start) "// ")
+ (set (make-local-variable 'comment-end) "")
+
+ ;; Go style
+ (setq indent-tabs-mode t))
+
+;;;###autoload
+(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."
+
+ (interactive)
+ (unload-feature 'go-mode)
+ (require 'go-mode)
+ (go-mode))
+
+;;;###autoload
+(defun gofmt ()
+ "Pipe the current buffer through the external tool `gofmt`.
+Replace the current buffer on success; display errors on failure."
+
+ (interactive)
+ (let ((currconf (current-window-configuration)))
+ (let ((srcbuf (current-buffer)))
+ (with-temp-buffer
+ (let ((outbuf (current-buffer))
+ (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 (erase-buffer))
+ (with-current-buffer srcbuf
+ (save-restriction
+ (let (deactivate-mark)
+ (widen)
+ (if (= 0 (shell-command-on-region (point-min) (point-max)
"gofmt"
+ outbuf nil errbuf))
+ ;; restore window config
+ ;; gofmt succeeded: replace the current buffer with outbuf,
+ ;; restore the mark and point, and discard errbuf.
+ (let ((old-mark (mark t))
+ (old-point (point))
+ (old-start (window-start)))
+ (erase-buffer)
+ (insert-buffer-substring outbuf)
+ (set-window-configuration currconf)
+ (set-window-start (selected-window) (min old-start (point-max)))
+ (goto-char (min old-point (point-max)))
+ (if old-mark (push-mark (min old-mark (point-max)) t))
+ (kill-buffer errbuf))
+
+ ;; gofmt failed: display the errors
+ (display-buffer errbuf)))))
+
+ ;; Collapse any window opened on outbuf if shell-command-on-region
+ ;; displayed it.
+ (delete-windows-on outbuf))))))
+
+;;;###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)"
+
+ (interactive)
+ (when (eq major-mode 'go-mode) (gofmt)))
+
+(defun godoc-read-query ()
+ "Read a godoc query from the minibuffer."
+ ;; Compute the default query as the symbol under the cursor.
+ ;; TODO: This does the wrong thing for e.g. multipart.NewReader (it only grabs
+ ;; half) but I see no way to disambiguate that from e.g. foobar.SomeMethod.
+ (let* ((bounds (bounds-of-thing-at-point 'symbol))
+ (symbol (if bounds
+ (buffer-substring-no-properties (car bounds)
+ (cdr bounds)))))
+ (read-string (if symbol
+ (format "godoc (default %s): " symbol)
+ "godoc: ")
+ nil nil symbol)))
+
+(defun godoc-get-buffer (query)
+ "Get an empty buffer for a godoc query."
+ (let* ((buffer-name (concat "*godoc " query "*"))
+ (buffer (get-buffer buffer-name)))
+ ;; Kill the existing buffer if it already exists.
+ (when buffer (kill-buffer buffer))
+ (get-buffer-create buffer-name)))
+
+(defun godoc-buffer-sentinel (proc event)
+ "Sentinel function run when godoc command completes."
+ (with-current-buffer (process-buffer proc)
+ (cond ((string= event "finished\n") ;; Successful exit.
+ (goto-char (point-min))
+ (display-buffer (current-buffer) 'not-this-window))
+ ((not (= (process-exit-status proc) 0)) ;; Error exit.
+ (let ((output (buffer-string)))
+ (kill-buffer (current-buffer))
+ (message (concat "godoc: " output)))))))
+
+;;;###autoload
+(defun godoc (query)
+ "Show go documentation for a query, much like M-x man."
+ (interactive (list (godoc-read-query)))
+ (unless (string= query "")
+ (set-process-sentinel
+ (start-process-shell-command "godoc" (godoc-get-buffer query)
+ (concat "godoc " query))
+ 'godoc-buffer-sentinel)
+ nil))
+
+(provide 'go-mode)
diff -r 9a5e5e733261 -r a246dff1f055 prog-modes.texi
--- a/prog-modes.texi Fri Dec 23 11:41:10 2011 -0500
+++ b/prog-modes.texi Sun May 13 21:33:45 2012 +0200
@@ -3,6 +3,7 @@
@c Copyright (C) 2002, 2003 Free Software Foundation, Inc.
@c Copyright (C) 2003 Jake Colman <jake.colman(a)xemacs.org>
@c Copyright (C) 2004 Ville Skytt~ <scop(a)xemacs.org>
+@c Copyright (C) 2012 Mats Lidell <matsl(a)xemacs.org>
@c
@c @setfilename prog-modes.info
@settitle Programming Modes for XEmacs
@@ -67,6 +68,7 @@
* cvs:: Light CVS Support
* diff-mode:: Viewing and Editing Context Diffs
* eiffel:: Editing Eiffel Code
+* go-mode:: Editing Go Code
* icon:: Editing Icon Code
* javascript-mode:: Editing JavaScript Code
* ksh-mode:: Editing Shell Script (sh, ksh, bash) Code
@@ -388,7 +390,7 @@
M-x customize-group RET diff-mode RET
@end example
-@node eiffel, icon, diff-mode, Top
+@node eiffel, go-mode, diff-mode, Top
@chapter Editing Eiffel Code
This mode is used for editing Eiffel code. It is automatically invoked for
@@ -433,7 +435,61 @@
@uref{http://www.engin.umd.umich.edu/CIS/course.des/cis400/eiffel/eiffel.html,the
Eiffel Web Site}.
-@node icon, javascript-mode, eiffel, Top
+@node go-mode, icon, eiffel, Top
+
+@chapter Editing Go Code
+
+This mode is used for editing Go code. It is automatically invoked for
+buffers visiting any file ending in an extension of @file{.go}.
+
+Turning on Go mode runs the hook @code{go-mode-hook} at the end of
+initialization.
+
+The mode provides the following features:
+
+@itemize @bullet
+@item Basic syntax highlighting for keywords, built-ins, functions and
+some types
+@item Tab indents for Go code. Identation is almost identical to gofmt
+@end itemize
+
+The following key mappings are defined:
+
+@multitable {M-backspace } {backward-delete-char-untabify}
+@item @code{)} @tab go-mode-insert-and-indent
+@item @code{,} @tab go-mode-insert-and-indent
+@item @code{:} @tab go-mode-delayed-electric
+@item @code{=} @tab go-mode-insert-and-indent
+@item @code{@}} @tab go-mode-insert-and-indent
+@end multitable
+
+Other useful functions are:
+
+@table @code
+@item gofmt
+Uses the gofmt tool to format the current buffer. The current buffer
+is replaced on success. On failure an error message is displayed.
+
+@item godoc
+Show Go documentation for a query in a style similar to M-x man.
+
+@end table
+
+@menu
+* About Go: about-go.
+@end menu
+
+@node about-go, , go-mode, go-mode
+@section About Go
+
+Go is a high-level, general-purpose programming language that is
+compiled, uses garbage collection and has strong support for
+concurrent programming. It is open source and developed by Google Inc.
+
+For more information on Go see
+@uref{http://www.golang.org,The Go Programming Language}.
+
+@node icon, javascript-mode, go-mode, Top
@chapter Editing Icon Code
This mode is used for editing Icon code. It is automatically invoked for
Yours
--
%% Mats
_______________________________________________
XEmacs-Patches mailing list
XEmacs-Patches(a)xemacs.org
http://lists.xemacs.org/mailman/listinfo/xemacs-patches