aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Fried <ben.fried@gmail.com>2012-03-26 23:26:39 -0400
committerRuss Cox <rsc@golang.org>2012-03-26 23:26:39 -0400
commitbf9620ebbdf6f9dfa2f46e5823f6afc5bfa8206f (patch)
treebefcbdff627408ca065fd1405a6a5f2ac74e1180
parentb485629e476d4069b8294e0b09ea1a43d4b35954 (diff)
downloadgo-bf9620ebbdf6f9dfa2f46e5823f6afc5bfa8206f.tar.gz
go-bf9620ebbdf6f9dfa2f46e5823f6afc5bfa8206f.zip
misc/emacs: fix overindentation caused by mis-parsing lines ending with special chars
Fixes #3313 go-mode-backward-skip-comments is probably due for a more ambitious refactoring --- it repeats guard conditions after every nearly every movement of point. R=sameer, r CC=golang-dev https://golang.org/cl/5844063
-rw-r--r--misc/emacs/go-mode.el203
1 files changed, 179 insertions, 24 deletions
diff --git a/misc/emacs/go-mode.el b/misc/emacs/go-mode.el
index 1af38e3d0e..33ee7022fd 100644
--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -110,6 +110,7 @@ built-ins, functions, and some types.")
(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)
@@ -161,6 +162,18 @@ 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.")
@@ -180,6 +193,24 @@ nesting caches from the modified point on."
(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))))
@@ -237,7 +268,7 @@ directly; use `go-mode-cs'."
(cond
((looking-at "//")
(end-of-line)
- (point))
+ (1+ (point)))
((looking-at "/\\*")
(goto-char (+ pos 2))
(if (search-forward "*/" (1+ end) t)
@@ -273,7 +304,114 @@ directly; use `go-mode-cs'."
(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,
@@ -406,21 +544,31 @@ token on the line."
(when (/= (skip-chars-backward "[:word:]_") 0)
(not (looking-at go-mode-non-terminating-keywords-regexp)))))))
+(defun go-mode-whitespace-p (char)
+ "Is char whitespace in the syntax table for go."
+ (eq 32 (char-syntax char)))
+
(defun go-mode-backward-skip-comments ()
"Skip backward over comments and whitespace."
- (when (not (bobp))
- (backward-char))
- (while (and (not (bobp))
- (or (eq 32 (char-syntax (char-after (point))))
- (go-mode-cs)))
- (skip-syntax-backward "-")
- (when (and (not (bobp)) (eq 32 (char-syntax (char-after (point)))))
- (backward-char))
- (when (go-mode-cs)
- (let ((pos (previous-single-property-change (point) 'go-mode-cs)))
- (if pos (goto-char pos) (goto-char (point-min))))))
- (when (and (not (go-mode-cs)) (eq 32 (char-syntax (char-after (1+ (point))))))
- (forward-char 1)))
+ ;; 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.
@@ -467,10 +615,10 @@ indented one level."
(incf indent tab-width))
((?\()
(goto-char (car nest))
- (beginning-of-line)
(go-mode-backward-skip-comments)
+ (backward-char)
;; Really just want the token before
- (when (looking-back "\\<import\\|const\\|var\\|type"
+ (when (looking-back "\\<import\\|const\\|var\\|type\\|package"
(max (- (point) 7) (point-min)))
(incf indent tab-width)
(when first
@@ -481,9 +629,13 @@ indented one 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)
- (go-mode-backward-skip-comments)
+ (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
@@ -517,12 +669,15 @@ indented one level."
"Indent the current line according to `go-mode-indentation'."
(interactive)
- (let ((col (go-mode-indentation)))
- (when col
- (let ((offset (- (current-column) (current-indentation))))
- (indent-line-to col)
- (when (> offset 0)
- (forward-char offset))))))
+ ;; 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