From 0a193e032ba1ecf3f003e027e833dc9d274cb740 Mon Sep 17 00:00:00 2001 From: Kaleb Keithley Date: Fri, 14 Nov 2003 16:49:22 +0000 Subject: Initial revision --- lisp/modules/progmodes/c.lsp | 1118 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1118 insertions(+) create mode 100644 lisp/modules/progmodes/c.lsp (limited to 'lisp/modules/progmodes/c.lsp') diff --git a/lisp/modules/progmodes/c.lsp b/lisp/modules/progmodes/c.lsp new file mode 100644 index 0000000..bc4474b --- /dev/null +++ b/lisp/modules/progmodes/c.lsp @@ -0,0 +1,1118 @@ +;; +;; Copyright (c) 2002 by The XFree86 Project, Inc. +;; +;; Permission is hereby granted, free of charge, to any person obtaining a +;; copy of this software and associated documentation files (the "Software"), +;; to deal in the Software without restriction, including without limitation +;; the rights to use, copy, modify, merge, publish, distribute, sublicense, +;; and/or sell copies of the Software, and to permit persons to whom the +;; Software is furnished to do so, subject to the following conditions: +;; +;; The above copyright notice and this permission notice shall be included in +;; all copies or substantial portions of the Software. +;; +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +;; THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +;; OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +;; SOFTWARE. +;; +;; Except as contained in this notice, the name of the XFree86 Project shall +;; not be used in advertising or otherwise to promote the sale, use or other +;; dealings in this Software without prior written authorization from the +;; XFree86 Project. +;; +;; Author: Paulo César Pereira de Andrade +;; +;; +;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/c.lsp,v 1.26 2003/01/29 03:05:54 paulo Exp $ +;; + +(require "syntax") +(require "indent") +(in-package "XEDIT") + +(defsynprop *prop-format* + "format" + :font "*lucidatypewriter-medium-r*12*" + :foreground "RoyalBlue2" + :underline t +) + +(defsynoptions *c-DEFAULT-style* + ;; Positive number. Basic indentation. + (:indentation . 4) + + ;; Boolean. Support for GNU style indentation. + (:brace-indent . nil) + + ;; Boolean. Add one indentation level to case and default? + (:case-indent . t) + + ;; Boolean. Remove one indentation level for labels? + (:label-dedent . t) + + ;; Boolean. Add one indentation level to continuations? + (:cont-indent . t) + + ;; Boolean. Move cursor to the indent column after pressing ? + (:newline-indent . t) + + ;; Boolean. Set to T if tabs shouldn't be used to fill indentation. + (:emulate-tabs . nil) + + ;; Boolean. Force a newline before braces? + (:newline-before-brace . nil) + + ;; Boolean. Force a newline after braces? + (:newline-after-brace . nil) + + ;; Boolean. Force a newline after semicolons? + (:newline-after-semi . nil) + + ;; Boolean. Only calculate indentation after pressing ? + ;; This may be useful if the parser does not always + ;; do what the user expects... + (:only-newline-indent . nil) + + ;; Boolean. Remove extra spaces from previous line. + ;; This should default to T when newline-indent is not NIL. + (:trim-blank-lines . t) + + ;; Boolean. If this hash-table entry is set, no indentation is done. + ;; Useful to temporarily disable indentation. + (:disable-indent . nil) +) + +;; BSD like style +(defsynoptions *c-BSD-style* + (:indentation . 8) + (:brace-indent . nil) + (:case-indent . nil) + (:label-dedent . t) + (:cont-indent . t) + (:newline-indent . t) + (:emulate-tabs . nil) + (:newline-before-brace . nil) + (:newline-after-brace . t) + (:newline-after-semi . t) + (:trim-blank-lines . t) +) + +;; GNU like style +(defsynoptions *c-GNU-style* + (:indentation . 2) + (:brace-indent . t) + (:case-indent . nil) + (:label-dedent . t) + (:cont-indent . t) + (:newline-indent . nil) + (:emulate-tabs . nil) + (:newline-before-brace . t) + (:newline-after-brace . t) + (:newline-after-semi . t) + (:trim-blank-lines . nil) +) + +;; K&R like style +(defsynoptions *c-K&R-style* + (:indentation . 5) + (:brace-indent . nil) + (:case-indent . nil) + (:label-dedent . t) + (:cont-indent . t) + (:newline-indent . t) + (:emulate-tabs . t) + (:newline-before-brace . t) + (:newline-after-brace . t) + (:newline-after-semi . t) + (:trim-blank-lines . t) +) + +(defvar *c-styles* '( + ("xedit" . *c-DEFAULT-style*) + ("BSD" . *c-BSD-style*) + ("GNU" . *c-GNU-style*) + ("K&R" . *c-K&R-style*) +)) + +(defvar *c-mode-options* *c-DEFAULT-style*) +; (setq *c-mode-options* *c-gnu-style*) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; This is a very lazy "pattern matcher" for the C language. +;; If the syntax in the code is not correct, it may get confused, and +;; because it is "lazy" some wrong constructs will be recognized as +;; correct when reducing patterns. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defindent *c-mode-indent* :main + ;; this must be the first token + (indtoken "^\\s*" :start-of-line) + (indtoken "\\" :c-case) + (indtoken "\\" :c-default) + (indtoken "\\" :do) + (indtoken "\\" :c-if) + (indtoken "\\" :c-else) + (indtoken "\\" :c-for) + (indtoken "\\" :c-switch) + (indtoken "\\" :c-while) + ;; Match identifiers and numbers as an expression + (indtoken "\\w+" :expression) + (indtoken ";" :semi :nospec t) + (indtoken "," :comma :nospec t) + (indtoken ":" :collon :nospec t) + ;; Ignore spaces before collon, this avoids dedenting ternary + ;; and bitfield definitions as the parser does not distinguish + ;; labels from those, another option would be to use the pattern + ;; "\\w+:", but this way should properly handle labels generated + ;; by macros, example: `MACRO_LABEL(value):' + (indtoken "\\s+:" nil) + + (indinit (c-braces 0)) + (indtoken "{" + :obrace + :nospec t + :code (decf c-braces) + ) + (indtoken "}" + :cbrace + :nospec t + :begin :braces + :code (incf c-braces) + ) + (indtable :braces + (indtoken "{" + :obrace + :nospec t + :switch -1 + :code (decf c-braces) + ) + (indtoken "}" + :cbrace + :nospec t + :begin :braces + :code (incf c-braces) + ) + ) + + (indinit (c-bra 0)) + (indtoken ")" :cparen :nospec t :code (incf c-bra)) + (indtoken "(" :oparen :nospec t :code (decf c-bra)) + (indtoken "]" :cbrack :nospec t :code (incf c-bra)) + (indtoken "[" :obrack :nospec t :code (decf c-bra)) + (indtoken "\\\\$" :continuation) + + ;; C++ style comment, disallow other tokens to match inside comment + (indtoken "//.*$" nil) + + (indtoken "#" :hash :nospec t) + + ;; if in the same line, reduce now, this must be done because the + ;; delimiters are identical + (indtoken "'([^\\']|\\\\.)*'" :expression) + (indtoken "\"([^\\\"]|\\\\.)*\"" :expression) + + (indtoken "\"" :cstring :nospec t :begin :string) + + (indtoken "'" :cconstant :nospec t :begin :constant) + + (indtoken "*/" :ccomment :nospec t :begin :comment) + ;; this must be the last token + (indtoken "$" :end-of-line) + + (indtable :string + ;; Ignore escaped characters + (indtoken "\\." nil) + ;; Return to the toplevel when the start of the string is found + (indtoken "\"" :ostring :nospec t :switch -1) + ) + (indtable :constant + ;; Ignore escaped characters + (indtoken "\\." nil) + ;; Return to the toplevel when the start of the character is found + (indtoken "'" :oconstant :nospec t :switch -1) + ) + (indtable :comment + (indtoken "/*" :ocomment :nospec t :switch -1) + ) + + ;; "Complex" statements + (indinit (c-complex 0) (c-cases 0)) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Order of reduce rules here is important, process comment, + ;; continuations, preprocessor and set states when an eol is found. + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + (indinit (c-offset (point-max)) + (c-prev-offset c-offset) + ) + (indreduce :indent + t + ((:start-of-line)) + (and (= *ind-start* *ind-offset*) + (setq + *offset* (+ *ind-offset* *ind-length*) + ) + ) + (setq + c-prev-offset c-offset + c-offset *ind-offset* + ) + ) + + ;; Delete comments + (indreduce nil + t + ((:ocomment nil :ccomment)) + ) + + ;; Join in a single token to simplify removal of possible multiline + ;; preprocessor directives + (indinit c-continuation) + (indreduce :continuation + t + ((:continuation :end-of-line)) + (setq c-continuation t) + ) + + (indreduce :eol + t + ((:end-of-line)) + ;; Anything after the eol offset is safe to parse now + (setq c-continuation nil) + ) + + ;; Delete blank lines + (indreduce nil + t + ((:indent :eol)) + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Preprocessor + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indreduce nil + (>= *ind-offset* *ind-start*) + ((:indent :hash)) + (setq *indent* 0) + (indent-macro-reject-left) + ) + (indreduce nil + t + ((:indent :hash nil :eol)) + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Expressions + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indreduce :expression + t + ;; Reduce to a single expression + ((:expression :parens) + (:expression :bracks) + (:expression :expression) + ;; These may be multiline + (:ostring (not :ostring) :cstring) + (:oconstant (not :oconstant) :cconstant) + ) + ) + + (indreduce :expression + t + ((:expression :eol :indent :expression) + (:expression :eol :expression) + ) + ) + + (indreduce :exp-comma + t + ((:expression :comma) + ) + ) + + ;; A semicollon, start a statement + (indreduce :stat + t + ((:semi)) + ) + + ;; Expression following (possibly empty) statement + (indreduce :stat + t + (((or :expression :exp-comma) :stat)) + ) + + ;; Multiline statements + (indreduce :stat + t + (((or :expression :exp-comma) :eol :indent :stat) + ;; rule below may have removed the :indent + ((or :expression :exp-comma) :eol :stat) + ) + ) + + (indinit c-exp-indent) + ;; XXX This rule avoids parsing large amounts of code + (indreduce :stat + t + ;; Eat eol if following expression + ((:indent :stat :eol) + (:indent :stat) + ) + (if + (or + (null c-exp-indent) + (/= (cdar c-exp-indent) (+ *ind-offset* *ind-length*)) + ) + ;; A new statement, i.e. not just joining a multiline one + (push + (cons + (offset-indentation *ind-offset* :resolve t) + (+ *ind-offset* *ind-length*) + ) + c-exp-indent + ) + ;; Update start of statement + (rplaca + (car c-exp-indent) + (offset-indentation *ind-offset* :resolve t) + ) + ) + (when (consp (cdr c-exp-indent)) + (if (and + (zerop c-complex) + (zerop c-cases) + (zerop c-bra) + (= (caar c-exp-indent) (caadr c-exp-indent)) + ) + ;; Two statements with the same indentation + (progn + (setq *indent* (caar c-exp-indent)) + (indent-macro-reject-left) + ) + ;; Different indentation or complex state + (progn + (rplacd c-exp-indent nil) + (setq c-complex 0) + ) + ) + ) + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Handle braces + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indreduce :stat + ;; If block finishes before current line, group as a statement + (< (+ *ind-offset* *ind-length*) *ind-start*) + ((:obrace (not :obrace) :cbrace)) + ) + (indreduce :obrace + ;; If not in the first line + (< *ind-offset* *ind-start*) + ;; If the opening { is the first non blank char in the line + ((:indent :obrace)) + (setq *indent* (offset-indentation (+ *ind-offset* *ind-length*))) + + ;; XXX This may be the starting brace of a switch + (setq c-case-flag nil) + (indent-macro-reject-left) + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Labels + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; XXX this frequently doesn't do what is expected, should redefine + ;; some rules, as it frequently will dedent while typing something + ;; like test ? exp1 : exp2 + ;; ^ dedents here because it reduces everything + ;; before ':' to a single :expression token. + (indreduce :label + t + ((:indent :expression :collon :eol)) + (when (and *label-dedent* (>= *ind-offset* *ind-start*)) + (setq + *indent* + (- (offset-indentation *ind-offset* :resolve t) *base-indent*) + ) + (indent-macro-reject-left) + ) + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Handle if + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indreduce :if + t + ((:c-if :parens) + ) + (incf c-complex) + ) + + (indreduce :else + t + ((:c-else)) + (incf c-complex) + ) + + ;; Join + (indreduce :else-if + t + ((:else :if) + (:else :eol :indent :if) + ) + (incf c-complex) + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Handle for + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Join with the parentheses + (indreduce :for + t + ((:c-for :parens) + ) + (incf c-complex) + ) + ;; Before current line, simplify + (indreduce :stat + (< (+ *ind-offset* *ind-length*) *ind-point*) + ((:for :stat) + ) + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Handle while and do + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indreduce :while + t + ((:c-while :parens) + ;; Assume that it is yet being edited, or adjusting indentation + (:c-while) + ) + (incf c-complex) + ) + (indreduce :stat + t + ((:do :stat :while) + (:while :stat) + ) + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Handle switch + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indinit c-case-flag) + + (indreduce :switch + t + ((:c-switch :parens) + ) + ) + ;; Transform in a statement + (indreduce :stat + (< (+ *ind-offset* *ind-length*) *ind-start*) + ((:switch :stat) + ;; Do it now or some rule may stop parsing, and calculate + ;; a wrong indentation for nested switches + (:switch :eol :indent :stat) + ) + ) + ;; An open switch + (indreduce :obrace + (and + (<= c-braces 0) + (> *ind-start* *ind-offset*) + ) + ((:indent :switch :obrace) + ) + (setq + *indent* (offset-indentation *ind-offset* :resolve t) + c-case-flag nil + ) + (indent-macro-reject-left) + ) + (indreduce :obrace + (and + (<= c-braces 0) + (> *ind-start* *ind-offset*) + ) + ((:indent :switch :eol :indent :obrace) + ) + (setq + *indent* (- (offset-indentation *ind-offset* :resolve t) *base-indent*) + c-case-flag nil + ) + (and *brace-indent* (incf *indent* *base-indent*)) + (indent-macro-reject-left) + ) + ;; Before current line + (indreduce :case + (and + (or + (not *case-indent*) + (prog1 c-case-flag (setq c-case-flag t)) + ) + (<= c-braces 0) + (< *ind-offset* *ind-start*) + ) + ((:indent :case) + ) + (setq + *indent* (offset-indentation *ind-offset* :resolve t) + c-case-flag nil + ) + (indent-macro-reject-left) + ) + (indreduce :case + t + ((:c-case :expression :collon) + (:c-default :collon) + ;; Assume that it is yet being edited, or adjusting indentation + (:c-case) + (:c-default) + ) + (and (>= *ind-offset* *ind-start*) + (incf c-cases) + ) + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Handle parentheses and brackets + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Reduce matches + (indreduce :parens + t + ((:oparen (not :oparen) :cparen)) + (when + (and + (< *ind-offset* *ind-start*) + (> (+ *ind-offset* *ind-length*) *ind-start*) + ) + (setq *indent* (1+ (offset-indentation *ind-offset* :align t))) + (indent-macro-reject-left) + ) + ) + (indreduce :bracks + t + ((:obrack (not :obrack) :cbrack)) + (when + (and + (< *ind-offset* *ind-start*) + (> (+ *ind-offset* *ind-length*) *ind-start*) + ) + (setq *indent* (1+ (offset-indentation *ind-offset* :align t))) + (indent-macro-reject-left) + ) + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Assuming previous lines have correct indentation, this allows + ;; resolving the indentation fastly + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Line ended with an open brace + (indreduce :obrace + (< *ind-offset* *ind-start*) + ((:indent (or :for :while :if :else-if :else :do) :obrace) + ) + (setq *indent* (offset-indentation *ind-offset* :resolve t)) + (indent-macro-reject-left) + ) + ;; Adjust indentation level if current line starts with an open brace + (indreduce nil + (< *ind-offset* *ind-start* (+ *ind-offset* *ind-length*)) + ;; Just set initial indentation + ((:indent (or :for :while :if :else-if :else :do) :eol :indent :obrace) + ) + (setq + *indent* + (- (offset-indentation *ind-offset* :resolve t) *base-indent*) + ) + (and *brace-indent* (incf *indent* *base-indent*)) + (indent-macro-reject-left) + ) + ;; Previous rule failed, current line does not start with an open brace + (indreduce :flow + ;; first statement is in current line + (and + (<= c-braces 0) + (> (+ *ind-offset* *ind-length*) *ind-start* *ind-offset*) + ) + ((:indent (or :for :while :if :else-if :else :do) :eol :indent) + ) + (setq *indent* (offset-indentation *ind-offset* :resolve t)) + (indent-macro-reject-left) + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Simplify, remove old (:eol :indent) + ;; This must be the last rule, to avoid not matching the + ;; rules for fast calculation of indentation above + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indreduce nil + (> *ind-offset* c-prev-offset) + ((:eol :indent)) + ) + + + (indinit (c-flow 0)) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; If + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indinit c-if-flow) + (indresolve :if + (and (< *ind-offset* *ind-start*) + (push c-flow c-if-flow) + (incf *indent* *base-indent*) + (incf c-flow) + ) + ) + (indresolve (:else-if :else) + (when c-if-flow + (while (< c-flow (car c-if-flow)) + (incf *indent* *base-indent*) + (incf c-flow) + ) + (or (eq *ind-token* :else-if) (pop c-if-flow)) + ) + (and (< *ind-offset* *ind-start*) + (incf *indent* *base-indent*) + (incf c-flow) + ) + ) + + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; For/while/do + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indinit c-do-flow) + (indresolve (:for :while :do) + (if (eq *ind-token* :do) + (and (< *ind-offset* *ind-start*) (push c-flow c-do-flow)) + (when (and c-do-flow (eq *ind-token* :while)) + (while (< c-flow (car c-do-flow)) + (incf *indent* *base-indent*) + (incf c-flow) + ) + (pop c-do-flow) + ) + ) + (and (< *ind-offset* *ind-start*) + (incf *indent* *base-indent*) + (incf c-flow) + ) + ) + + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Switch + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indresolve :switch + (setq c-case-flag nil) + ) + (indresolve (:case :c-case) + (if (< *ind-offset* *ind-start*) + (or c-case-flag + (setq + *indent* + (+ (offset-indentation *ind-offset* :resolve t) + *base-indent* + ) + ) + ) + (if c-case-flag + (and (= (decf c-cases) 0) + (decf *indent* *base-indent*) + ) + (or *case-indent* + (decf *indent* *base-indent*) + ) + ) + ) + (setq c-case-flag t) + ) + + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Braces/flow control + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indresolve :flow + (incf *indent* *base-indent*) + ) + (indresolve :obrace + (and (< *ind-offset* *ind-start*) + (incf *indent* *base-indent*) + ) + ) + (indresolve :cbrace + (decf *indent* *base-indent*) + (and *case-indent* c-case-flag + (decf *indent* *base-indent*) + (setq c-case-flag nil) + ) + (and (not *offset*) (>= *ind-offset* *ind-start*) + (setq *offset* *ind-offset*) + ) + ) + + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Statements + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indresolve :stat + (when (< *ind-offset* *ind-start*) + (while (> c-flow 0) + (setq + *indent* (- *indent* *base-indent*) + c-flow (1- c-flow) + ) + ) + ) + (and + *cont-indent* + (< *ind-offset* *ind-start*) + (> (+ *ind-offset* *ind-length*) *ind-start*) + (incf *indent* *base-indent*) + ) + ) + + (indresolve :expression + (and + *cont-indent* + (zerop c-bra) + (> *indent* 0) + (< *ind-offset* *ind-start*) + (> (+ *ind-offset* *ind-length*) *ind-start*) + (incf *indent* *base-indent*) + ) + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Open + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (indresolve (:oparen :obrack) + (and (< *ind-offset* *ind-start*) + (setq *indent* (1+ (offset-indentation *ind-offset* :align t))) + ) + ) +) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Find a "good" offset to start parsing backwards, so that it should +;; always generate the same results. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defun c-offset-indent (&aux char (point (point))) + ;; Skip spaces forward + (while (member (setq char (char-after point)) indent-spaces) + (incf point) + ) + (or (characterp char) (return-from c-offset-indent point)) + + ;; Skip word chars + (when (alphanumericp char) + (while (and (setq char (char-after point)) (alphanumericp char)) + (incf point) + ) + (or (characterp char) (return-from c-offset-indent point)) + + ;; Skip spaces forward + (while (member (setq char (char-after point)) indent-spaces) + (incf point) + ) + (or (characterp char) (return-from c-offset-indent point)) + ) + + ;; don't include " or ' to avoid parsing strings "inverted" + (if (member char '(#\Newline #\" #\')) point (1+ point)) +) +(compile 'c-offset-indent) + +(defun c-should-indent (options) + (when (hash-table-p options) + ;; check if previous line has extra spaces + (and (gethash :trim-blank-lines options) + (indent-clear-empty-line) + ) + + ;; indentation disabled? + (and (gethash :disable-indent options) + (return-from c-should-indent) + ) + + (let* + ( + (point (point)) + (start (scan point :eol :left)) + (char (char-before point)) + offset + match + text + ) + + ;; at the start of an empty file + (or (characterp char) + (return-from c-should-indent) + ) + + ;; if at bol and should indent only when starting a line + (and (gethash :only-newline-indent options) + (return-from c-should-indent (= point start)) + ) + + (and + (char= char #\;) + (gethash :newline-after-semi options) + (return-from c-should-indent t) + ) + + ;; if one of these was typed, must check indentation + (and (member char '(#\{ #\} #\: #\] #\) #\#)) + (return-from c-should-indent t) + ) + + ;; at the start of a line + (and (= point start) + (return-from c-should-indent (gethash :newline-indent options)) + ) + + ;; if first character + (and (= point (1+ start)) + (return-from c-should-indent t) + ) + + ;; check if is the first non-blank character in a new line + (when + (and + (gethash :cont-indent options) + (= point (scan point :eol :right)) + (alphanumericp char) + ) + (setq offset (1- point)) + (while + (and + (> offset start) + (member (char-before offset) indent-spaces) + ) + (decf offset) + ) + ;; line has only one character with possible spaces before it + (and (<= offset start) + (return-from c-should-indent t) + ) + ) + + ;; check for keywords that change indentation + (when (alphanumericp char) + (setq offset (1- point)) + (while + (and + (alphanumericp (char-before offset)) + (> offset start) + ) + (decf offset) + ) + (setq + text (read-text offset (- point offset)) + match (re-exec #.(re-comp "(case|else|while)\\w?\\>") + text) + ) + (and + (consp match) + (return-from c-should-indent (<= (- (caar match) offset) 2)) + ) + ) + ) + ) + ;; Should not indent + nil +) +(compile 'c-should-indent) + + +(defun c-indent-check (syntax syntable options + &aux start point char left brace change) + (setq + point (point) + char (char-before point) + left point + brace (member char '(#\{ #\})) + ) + + (when + (and brace (gethash :newline-before-brace options)) + (setq start (scan point :eol :left)) + (while + (and + (> (decf left) start) + (member (char-before left) indent-spaces) + ) + ;; skip blanks + ) + (when (> left start) + (replace-text left left (string #\Newline)) + (c-indent syntax syntable) + (setq change t) + ) + ) + + (when + (or + (and brace (not change) (gethash :newline-after-brace options)) + (and (char= char #\;) (gethash :newline-after-semi options)) + ) + (setq left (point)) + (replace-text left left (string #\Newline)) + (goto-char (1+ left)) + (c-indent syntax syntable) + ) +) + +(defun c-indent (syntax syntable) + (let* + ( + (options (syntax-options syntax)) + *base-indent* + *brace-indent* + *case-indent* + *label-dedent* + *cont-indent* + ) + + (or (c-should-indent options) (return-from c-indent)) + + (setq + *base-indent* (gethash :indentation options 4) + *brace-indent* (gethash :brace-indent options nil) + *case-indent* (gethash :case-indent options t) + *label-dedent* (gethash :label-dedent options t) + *cont-indent* (gethash :cont-indent options t) + ) + + (indent-macro + *c-mode-indent* + (c-offset-indent) + (gethash :emulate-tabs options) + ) + + (c-indent-check syntax syntable options) + ) +) +(compile 'c-indent) + +(defsyntax *c-mode* :main nil #'c-indent *c-mode-options* + ;; All recognized C keywords. + (syntoken + (string-concat + "\\<(" + "asm|auto|break|case|catch|char|class|const|continue|default|" + "delete|do|double|else|enum|extern|float|for|friend|goto|if|" + "inline|int|long|new|operator|private|protected|public|register|" + "return|short|signed|sizeof|static|struct|switch|template|this|" + "throw|try|typedef|union|unsigned|virtual|void|volatile|while" + ")\\>") + :property *prop-keyword*) + + ;; Numbers, this is optional, comment this rule if xedit is + ;; too slow to load c files. + (syntoken + (string-concat + "\\<(" + ;; Integers + "(\\d+|0x\\x+)(u|ul|ull|l|ll|lu|llu)?|" + ;; Floats + "\\d+\\.?\\d*(e[+-]?\\d+)?[lf]?" + ")\\>") + :icase t + :property *prop-number* + ) + + ;; String start rule. + (syntoken "\"" :nospec t :begin :string :contained t) + + ;; Character start rule. + (syntoken "'" :nospec t :begin :character :contained t) + + ;; Preprocessor start rule. + (syntoken "^\\s*#\\s*\\w+" :begin :preprocessor :contained t) + + ;; Comment start rule. + (syntoken "/*" :nospec t :begin :comment :contained t) + + ;; C++ style comments. + (syntoken "//.*" :property *prop-comment*) + + ;; Punctuation, this is also optional, comment this rule if xedit is + ;; too slow to load c files. + (syntoken "[][(){}/*+:;=<>,&.!%|^~?-][][(){}*+:;=<>,&.!%|^~?-]?" + :property *prop-punctuation*) + + + ;; Rules for comments. + (syntable :comment *prop-comment* #'default-indent + ;; Match nested comments as an error. + (syntoken "/*" :nospec t :property *prop-error*) + + (syntoken "XXX|TODO|FIXME" :property *prop-annotation*) + + ;; Rule to finish a comment. + (syntoken "*/" :nospec t :switch -1) + ) + + ;; Rules for strings. + (syntable :string *prop-string* #'default-indent + ;; Ignore escaped characters, this includes \". + (syntoken "\\\\.") + + ;; Match, most, printf arguments. + (syntoken "%%|%([+-]?\\d+)?(l?[deEfgiouxX]|[cdeEfgiopsuxX])" + :property *prop-format*) + + ;; Ignore continuation in the next line. + (syntoken "\\\\$") + + ;; Rule to finish a string. + (syntoken "\"" :nospec t :switch -1) + + ;; Don't allow strings continuing in the next line. + (syntoken ".?$" :begin :error) + ) + + ;; Rules for characters. + (syntable :character *prop-constant* nil + ;; Ignore escaped characters, this includes \'. + (syntoken "\\\\.") + + ;; Ignore continuation in the next line. + (syntoken "\\\\$") + + ;; Rule to finish a character constant. + (syntoken "'" :nospec t :switch -1) + + ;; Don't allow constants continuing in the next line. + (syntoken ".?$" :begin :error) + ) + + ;; Rules for preprocessor. + (syntable :preprocessor *prop-preprocessor* #'default-indent + ;; Preprocessor includes comments. + (syntoken "/*" :nospec t :begin :comment :contained t) + + ;; Ignore lines finishing with a backslash. + (syntoken "\\\\$") + + ;; Return to previous state if end of line found. + (syntoken ".?$" :switch -1) + ) + + (syntable :error *prop-error* nil + (syntoken "^.*$" :switch -2) + ) + + ;; You may also want to comment this rule if the parsing is + ;; noticeably slow. + (syntoken "\\c" :property *prop-control*) +) -- cgit v1.2.3