diff options
| author | Alan Mackenzie | 2009-12-10 14:29:11 +0000 |
|---|---|---|
| committer | Alan Mackenzie | 2009-12-10 14:29:11 +0000 |
| commit | d0fcee66986308f526578b35fde713be47f1bbab (patch) | |
| tree | 01b9b1350ae675c74a528e3195278250329c1a48 | |
| parent | f1943c1ba5cd799e4b24542ef2c0089a42542d69 (diff) | |
| download | emacs-d0fcee66986308f526578b35fde713be47f1bbab.tar.gz emacs-d0fcee66986308f526578b35fde713be47f1bbab.zip | |
Fix bug#5091: indentation in c++-mode.
* cc-mode.el (c-basic-common-init): make text property `category' rear
non-sticky.
* cc-engine.el (c-ssb-lit-begin): New defsubst, extracted from ....
(c-syntactic-skip-backward): Refactor, extracting the above.
(c-guess-basic-syntax CASEs 5D.3, 5L): Add extra anchor point;
(c-guess-basic-syntax CASE 19): New CASE to handle template construct
continued over line boundary.
(c-guess-basic-syntax CASE 7): don't trigger on '<'.
| -rw-r--r-- | lisp/progmodes/cc-engine.el | 289 | ||||
| -rw-r--r-- | lisp/progmodes/cc-mode.el | 16 |
2 files changed, 169 insertions, 136 deletions
diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 664ce0cb376..27118620acd 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el | |||
| @@ -3743,6 +3743,57 @@ comment at the start of cc-engine.el for more info." | |||
| 3743 | (goto-char bound)) | 3743 | (goto-char bound)) |
| 3744 | nil))) | 3744 | nil))) |
| 3745 | 3745 | ||
| 3746 | (defsubst c-ssb-lit-begin () | ||
| 3747 | ;; Return the start of the literal point is in, or nil. | ||
| 3748 | ;; We read and write the variables `safe-pos', `safe-pos-list', `state' | ||
| 3749 | ;; bound in the caller. | ||
| 3750 | |||
| 3751 | ;; Use `parse-partial-sexp' from a safe position down to the point to check | ||
| 3752 | ;; if it's outside comments and strings. | ||
| 3753 | (save-excursion | ||
| 3754 | (let ((pos (point)) safe-pos state pps-end-pos) | ||
| 3755 | ;; Pick a safe position as close to the point as possible. | ||
| 3756 | ;; | ||
| 3757 | ;; FIXME: Consult `syntax-ppss' here if our cache doesn't give a good | ||
| 3758 | ;; position. | ||
| 3759 | |||
| 3760 | (while (and safe-pos-list | ||
| 3761 | (> (car safe-pos-list) (point))) | ||
| 3762 | (setq safe-pos-list (cdr safe-pos-list))) | ||
| 3763 | (unless (setq safe-pos (car-safe safe-pos-list)) | ||
| 3764 | (setq safe-pos (max (or (c-safe-position | ||
| 3765 | (point) (or c-state-cache | ||
| 3766 | (c-parse-state))) | ||
| 3767 | 0) | ||
| 3768 | (point-min)) | ||
| 3769 | safe-pos-list (list safe-pos))) | ||
| 3770 | |||
| 3771 | ;; Cache positions along the way to use if we have to back up more. We | ||
| 3772 | ;; cache every closing paren on the same level. If the paren cache is | ||
| 3773 | ;; relevant in this region then we're typically already on the same | ||
| 3774 | ;; level as the target position. Note that we might cache positions | ||
| 3775 | ;; after opening parens in case safe-pos is in a nested list. That's | ||
| 3776 | ;; both uncommon and harmless. | ||
| 3777 | (while (progn | ||
| 3778 | (setq state (parse-partial-sexp | ||
| 3779 | safe-pos pos 0)) | ||
| 3780 | (< (point) pos)) | ||
| 3781 | (setq safe-pos (point) | ||
| 3782 | safe-pos-list (cons safe-pos safe-pos-list))) | ||
| 3783 | |||
| 3784 | ;; If the state contains the start of the containing sexp we cache that | ||
| 3785 | ;; position too, so that parse-partial-sexp in the next run has a bigger | ||
| 3786 | ;; chance of starting at the same level as the target position and thus | ||
| 3787 | ;; will get more good safe positions into the list. | ||
| 3788 | (if (elt state 1) | ||
| 3789 | (setq safe-pos (1+ (elt state 1)) | ||
| 3790 | safe-pos-list (cons safe-pos safe-pos-list))) | ||
| 3791 | |||
| 3792 | (if (or (elt state 3) (elt state 4)) | ||
| 3793 | ;; Inside string or comment. Continue search at the | ||
| 3794 | ;; beginning of it. | ||
| 3795 | (elt state 8))))) | ||
| 3796 | |||
| 3746 | (defun c-syntactic-skip-backward (skip-chars &optional limit paren-level) | 3797 | (defun c-syntactic-skip-backward (skip-chars &optional limit paren-level) |
| 3747 | "Like `skip-chars-backward' but only look at syntactically relevant chars, | 3798 | "Like `skip-chars-backward' but only look at syntactically relevant chars, |
| 3748 | i.e. don't stop at positions inside syntactic whitespace or string | 3799 | i.e. don't stop at positions inside syntactic whitespace or string |
| @@ -3761,140 +3812,100 @@ Note that this function might do hidden buffer changes. See the | |||
| 3761 | comment at the start of cc-engine.el for more info." | 3812 | comment at the start of cc-engine.el for more info." |
| 3762 | 3813 | ||
| 3763 | (let ((start (point)) | 3814 | (let ((start (point)) |
| 3764 | state | 3815 | state-2 |
| 3765 | ;; A list of syntactically relevant positions in descending | 3816 | ;; A list of syntactically relevant positions in descending |
| 3766 | ;; order. It's used to avoid scanning repeatedly over | 3817 | ;; order. It's used to avoid scanning repeatedly over |
| 3767 | ;; potentially large regions with `parse-partial-sexp' to verify | 3818 | ;; potentially large regions with `parse-partial-sexp' to verify |
| 3768 | ;; each position. | 3819 | ;; each position. Used in `c-ssb-lit-begin' |
| 3769 | safe-pos-list | 3820 | safe-pos-list |
| 3770 | ;; The position at the beginning of `safe-pos-list'. | ||
| 3771 | safe-pos | ||
| 3772 | ;; The result from `c-beginning-of-macro' at the start position or the | 3821 | ;; The result from `c-beginning-of-macro' at the start position or the |
| 3773 | ;; start position itself if it isn't within a macro. Evaluated on | 3822 | ;; start position itself if it isn't within a macro. Evaluated on |
| 3774 | ;; demand. | 3823 | ;; demand. |
| 3775 | start-macro-beg | 3824 | start-macro-beg |
| 3776 | ;; The earliest position after the current one with the same paren | 3825 | ;; The earliest position after the current one with the same paren |
| 3777 | ;; level. Used only when `paren-level' is set. | 3826 | ;; level. Used only when `paren-level' is set. |
| 3827 | lit-beg | ||
| 3778 | (paren-level-pos (point))) | 3828 | (paren-level-pos (point))) |
| 3779 | 3829 | ||
| 3780 | (while (progn | 3830 | (while |
| 3781 | (while (and | 3831 | (progn |
| 3782 | (< (skip-chars-backward skip-chars limit) 0) | 3832 | ;; The next loop "tries" to find the end point each time round, |
| 3783 | 3833 | ;; loops when it hasn't succeeded. | |
| 3784 | ;; Use `parse-partial-sexp' from a safe position down to | 3834 | (while |
| 3785 | ;; the point to check if it's outside comments and | 3835 | (and |
| 3786 | ;; strings. | 3836 | (< (skip-chars-backward skip-chars limit) 0) |
| 3787 | (let ((pos (point)) state-2 pps-end-pos) | 3837 | |
| 3788 | ;; Pick a safe position as close to the point as | 3838 | (let ((pos (point)) state-2 pps-end-pos) |
| 3789 | ;; possible. | 3839 | |
| 3790 | ;; | 3840 | (cond |
| 3791 | ;; FIXME: Consult `syntax-ppss' here if our | 3841 | ;; Don't stop inside a literal |
| 3792 | ;; cache doesn't give a good position. | 3842 | ((setq lit-beg (c-ssb-lit-begin)) |
| 3793 | (while (and safe-pos-list | 3843 | (goto-char lit-beg) |
| 3794 | (> (car safe-pos-list) (point))) | 3844 | t) |
| 3795 | (setq safe-pos-list (cdr safe-pos-list))) | 3845 | |
| 3796 | (unless (setq safe-pos (car-safe safe-pos-list)) | 3846 | ((and paren-level |
| 3797 | (setq safe-pos (max (or (c-safe-position | 3847 | (save-excursion |
| 3798 | (point) (or c-state-cache | 3848 | (setq state-2 (parse-partial-sexp |
| 3799 | (c-parse-state))) | 3849 | pos paren-level-pos -1) |
| 3800 | 0) | 3850 | pps-end-pos (point)) |
| 3801 | (point-min)) | 3851 | (/= (car state-2) 0))) |
| 3802 | safe-pos-list (list safe-pos))) | 3852 | ;; Not at the right level. |
| 3803 | 3853 | ||
| 3804 | ;; Cache positions along the way to use if we have to | 3854 | (if (and (< (car state-2) 0) |
| 3805 | ;; back up more. We cache every closing paren on the | 3855 | ;; We stop above if we go out of a paren. |
| 3806 | ;; same level. If the paren cache is relevant in this | 3856 | ;; Now check whether it precedes or is |
| 3807 | ;; region then we're typically already on the same | 3857 | ;; nested in the starting sexp. |
| 3808 | ;; level as the target position. Note that we might | 3858 | (save-excursion |
| 3809 | ;; cache positions after opening parens in case | 3859 | (setq state-2 |
| 3810 | ;; safe-pos is in a nested list. That's both uncommon | 3860 | (parse-partial-sexp |
| 3811 | ;; and harmless. | 3861 | pps-end-pos paren-level-pos |
| 3812 | (while (progn | 3862 | nil nil state-2)) |
| 3813 | (setq state (parse-partial-sexp | 3863 | (< (car state-2) 0))) |
| 3814 | safe-pos pos 0)) | 3864 | |
| 3815 | (< (point) pos)) | 3865 | ;; We've stopped short of the starting position |
| 3816 | (setq safe-pos (point) | 3866 | ;; so the hit was inside a nested list. Go up |
| 3817 | safe-pos-list (cons safe-pos safe-pos-list))) | 3867 | ;; until we are at the right level. |
| 3818 | 3868 | (condition-case nil | |
| 3819 | (cond | 3869 | (progn |
| 3820 | ((or (elt state 3) (elt state 4)) | 3870 | (goto-char (scan-lists pos -1 |
| 3821 | ;; Inside string or comment. Continue search at the | 3871 | (- (car state-2)))) |
| 3822 | ;; beginning of it. | 3872 | (setq paren-level-pos (point)) |
| 3823 | (goto-char (elt state 8)) | 3873 | (if (and limit (>= limit paren-level-pos)) |
| 3824 | t) | ||
| 3825 | |||
| 3826 | ((and paren-level | ||
| 3827 | (save-excursion | ||
| 3828 | (setq state-2 (parse-partial-sexp | ||
| 3829 | pos paren-level-pos -1) | ||
| 3830 | pps-end-pos (point)) | ||
| 3831 | (/= (car state-2) 0))) | ||
| 3832 | ;; Not at the right level. | ||
| 3833 | |||
| 3834 | (if (and (< (car state-2) 0) | ||
| 3835 | ;; We stop above if we go out of a paren. | ||
| 3836 | ;; Now check whether it precedes or is | ||
| 3837 | ;; nested in the starting sexp. | ||
| 3838 | (save-excursion | ||
| 3839 | (setq state-2 | ||
| 3840 | (parse-partial-sexp | ||
| 3841 | pps-end-pos paren-level-pos | ||
| 3842 | nil nil state-2)) | ||
| 3843 | (< (car state-2) 0))) | ||
| 3844 | |||
| 3845 | ;; We've stopped short of the starting position | ||
| 3846 | ;; so the hit was inside a nested list. Go up | ||
| 3847 | ;; until we are at the right level. | ||
| 3848 | (condition-case nil | ||
| 3849 | (progn | 3874 | (progn |
| 3850 | (goto-char (scan-lists pos -1 | 3875 | (goto-char limit) |
| 3851 | (- (car state-2)))) | 3876 | nil) |
| 3852 | (setq paren-level-pos (point)) | 3877 | t)) |
| 3853 | (if (and limit (>= limit paren-level-pos)) | 3878 | (error |
| 3854 | (progn | 3879 | (goto-char (or limit (point-min))) |
| 3855 | (goto-char limit) | 3880 | nil)) |
| 3856 | nil) | 3881 | |
| 3857 | t)) | 3882 | ;; The hit was outside the list at the start |
| 3858 | (error | 3883 | ;; position. Go to the start of the list and exit. |
| 3859 | (goto-char (or limit (point-min))) | 3884 | (goto-char (1+ (elt state-2 1))) |
| 3860 | nil)) | 3885 | nil)) |
| 3861 | 3886 | ||
| 3862 | ;; The hit was outside the list at the start | 3887 | ((c-beginning-of-macro limit) |
| 3863 | ;; position. Go to the start of the list and exit. | 3888 | ;; Inside a macro. |
| 3864 | (goto-char (1+ (elt state-2 1))) | 3889 | (if (< (point) |
| 3865 | nil)) | 3890 | (or start-macro-beg |
| 3866 | 3891 | (setq start-macro-beg | |
| 3867 | ((c-beginning-of-macro limit) | 3892 | (save-excursion |
| 3868 | ;; Inside a macro. | 3893 | (goto-char start) |
| 3869 | (if (< (point) | 3894 | (c-beginning-of-macro limit) |
| 3870 | (or start-macro-beg | 3895 | (point))))) |
| 3871 | (setq start-macro-beg | 3896 | t |
| 3872 | (save-excursion | 3897 | |
| 3873 | (goto-char start) | 3898 | ;; It's inside the same macro we started in so it's |
| 3874 | (c-beginning-of-macro limit) | 3899 | ;; a relevant match. |
| 3875 | (point))))) | 3900 | (goto-char pos) |
| 3876 | t | 3901 | nil)))))) |
| 3877 | 3902 | ||
| 3878 | ;; It's inside the same macro we started in so it's | 3903 | (> (point) |
| 3879 | ;; a relevant match. | 3904 | (progn |
| 3880 | (goto-char pos) | 3905 | ;; Skip syntactic ws afterwards so that we don't stop at the |
| 3881 | nil))))) | 3906 | ;; end of a comment if `skip-chars' is something like "^/". |
| 3882 | 3907 | (c-backward-syntactic-ws) | |
| 3883 | ;; If the state contains the start of the containing sexp we | 3908 | (point))))) |
| 3884 | ;; cache that position too, so that parse-partial-sexp in the | ||
| 3885 | ;; next run has a bigger chance of starting at the same level | ||
| 3886 | ;; as the target position and thus will get more good safe | ||
| 3887 | ;; positions into the list. | ||
| 3888 | (if (elt state 1) | ||
| 3889 | (setq safe-pos (1+ (elt state 1)) | ||
| 3890 | safe-pos-list (cons safe-pos safe-pos-list)))) | ||
| 3891 | |||
| 3892 | (> (point) | ||
| 3893 | (progn | ||
| 3894 | ;; Skip syntactic ws afterwards so that we don't stop at the | ||
| 3895 | ;; end of a comment if `skip-chars' is something like "^/". | ||
| 3896 | (c-backward-syntactic-ws) | ||
| 3897 | (point))))) | ||
| 3898 | 3909 | ||
| 3899 | ;; We might want to extend this with more useful return values in | 3910 | ;; We might want to extend this with more useful return values in |
| 3900 | ;; the future. | 3911 | ;; the future. |
| @@ -8426,6 +8437,7 @@ comment at the start of cc-engine.el for more info." | |||
| 8426 | literal char-before-ip before-ws-ip char-after-ip macro-start | 8437 | literal char-before-ip before-ws-ip char-after-ip macro-start |
| 8427 | in-macro-expr c-syntactic-context placeholder c-in-literal-cache | 8438 | in-macro-expr c-syntactic-context placeholder c-in-literal-cache |
| 8428 | step-type tmpsymbol keyword injava-inher special-brace-list tmp-pos | 8439 | step-type tmpsymbol keyword injava-inher special-brace-list tmp-pos |
| 8440 | containing-< | ||
| 8429 | ;; The following record some positions for the containing | 8441 | ;; The following record some positions for the containing |
| 8430 | ;; declaration block if we're directly within one: | 8442 | ;; declaration block if we're directly within one: |
| 8431 | ;; `containing-decl-open' is the position of the open | 8443 | ;; `containing-decl-open' is the position of the open |
| @@ -9040,7 +9052,7 @@ comment at the start of cc-engine.el for more info." | |||
| 9040 | (back-to-indentation))) | 9052 | (back-to-indentation))) |
| 9041 | ;; FIXME: Should use c-add-stmt-syntax, but it's not yet | 9053 | ;; FIXME: Should use c-add-stmt-syntax, but it's not yet |
| 9042 | ;; template aware. | 9054 | ;; template aware. |
| 9043 | (c-add-syntax 'template-args-cont (point))) | 9055 | (c-add-syntax 'template-args-cont (point) placeholder)) |
| 9044 | 9056 | ||
| 9045 | ;; CASE 5D.4: perhaps a multiple inheritance line? | 9057 | ;; CASE 5D.4: perhaps a multiple inheritance line? |
| 9046 | ((and (c-major-mode-is 'c++-mode) | 9058 | ((and (c-major-mode-is 'c++-mode) |
| @@ -9252,10 +9264,11 @@ comment at the start of cc-engine.el for more info." | |||
| 9252 | ;; arglist that begins on the previous line. | 9264 | ;; arglist that begins on the previous line. |
| 9253 | ((and c-recognize-<>-arglists | 9265 | ((and c-recognize-<>-arglists |
| 9254 | (eq (char-before) ?<) | 9266 | (eq (char-before) ?<) |
| 9267 | (setq placeholder (1- (point))) | ||
| 9255 | (not (and c-overloadable-operators-regexp | 9268 | (not (and c-overloadable-operators-regexp |
| 9256 | (c-after-special-operator-id lim)))) | 9269 | (c-after-special-operator-id lim)))) |
| 9257 | (c-beginning-of-statement-1 (c-safe-position (point) paren-state)) | 9270 | (c-beginning-of-statement-1 (c-safe-position (point) paren-state)) |
| 9258 | (c-add-syntax 'template-args-cont (c-point 'boi))) | 9271 | (c-add-syntax 'template-args-cont (c-point 'boi) placeholder)) |
| 9259 | 9272 | ||
| 9260 | ;; CASE 5Q: we are at a statement within a macro. | 9273 | ;; CASE 5Q: we are at a statement within a macro. |
| 9261 | (macro-start | 9274 | (macro-start |
| @@ -9277,14 +9290,38 @@ comment at the start of cc-engine.el for more info." | |||
| 9277 | 9290 | ||
| 9278 | ;; (CASE 6 has been removed.) | 9291 | ;; (CASE 6 has been removed.) |
| 9279 | 9292 | ||
| 9293 | ;; CASE 19: line is an expression, not a statement, and is directly | ||
| 9294 | ;; contained by a template delimiter. Most likely, we are in a | ||
| 9295 | ;; template arglist within a statement. This case is based on CASE | ||
| 9296 | ;; 7. At some point in the future, we may wish to create more | ||
| 9297 | ;; syntactic symbols such as `template-intro', | ||
| 9298 | ;; `template-cont-nonempty', etc., and distinguish between them as we | ||
| 9299 | ;; do for `arglist-intro' etc. (2009-12-07). | ||
| 9300 | ((and c-recognize-<>-arglists | ||
| 9301 | (setq containing-< (c-up-list-backward indent-point containing-sexp)) | ||
| 9302 | (eq (char-after containing-<) ?\<)) | ||
| 9303 | (setq placeholder (c-point 'boi containing-<)) | ||
| 9304 | (goto-char containing-sexp) ; Most nested Lbrace/Lparen (but not | ||
| 9305 | ; '<') before indent-point. | ||
| 9306 | (if (>= (point) placeholder) | ||
| 9307 | (progn | ||
| 9308 | (forward-char) | ||
| 9309 | (skip-chars-forward " \t")) | ||
| 9310 | (goto-char placeholder)) | ||
| 9311 | (c-add-stmt-syntax 'template-args-cont (list containing-<) t | ||
| 9312 | (c-most-enclosing-brace c-state-cache (point)) | ||
| 9313 | paren-state)) | ||
| 9314 | |||
| 9315 | |||
| 9280 | ;; CASE 7: line is an expression, not a statement. Most | 9316 | ;; CASE 7: line is an expression, not a statement. Most |
| 9281 | ;; likely we are either in a function prototype or a function | 9317 | ;; likely we are either in a function prototype or a function |
| 9282 | ;; call argument list | 9318 | ;; call argument list, or a template argument list. |
| 9283 | ((not (or (and c-special-brace-lists | 9319 | ((not (or (and c-special-brace-lists |
| 9284 | (save-excursion | 9320 | (save-excursion |
| 9285 | (goto-char containing-sexp) | 9321 | (goto-char containing-sexp) |
| 9286 | (c-looking-at-special-brace-list))) | 9322 | (c-looking-at-special-brace-list))) |
| 9287 | (eq (char-after containing-sexp) ?{))) | 9323 | (eq (char-after containing-sexp) ?{) |
| 9324 | (eq (char-after containing-sexp) ?<))) | ||
| 9288 | (cond | 9325 | (cond |
| 9289 | 9326 | ||
| 9290 | ;; CASE 7A: we are looking at the arglist closing paren. | 9327 | ;; CASE 7A: we are looking at the arglist closing paren. |
| @@ -9381,7 +9418,7 @@ comment at the start of cc-engine.el for more info." | |||
| 9381 | (c-forward-syntactic-ws) | 9418 | (c-forward-syntactic-ws) |
| 9382 | (point)) | 9419 | (point)) |
| 9383 | (c-point 'bonl))) | 9420 | (c-point 'bonl))) |
| 9384 | (goto-char containing-sexp) | 9421 | (goto-char containing-sexp) ; paren opening the arglist |
| 9385 | (setq placeholder (c-point 'boi)) | 9422 | (setq placeholder (c-point 'boi)) |
| 9386 | (if (and (c-safe (backward-up-list 1) t) | 9423 | (if (and (c-safe (backward-up-list 1) t) |
| 9387 | (>= (point) placeholder)) | 9424 | (>= (point) placeholder)) |
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index 49d9ebeb492..14a3533e0e2 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el | |||
| @@ -541,19 +541,15 @@ that requires a literal mode spec at compile time." | |||
| 541 | (make-local-variable 'lookup-syntax-properties) | 541 | (make-local-variable 'lookup-syntax-properties) |
| 542 | (setq lookup-syntax-properties t))) | 542 | (setq lookup-syntax-properties t))) |
| 543 | 543 | ||
| 544 | ;; Use this in Emacs 21 to avoid meddling with the rear-nonsticky | 544 | ;; Use this in Emacs 21+ to avoid meddling with the rear-nonsticky |
| 545 | ;; property on each character. | 545 | ;; property on each character. |
| 546 | (when (boundp 'text-property-default-nonsticky) | 546 | (when (boundp 'text-property-default-nonsticky) |
| 547 | (make-local-variable 'text-property-default-nonsticky) | 547 | (make-local-variable 'text-property-default-nonsticky) |
| 548 | (let ((elem (assq 'syntax-table text-property-default-nonsticky))) | 548 | (mapc (lambda (tprop) |
| 549 | (if elem | 549 | (unless (assq tprop text-property-default-nonsticky) |
| 550 | (setcdr elem t) | 550 | (setq text-property-default-nonsticky |
| 551 | (setq text-property-default-nonsticky | 551 | (cons `(,tprop . t) text-property-default-nonsticky)))) |
| 552 | (cons '(syntax-table . t) | 552 | '(syntax-table category c-type))) |
| 553 | text-property-default-nonsticky)))) | ||
| 554 | (setq text-property-default-nonsticky | ||
| 555 | (cons '(c-type . t) | ||
| 556 | text-property-default-nonsticky))) | ||
| 557 | 553 | ||
| 558 | ;; In Emacs 21 and later it's possible to turn off the ad-hoc | 554 | ;; In Emacs 21 and later it's possible to turn off the ad-hoc |
| 559 | ;; heuristic that open parens in column 0 are defun starters. Since | 555 | ;; heuristic that open parens in column 0 are defun starters. Since |