mirror of https://github.com/golang/go.git
go-mode.el: fix syntax highlighting of backticks
Instead of syntax-tables, an extended go-mode-cs is used for from a font-lock callback. Cache invalidation must happen in a before-change-function because font-lock runs in an after-change-function, potentially before the cache invalidation takes place. Performance is reasonable, even with src/pkg/html/entity.go and test/fixedbugs/bug257.go. Fixes #2330. R=golang-dev, rsc CC=golang-dev https://golang.org/cl/5529045
This commit is contained in:
parent
8a4bd094a0
commit
70ed0ac588
|
|
@ -44,17 +44,11 @@
|
||||||
(modify-syntax-entry ?< "." st)
|
(modify-syntax-entry ?< "." st)
|
||||||
(modify-syntax-entry ?> "." st)
|
(modify-syntax-entry ?> "." st)
|
||||||
|
|
||||||
;; Strings
|
;; Strings and comments are font-locked separately.
|
||||||
(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)
|
||||||
|
|
||||||
;; Comments
|
|
||||||
(modify-syntax-entry ?/ ". 124b" st)
|
|
||||||
(modify-syntax-entry ?* ". 23" st)
|
|
||||||
(modify-syntax-entry ?\n "> b" st)
|
|
||||||
(modify-syntax-entry ?\^m "> b" st)
|
|
||||||
|
|
||||||
st)
|
st)
|
||||||
"Syntax table for Go mode.")
|
"Syntax table for Go mode.")
|
||||||
|
|
@ -74,7 +68,9 @@ some syntax analysis.")
|
||||||
(constants '("nil" "true" "false" "iota"))
|
(constants '("nil" "true" "false" "iota"))
|
||||||
(type-name "\\s *\\(?:[*(]\\s *\\)*\\(?:\\w+\\s *\\.\\s *\\)?\\(\\w+\\)")
|
(type-name "\\s *\\(?:[*(]\\s *\\)*\\(?:\\w+\\s *\\.\\s *\\)?\\(\\w+\\)")
|
||||||
)
|
)
|
||||||
`((,(regexp-opt go-mode-keywords 'words) . font-lock-keyword-face)
|
`((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 builtins 'words) . font-lock-builtin-face)
|
||||||
(,(regexp-opt constants 'words) . font-lock-constant-face)
|
(,(regexp-opt constants 'words) . font-lock-constant-face)
|
||||||
;; Function names in declarations
|
;; Function names in declarations
|
||||||
|
|
@ -165,27 +161,25 @@ will be marked from the beginning up to this point (that is, up
|
||||||
to and including character (1- go-mode-mark-cs-end)).")
|
to and including character (1- go-mode-mark-cs-end)).")
|
||||||
(make-variable-buffer-local 'go-mode-mark-cs-end)
|
(make-variable-buffer-local 'go-mode-mark-cs-end)
|
||||||
|
|
||||||
(defvar go-mode-mark-cs-state nil
|
|
||||||
"The `parse-partial-sexp' state of the comment/string parser as
|
|
||||||
of the point `go-mode-mark-cs-end'.")
|
|
||||||
(make-variable-buffer-local 'go-mode-mark-cs-state)
|
|
||||||
|
|
||||||
(defvar go-mode-mark-nesting-end 1
|
(defvar go-mode-mark-nesting-end 1
|
||||||
"The point at which the nesting cache ends. The buffer will be
|
"The point at which the nesting cache ends. The buffer will be
|
||||||
marked from the beginning up to this point.")
|
marked from the beginning up to this point.")
|
||||||
(make-variable-buffer-local 'go-mode-mark-nesting-end)
|
(make-variable-buffer-local 'go-mode-mark-nesting-end)
|
||||||
|
|
||||||
(defun go-mode-mark-clear-cache (b e l)
|
(defun go-mode-mark-clear-cache (b e)
|
||||||
"An after-change-function that clears the comment/string and
|
"A before-change-function that clears the comment/string and
|
||||||
nesting caches from the modified point on."
|
nesting caches from the modified point on."
|
||||||
|
|
||||||
(save-restriction
|
(save-restriction
|
||||||
(widen)
|
(widen)
|
||||||
(when (< b go-mode-mark-cs-end)
|
(when (<= b go-mode-mark-cs-end)
|
||||||
(remove-text-properties b (min go-mode-mark-cs-end (point-max)) '(go-mode-cs nil))
|
;; Remove the property adjacent to the change position.
|
||||||
(setq go-mode-mark-cs-end b
|
;; It may contain positions pointing beyond the new end mark.
|
||||||
go-mode-mark-cs-state nil))
|
(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-nesting-end)
|
(when (< b go-mode-mark-nesting-end)
|
||||||
(remove-text-properties b (min go-mode-mark-nesting-end (point-max)) '(go-mode-nesting nil))
|
(remove-text-properties b (min go-mode-mark-nesting-end (point-max)) '(go-mode-nesting nil))
|
||||||
(setq go-mode-mark-nesting-end b))))
|
(setq go-mode-mark-nesting-end b))))
|
||||||
|
|
@ -210,7 +204,7 @@ context-sensitive."
|
||||||
(progn ,@body)
|
(progn ,@body)
|
||||||
(set-buffer-modified-p ,modified-var)))))))
|
(set-buffer-modified-p ,modified-var)))))))
|
||||||
|
|
||||||
(defsubst go-mode-cs (&optional pos)
|
(defun go-mode-cs (&optional pos)
|
||||||
"Return the comment/string state at point POS. If point is
|
"Return the comment/string state at point POS. If point is
|
||||||
inside a comment or string (including the delimiters), this
|
inside a comment or string (including the delimiters), this
|
||||||
returns a pair (START . END) indicating the extents of the
|
returns a pair (START . END) indicating the extents of the
|
||||||
|
|
@ -218,45 +212,111 @@ comment or string."
|
||||||
|
|
||||||
(unless pos
|
(unless pos
|
||||||
(setq pos (point)))
|
(setq pos (point)))
|
||||||
(if (= pos 1)
|
(when (> pos go-mode-mark-cs-end)
|
||||||
nil
|
(go-mode-mark-cs pos))
|
||||||
(when (> pos go-mode-mark-cs-end)
|
(get-text-property pos 'go-mode-cs))
|
||||||
(go-mode-mark-cs pos))
|
|
||||||
(get-text-property (- pos 1) 'go-mode-cs)))
|
|
||||||
|
|
||||||
(defun go-mode-mark-cs (end)
|
(defun go-mode-mark-cs (end)
|
||||||
"Mark comments and strings up to point END. Don't call this
|
"Mark comments and strings up to point END. Don't call this
|
||||||
directly; use `go-mode-cs'."
|
directly; use `go-mode-cs'."
|
||||||
|
|
||||||
(setq end (min end (point-max)))
|
(setq end (min end (point-max)))
|
||||||
(go-mode-parser
|
(go-mode-parser
|
||||||
(let* ((pos go-mode-mark-cs-end)
|
(save-match-data
|
||||||
(state (or go-mode-mark-cs-state (syntax-ppss pos))))
|
(let ((pos
|
||||||
;; Mark comments and strings
|
;; Back up to the last known state.
|
||||||
(when (nth 8 state)
|
(let ((last-cs
|
||||||
;; Get to the beginning of the comment/string
|
(and (> go-mode-mark-cs-end 1)
|
||||||
(setq pos (nth 8 state)
|
(get-text-property (1- go-mode-mark-cs-end)
|
||||||
state nil))
|
'go-mode-cs))))
|
||||||
(while (> end pos)
|
(if last-cs
|
||||||
;; Find beginning of comment/string
|
(car last-cs)
|
||||||
(while (and (> end pos)
|
(max 1 (1- go-mode-mark-cs-end))))))
|
||||||
(progn
|
(while (< pos end)
|
||||||
(setq state (parse-partial-sexp pos end nil nil state 'syntax-table)
|
(goto-char pos)
|
||||||
pos (point))
|
(let ((cs-end ; end of the text property
|
||||||
(not (nth 8 state)))))
|
(cond
|
||||||
;; Find end of comment/string
|
((looking-at "//")
|
||||||
(let ((start (nth 8 state)))
|
(end-of-line)
|
||||||
(when start
|
(point))
|
||||||
(setq state (parse-partial-sexp pos (point-max) nil nil state 'syntax-table)
|
((looking-at "/\\*")
|
||||||
pos (point))
|
(goto-char (+ pos 2))
|
||||||
;; Mark comment
|
(if (search-forward "*/" (1+ end) t)
|
||||||
(put-text-property start (- pos 1) 'go-mode-cs (cons start pos))
|
(point)
|
||||||
(when nil
|
end))
|
||||||
(put-text-property start (- pos 1) 'face
|
((looking-at "\"")
|
||||||
`((:background "midnight blue")))))))
|
(goto-char (1+ pos))
|
||||||
;; Update state
|
(if (looking-at "[^\"\n\\\\]*\\(\\\\.[^\"\n\\\\]*\\)*\"")
|
||||||
(setq go-mode-mark-cs-end pos
|
(match-end 0)
|
||||||
go-mode-mark-cs-state state))))
|
(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-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) (cdr cs) (current-buffer)))
|
||||||
|
(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)
|
(defsubst go-mode-nesting (&optional pos)
|
||||||
"Return the nesting at point POS. The nesting is a list
|
"Return the nesting at point POS. The nesting is a list
|
||||||
|
|
@ -470,9 +530,8 @@ functions, and some types. It also provides indentation that is
|
||||||
|
|
||||||
;; Reset the syntax mark caches
|
;; Reset the syntax mark caches
|
||||||
(setq go-mode-mark-cs-end 1
|
(setq go-mode-mark-cs-end 1
|
||||||
go-mode-mark-cs-state nil
|
|
||||||
go-mode-mark-nesting-end 1)
|
go-mode-mark-nesting-end 1)
|
||||||
(add-hook 'after-change-functions #'go-mode-mark-clear-cache nil t)
|
(add-hook 'before-change-functions #'go-mode-mark-clear-cache nil t)
|
||||||
|
|
||||||
;; Indentation
|
;; Indentation
|
||||||
(set (make-local-variable 'indent-line-function)
|
(set (make-local-variable 'indent-line-function)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue