Home | History | Annotate | Line # | Download | only in progmodes
      1 ;;
      2 ;; Copyright (c) 2002 by The XFree86 Project, Inc.
      3 ;;
      4 ;; Permission is hereby granted, free of charge, to any person obtaining a
      5 ;; copy of this software and associated documentation files (the "Software"),
      6 ;; to deal in the Software without restriction, including without limitation
      7 ;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8 ;; and/or sell copies of the Software, and to permit persons to whom the
      9 ;; Software is furnished to do so, subject to the following conditions:
     10 ;;
     11 ;; The above copyright notice and this permission notice shall be included in
     12 ;; all copies or substantial portions of the Software.
     13 ;;
     14 ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     17 ;; THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     18 ;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
     19 ;; OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     20 ;; SOFTWARE.
     21 ;;
     22 ;; Except as contained in this notice, the name of the XFree86 Project shall
     23 ;; not be used in advertising or otherwise to promote the sale, use or other
     24 ;; dealings in this Software without prior written authorization from the
     25 ;; XFree86 Project.
     26 ;;
     27 ;; Author: Paulo Csar Pereira de Andrade
     28 ;;
     29 ;;
     30 ;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/c.lsp,v 1.27 2004/01/12 17:53:20 paulo Exp $
     31 ;;
     32 
     33 (require "syntax")
     34 (require "indent")
     35 (in-package "XEDIT")
     36 
     37 (defsynprop *prop-format*
     38     "format"
     39     :font	"*lucidatypewriter-medium-r*-12-*"
     40     :foreground	"RoyalBlue2"
     41     :underline	t
     42 )
     43 
     44 (defsynoptions *c-DEFAULT-style*
     45     ;; Positive number. Basic indentation.
     46     (:indentation		.	4)
     47 
     48     ;; Boolean. Support for GNU style indentation.
     49     (:brace-indent		.	nil)
     50 
     51     ;; Boolean. Add one indentation level to case and default?
     52     (:case-indent		.	t)
     53 
     54     ;; Boolean. Remove one indentation level for labels?
     55     (:label-dedent		.	t)
     56 
     57     ;; Boolean. Add one indentation level to continuations?
     58     (:cont-indent		.	t)
     59 
     60     ;; Boolean. Move cursor to the indent column after pressing <Enter>?
     61     (:newline-indent		.	t)
     62 
     63     ;; Boolean. Set to T if tabs shouldn't be used to fill indentation.
     64     (:emulate-tabs		.	nil)
     65 
     66     ;; Boolean. Force a newline before braces?
     67     (:newline-before-brace	.	nil)
     68 
     69     ;; Boolean. Force a newline after braces?
     70     (:newline-after-brace	.	nil)
     71 
     72     ;; Boolean. Force a newline after semicolons?
     73     (:newline-after-semi	.	nil)
     74 
     75     ;; Boolean. Only calculate indentation after pressing <Enter>?
     76     ;;		This may be useful if the parser does not always
     77     ;;		do what the user expects...
     78     (:only-newline-indent	.	nil)
     79 
     80     ;; Boolean. Remove extra spaces from previous line.
     81     ;;		This should default to T when newline-indent is not NIL.
     82     (:trim-blank-lines		.	t)
     83 
     84     ;; Boolean. If this hash-table entry is set, no indentation is done.
     85     ;;		Useful to temporarily disable indentation.
     86     (:disable-indent		.	nil)
     87 )
     88 
     89 ;; BSD like style
     90 (defsynoptions *c-BSD-style*
     91     (:indentation		.	8)
     92     (:brace-indent		.	nil)
     93     (:case-indent		.	nil)
     94     (:label-dedent		.	t)
     95     (:cont-indent		.	t)
     96     (:newline-indent		.	t)
     97     (:emulate-tabs		.	nil)
     98     (:newline-before-brace	.	nil)
     99     (:newline-after-brace	.	t)
    100     (:newline-after-semi	.	t)
    101     (:trim-blank-lines		.	t)
    102 )
    103 
    104 ;; GNU like style
    105 (defsynoptions *c-GNU-style*
    106     (:indentation		.	2)
    107     (:brace-indent		.	t)
    108     (:case-indent		.	nil)
    109     (:label-dedent		.	t)
    110     (:cont-indent		.	t)
    111     (:newline-indent		.	nil)
    112     (:emulate-tabs		.	nil)
    113     (:newline-before-brace	.	t)
    114     (:newline-after-brace	.	t)
    115     (:newline-after-semi	.	t)
    116     (:trim-blank-lines		.	nil)
    117 )
    118 
    119 ;; K&R like style
    120 (defsynoptions *c-K&R-style*
    121     (:indentation		.	5)
    122     (:brace-indent		.	nil)
    123     (:case-indent		.	nil)
    124     (:label-dedent		.	t)
    125     (:cont-indent		.	t)
    126     (:newline-indent		.	t)
    127     (:emulate-tabs		.	t)
    128     (:newline-before-brace	.	t)
    129     (:newline-after-brace	.	t)
    130     (:newline-after-semi	.	t)
    131     (:trim-blank-lines		.	t)
    132 )
    133 
    134 (defvar *c-styles* '(
    135     ("xedit"	.	*c-DEFAULT-style*)
    136     ("BSD"	.	*c-BSD-style*)
    137     ("GNU"	.	*c-GNU-style*)
    138     ("K&R"	.	*c-K&R-style*)
    139 ))
    140 
    141 (defvar *c-mode-options* *c-DEFAULT-style*)
    142 ; (setq *c-mode-options* *c-gnu-style*)
    143 
    144 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    145 ;; This is a very lazy "pattern matcher" for the C language.
    146 ;; If the syntax in the code is not correct, it may get confused, and
    147 ;; because it is "lazy" some wrong constructs will be recognized as
    148 ;; correct when reducing patterns.
    149 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    150 (defindent *c-mode-indent* :main
    151     ;; this must be the first token
    152     (indtoken "^\\s*"		:start-of-line)
    153     (indtoken "\\<case\\>"	:c-case)
    154     (indtoken "\\<default\\>"	:c-default)
    155     (indtoken "\\<do\\>"	:do)
    156     (indtoken "\\<if\\>"	:c-if)
    157     (indtoken "\\<else\\>"	:c-else)
    158     (indtoken "\\<for\\>"	:c-for)
    159     (indtoken "\\<switch\\>"	:c-switch)
    160     (indtoken "\\<while\\>"	:c-while)
    161     ;; Match identifiers and numbers as an expression
    162     (indtoken "\\w+"		:expression)
    163     (indtoken ";"		:semi		:nospec t)
    164     (indtoken ","		:comma		:nospec t)
    165     (indtoken ":"		:collon		:nospec t)
    166     ;;  Ignore spaces before collon, this avoids dedenting ternary
    167     ;; and bitfield definitions as the parser does not distinguish
    168     ;; labels from those, another option would be to use the pattern
    169     ;; "\\w+:", but this way should properly handle labels generated
    170     ;; by macros, example: `MACRO_LABEL(value):'
    171     (indtoken "\\s+:"		nil)
    172 
    173     (indinit			(c-braces 0))
    174     (indtoken "{"
    175 	:obrace
    176 	:nospec t
    177 	:code	(decf c-braces)
    178     )
    179     (indtoken "}"
    180 	:cbrace
    181 	:nospec t
    182 	:begin	:braces
    183 	:code	(incf c-braces)
    184     )
    185     (indtable :braces
    186 	(indtoken "{"
    187 	    :obrace
    188 	    :nospec t
    189 	    :switch -1
    190 	    :code   (decf c-braces)
    191 	)
    192 	(indtoken "}"
    193 	    :cbrace
    194 	    :nospec t
    195 	    :begin  :braces
    196 	    :code   (incf c-braces)
    197 	)
    198     )
    199 
    200     (indinit			(c-bra 0))
    201     (indtoken ")"		:cparen		:nospec t :code (incf c-bra))
    202     (indtoken "("		:oparen		:nospec t :code (decf c-bra))
    203     (indtoken "]"		:cbrack		:nospec t :code (incf c-bra))
    204     (indtoken "["		:obrack		:nospec t :code (decf c-bra))
    205     (indtoken "\\\\$"		:continuation)
    206 
    207     ;; C++ style comment, disallow other tokens to match inside comment
    208     (indtoken "//.*$"		nil)
    209 
    210     (indtoken "#"		:hash		:nospec t)
    211 
    212     ;; if in the same line, reduce now, this must be done because the
    213     ;; delimiters are identical
    214     (indtoken "'([^\\']|\\\\.)*'"	:expression)
    215     (indtoken "\"([^\\\"]|\\\\.)*\""	:expression)
    216 
    217     (indtoken "\""		:cstring	:nospec t	:begin :string)
    218 
    219     (indtoken "'"		:cconstant	:nospec t	:begin :constant)
    220 
    221     (indtoken "*/"		:ccomment	:nospec t	:begin :comment)
    222     ;; this must be the last token
    223     (indtoken "$"		:end-of-line)
    224 
    225     (indtable :string
    226 	;; Ignore escaped characters
    227 	(indtoken "\\." 	nil)
    228 	;; Return to the toplevel when the start of the string is found
    229 	(indtoken "\""		:ostring	:nospec t	:switch -1)
    230     )
    231     (indtable :constant
    232 	;; Ignore escaped characters
    233 	(indtoken "\\." 	nil)
    234 	;; Return to the toplevel when the start of the character is found
    235 	(indtoken "'"		:oconstant	:nospec t	:switch -1)
    236     )
    237     (indtable :comment
    238 	(indtoken "/*"		:ocomment	:nospec t	:switch -1)
    239     )
    240 
    241     ;; "Complex" statements
    242     (indinit		(c-complex 0) (c-cases 0))
    243 
    244     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    245     ;; Order of reduce rules here is important, process comment,
    246     ;; continuations, preprocessor and set states when an eol is found.
    247     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    248 
    249     (indinit	(c-offset (point-max))
    250 		(c-prev-offset c-offset)
    251     )
    252     (indreduce :indent
    253 	t
    254 	((:start-of-line))
    255 	(and (= *ind-start* *ind-offset*)
    256 	    (setq
    257 		*offset* (+ *ind-offset* *ind-length*)
    258 	    )
    259 	)
    260 	(setq
    261 	    c-prev-offset   c-offset
    262 	    c-offset	    *ind-offset*
    263 	)
    264     )
    265 
    266     ;; Delete comments
    267     (indreduce nil
    268 	t
    269 	((:ocomment nil :ccomment))
    270     )
    271 
    272     ;; Join in a single token to simplify removal of possible multiline
    273     ;; preprocessor directives
    274     (indinit			c-continuation)
    275     (indreduce :continuation
    276 	t
    277 	((:continuation :end-of-line))
    278 	(setq c-continuation t)
    279     )
    280 
    281     (indreduce :eol
    282 	t
    283 	((:end-of-line))
    284 	;; Anything after the eol offset is safe to parse now
    285 	(setq c-continuation nil)
    286     )
    287 
    288     ;; Delete blank lines
    289     (indreduce nil
    290 	t
    291 	((:indent :eol))
    292     )
    293 
    294     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    295     ;; Preprocessor
    296     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    297     (indreduce nil
    298 	(>= *ind-offset* *ind-start*)
    299 	((:indent :hash))
    300 	(setq *indent* 0)
    301 	(indent-macro-reject-left)
    302     )
    303     (indreduce nil
    304 	t
    305 	((:indent :hash nil :eol))
    306     )
    307 
    308     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    309     ;; Expressions
    310     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    311     (indreduce :expression
    312 	t
    313 	;; Reduce to a single expression
    314 	((:expression :parens)
    315 	 (:expression :bracks)
    316 	 (:expression :expression)
    317 	;; These may be multiline
    318 	 (:ostring (not :ostring) :cstring)
    319 	 (:oconstant (not :oconstant) :cconstant)
    320 	)
    321     )
    322 
    323     (indreduce :expression
    324 	t
    325 	((:expression :eol :indent :expression)
    326 	 (:expression :eol :expression)
    327 	)
    328     )
    329 
    330     (indreduce :exp-comma
    331 	t
    332 	((:expression :comma)
    333 	)
    334     )
    335 
    336     ;; A semicollon, start a statement
    337     (indreduce :stat
    338 	t
    339 	((:semi))
    340     )
    341 
    342     ;; Expression following (possibly empty) statement
    343     (indreduce :stat
    344 	t
    345 	(((or :expression :exp-comma) :stat))
    346     )
    347 
    348     ;; Multiline statements
    349     (indreduce :stat
    350 	t
    351 	(((or :expression :exp-comma) :eol :indent :stat)
    352 	 ;; rule below may have removed the :indent
    353 	 ((or :expression :exp-comma) :eol :stat)
    354 	)
    355     )
    356 
    357     (indinit	c-exp-indent)
    358     ;; XXX This rule avoids parsing large amounts of code
    359     (indreduce :stat
    360 	t
    361 	;; Eat eol if following expression
    362 	((:indent :stat :eol)
    363 	 (:indent :stat)
    364 	)
    365 	(if
    366 	    (or
    367 		(null c-exp-indent)
    368 		(/= (cdar c-exp-indent) (+ *ind-offset* *ind-length*))
    369 	    )
    370 	    ;; A new statement, i.e. not just joining a multiline one
    371 	    (push
    372 		(cons
    373 		    (offset-indentation *ind-offset* :resolve t)
    374 		    (+ *ind-offset* *ind-length*)
    375 		)
    376 		c-exp-indent
    377 	    )
    378 	    ;; Update start of statement
    379 	    (rplaca
    380 		(car c-exp-indent)
    381 		(offset-indentation *ind-offset* :resolve t)
    382 	    )
    383 	)
    384 	(when (consp (cdr c-exp-indent))
    385 	    (if (and
    386 		    (zerop c-complex)
    387 		    (zerop c-cases)
    388 		    (zerop c-bra)
    389 		    (= (caar c-exp-indent) (caadr c-exp-indent))
    390 		)
    391 		;; Two statements with the same indentation
    392 		(progn
    393 		    (setq *indent* (caar c-exp-indent))
    394 		    (indent-macro-reject-left)
    395 		)
    396 		;; Different indentation or complex state
    397 		(progn
    398 		    (rplacd c-exp-indent nil)
    399 		    (setq c-complex 0)
    400 		)
    401 	    )
    402 	)
    403     )
    404 
    405     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    406     ;; Handle braces
    407     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    408     (indreduce :stat
    409 	;; If block finishes before current line, group as a statement
    410 	(< (+ *ind-offset* *ind-length*) *ind-start*)
    411 	((:obrace (not :obrace) :cbrace))
    412     )
    413     (indreduce :obrace
    414 	;; If not in the first line
    415 	(< *ind-offset* *ind-start*)
    416 	;; If the opening { is the first non blank char in the line
    417 	((:indent :obrace))
    418 	(setq *indent* (offset-indentation (+ *ind-offset* *ind-length*)))
    419 
    420 	;; XXX This may be the starting brace of a switch
    421 	(setq c-case-flag nil)
    422 	(indent-macro-reject-left)
    423     )
    424 
    425     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    426     ;; Labels
    427     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    428     ;; XXX this frequently doesn't do what is expected, should redefine
    429     ;; some rules, as it frequently will dedent while typing something
    430     ;; like  test ? exp1 : exp2
    431     ;;                   ^ dedents here because it reduces everything
    432     ;;			   before ':' to a single :expression token.
    433     (indreduce :label
    434 	t
    435 	((:indent :expression :collon :eol))
    436 	(when (and *label-dedent* (>= *ind-offset* *ind-start*))
    437 	    (setq
    438 		*indent*
    439 		(- (offset-indentation *ind-offset* :resolve t) *base-indent*)
    440 	    )
    441 	    (indent-macro-reject-left)
    442 	)
    443     )
    444 
    445     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    446     ;; Handle if
    447     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    448     (indreduce :if
    449 	t
    450 	((:c-if :parens)
    451 	)
    452 	(incf c-complex)
    453     )
    454 
    455     (indreduce :else
    456 	t
    457 	((:c-else))
    458 	(incf c-complex)
    459     )
    460 
    461     ;; Join
    462     (indreduce :else-if
    463 	t
    464 	((:else :if)
    465 	 (:else :eol :indent :if)
    466 	)
    467 	(incf c-complex)
    468     )
    469 
    470     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    471     ;; Handle for
    472     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    473     ;; Join with the parentheses
    474     (indreduce :for
    475 	t
    476 	((:c-for :parens)
    477 	)
    478 	(incf c-complex)
    479     )
    480     ;; Before current line, simplify
    481     (indreduce :stat
    482 	(< (+ *ind-offset* *ind-length*) *ind-point*)
    483 	((:for :stat)
    484 	)
    485     )
    486 
    487     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    488     ;; Handle while and do
    489     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    490     (indreduce :while
    491 	t
    492 	((:c-while :parens)
    493 	)
    494 	(incf c-complex)
    495     )
    496     (indreduce :stat
    497 	t
    498 	((:do :stat :while)
    499 	 (:while :stat)
    500 	)
    501     )
    502 
    503     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    504     ;; Handle switch
    505     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    506     (indinit			c-case-flag)
    507 
    508     (indreduce :switch
    509 	t
    510 	((:c-switch :parens)
    511 	)
    512     )
    513     ;; Transform in a statement
    514     (indreduce :stat
    515 	(< (+ *ind-offset* *ind-length*) *ind-start*)
    516 	((:switch :stat)
    517 	 ;; Do it now or some rule may stop parsing, and calculate
    518 	 ;; a wrong indentation for nested switches
    519 	 (:switch :eol :indent :stat)
    520 	)
    521     )
    522     ;; An open switch
    523     (indreduce :obrace
    524 	(and
    525 	    (<= c-braces 0)
    526 	    (> *ind-start* *ind-offset*)
    527 	)
    528 	((:indent :switch :obrace)
    529 	)
    530 	(setq
    531 	    *indent* (offset-indentation *ind-offset* :resolve t)
    532 	    c-case-flag nil
    533 	)
    534 	(indent-macro-reject-left)
    535     )
    536     (indreduce :obrace
    537 	(and
    538 	    (<= c-braces 0)
    539 	    (> *ind-start* *ind-offset*)
    540 	)
    541 	((:indent :switch :eol :indent :obrace)
    542 	)
    543 	(setq
    544 	    *indent* (- (offset-indentation *ind-offset* :resolve t) *base-indent*)
    545 	    c-case-flag nil
    546 	)
    547 	(and *brace-indent* (incf *indent* *base-indent*))
    548 	(indent-macro-reject-left)
    549     )
    550     ;; Before current line
    551     (indreduce :case
    552 	(and
    553 	    (or
    554 		(not *case-indent*)
    555 		(prog1 c-case-flag (setq c-case-flag t))
    556 	    )
    557 	    (<= c-braces 0)
    558 	    (< *ind-offset* *ind-start*)
    559 	)
    560 	((:indent :case)
    561 	)
    562 	(setq
    563 	    *indent* (offset-indentation *ind-offset* :resolve t)
    564 	    c-case-flag nil
    565 	)
    566 	(indent-macro-reject-left)
    567     )
    568     (indreduce :case
    569 	t
    570 	((:c-case :expression :collon)
    571 	 (:c-default :collon)
    572 	 ;; Assume that it is yet being edited, or adjusting indentation
    573 	 (:c-case)
    574 	 (:c-default)
    575 	)
    576 	(and (>= *ind-offset* *ind-start*)
    577 	    (incf c-cases)
    578 	)
    579     )
    580 
    581     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    582     ;; Handle parentheses and brackets
    583     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    584     ;; Reduce matches
    585     (indreduce :parens
    586 	t
    587 	((:oparen (not :oparen) :cparen))
    588 	(when
    589 	    (and
    590 		(< *ind-offset* *ind-start*)
    591 		(> (+ *ind-offset* *ind-length*) *ind-start*)
    592 	    )
    593 	    (setq *indent* (1+ (offset-indentation *ind-offset* :align t)))
    594 	    (indent-macro-reject-left)
    595 	)
    596     )
    597     (indreduce :bracks
    598 	t
    599 	((:obrack (not :obrack) :cbrack))
    600 	(when
    601 	    (and
    602 		(< *ind-offset* *ind-start*)
    603 		(> (+ *ind-offset* *ind-length*) *ind-start*)
    604 	    )
    605 	    (setq *indent* (1+ (offset-indentation *ind-offset* :align t)))
    606 	    (indent-macro-reject-left)
    607 	)
    608     )
    609 
    610     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    611     ;; Assuming previous lines have correct indentation, this allows
    612     ;; resolving the indentation fastly
    613     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    614     ;; Line ended with an open brace
    615     (indreduce :obrace
    616 	(< *ind-offset* *ind-start*)
    617 	((:indent (or :for :while :if :else-if :else :do) :obrace)
    618 	)
    619 	(setq *indent* (offset-indentation *ind-offset* :resolve t))
    620 	(indent-macro-reject-left)
    621     )
    622     ;; Adjust indentation level if current line starts with an open brace
    623     (indreduce nil
    624 	(< *ind-offset* *ind-start* (+ *ind-offset* *ind-length*))
    625 	 ;; Just set initial indentation
    626 	((:indent (or :for :while :if :else-if :else :do) :eol :indent :obrace)
    627 	)
    628 	(setq
    629 	    *indent*
    630 	    (- (offset-indentation *ind-offset* :resolve t) *base-indent*)
    631 	)
    632 	(and *brace-indent* (incf *indent* *base-indent*))
    633 	(indent-macro-reject-left)
    634     )
    635     ;; Previous rule failed, current line does not start with an open brace
    636     (indreduce :flow
    637 	;; first statement is in current line
    638 	(and
    639 	    (<= c-braces 0)
    640 	    (> (+ *ind-offset* *ind-length*) *ind-start* *ind-offset*)
    641 	)
    642 	((:indent (or :for :while :if :else-if :else :do) :eol :indent)
    643 	)
    644 	(setq *indent* (offset-indentation *ind-offset* :resolve t))
    645 	(indent-macro-reject-left)
    646     )
    647 
    648     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    649     ;; Simplify, remove old (:eol :indent)
    650     ;; This must be the last rule, to avoid not matching the
    651     ;; rules for fast calculation of indentation above
    652     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    653     (indreduce nil
    654 	(> *ind-offset* c-prev-offset)
    655 	((:eol :indent))
    656     )
    657 
    658 
    659     (indinit			(c-flow 0))
    660 
    661     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    662     ;; If
    663     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    664     (indinit			c-if-flow)
    665     (indresolve :if
    666 	(and (< *ind-offset* *ind-start*)
    667 	    (push c-flow c-if-flow)
    668 	    (incf *indent* *base-indent*)
    669 	    (incf c-flow)
    670 	)
    671     )
    672     (indresolve (:else-if :else)
    673 	(when c-if-flow
    674 	    (while (< c-flow (car c-if-flow))
    675 		(incf *indent* *base-indent*)
    676 		(incf c-flow)
    677 	    )
    678 	    (or (eq *ind-token* :else-if) (pop c-if-flow))
    679 	)
    680 	(and (< *ind-offset* *ind-start*)
    681 	    (incf *indent* *base-indent*)
    682 	    (incf c-flow)
    683 	)
    684     )
    685 
    686 
    687     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    688     ;; For/while/do
    689     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    690     (indinit			c-do-flow)
    691     (indresolve (:for :while :do)
    692 	(if (eq *ind-token* :do)
    693 	    (and (< *ind-offset* *ind-start*) (push c-flow c-do-flow))
    694 	    (when (and c-do-flow (eq *ind-token* :while))
    695 		(while (< c-flow (car c-do-flow))
    696 		    (incf *indent* *base-indent*)
    697 		    (incf c-flow)
    698 		)
    699 		(pop c-do-flow)
    700 	    )
    701 	)
    702 	(and (< *ind-offset* *ind-start*)
    703 	    (incf *indent* *base-indent*)
    704 	    (incf c-flow)
    705 	)
    706     )
    707 
    708 
    709     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    710     ;; Switch
    711     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    712     (indresolve :switch
    713 	(setq c-case-flag nil)
    714     )
    715     (indresolve (:case :c-case)
    716 	(if (< *ind-offset* *ind-start*)
    717 	    (or c-case-flag
    718 		(setq
    719 		    *indent*
    720 		    (+ (offset-indentation *ind-offset* :resolve t)
    721 			*base-indent*
    722 		    )
    723 		)
    724 	    )
    725 	    (if c-case-flag
    726 		(and (= (decf c-cases) 0)
    727 		    (decf *indent* *base-indent*)
    728 		)
    729 		(or *case-indent*
    730 		    (decf *indent* *base-indent*)
    731 		)
    732 	    )
    733 	)
    734 	(setq c-case-flag t)
    735     )
    736 
    737 
    738     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    739     ;; Braces/flow control
    740     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    741     (indresolve :flow
    742 	(incf *indent* *base-indent*)
    743     )
    744     (indresolve :obrace
    745 	(and (< *ind-offset* *ind-start*)
    746 	    (incf *indent* *base-indent*)
    747 	)
    748     )
    749     (indresolve :cbrace
    750 	(decf *indent* *base-indent*)
    751 	(and *case-indent* c-case-flag
    752 	    (decf *indent* *base-indent*)
    753 	    (setq c-case-flag nil)
    754 	)
    755 	(and (not *offset*) (>= *ind-offset* *ind-start*)
    756 	    (setq *offset* *ind-offset*)
    757 	)
    758     )
    759 
    760 
    761     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    762     ;; Statements
    763     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    764     (indresolve :stat
    765 	(when (< *ind-offset* *ind-start*)
    766 	    (while (> c-flow 0)
    767 		(setq
    768 		    *indent*	(- *indent* *base-indent*)
    769 		    c-flow	(1- c-flow)
    770 		)
    771 	    )
    772 	)
    773 	(and
    774 	    *cont-indent*
    775 	    (< *ind-offset* *ind-start*)
    776 	    (> (+ *ind-offset* *ind-length*) *ind-start*)
    777 	    (incf *indent* *base-indent*)
    778 	)
    779     )
    780 
    781     (indresolve :expression
    782 	(and
    783 	    *cont-indent*
    784 	    (zerop c-bra)
    785 	    (> *indent* 0)
    786 	    (< *ind-offset* *ind-start*)
    787 	    (> (+ *ind-offset* *ind-length*) *ind-start*)
    788 	    (incf *indent* *base-indent*)
    789 	)
    790     )
    791 
    792     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    793     ;; Open
    794     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    795     (indresolve (:oparen :obrack)
    796 	(and (< *ind-offset* *ind-start*)
    797 	    (setq *indent* (1+ (offset-indentation *ind-offset* :align t)))
    798 	)
    799     )
    800 )
    801 
    802 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    803 ;; Find a "good" offset to start parsing backwards, so that it should
    804 ;; always generate the same results.
    805 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    806 (defun c-offset-indent (&aux char (point (point)))
    807     ;; Skip spaces forward
    808     (while (member (setq char (char-after point)) indent-spaces)
    809 	(incf point)
    810     )
    811     (or (characterp char) (return-from c-offset-indent point))
    812 
    813     ;;	Skip word chars
    814     (when (alphanumericp char)
    815 	(while (and (setq char (char-after point)) (alphanumericp char))
    816 	    (incf point)
    817 	)
    818 	(or (characterp char) (return-from c-offset-indent point))
    819 
    820 	;; Skip spaces forward
    821 	(while (member (setq char (char-after point)) indent-spaces)
    822 	    (incf point)
    823 	)
    824 	(or (characterp char) (return-from c-offset-indent point))
    825     )
    826 
    827     ;; don't include " or ' to avoid parsing strings "inverted"
    828     (if (member char '(#\Newline #\" #\')) point (1+ point))
    829 )
    830 (compile 'c-offset-indent)
    831 
    832 (defun c-should-indent (options)
    833     (when (hash-table-p options)
    834 	;; check if previous line has extra spaces
    835 	(and (gethash :trim-blank-lines options)
    836 	    (indent-clear-empty-line)
    837 	)
    838 
    839 	;; indentation disabled?
    840 	(and (gethash :disable-indent options)
    841 	    (return-from c-should-indent)
    842 	)
    843 
    844 	(let*
    845 	    (
    846 	    (point (point))
    847 	    (start (scan point :eol :left))
    848 	    (char (char-before point))
    849 	    offset
    850 	    match
    851 	    text
    852 	    )
    853 
    854 	    ;; at the start of an empty file
    855 	    (or (characterp char)
    856 		(return-from c-should-indent)
    857 	    )
    858 
    859 	    ;; if at bol and should indent only when starting a line
    860 	    (and (gethash :only-newline-indent options)
    861 		(return-from c-should-indent (= point start))
    862 	    )
    863 
    864 	    (and
    865 		(char= char #\;)
    866 		(gethash :newline-after-semi options)
    867 		(return-from c-should-indent t)
    868 	    )
    869 
    870 	    ;; if one of these was typed, must check indentation
    871 	    (and (member char '(#\{ #\} #\: #\] #\) #\#))
    872 		(return-from c-should-indent t)
    873 	    )
    874 
    875 	    ;; at the start of a line
    876 	    (and (= point start)
    877 		(return-from c-should-indent (gethash :newline-indent options))
    878 	    )
    879 
    880 	    ;; if first character
    881 	    (and (= point (1+ start))
    882 		(return-from c-should-indent t)
    883 	    )
    884 
    885 	    ;; check if is the first non-blank character in a new line
    886 	    (when
    887 		(and
    888 		    (gethash :cont-indent options)
    889 		    (= point (scan point :eol :right))
    890 		    (alphanumericp char)
    891 		)
    892 		(setq offset (1- point))
    893 		(while
    894 		    (and
    895 			(> offset start)
    896 			(member (char-before offset) indent-spaces)
    897 		    )
    898 		    (decf offset)
    899 		)
    900 		;; line has only one character with possible spaces before it
    901 		(and (<= offset start)
    902 		    (return-from c-should-indent t)
    903 		)
    904 	    )
    905 
    906 	    ;; check for keywords that change indentation
    907 	    (when (alphanumericp char)
    908 		(setq offset (1- point))
    909 		(while
    910 		    (and
    911 			(alphanumericp (char-before offset))
    912 			(> offset start)
    913 		    )
    914 		    (decf offset)
    915 		)
    916 		(setq
    917 		    text	(read-text offset (- point offset))
    918 		    match	(re-exec #.(re-comp "(case|else|while)\\w?\\>")
    919 				    text)
    920 		)
    921 		(and
    922 		    (consp match)
    923 		    (return-from c-should-indent (<= (- (caar match) offset) 2))
    924 		)
    925 	    )
    926 	)
    927     )
    928     ;; Should not indent
    929     nil
    930 )
    931 (compile 'c-should-indent)
    932 
    933 
    934 (defun c-indent-check (syntax syntable options
    935 		       &aux start point char left brace change)
    936     (setq
    937 	point	(point)
    938 	char	(char-before point)
    939 	left	point
    940 	brace	(member char '(#\{ #\}))
    941     )
    942 
    943     (when
    944 	(and brace (gethash :newline-before-brace options))
    945 	(setq start (scan point :eol :left))
    946 	(while
    947 	    (and
    948 		(> (decf left) start)
    949 		(member (char-before left) indent-spaces)
    950 	    )
    951 	    ;; skip blanks
    952 	)
    953 	(when (> left start)
    954 	    (replace-text left left (string #\Newline))
    955 	    (c-indent syntax syntable)
    956 	    (setq change t)
    957 	)
    958     )
    959 
    960     (when
    961 	(or
    962 	    (and brace (not change) (gethash :newline-after-brace options))
    963 	    (and (char= char #\;) (gethash :newline-after-semi options))
    964 	)
    965 	(setq left (point))
    966 	(replace-text left left (string #\Newline))
    967 	(goto-char (1+ left))
    968 	(c-indent syntax syntable)
    969     )
    970 )
    971 
    972 (defun c-indent (syntax syntable)
    973     (let*
    974 	(
    975 	(options (syntax-options syntax))
    976 	*base-indent*
    977 	*brace-indent*
    978 	*case-indent*
    979 	*label-dedent*
    980 	*cont-indent*
    981 	)
    982 
    983 	(or (c-should-indent options) (return-from c-indent))
    984 
    985 	(setq
    986 	    *base-indent*	(gethash :indentation options 4)
    987 	    *brace-indent*	(gethash :brace-indent options nil)
    988 	    *case-indent*	(gethash :case-indent options t)
    989 	    *label-dedent*	(gethash :label-dedent options t)
    990 	    *cont-indent*	(gethash :cont-indent options t)
    991 	)
    992 
    993 	(indent-macro
    994 	    *c-mode-indent*
    995 	    (c-offset-indent)
    996 	    (gethash :emulate-tabs options)
    997 	)
    998 
    999 	(c-indent-check syntax syntable options)
   1000     )
   1001 )
   1002 (compile 'c-indent)
   1003 
   1004 (defsyntax *c-mode* :main nil #'c-indent *c-mode-options*
   1005     ;;  All recognized C keywords.
   1006     (syntoken
   1007 	(string-concat
   1008 	    "\\<("
   1009 	    "asm|auto|break|case|catch|char|class|const|continue|default|"
   1010 	    "delete|do|double|else|enum|extern|float|for|friend|goto|if|"
   1011 	    "inline|int|long|new|operator|private|protected|public|register|"
   1012 	    "return|short|signed|sizeof|static|struct|switch|template|this|"
   1013 	    "throw|try|typedef|union|unsigned|virtual|void|volatile|while"
   1014 	    ")\\>")
   1015 	:property *prop-keyword*)
   1016 
   1017     ;; Numbers, this is optional, comment this rule if xedit is
   1018     ;; too slow to load c files.
   1019     (syntoken
   1020 	(string-concat
   1021 	    "\\<("
   1022 	    ;; Integers
   1023 	    "(\\d+|0x\\x+)(u|ul|ull|l|ll|lu|llu)?|"
   1024 	    ;; Floats
   1025 	    "\\d+\\.?\\d*(e[+-]?\\d+)?[lf]?"
   1026 	    ")\\>")
   1027 	:icase t
   1028 	:property *prop-number*
   1029     )
   1030 
   1031     ;; String start rule.
   1032     (syntoken "\"" :nospec t :begin :string :contained t)
   1033 
   1034     ;; Character start rule.
   1035     (syntoken "'" :nospec t :begin :character :contained t)
   1036 
   1037     ;; Preprocessor start rule.
   1038     (syntoken "^\\s*#\\s*\\w+" :begin :preprocessor :contained t)
   1039 
   1040     ;; Comment start rule.
   1041     (syntoken "/*" :nospec t :begin :comment :contained t)
   1042 
   1043     ;; C++ style comments.
   1044     (syntoken "//.*" :property *prop-comment*)
   1045 
   1046     ;; Punctuation, this is also optional, comment this rule if xedit is
   1047     ;; too slow to load c files.
   1048     (syntoken "[][(){}/*+:;=<>,&.!%|^~?-][][(){}*+:;=<>,&.!%|^~?-]?"
   1049 	:property *prop-punctuation*)
   1050 
   1051 
   1052     ;; Rules for comments.
   1053     (syntable :comment *prop-comment* #'default-indent
   1054 	;; Match nested comments as an error.
   1055 	(syntoken "/*" :nospec t :property *prop-error*)
   1056 
   1057 	(syntoken "XXX|TODO|FIXME" :property *prop-annotation*)
   1058 
   1059 	;;  Rule to finish a comment.
   1060 	(syntoken "*/" :nospec t :switch -1)
   1061     )
   1062 
   1063     ;; Rules for strings.
   1064     (syntable :string *prop-string* #'default-indent
   1065 	;; Ignore escaped characters, this includes \".
   1066 	(syntoken "\\\\.")
   1067 
   1068 	;; Match, most, printf arguments.
   1069 	(syntoken "%%|%([+-]?\\d+)?(l?[deEfgiouxX]|[cdeEfgiopsuxX])"
   1070 	    :property *prop-format*)
   1071 
   1072 	;; Ignore continuation in the next line.
   1073 	(syntoken "\\\\$")
   1074 
   1075 	;; Rule to finish a string.
   1076 	(syntoken "\"" :nospec t :switch -1)
   1077 
   1078 	;; Don't allow strings continuing in the next line.
   1079 	(syntoken ".?$" :begin :error)
   1080     )
   1081 
   1082     ;; Rules for characters.
   1083     (syntable :character *prop-constant* nil
   1084 	;; Ignore escaped characters, this includes \'.
   1085 	(syntoken "\\\\.")
   1086 
   1087 	;; Ignore continuation in the next line.
   1088 	(syntoken "\\\\$")
   1089 
   1090 	;; Rule to finish a character constant.
   1091 	(syntoken "'" :nospec t :switch -1)
   1092 
   1093 	;; Don't allow constants continuing in the next line.
   1094 	(syntoken ".?$" :begin :error)
   1095     )
   1096 
   1097     ;;  Rules for preprocessor.
   1098     (syntable :preprocessor *prop-preprocessor* #'default-indent
   1099 	;;  Preprocessor includes comments.
   1100 	(syntoken "/*" :nospec t :begin :comment :contained t)
   1101 
   1102 	;;  Ignore strings and constants in the same line and finishes table
   1103 	;; This is kind hackish, but must be done because the current parser
   1104 	;; will not flag eol. Maybe it could be extended to properly handle
   1105 	;; and have an internal flag to tell it to pass again if there
   1106 	;; is a regex that can match eol on an empty string.
   1107 	;;  A test is already done (but at compile time) to not allow patterns
   1108 	;; that match an empty string (but allow patterns matching
   1109 	;; bol, eol or both on an empty string).
   1110 	(syntoken "\"([^\\\"]|\\\\.)*\"$" :property *prop-string* :switch -1)
   1111 	(syntoken "'([^']|\\\\.)*'$" :property *prop-constant* :switch -1)
   1112 
   1113 	;;  Ignore strings and constants in the same line
   1114 	(syntoken "\"([^\\\"]|\\\\.)*\"" :property *prop-string*)
   1115 	(syntoken "'([^']|\\\\.)*'" :property *prop-constant*)
   1116 
   1117 	;;  Ignore lines finishing with a backslash.
   1118 	(syntoken "\\\\$")
   1119 
   1120 	;; multiline strings
   1121 	(syntoken "\"" :nospec t :begin :string)
   1122 
   1123 	;; multiline constants
   1124 	(syntoken "'" :nospec t :begin :character)
   1125 
   1126 	;;  C++ style comments
   1127 	(syntoken "//.*$" :property *prop-comment* :switch -1)
   1128 
   1129 	;; Return to previous state if end of line found.
   1130 	(syntoken ".?$" :switch -1)
   1131     )
   1132 
   1133     (syntable :error *prop-error* nil
   1134 	(syntoken "^.*$" :switch -2)
   1135     )
   1136 
   1137     ;;  You may also want to comment this rule if the parsing is
   1138     ;; noticeably slow.
   1139     (syntoken "\\c" :property *prop-control*)
   1140 )
   1141