diff options
| author | Micah Chalmer <micah@micahchalmer.net> | 2015-07-25 17:21:44 -0400 |
|---|---|---|
| committer | Micah Chalmer <micah@micahchalmer.net> | 2015-07-30 21:04:37 -0400 |
| commit | 99b128c6d0e7dc8af6ae3430364f3c92d2686009 (patch) | |
| tree | 68097a79c05d3cdd03d4dbd4114c890446e96ce5 | |
| parent | ee564d555f53c94c7a36ab1ef1d9df2b7d6f3bf8 (diff) | |
| download | rust-mode-99b128c6d0e7dc8af6ae3430364f3c92d2686009.tar.gz | |
Fix slowness in angle bracket matching
The problem was caused by the logic that made it refontify the whole
buffer when "font-lock-syntactically-fontified" was set to a position
before the start of the region to be potentially fontified.
Unfortunately that variable is not reliably set when fontifying a large
buffer. Fortunately, the new logic is much simpler, and font-lock
already takes care of ensuring that everything before font-lock-beg was
syntactically fontified.
The other problem was calling syntax-ppss on positions known not to be
fontified yet.
This fixes both of these issues, and the angle bracket matching now
works on larger buffers without pausing on every keystroke.
| -rw-r--r-- | rust-mode.el | 196 |
1 files changed, 99 insertions, 97 deletions
diff --git a/rust-mode.el b/rust-mode.el index e451dfa..9e20aa2 100644 --- a/rust-mode.el +++ b/rust-mode.el @@ -428,24 +428,18 @@ part of it. Adjusts to include the r[#] of a raw string as well." - (let ((orig-beg font-lock-beg) - (orig-end font-lock-end)) - (cond - ;; If we are not syntactically fontified yet, we cannot correctly cover - ;; anything less than the full buffer. The syntactic fontification - ;; modifies the syntax, so until it's done we can't use the syntax to - ;; determine what to fontify. - ((< (or font-lock-syntactically-fontified 0) font-lock-end) - (setq font-lock-beg 1) - (setq font-lock-end (buffer-end 1))) - - ((let* ((beg-ppss (syntax-ppss font-lock-beg)) - (beg-in-cmnt (and (nth 4 beg-ppss) (nth 8 beg-ppss))) - (beg-in-str (nth 3 beg-ppss)) - (end-ppss (syntax-ppss font-lock-end)) - (end-in-str (nth 3 end-ppss))) - - (when (and beg-in-str (> font-lock-beg (nth 8 beg-ppss))) + (save-excursion + (let ((orig-beg font-lock-beg) + (orig-end font-lock-end)) + + (let* + ;; It's safe to call `syntax-ppss' here on positions that are + ;; already syntactically fontified + ((beg-ppss (syntax-ppss font-lock-beg)) + (beg-in-cmnt (and beg-ppss (nth 4 beg-ppss) (nth 8 beg-ppss))) + (beg-in-str (and beg-ppss (nth 3 beg-ppss) (nth 8 beg-ppss)))) + + (when (and beg-in-str (>= font-lock-beg beg-in-str)) (setq font-lock-beg (nth 8 beg-ppss)) (while (equal ?# (char-before font-lock-beg)) (setq font-lock-beg (1- font-lock-beg))) @@ -453,18 +447,24 @@ (setq font-lock-beg (1- font-lock-beg)))) (when (and beg-in-cmnt (> font-lock-beg beg-in-cmnt)) - (setq font-lock-beg beg-in-cmnt)) - - (when end-in-str - (save-excursion - (goto-char (nth 8 end-ppss)) - (ignore-errors (forward-sexp)) - (setq font-lock-end (max font-lock-end (point))))) - ))) - - (or (/= font-lock-beg orig-beg) - (/= font-lock-end orig-end)) - )) + (setq font-lock-beg beg-in-cmnt))) + + ;; We need to make sure that if the region ends inside a raw string, we + ;; extend it out past the end of it. But we can't use `syntax-ppss' to + ;; detect that, becaue that depends on font-lock already being done, and we + ;; are trying to figure out how much to font-lock before that. So we use + ;; the regexp directly. + (save-match-data + (goto-char font-lock-beg) + (while (and (< (point) font-lock-end) + (re-search-forward rust-re-non-standard-string (buffer-end 1) t) + (<= (match-beginning 0) font-lock-end)) + (setq font-lock-end (max font-lock-end (match-end 0))) + (goto-char (1+ (match-beginning 0))))) + + (or (/= font-lock-beg orig-beg) + (/= font-lock-end orig-end)) + ))) (defun rust-conditional-re-search-forward (regexp bound condition) ;; Search forward for regexp (with bound). If found, call condition and return the found @@ -492,77 +492,79 @@ (set-match-data (nth 1 ret-list)) (nth 0 ret-list)))) +(defconst rust-re-non-standard-string + (rx + (or + ;; Raw string: if it matches, it ends up with the starting character + ;; of the string as group 1, any ending backslashes as group 4, and + ;; the ending character as either group 5 or group 6. + (seq + ;; The "r" starts the raw string. Capture it as group 1 to mark it as such syntactically: + (group "r") + + ;; Then either: + (or + ;; a sequence at least one "#" (followed by quote). Capture all + ;; but the last "#" as group 2 for this case. + (seq (group (* "#")) "#\"") + + ;; ...or a quote without any "#". Capture it as group 3. This is + ;; used later to match the opposite quote only if this capture + ;; occurred + (group "\"")) + + ;; The contents of the string: + (*? anything) + + ;; If there are any backslashes at the end of the string, capture + ;; them as group 4 so we can suppress the normal escape syntax + ;; parsing: + (group (* "\\")) + + ;; Then the end of the string--the backreferences ensure that we + ;; only match the kind of ending that corresponds to the beginning + ;; we had: + (or + ;; There were "#"s - capture the last one as group 5 to mark it as + ;; the end of the string: + (seq "\"" (backref 2) (group "#")) + + ;; No "#"s - capture the ending quote (using a backref to group 3, + ;; so that we can't match a quote if we had "#"s) as group 6 + (group (backref 3)))) + + ;; Character literal: match the beginning ' of a character literal + ;; as group 7, and the ending one as group 8 + (seq + (group "'") + (or + (seq + "\\" + (or + (: "U" (= 8 xdigit)) + (: "u" (= 4 xdigit)) + (: "x" (= 2 xdigit)) + (any "'nrt0\"\\"))) + (not (any "'\\")) + ) + (group "'")) + ) + )) + (defun rust-look-for-non-standard-string (bound) ;; Find a raw string or character literal, but only if it's not in the middle ;; of another string or a comment. - (let* ((non-standard-str-regexp - (rx - (or - ;; Raw string: if it matches, it ends up with the starting character - ;; of the string as group 1, any ending backslashes as group 4, and - ;; the ending character as either group 5 or group 6. - (seq - ;; The "r" starts the raw string. Capture it as group 1 to mark it as such syntactically: - (group "r") - - ;; Then either: - (or - ;; a sequence at least one "#" (followed by quote). Capture all - ;; but the last "#" as group 2 for this case. - (seq (group (* "#")) "#\"") - - ;; ...or a quote without any "#". Capture it as group 3. This is - ;; used later to match the opposite quote only if this capture - ;; occurred - (group "\"")) - - ;; The contents of the string: - (*? anything) - - ;; If there are any backslashes at the end of the string, capture - ;; them as group 4 so we can suppress the normal escape syntax - ;; parsing: - (group (* "\\")) - - ;; Then the end of the string--the backreferences ensure that we - ;; only match the kind of ending that corresponds to the beginning - ;; we had: - (or - ;; There were "#"s - capture the last one as group 5 to mark it as - ;; the end of the string: - (seq "\"" (backref 2) (group "#")) - - ;; No "#"s - capture the ending quote (using a backref to group 3, - ;; so that we can't match a quote if we had "#"s) as group 6 - (group (backref 3)))) - - ;; Character literal: match the beginning ' of a character literal - ;; as group 7, and the ending one as group 8 - (seq - (group "'") - (or - (seq - "\\" - (or - (: "U" (= 8 xdigit)) - (: "u" (= 4 xdigit)) - (: "x" (= 2 xdigit)) - (any "'nrt0\"\\"))) - (not (any "'\\")) - ) - (group "'")) - ) - ))) - (rust-conditional-re-search-forward - non-standard-str-regexp bound - (lambda () - (let ((pstate (syntax-ppss (match-beginning 0)))) - (not - (or - (nth 4 pstate) ;; Skip if in a comment - (and (nth 3 pstate) (wholenump (nth 8 pstate)) (< (nth 8 pstate) (match-beginning 0))) ;; Skip if in a string that isn't starting here - ))))))) + (rust-conditional-re-search-forward + rust-re-non-standard-string + bound + (lambda () + (let ((pstate (syntax-ppss (match-beginning 0)))) + (not + (or + (nth 4 pstate) ;; Skip if in a comment + (and (nth 3 pstate) (wholenump (nth 8 pstate)) (< (nth 8 pstate) (match-beginning 0))) ;; Skip if in a string that isn't starting here + )))))) (defun rust-syntax-class-before-point () (when (> (point) 1) |
