From bcbd8f7e4e929604bb3dfef9937432cb05b5f648 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Sun, 7 May 2017 09:27:00 +0000 Subject: [PATCH] CC Mode internal cache: Handle a cache pos being inside a two-char construct. Cache c-state-semi-nonlit-pos-cache was failing when a cache position was, e.g., between the two characters of an opening comment "/*", and additionally there were an odd number of quote marks (apostrophes) in the comment. This happened in .../src/xdisp.c in the Emacs master branch around 2017-05-02 at buffer position 615001. * lisp/progmodes/cc-defs.el (c-emacs-features): Repurpose symbol pps-extended-state to mean that there are at least 11 elements in the parser state. * lisp/progmodes/cc-engine.el (c-cache-to-parse-ps-state) (c-parse-ps-state-to-cache): Rewrite these to use enhanced cache element list types which indicate potentially being inside two-char constructs. (c-parse-ps-state-below): Rewrite to use the new versions of the above two functions. --- lisp/progmodes/cc-defs.el | 27 ++++--- lisp/progmodes/cc-engine.el | 155 +++++++++++++++++++++++++----------- 2 files changed, 125 insertions(+), 57 deletions(-) diff --git a/lisp/progmodes/cc-defs.el b/lisp/progmodes/cc-defs.el index 8dd56106af4..dd8f8afc6a3 100644 --- a/lisp/progmodes/cc-defs.el +++ b/lisp/progmodes/cc-defs.el @@ -1914,14 +1914,18 @@ non-nil, a caret is prepended to invert the set." (set-buffer-modified-p nil)) (kill-buffer buf)) - ;; See if `parse-partial-sexp' returns the eighth element. - (if (c-safe (>= (length (save-excursion - (parse-partial-sexp (point) (point)))) - 10)) - (setq list (cons 'pps-extended-state list)) - (error (concat - "CC Mode is incompatible with this version of Emacs - " - "`parse-partial-sexp' has to return at least 10 elements."))) + ;; Check how many elements `parse-partial-sexp' returns. + (let ((ppss-size (or (c-safe (length + (save-excursion + (parse-partial-sexp (point) (point))))) + 0))) + (cond + ((>= ppss-size 11) (setq list (cons 'pps-extended-state list))) + ((>= ppss-size 10)) + (t (error + (concat + "CC Mode is incompatible with this version of Emacs - " + "`parse-partial-sexp' has to return at least 10 elements."))))) ;;(message "c-emacs-features: %S" list) list) @@ -1944,10 +1948,9 @@ might be present: (i.e. the syntax class `!'). `gen-string-delim' Generic string delimiters work (i.e. the syntax class `|'). -`pps-extended-state' `parse-partial-sexp' returns a list with at least 10 - elements, i.e. it contains the position of the start of - the last comment or string. It's always set - CC Mode - no longer works in emacsen without this feature. +`pps-extended-state' `parse-partial-sexp' returns a list with at least 11 + elements, i.e. it indicates having stopped after the + first character of a potential two-char construct. `posix-char-classes' The regexp engine understands POSIX character classes. `col-0-paren' It's possible to turn off the ad-hoc rule that a paren in column zero is the start of a defun. diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 4af7c359747..6d7bab7a65c 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -2575,17 +2575,24 @@ comment at the start of cc-engine.el for more info." (defun c-cache-to-parse-ps-state (elt) ;; Create a list suitable to use as the old-state parameter to - ;; `parse-partial-sexp', out of ELT. ELT is either just a number, a buffer - ;; position, or it is a list (POS TYPE STARTING-POS). Here POS is the - ;; buffer position the other elements are pertinent for, TYPE is either 'c - ;; or 'c++ (for a comment) or a character (for a string delimiter) or t - ;; (meaning a string fence opened the string), STARTING-POS is the starting - ;; position of the comment or string. - (if (consp elt) + ;; `parse-partial-sexp', out of ELT, a member of + ;; `c-state-semi-nonlit-pos-cache'. ELT is either just a number, or a list + ;; with 2, 3, or 4 members (See `c-parse-ps-state-to-cache'). That number + ;; or the car of the list is the "position element" of ELT, the position + ;; where ELT is valid. + ;; + ;; POINT is left at the postition for which the returned state is valid. It + ;; will be either the position element of ELT, or one character before + ;; that. (The latter happens in Emacs <= 25 and XEmacs, when ELT indicates + ;; its position element directly follows a potential first character of a + ;; two char construct (such as a comment opener or an escaped character).) + (if (and (consp elt) (>= (length elt) 3)) + ;; Inside a string or comment (let ((depth 0) (containing nil) (last nil) in-string in-comment (after-quote nil) (min-depth 0) com-style com-str-start (intermediate nil) - (between-syntax nil) + (char-1 (nth 3 elt)) ; first char of poss. 2-char construct + (pos (car elt)) (type (cadr elt))) (setq com-str-start (car (cddr elt))) (cond @@ -2596,28 +2603,88 @@ comment at the start of cc-engine.el for more info." com-style (if (eq type 'c++) 1 nil))) (t (c-benign-error "Invalid type %s in c-cache-to-parse-ps-state" elt))) - (list depth containing last - in-string in-comment after-quote - min-depth com-style com-str-start - intermediate nil)) - (copy-tree '(0 nil nil nil nil nil 0 nil nil nil nil)))) + (if (memq 'pps-extended-state c-emacs-features) + (progn + (goto-char pos) + (list depth containing last + in-string in-comment after-quote + min-depth com-style com-str-start + intermediate char-1)) + (goto-char (if char-1 + (1- pos) + pos)) + (list depth containing last + in-string in-comment nil + min-depth com-style com-str-start + intermediate))) + + ;; Not in a string or comment. + (if (memq 'pps-extended-state c-emacs-features) + (progn + (goto-char (if (consp elt) (car elt) elt)) + (list 0 nil nil nil nil + (and (consp elt) (eq (nth 1 elt) 9)) ; 9 is syntax code for "escape". + 0 nil nil nil + (and (consp elt) (nth 1 elt)))) + (goto-char (if (consp elt) (car elt) elt)) + (if (and (consp elt) (cdr elt)) (backward-char)) + (copy-tree '(0 nil nil nil nil + nil + 0 nil nil nil))))) (defun c-parse-ps-state-to-cache (state) ;; Convert STATE, a `parse-partial-sexp' state valid at POINT, to an element - ;; for the `c-state-semi-nonlit-pos-cache' cache. This is either POINT - ;; (when point is not in a literal) or a list (POINT TYPE STARTING-POS), - ;; where TYPE is the type of the literal, either 'string, 'c, or 'c++, and - ;; STARTING-POS is the starting position of the comment or string. - (cond - ((nth 3 state) ; A string - (list (point) (nth 3 state) (nth 8 state))) - ((and (nth 4 state) ; A comment - (not (eq (nth 7 state) 'syntax-table))) ; but not a psuedo comment. - (list (point) - (if (eq (nth 7 state) 1) 'c++ 'c) - (nth 8 state))) - (t ; Neither string nor comment. - (point)))) + ;; for the `c-state-semi-nonlit-pos-cache' cache. This is one of + ;; o - POINT (when point is not in a literal); + ;; o - (POINT CHAR-1) (when the last character before point is potentially + ;; the first of a two-character construct + ;; o - (POINT TYPE STARTING-POS) (when in a literal); + ;; o - (POINT TYPE STARTING-POS CHAR-1) (Combination of the previous two), + ;; where TYPE is the type of the literal (either 'c, or 'c++, or the + ;; character which closes the string), STARTING-POS is the starting + ;; position of the comment or string. CHAR-1 is either the character + ;; potentially forming the first half of a two-char construct (in Emacs <= + ;; 25 and XEmacs) or the syntax of the character (in Emacs >= 26). + (if (memq 'pps-extended-state c-emacs-features) + ;; Emacs >= 26. + (let ((basic + (cond + ((nth 3 state) ; A string + (list (point) (nth 3 state) (nth 8 state))) + ((and (nth 4 state) ; A comment + (not (eq (nth 7 state) 'syntax-table))) ; but not a psuedo comment. + (list (point) + (if (eq (nth 7 state) 1) 'c++ 'c) + (nth 8 state))) + (t ; Neither string nor comment. + (point))))) + (if (nth 10 state) + (append (if (consp basic) + basic + (list basic)) + (list (nth 10 state))) + basic)) + + ;; Emacs <= 25, XEmacs. + (cond + ((nth 3 state) ; A string + (if (eq (char-before) ?\\) + (list (point) (nth 3 state) (nth 8 state) ?\\) + (list (point) (nth 3 state) (nth 8 state)))) + ((and (nth 4 state) ; comment + (not (eq (nth 7 state) 'syntax-table))) + (if (and (eq (char-before) ?*) + (> (- (point) (nth 8 state)) 2)) ; not "/*/". + (list (point) + (if (eq (nth 7 state) 1) 'c++ 'c) + (nth 8 state) + ?*) + (list (point) + (if (eq (nth 7 state) 1) 'c++ 'c) + (nth 8 state)))) + (t (if (memq (char-before) '(?/ ?\\)) + (list (point) (char-before)) + (point)))))) (defsubst c-ps-state-cache-pos (elt) ;; Get the buffer position from ELT, an element from the cache @@ -2637,7 +2704,7 @@ comment at the start of cc-engine.el for more info." (save-restriction (widen) (let ((c c-state-semi-nonlit-pos-cache) - elt state pos npos high-elt) + elt state npos high-elt) ;; Trim the cache to take account of buffer changes. (while (and c (> (c-ps-state-cache-pos (car c)) c-state-semi-nonlit-pos-cache-limit)) @@ -2647,29 +2714,27 @@ comment at the start of cc-engine.el for more info." (while (and c (> (c-ps-state-cache-pos (car c)) here)) (setq high-elt (car c)) (setq c (cdr c))) - (setq pos (or (and c (c-ps-state-cache-pos (car c))) - (point-min))) + (goto-char (or (and c (c-ps-state-cache-pos (car c))) + (point-min))) + (setq state + (if c + (c-cache-to-parse-ps-state (car c)) + (copy-tree '(0 nil nil nil nil nil 0 nil nil nil nil)))) - (if high-elt - (setq state (c-cache-to-parse-ps-state (car c))) - (setq elt (if c (car c) (point-min))) - (setq state - (if c - (c-cache-to-parse-ps-state (car c)) - (copy-tree '(0 nil nil nil nil nil 0 nil nil nil nil)))) + (when (not high-elt) + ;; We need to extend the cache. Add an element to + ;; `c-state-semi-nonlit-pos-cache' each iteration of the following. (while - ;; Add an element to `c-state-semi-nonlit-pos-cache' each iteration. - (<= (setq npos (+ pos c-state-nonlit-pos-interval)) here) - (setq state (parse-partial-sexp pos npos nil nil state)) + (<= (setq npos (+ (point) c-state-nonlit-pos-interval)) here) + (setq state (parse-partial-sexp (point) npos nil nil state)) (setq elt (c-parse-ps-state-to-cache state)) (setq c-state-semi-nonlit-pos-cache - (cons elt c-state-semi-nonlit-pos-cache)) - (setq pos npos))) + (cons elt c-state-semi-nonlit-pos-cache)))) - (if (> pos c-state-semi-nonlit-pos-cache-limit) - (setq c-state-semi-nonlit-pos-cache-limit pos)) + (if (> (point) c-state-semi-nonlit-pos-cache-limit) + (setq c-state-semi-nonlit-pos-cache-limit (point))) - (cons pos state))))) + (cons (point) state))))) (defun c-state-safe-place (here) ;; Return a buffer position before HERE which is "safe", i.e. outside any -- 2.39.2