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