From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.devel Subject: Re: Slow fontification in C mode buffers Date: Sat, 3 Dec 2011 21:15:29 +0000 Message-ID: <20111203211529.GC4566@acm.acm> References: <83vcpxbxn0.fsf@gnu.org> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: dough.gmane.org 1322947298 9051 80.91.229.12 (3 Dec 2011 21:21:38 GMT) X-Complaints-To: usenet@dough.gmane.org NNTP-Posting-Date: Sat, 3 Dec 2011 21:21:38 +0000 (UTC) Cc: emacs-devel@gnu.org To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sat Dec 03 22:21:28 2011 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([140.186.70.17]) by lo.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1RWx1T-0004bo-1N for ged-emacs-devel@m.gmane.org; Sat, 03 Dec 2011 22:21:27 +0100 Original-Received: from localhost ([::1]:56891 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RWx1P-0006qV-8i for ged-emacs-devel@m.gmane.org; Sat, 03 Dec 2011 16:21:23 -0500 Original-Received: from eggs.gnu.org ([140.186.70.92]:41853) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RWx1L-0006qQ-4G for emacs-devel@gnu.org; Sat, 03 Dec 2011 16:21:20 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RWx1H-00054Z-TC for emacs-devel@gnu.org; Sat, 03 Dec 2011 16:21:19 -0500 Original-Received: from colin.muc.de ([193.149.48.1]:14792 helo=mail.muc.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RWx1H-00054L-Ey for emacs-devel@gnu.org; Sat, 03 Dec 2011 16:21:15 -0500 Original-Received: (qmail 79316 invoked by uid 3782); 3 Dec 2011 21:21:13 -0000 Original-Received: from acm.muc.de (pD951A68E.dip.t-dialin.net [217.81.166.142]) by colin.muc.de (tmda-ofmipd) with ESMTP; Sat, 03 Dec 2011 22:21:11 +0100 Original-Received: (qmail 18942 invoked by uid 1000); 3 Dec 2011 21:15:29 -0000 Content-Disposition: inline In-Reply-To: <83vcpxbxn0.fsf@gnu.org> User-Agent: Mutt/1.5.21 (2010-09-15) X-Delivery-Agent: TMDA/1.1.12 (Macallan) X-Primary-Address: acm@muc.de X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 193.149.48.1 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:146471 Archived-At: Hello again, Eli! On Sat, Dec 03, 2011 at 03:19:47PM +0200, Eli Zaretskii wrote: > Did anyone else notice that scrolling the first time through a very > large comment in C mode became extremely slow lately? For example, > visit xdisp.c in "emacs -Q", type M-<, then hit C-v several times in > quick succession. Then watch in disbelief how long it takes for Emacs > to scroll by these several screenfuls. In my case (6.5 year old > hardware), I even see the "hourglass cursor" kick in, and the CPU > meter shows 100% utilization of one execution unit for about 5 > seconds. > If you then type M-< and again hit C-v several times, scrolling will > be at its usual speed. Scrolling outside of large comments is also > reasonably fast, even for the first time. > Therefore, my prime suspect is font-lock, which is triggered by the > need to display a portion of the buffer that was not fontified yet. > This suspicion is corroborated by the fact that jit-stealth font-lock > of xdisp.c causes extremely high peaks of CPU usage, and Emacs > response time becomes very sluggish. > Is there something in C Mode's arrangements for fontification, > specifically related to comments, that was changed lately and that can > explain this slowdown? Would you please try out this patch. It is not, perhaps, as fast as one might wish, but it is a considerable improvement nonetheless. === modified file 'lisp/progmodes/cc-engine.el' *** lisp/progmodes/cc-engine.el 2011-11-23 07:03:56 +0000 --- lisp/progmodes/cc-engine.el 2011-12-03 20:58:54 +0000 *************** *** 195,203 **** (not prevstate) (> arg 0))) - ;; Dynamically bound cache for `c-in-literal'. - (defvar c-in-literal-cache t) - ;; Basic handling of preprocessor directives. --- 195,200 ---- *************** *** 2093,2120 **** ;; `c-state-literal-at'. (defsubst c-state-pp-to-literal (from to) ! ;; Do a parse-partial-sexp from FROM to TO, returning the bounds of any ! ;; literal at TO as a cons, otherwise NIL. ! ;; FROM must not be in a literal, and the buffer should already be wide ! ;; enough. (save-excursion ! (let ((s (parse-partial-sexp from to))) (when (or (nth 3 s) (nth 4 s)) ; in a string or comment (parse-partial-sexp (point) (point-max) nil ; TARGETDEPTH nil ; STOPBEFORE s ; OLDSTATE ! 'syntax-table) ; stop at end of literal ! (cons (nth 8 s) (point)))))) ! ! (defun c-state-literal-at (here) ! ;; If position HERE is inside a literal, return (START . END), the ! ;; boundaries of the literal (which may be outside the accessible bit of the ! ;; buffer). Otherwise, return nil. ! ;; ! ;; This function is almost the same as `c-literal-limits'. It differs in ! ;; that it is a lower level function, and that it rigourously follows the ! ;; syntax from BOB, whereas `c-literal-limits' uses a "local" safe position. ;; ;; NOTE: This function manipulates `c-state-nonlit-pos-cache'. This cache ;; MAY NOT contain any positions within macros, since macros are frequently --- 2090,2124 ---- ;; `c-state-literal-at'. (defsubst c-state-pp-to-literal (from to) ! ;; Do a parse-partial-sexp from FROM to TO, returning either ! ;; (STATE TYPE (BEG . END)) if TO is in a literal; or ! ;; (STATE) otherwise, ! ;; where STATE is the parsing state at TO, TYPE is the type of the literal ! ;; (one of 'c, 'c++, 'string) and (BEG . END) is the boundaries of the literal. ! ;; ! ;; Only elements 3 (in a string), 4 (in a comment), 5 (following a quote), ! ;; 7 (comment type) and 8 (start of comment/string) (and possibly 9) of ! ;; STATE are valid. (save-excursion ! (let ((s (parse-partial-sexp from to)) ! ty) (when (or (nth 3 s) (nth 4 s)) ; in a string or comment + (setq ty (cond + ((nth 3 s) 'string) + ((eq (nth 7 s) t) 'c++) + (t 'c))) (parse-partial-sexp (point) (point-max) nil ; TARGETDEPTH nil ; STOPBEFORE s ; OLDSTATE ! 'syntax-table)) ; stop at end of literal ! (if ty ! `(,s ,ty (,(nth 8 s) . ,(point))) ! `(,s))))) ! ! (defun c-state-safe-place (here) ! ;; Return a buffer position before HERE which is "safe", i.e. outside any ! ;; string, comment, or macro. ;; ;; NOTE: This function manipulates `c-state-nonlit-pos-cache'. This cache ;; MAY NOT contain any positions within macros, since macros are frequently *************** *** 2137,2143 **** (while (<= (setq npos (+ pos c-state-nonlit-pos-interval)) here) ! (setq lit (c-state-pp-to-literal pos npos)) (setq pos (or (cdr lit) npos)) ; end of literal containing npos. (goto-char pos) (when (and (c-beginning-of-macro) (/= (point) pos)) --- 2141,2147 ---- (while (<= (setq npos (+ pos c-state-nonlit-pos-interval)) here) ! (setq lit (car (cddr (c-state-pp-to-literal pos npos)))) (setq pos (or (cdr lit) npos)) ; end of literal containing npos. (goto-char pos) (when (and (c-beginning-of-macro) (/= (point) pos)) *************** *** 2148,2156 **** (if (> pos c-state-nonlit-pos-cache-limit) (setq c-state-nonlit-pos-cache-limit pos)) ! (if (< pos here) ! (setq lit (c-state-pp-to-literal pos here))) ! lit)))) (defsubst c-state-lit-beg (pos) ;; Return the start of the literal containing POS, or POS itself. --- 2152,2173 ---- (if (> pos c-state-nonlit-pos-cache-limit) (setq c-state-nonlit-pos-cache-limit pos)) ! pos)))) ! ! (defun c-state-literal-at (here) ! ;; If position HERE is inside a literal, return (START . END), the ! ;; boundaries of the literal (which may be outside the accessible bit of the ! ;; buffer). Otherwise, return nil. ! ;; ! ;; This function is almost the same as `c-literal-limits'. Previously, it ! ;; differed in that it was a lower level function, and that it rigourously ! ;; followed the syntax from BOB. `c-literal-limits' is now (2011-12) ! ;; virtually identical to this function. ! (save-restriction ! (widen) ! (save-excursion ! (let ((pos (c-state-safe-place here))) ! (car (cddr (c-state-pp-to-literal pos here))))))) (defsubst c-state-lit-beg (pos) ;; Return the start of the literal containing POS, or POS itself. *************** *** 4181,4187 **** ;; Tools for handling comments and string literals. ! (defun c-slow-in-literal (&optional lim detect-cpp) "Return the type of literal point is in, if any. The return value is `c' if in a C-style comment, `c++' if in a C++ style comment, `string' if in a string literal, `pound' if DETECT-CPP --- 4198,4204 ---- ;; Tools for handling comments and string literals. ! (defun c-in-literal (&optional lim detect-cpp) "Return the type of literal point is in, if any. The return value is `c' if in a C-style comment, `c++' if in a C++ style comment, `string' if in a string literal, `pound' if DETECT-CPP *************** *** 4194,4260 **** Note that this function might do hidden buffer changes. See the comment at the start of cc-engine.el for more info." ! ! (if (and (vectorp c-in-literal-cache) ! (= (point) (aref c-in-literal-cache 0))) ! (aref c-in-literal-cache 1) ! (let ((rtn (save-excursion ! (let* ((pos (point)) ! (lim (or lim (progn ! (c-beginning-of-syntax) ! (point)))) ! (state (parse-partial-sexp lim pos))) ! (cond ! ((elt state 3) 'string) ! ((elt state 4) (if (elt state 7) 'c++ 'c)) ! ((and detect-cpp (c-beginning-of-macro lim)) 'pound) ! (t nil)))))) ! ;; cache this result if the cache is enabled ! (if (not c-in-literal-cache) ! (setq c-in-literal-cache (vector (point) rtn))) ! rtn))) ! ! ;; XEmacs has a built-in function that should make this much quicker. ! ;; I don't think we even need the cache, which makes our lives more ! ;; complicated anyway. In this case, lim is only used to detect ! ;; cpp directives. ! ;; ! ;; Note that there is a bug in XEmacs's buffer-syntactic-context when used in ! ;; conjunction with syntax-table-properties. The bug is present in, e.g., ! ;; XEmacs 21.4.4. It manifested itself thus: ! ;; ! ;; Starting with an empty AWK Mode buffer, type ! ;; /regexp/ { ! ;; Point gets wrongly left at column 0, rather than being indented to tab-width. ! ;; ! ;; AWK Mode is designed such that when the first / is typed, it gets the ! ;; syntax-table property "string fence". When the second / is typed, BOTH /s ! ;; are given the s-t property "string". However, buffer-syntactic-context ! ;; fails to take account of the change of the s-t property on the opening / to ! ;; "string", and reports that the { is within a string started by the second /. ! ;; ! ;; The workaround for this is for the AWK Mode initialization to switch the ! ;; defalias for c-in-literal to c-slow-in-literal. This will slow down other ! ;; cc-modes in XEmacs whenever an awk-buffer has been initialized. ! ;; ! ;; (Alan Mackenzie, 2003/4/30). ! ! (defun c-fast-in-literal (&optional lim detect-cpp) ! ;; This function might do hidden buffer changes. ! (let ((context (buffer-syntactic-context))) ! (cond ! ((eq context 'string) 'string) ! ((eq context 'comment) 'c++) ! ((eq context 'block-comment) 'c) ! ((and detect-cpp (save-excursion (c-beginning-of-macro lim))) 'pound)))) ! ! (defalias 'c-in-literal ! (if (fboundp 'buffer-syntactic-context) ! 'c-fast-in-literal ; XEmacs ! 'c-slow-in-literal)) ; GNU Emacs ! ! ;; The defalias above isn't enough to shut up the byte compiler. ! (cc-bytecomp-defun c-in-literal) (defun c-literal-limits (&optional lim near not-in-delimiter) "Return a cons of the beginning and end positions of the comment or --- 4211,4222 ---- Note that this function might do hidden buffer changes. See the comment at the start of cc-engine.el for more info." ! (let* ((safe-place (c-state-safe-place (point))) ! (lit (c-state-pp-to-literal safe-place (point)))) ! (or (cadr lit) ! (and detect-cpp ! (save-excursion (c-beginning-of-macro)) ! 'pound)))) (defun c-literal-limits (&optional lim near not-in-delimiter) "Return a cons of the beginning and end positions of the comment or *************** *** 4273,4336 **** (save-excursion (let* ((pos (point)) ! (lim (or lim (progn ! (c-beginning-of-syntax) ! (point)))) ! (state (parse-partial-sexp lim pos))) ! ! (cond ((elt state 3) ; String. ! (goto-char (elt state 8)) ! (cons (point) (or (c-safe (c-forward-sexp 1) (point)) ! (point-max)))) ! ! ((elt state 4) ; Comment. ! (goto-char (elt state 8)) ! (cons (point) (progn (c-forward-single-comment) (point)))) ! ! ((and (not not-in-delimiter) ! (not (elt state 5)) ! (eq (char-before) ?/) ! (looking-at "[/*]")) ! ;; We're standing in a comment starter. ! (backward-char 1) ! (cons (point) (progn (c-forward-single-comment) (point)))) ! ! (near ! (goto-char pos) ! ! ;; Search forward for a literal. ! (skip-chars-forward " \t") ! ! (cond ! ((looking-at c-string-limit-regexp) ; String. ! (cons (point) (or (c-safe (c-forward-sexp 1) (point)) ! (point-max)))) ! ((looking-at c-comment-start-regexp) ; Line or block comment. ! (cons (point) (progn (c-forward-single-comment) (point)))) ! (t ! ;; Search backward. ! (skip-chars-backward " \t") ! (let ((end (point)) beg) ! (cond ! ((save-excursion ! (< (skip-syntax-backward c-string-syntax) 0)) ; String. ! (setq beg (c-safe (c-backward-sexp 1) (point)))) ! ! ((and (c-safe (forward-char -2) t) ! (looking-at "*/")) ! ;; Block comment. Due to the nature of line ! ;; comments, they will always be covered by the ! ;; normal case above. ! (goto-char end) ! (c-backward-single-comment) ! ;; If LIM is bogus, beg will be bogus. ! (setq beg (point)))) ! (if beg (cons beg end)))))) ! )))) ;; In case external callers use this; it did have a docstring. (defalias 'c-literal-limits-fast 'c-literal-limits) --- 4235,4290 ---- (save-excursion (let* ((pos (point)) ! (lim (or lim (c-state-safe-place pos))) ! (pp-to-lit (c-state-pp-to-literal lim pos)) ! (state (car pp-to-lit)) ! (lit-type (cadr pp-to-lit)) ! (lit-limits (car (cddr pp-to-lit)))) ! (cond ! (lit-limits) ! ((and (not not-in-delimiter) ! (not (elt state 5)) ! (eq (char-before) ?/) ! (looking-at "[/*]")) ; FIXME!!! use c-line/block-comment-starter. 2008-09-28. ! ;; We're standing in a comment starter. ! (backward-char 1) ! (cons (point) (progn (c-forward-single-comment) (point)))) ! (near ! (goto-char pos) ! ;; Search forward for a literal. ! (skip-chars-forward " \t") ! (cond ! ((looking-at c-string-limit-regexp) ; String. ! (cons (point) (or (c-safe (c-forward-sexp 1) (point)) ! (point-max)))) ! ! ((looking-at c-comment-start-regexp) ; Line or block comment. ! (cons (point) (progn (c-forward-single-comment) (point)))) ! ! (t ! ;; Search backward. ! (skip-chars-backward " \t") ! (let ((end (point)) beg) ! (cond ! ((save-excursion ! (< (skip-syntax-backward c-string-syntax) 0)) ; String. ! (setq beg (c-safe (c-backward-sexp 1) (point)))) ! ! ((and (c-safe (forward-char -2) t) ! (looking-at "*/")) ! ;; Block comment. Due to the nature of line ! ;; comments, they will always be covered by the ! ;; normal case above. ! (goto-char end) ! (c-backward-single-comment) ! ;; If LIM is bogus, beg will be bogus. ! (setq beg (point)))) ! (if beg (cons beg end)))))) ! )))) ;; In case external callers use this; it did have a docstring. (defalias 'c-literal-limits-fast 'c-literal-limits) === modified file 'lisp/progmodes/cc-fonts.el' *** lisp/progmodes/cc-fonts.el 2011-11-20 02:29:42 +0000 --- lisp/progmodes/cc-fonts.el 2011-12-03 20:47:42 +0000 *************** *** 1535,1544 **** ;; Fontification". (let* ((paren-state (c-parse-state)) (start (point)) decl-context bo-decl in-typedef type-type ps-elt) ;; First, are we actually in a "local" declaration? ! (setq decl-context (c-beginning-of-decl-1) bo-decl (point) in-typedef (looking-at c-typedef-key)) (if in-typedef (c-forward-token-2)) --- 1535,1545 ---- ;; Fontification". (let* ((paren-state (c-parse-state)) (start (point)) + (bod-lim (max (- (point) 500) (point-min))) decl-context bo-decl in-typedef type-type ps-elt) ;; First, are we actually in a "local" declaration? ! (setq decl-context (c-beginning-of-decl-1 bod-lim) bo-decl (point) in-typedef (looking-at c-typedef-key)) (if in-typedef (c-forward-token-2)) === modified file 'lisp/progmodes/cc-mode.el' *** lisp/progmodes/cc-mode.el 2011-11-20 02:29:42 +0000 --- lisp/progmodes/cc-mode.el 2011-12-03 21:01:50 +0000 *************** *** 1562,1571 **** (c-common-init 'awk-mode) (c-awk-unstick-NL-prop) - ;; Prevent XEmacs's buffer-syntactic-context being used. See the comment - ;; in cc-engine.el, just before (defun c-fast-in-literal ... - (defalias 'c-in-literal 'c-slow-in-literal) - (c-run-mode-hooks 'c-mode-common-hook 'awk-mode-hook) (c-update-modeline)) --- 1562,1567 ---- -- Alan Mackenzie (Nuremberg, Germany).