Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2817-BinPackingSpeedup/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/IndentEngine/IndentState.cs @ 15303

Last change on this file since 15303 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 47.3 KB
Line 
1//
2// IndentState.cs
3//
4// Author:
5//       Matej Miklečić <matej.miklecic@gmail.com>
6//
7// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com)
8//
9// Permission is hereby granted, free of charge, to any person obtaining a copy
10// of this software and associated documentation files (the "Software"), to deal
11// in the Software without restriction, including without limitation the rights
12// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13// copies of the Software, and to permit persons to whom the Software is
14// furnished to do so, subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be included in
17// all copies or substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25// THE SOFTWARE.
26using System;
27using System.Collections.Generic;
28using System.Globalization;
29using System.Linq;
30using System.Text;
31
32namespace ICSharpCode.NRefactory.CSharp
33{
34  #region IndentState
35
36  /// <summary>
37  ///     The base class for all indentation states.
38  ///     Each state defines the logic for indentation based on chars that
39  ///     are pushed to it.
40  /// </summary>
41  public abstract class IndentState : ICloneable
42  {
43    #region Properties
44
45    /// <summary>
46    ///     The indentation engine using this state.
47    /// </summary>
48    public CSharpIndentEngine Engine;
49
50    /// <summary>
51    ///     The parent state.
52    ///     This state can use the indentation levels of its parent.
53    ///     When this state exits, the engine returns to the parent.
54    /// </summary>
55    public IndentState Parent;
56
57    /// <summary>
58    ///     The indentation of the current line.
59    ///     This is set when the state is created and will be changed to
60    ///     <see cref="NextLineIndent"/> when the <see cref="CSharpIndentEngine.newLineChar"/>
61    ///     is pushed.
62    /// </summary>
63    public Indent ThisLineIndent;
64
65    /// <summary>
66    ///     The indentation of the next line.
67    ///     This is set when the state is created and can change depending
68    ///     on the pushed chars.
69    /// </summary>
70    public Indent NextLineIndent;
71
72    #endregion
73
74    #region Constructors
75
76    protected IndentState()
77    {
78    }
79
80    /// <summary>
81    ///     Creates a new indentation state that is a copy of the given
82    ///     prototype.
83    /// </summary>
84    /// <param name="prototype">
85    ///     The prototype state.
86    /// </param>
87    /// <param name="engine">
88    ///     The engine of the new state.
89    /// </param>
90    protected IndentState(IndentState prototype, CSharpIndentEngine engine)
91    {
92      Engine = engine;
93      Parent = prototype.Parent != null ? prototype.Parent.Clone(engine) : null;
94
95      ThisLineIndent = prototype.ThisLineIndent.Clone();
96      NextLineIndent = prototype.NextLineIndent.Clone();
97    }
98
99    #endregion
100
101    #region IClonable
102
103    object ICloneable.Clone()
104    {
105      return Clone(Engine);
106    }
107
108    public abstract IndentState Clone(CSharpIndentEngine engine);
109
110    #endregion
111
112    #region Methods
113
114    internal void Initialize (CSharpIndentEngine engine, IndentState parent = null)
115    {
116      Parent = parent;
117      Engine = engine;
118
119      InitializeState();
120    }
121
122    /// <summary>
123    ///     Initializes the state:
124    ///       - sets the default indentation levels.
125    /// </summary>
126    /// <remarks>
127    ///     Each state can override this method if it needs a different
128    ///     logic for setting up the default indentations.
129    /// </remarks>
130    public virtual void InitializeState()
131    {
132      ThisLineIndent = new Indent(Engine.textEditorOptions);
133      NextLineIndent = ThisLineIndent.Clone();
134    }
135
136    /// <summary>
137    ///     Actions performed when this state exits.
138    /// </summary>
139    public virtual void OnExit()
140    {
141      if (Parent != null)
142      {
143        // if a state exits on the newline character, it has to push
144        // it back to its parent (and so on recursively if the parent
145        // state also exits). Otherwise, the parent state wouldn't
146        // know that the engine isn't on the same line anymore.
147        if (Engine.currentChar == Engine.newLineChar)
148        {
149          Parent.Push(Engine.newLineChar);
150        }
151
152        // when a state exits the engine stays on the same line and this
153        // state has to override the Parent.ThisLineIndent.
154        Parent.ThisLineIndent = ThisLineIndent.Clone();
155      }
156    }
157
158    /// <summary>
159    ///     Changes the current state of the <see cref="CSharpIndentEngine"/> using the current
160    ///     state as the parent for the new one.
161    /// </summary>
162    /// <typeparam name="T">
163    ///     The type of the new state. Must be assignable from <see cref="IndentState"/>.
164    /// </typeparam>
165    public void ChangeState<T>()
166      where T : IndentState, new ()
167    {
168      var t = new T();
169      t.Initialize(Engine, Engine.currentState);
170      Engine.currentState = t;
171    }
172
173    /// <summary>
174    ///     Exits this state by setting the current state of the
175    ///     <see cref="CSharpIndentEngine"/> to this state's parent.
176    /// </summary>
177    public void ExitState()
178    {
179      OnExit();
180      Engine.currentState = Engine.currentState.Parent ?? new GlobalBodyState(Engine);
181    }
182
183    /// <summary>
184    ///     Common logic behind the push method.
185    ///     Each state can override this method and implement its own logic.
186    /// </summary>
187    /// <param name="ch">
188    ///     The current character that's being pushed.
189    /// </param>
190    public virtual void Push(char ch)
191    {
192      // replace ThisLineIndent with NextLineIndent if the newLineChar is pushed
193      if (ch == Engine.newLineChar)
194      {
195        var delta = Engine.textEditorOptions.ContinuationIndent;
196        while (NextLineIndent.CurIndent - ThisLineIndent.CurIndent > delta &&
197             NextLineIndent.PopIf(IndentType.Continuation)) ;
198        ThisLineIndent = NextLineIndent.Clone();
199      }
200    }
201
202    /// <summary>
203    ///     When derived, checks if the given sequence of chars form
204    ///     a valid keyword or variable name, depending on the state.
205    /// </summary>
206    /// <param name="keyword">
207    ///     A possible keyword.
208    /// </param>
209    public virtual void CheckKeyword(string keyword)
210    { }
211
212    /// <summary>
213    ///     When derived, checks if the given sequence of chars form
214    ///     a valid keyword or variable name, depending on the state.
215    /// </summary>
216    /// <param name="keyword">
217    ///     A possible keyword.
218    /// </param>
219    /// <remarks>
220    ///     This method should be called from <see cref="Push(char)"/>.
221    ///     It is left to derived classes to call this method because of
222    ///     performance issues.
223    /// </remarks>
224    public virtual void CheckKeywordOnPush(string keyword)
225    { }
226
227    #endregion
228  }
229
230  #endregion
231
232  #region Null state
233
234  /// <summary>
235  ///     Null state.
236  /// </summary>
237  /// <remarks>
238  ///     Doesn't define any transitions to new states.
239  /// </remarks>
240  public class NullState : IndentState
241  {
242    public NullState()
243    { }
244
245    public NullState(NullState prototype, CSharpIndentEngine engine)
246      : base(prototype, engine)
247    { }
248
249    public override void Push(char ch)
250    { }
251
252    public override IndentState Clone(CSharpIndentEngine engine)
253    {
254      return new NullState(this, engine);
255    }
256  }
257
258  #endregion
259
260  #region Brackets body states
261
262  #region Brackets body base
263
264  /// <summary>
265  ///     The base for all brackets body states.
266  /// </summary>
267  /// <remarks>
268  ///     Represents a block of code between a pair of brackets.
269  /// </remarks>
270  public abstract class BracketsBodyBaseState : IndentState
271  {
272 
273    /// <summary>
274    ///     When derived in a concrete bracket body state, represents
275    ///     the closed bracket character pair.
276    /// </summary>
277    public abstract char ClosedBracket { get; }
278
279    protected BracketsBodyBaseState()
280    { }
281
282    protected BracketsBodyBaseState(BracketsBodyBaseState prototype, CSharpIndentEngine engine)
283      : base(prototype, engine)
284    { }
285
286    public override void Push(char ch)
287    {
288      base.Push(ch);
289      switch (ch) {
290        case '#':
291          if (Engine.isLineStart)
292            ChangeState<PreProcessorState>();
293          break;
294        case '/':
295          if (Engine.previousChar == '/')
296            ChangeState<LineCommentState>();
297          break;
298        case '*':
299          if (Engine.previousChar == '/')
300            ChangeState<MultiLineCommentState>();
301          break;
302        case '"':
303          if (Engine.previousChar == '@')
304          {
305            ChangeState<VerbatimStringState>();
306          }
307          else
308          {
309            ChangeState<StringLiteralState>();
310          }
311          break;
312        case '\'':
313          ChangeState<CharacterState>();
314          break;
315        case '{':
316          ChangeState<BracesBodyState>();
317          break;
318        case '(':
319          ChangeState<ParenthesesBodyState>();
320          break;
321        case '[':
322          ChangeState<SquareBracketsBodyState>();
323          break;
324        default:
325          if (ch == ClosedBracket)
326            ExitState();
327          break;
328      }
329    }
330  }
331
332  #endregion
333
334  #region Braces body state
335
336  /// <summary>
337  ///     Braces body state.
338  /// </summary>
339  /// <remarks>
340  ///     Represents a block of code between { and }.
341  /// </remarks>
342  public class BracesBodyState : BracketsBodyBaseState
343  {
344    /// <summary>
345    ///     Type of the current block body.
346    /// </summary>
347    public Body CurrentBody;
348
349    /// <summary>
350    ///     Type of the next block body.
351    ///     Same as <see cref="CurrentBody"/> if none of the
352    ///     <see cref="Body"/> keywords have been read.
353    /// </summary>
354    public Body NextBody;
355
356    /// <summary>
357    ///     Type of the current statement.
358    /// </summary>
359    public Statement CurrentStatement
360    {
361      get
362      {
363        return currentStatement;
364      }
365      set
366      {
367        // clear NestedIfStatementLevels if this statement breaks the sequence
368        if (currentStatement == Statement.None && value != Statement.Else)
369        {
370          NestedIfStatementLevels.Clear();
371        }
372
373        currentStatement = value;
374      }
375    }
376    Statement currentStatement;
377
378    /// <summary>
379    ///    Contains indent levels of nested if statements.
380    /// </summary>
381    internal CloneableStack<Indent> NestedIfStatementLevels = new CloneableStack<Indent>();
382
383    /// <summary>
384    ///    Contains the indent level of the last statement or body keyword.
385    /// </summary>
386    public Indent LastBlockIndent;
387
388    /// <summary>
389    ///     True if the engine is on the right side of the equal operator '='.
390    /// </summary>
391    public bool IsRightHandExpression;
392
393    /// <summary>
394    ///     True if the '=' char has been pushed and it's not
395    ///     a part of a relational operator (&gt;=, &lt;=, !=, ==).
396    /// </summary>
397    public bool IsEqualCharPushed;
398
399    /// <summary>
400    ///     The indentation of the previous line.
401    /// </summary>
402    public int PreviousLineIndent;
403
404    /// <summary>
405    ///     True if the dot member (e.g. method invocation) indentation has
406    ///     been handled in the current statement.
407    /// </summary>
408    public bool IsMemberReferenceDotHandled;
409
410    public override char ClosedBracket
411    {
412      get { return '}'; }
413    }
414
415    public BracesBodyState()
416    {
417    }
418
419    public BracesBodyState(BracesBodyState prototype, CSharpIndentEngine engine)
420      : base(prototype, engine)
421    {
422      CurrentBody = prototype.CurrentBody;
423      NextBody = prototype.NextBody;
424      CurrentStatement = prototype.CurrentStatement;
425      NestedIfStatementLevels = prototype.NestedIfStatementLevels.Clone();
426      IsRightHandExpression = prototype.IsRightHandExpression;
427      IsEqualCharPushed = prototype.IsEqualCharPushed;
428      IsMemberReferenceDotHandled = prototype.IsMemberReferenceDotHandled;
429      LastBlockIndent = prototype.LastBlockIndent;
430      PreviousLineIndent = prototype.PreviousLineIndent;
431    }
432
433    public override void Push(char ch)
434    {
435      // handle IsRightHandExpression property
436      if (IsEqualCharPushed)
437      {
438        if (IsRightHandExpression)
439        {
440          if (ch == Engine.newLineChar)
441          {
442            NextLineIndent.RemoveAlignment();
443            NextLineIndent.Push(IndentType.Continuation);
444          }
445        }
446        // ignore "==" and "=>" operators
447        else if (ch != '=' && ch != '>')
448        {
449          IsRightHandExpression = true;
450
451          if (ch == Engine.newLineChar)
452          {
453            NextLineIndent.Push(IndentType.Continuation);
454          }
455          else
456          {
457            NextLineIndent.SetAlignment(Engine.column - NextLineIndent.CurIndent);
458          }
459        }
460
461        IsEqualCharPushed = ch == ' ' || ch == '\t';
462      }
463
464      if (ch == ';' || (ch == ',' && IsRightHandExpression))
465      {
466        OnStatementExit();
467      }
468      else if (ch == '=' && !(Engine.previousChar == '=' || Engine.previousChar == '<' || Engine.previousChar == '>' || Engine.previousChar == '!'))
469      {
470        IsEqualCharPushed = true;
471      }
472      else if (ch == '.' && !IsMemberReferenceDotHandled)
473      {
474        // OPTION: CSharpFormattingOptions.AlignToMemberReferenceDot
475        if (Engine.formattingOptions.AlignToMemberReferenceDot && !Engine.isLineStart)
476        {
477          IsMemberReferenceDotHandled = true;
478          NextLineIndent.RemoveAlignment();
479          NextLineIndent.SetAlignment(Engine.column - NextLineIndent.CurIndent - 1, true);
480        }
481        else if (Engine.isLineStart)
482        {
483          IsMemberReferenceDotHandled = true;
484
485          ThisLineIndent.RemoveAlignment();
486          while (ThisLineIndent.CurIndent > PreviousLineIndent &&
487                 ThisLineIndent.PopIf(IndentType.Continuation)) ;
488          ThisLineIndent.Push(IndentType.Continuation);
489          NextLineIndent = ThisLineIndent.Clone();
490        }
491      }
492      else if (ch == ':' && Engine.isLineStart && !IsRightHandExpression)
493      {
494        // try to capture ': base(...)', ': this(...)' and inherit statements when they are on a new line
495        ThisLineIndent.Push(IndentType.Continuation);
496      }
497      else if (ch == Engine.newLineChar)
498      {
499        PreviousLineIndent = ThisLineIndent.CurIndent;
500      }
501
502      if (Engine.wordToken.ToString() == "else")
503      {
504        CheckKeywordOnPush("else");
505      }
506
507      base.Push(ch);
508    }
509
510    public override void InitializeState()
511    {
512      ThisLineIndent = Parent.ThisLineIndent.Clone();
513      NextLineIndent = Parent.NextLineIndent.Clone();
514
515      // OPTION: IDocumentIndentEngine.EnableCustomIndentLevels
516      var parent = Parent as BracesBodyState;
517      if (parent == null || parent.LastBlockIndent == null || !Engine.EnableCustomIndentLevels)
518      {
519        NextLineIndent.RemoveAlignment();
520        NextLineIndent.PopIf(IndentType.Continuation);
521      }
522      else
523      {
524        NextLineIndent = parent.LastBlockIndent.Clone();
525      }
526
527      if (Engine.isLineStart)
528      {
529        ThisLineIndent = NextLineIndent.Clone();
530      }
531
532      CurrentBody = extractBody(Parent);
533      NextBody = Body.None;
534      CurrentStatement = Statement.None;
535
536      AddIndentation(CurrentBody);
537    }
538
539    public override IndentState Clone(CSharpIndentEngine engine)
540    {
541      return new BracesBodyState(this, engine);
542    }
543
544    public override void OnExit()
545    {
546      if (Parent is BracesBodyState && !((BracesBodyState)Parent).IsRightHandExpression)
547      {
548        ((BracesBodyState)Parent).OnStatementExit();
549      }
550
551      if (Engine.isLineStart)
552      {
553        ThisLineIndent.RemoveAlignment();
554        ThisLineIndent.PopTry();
555        BraceStyle style;
556        if (TryGetBraceStyle(this.CurrentBody, out style)) {
557          if (style == BraceStyle.NextLineShifted ||
558            style == BraceStyle.NextLineShifted2||
559            style == BraceStyle.BannerStyle) {
560            ThisLineIndent.Push(IndentType.Block);
561          }
562        }
563      }
564
565      base.OnExit();
566    }
567
568    /// <summary>
569    ///     Actions performed when the current statement exits.
570    /// </summary>
571    public virtual void OnStatementExit()
572    {
573      IsRightHandExpression = false;
574      IsMemberReferenceDotHandled = false;
575
576      NextLineIndent.RemoveAlignment();
577      NextLineIndent.PopWhile(IndentType.Continuation);
578
579      CurrentStatement = Statement.None;
580      NextBody = Body.None;
581      LastBlockIndent = null;
582    }
583
584    #region Helpers
585
586    /// <summary>
587    ///     Types of braces bodies.
588    /// </summary>
589    public enum Body
590    {
591      None,
592      Namespace,
593      Class,
594      Struct,
595      Interface,
596      Enum,
597      Switch,
598      Case,
599      Try,
600      Catch,
601      Finally
602    }
603
604    /// <summary>
605    ///     Types of statements.
606    /// </summary>
607    public enum Statement
608    {
609      None,
610      If,
611      Else,
612      Do,
613      While,
614      For,
615      Foreach,
616      Lock,
617      Using,
618      Return
619    }
620
621    static readonly Dictionary<string, Body> bodies = new Dictionary<string, Body>
622    {
623      { "namespace", Body.Namespace },
624      { "class", Body.Class },
625      { "struct", Body.Struct },
626      { "interface", Body.Interface },
627      { "enum", Body.Enum },
628      { "switch", Body.Switch },
629      { "try", Body.Try },
630      { "catch", Body.Catch },
631      { "finally", Body.Finally },
632    };
633
634    static readonly Dictionary<string, Statement> statements = new Dictionary<string, Statement>
635    {
636      { "if", Statement.If },
637      // { "else", Statement.Else }, // should be handled in CheckKeywordAtPush
638      { "do", Statement.Do },
639      { "while", Statement.While },
640      { "for", Statement.For },
641      { "foreach", Statement.Foreach },
642      { "lock", Statement.Lock },
643      { "using", Statement.Using },
644      { "return", Statement.Return },
645    };
646
647    static readonly HashSet<string> blocks = new HashSet<string>
648    {
649      "namespace",
650      "class",
651      "struct",
652      "interface",
653      "enum",
654      "switch",
655      "try",
656      "catch",
657      "finally",
658      "if",
659      "else",
660      "do",
661      "while",
662      "for",
663      "foreach",
664      "lock",
665      "using",
666    };
667
668    readonly string[] caseDefaultKeywords = {
669      "case",
670      "default"
671    };
672
673    readonly string[] classStructKeywords = {
674      "class",
675      "struct"
676    };
677
678    /// <summary>
679    ///     Checks if the given string is a keyword and sets the
680    ///     <see cref="NextBody"/> and the <see cref="CurrentStatement"/>
681    ///     variables appropriately.
682    /// </summary>
683    /// <param name="keyword">
684    ///     A possible keyword.
685    /// </param>
686    /// <remarks>
687    ///     This method is called from <see cref="Push(char)"/>
688    /// </remarks>
689    public override void CheckKeywordOnPush(string keyword)
690    {
691      if (keyword == "else")
692      {
693        CurrentStatement = Statement.Else;
694
695        // OPTION: CSharpFormattingOptions.AlignElseInIfStatements
696        if (!Engine.formattingOptions.AlignElseInIfStatements && NestedIfStatementLevels.Count > 0)
697        {
698          ThisLineIndent = NestedIfStatementLevels.Pop().Clone();
699          NextLineIndent = ThisLineIndent.Clone();
700        }
701
702        NextLineIndent.Push(IndentType.Continuation);
703      }
704
705      if (blocks.Contains(keyword) && Engine.NeedsReindent)
706      {
707        LastBlockIndent = Indent.ConvertFrom(Engine.CurrentIndent, ThisLineIndent, Engine.textEditorOptions);
708      }
709    }
710
711    /// <summary>
712    ///     Checks if the given string is a keyword and sets the
713    ///     <see cref="NextBody"/> and the <see cref="CurrentStatement"/>
714    ///     variables appropriately.
715    /// </summary>
716    /// <param name="keyword">
717    ///     A possible keyword.
718    /// </param>
719    public override void CheckKeyword(string keyword)
720    {
721      if (bodies.ContainsKey(keyword))
722      {
723        var isKeywordTemplateConstraint =
724          classStructKeywords.Contains(keyword) &&
725          (NextBody == Body.Class || NextBody == Body.Struct || NextBody == Body.Interface);
726
727        if (!isKeywordTemplateConstraint)
728        {
729          NextBody = bodies[keyword];
730        }
731      }
732      else if (caseDefaultKeywords.Contains(keyword) && CurrentBody == Body.Switch && Engine.isLineStartBeforeWordToken)
733      {
734        ChangeState<SwitchCaseState>();
735      }
736      else if (keyword == "where" && Engine.isLineStartBeforeWordToken)
737      {
738        // try to capture where (generic type constraint)
739        ThisLineIndent.Push(IndentType.Continuation);
740      }
741      else if (statements.ContainsKey(keyword))
742      {
743        Statement previousStatement = CurrentStatement;
744        CurrentStatement = statements[keyword];
745
746        // return if this is a using declaration or alias
747        if (CurrentStatement == Statement.Using &&
748           (this is GlobalBodyState || CurrentBody == Body.Namespace))
749        {
750          return;
751        }
752
753        // OPTION: CSharpFormattingOptions.AlignEmbeddedIfStatements
754        if (Engine.formattingOptions.AlignEmbeddedStatements &&
755          previousStatement == Statement.If &&
756          CurrentStatement == Statement.If)
757        {
758          ThisLineIndent.PopIf(IndentType.Continuation);
759          NextLineIndent.PopIf(IndentType.Continuation);
760        }
761
762        // OPTION: CSharpFormattingOptions.AlignEmbeddedStatements
763        if (Engine.formattingOptions.AlignEmbeddedStatements &&
764          previousStatement == Statement.Lock &&
765          CurrentStatement == Statement.Lock)
766        {
767          ThisLineIndent.PopIf(IndentType.Continuation);
768          NextLineIndent.PopIf(IndentType.Continuation);
769        }
770
771        // OPTION: CSharpFormattingOptions.AlignEmbeddedUsingStatements
772        if (Engine.formattingOptions.AlignEmbeddedStatements &&
773          previousStatement == Statement.Using &&
774          CurrentStatement == Statement.Using)
775        {
776          ThisLineIndent.PopIf(IndentType.Continuation);
777          NextLineIndent.PopIf(IndentType.Continuation);
778        }
779
780        // only add continuation for 'else' in 'else if' statement.
781        if (!(CurrentStatement == Statement.If && previousStatement == Statement.Else && !Engine.isLineStartBeforeWordToken))
782        {
783          NextLineIndent.Push(IndentType.Continuation);
784        }
785
786        if (CurrentStatement == Statement.If)
787        {
788          NestedIfStatementLevels.Push(ThisLineIndent);
789        }
790      }
791
792      if (blocks.Contains(keyword) && Engine.NeedsReindent)
793      {
794        LastBlockIndent = Indent.ConvertFrom(Engine.CurrentIndent, ThisLineIndent, Engine.textEditorOptions);
795      }
796    }
797
798    /// <summary>
799    ///     Pushes a new level of indentation depending on the given
800    ///     <paramref name="braceStyle"/>.
801    /// </summary>
802    void AddIndentation(BraceStyle braceStyle)
803    {
804      switch (braceStyle)
805      {
806        case BraceStyle.NextLineShifted:
807          ThisLineIndent.Push(IndentType.Block);
808          NextLineIndent.Push(IndentType.Block);
809          break;
810        case BraceStyle.DoNotChange:
811        case BraceStyle.EndOfLine:
812        case BraceStyle.EndOfLineWithoutSpace:
813        case BraceStyle.NextLine:
814        case BraceStyle.BannerStyle:
815          NextLineIndent.Push(IndentType.Block);
816          break;
817        case BraceStyle.NextLineShifted2:
818          ThisLineIndent.Push(IndentType.Block);
819          NextLineIndent.Push(IndentType.DoubleBlock);
820          break;
821      }
822    }
823
824    bool TryGetBraceStyle (Body body, out BraceStyle style)
825    {
826      style = BraceStyle.DoNotChange;
827      switch (body)
828      {
829        case Body.None:
830          if (!Engine.formattingOptions.IndentBlocks)
831            return false;
832          style = Engine.formattingOptions.StatementBraceStyle;
833          return true;
834        case Body.Namespace:
835          if (!Engine.formattingOptions.IndentNamespaceBody)
836            return false;
837          style = Engine.formattingOptions.NamespaceBraceStyle;
838          return true;
839        case Body.Class:
840          if (!Engine.formattingOptions.IndentClassBody)
841            return false;
842          style = Engine.formattingOptions.ClassBraceStyle;
843          return true;
844        case Body.Struct:
845          if (!Engine.formattingOptions.IndentStructBody)
846            return false;
847          style = Engine.formattingOptions.StructBraceStyle;
848          return true;
849        case Body.Interface:
850          if (!Engine.formattingOptions.IndentInterfaceBody)
851            return false;
852          style = Engine.formattingOptions.InterfaceBraceStyle;
853          return true;
854        case Body.Enum:
855          if (!Engine.formattingOptions.IndentEnumBody)
856            return false;
857          style = Engine.formattingOptions.EnumBraceStyle;
858          return true;
859        case Body.Switch:
860          if (!Engine.formattingOptions.IndentSwitchBody)
861            return false;
862          style = Engine.formattingOptions.StatementBraceStyle;
863          return true;
864        case Body.Try:
865        case Body.Catch:
866        case Body.Finally:
867          style = Engine.formattingOptions.StatementBraceStyle;
868          return true;
869      }
870      return false;
871    }
872
873    /// <summary>
874    ///     Pushes a new level of indentation depending on the given
875    ///     <paramref name="body"/>.
876    /// </summary>
877    void AddIndentation(Body body)
878    {
879      var isExpression = Parent is ParenthesesBodyState || Parent is SquareBracketsBodyState ||
880        (Parent is BracesBodyState && ((BracesBodyState)Parent).IsRightHandExpression);
881      if (isExpression && Engine.formattingOptions.IndentBlocksInsideExpressions && Engine.isLineStart)
882      {
883        AddIndentation(BraceStyle.NextLineShifted);
884      }
885
886      BraceStyle style;
887      if (TryGetBraceStyle(body, out style))
888      {
889        AddIndentation(style);
890      } else {
891        NextLineIndent.Push(IndentType.Empty);
892      }
893    }
894
895    /// <summary>
896    ///     Extracts the <see cref="CurrentBody"/> from the given state.
897    /// </summary>
898    /// <returns>
899    ///     The correct <see cref="Body"/> type for this state.
900    /// </returns>
901    static Body extractBody(IndentState state)
902    {
903      if (state != null && state is BracesBodyState)
904      {
905        return ((BracesBodyState)state).NextBody;
906      }
907
908      return Body.None;
909    }
910
911    #endregion
912  }
913
914  #endregion
915
916  #region Global body state
917
918  /// <summary>
919  ///     Global body state.
920  /// </summary>
921  /// <remarks>
922  ///     Represents the global space of the program.
923  /// </remarks>
924  public class GlobalBodyState : BracesBodyState
925  {
926    public override char ClosedBracket
927    {
928      get { return '\0'; }
929    }
930
931    public GlobalBodyState()
932    { }
933
934
935    public GlobalBodyState(CSharpIndentEngine engine)
936    {
937      Initialize (engine, null);
938    }
939
940    public GlobalBodyState(GlobalBodyState prototype, CSharpIndentEngine engine)
941      : base(prototype, engine)
942    { }
943
944    public override IndentState Clone(CSharpIndentEngine engine)
945    {
946      return new GlobalBodyState(this, engine);
947    }
948
949    public override void InitializeState()
950    {
951      ThisLineIndent = new Indent(Engine.textEditorOptions);
952      NextLineIndent = ThisLineIndent.Clone();
953    }
954  }
955
956  #endregion
957
958  #region Switch-case body state
959
960  /// <summary>
961  ///     Switch-case statement state.
962  /// </summary>
963  /// <remarks>
964  ///     Represents the block of code in one switch case (including default).
965  /// </remarks>
966  public class SwitchCaseState : BracesBodyState
967  {
968    public SwitchCaseState()
969    { }
970
971    public SwitchCaseState(SwitchCaseState prototype, CSharpIndentEngine engine)
972      : base(prototype, engine)
973    { }
974
975    public override void Push(char ch)
976    {
977      // on ClosedBracket both this state (a case or a default statement)
978      // and also the whole switch block (handled in the base class) must exit.
979      if (ch == ClosedBracket)
980      {
981        ExitState();
982        if (Parent is BracesBodyState)
983          Parent.OnExit();
984      }
985
986      base.Push(ch);
987    }
988
989    public override void InitializeState()
990    {
991      ThisLineIndent = Parent.ThisLineIndent.Clone();
992      NextLineIndent = ThisLineIndent.Clone();
993
994      // remove all continuations and extra spaces
995      ThisLineIndent.RemoveAlignment();
996      ThisLineIndent.PopWhile(IndentType.Continuation);
997
998      NextLineIndent.RemoveAlignment();
999      NextLineIndent.PopWhile(IndentType.Continuation);
1000
1001      if (Engine.formattingOptions.IndentCaseBody)
1002      {
1003        NextLineIndent.Push(IndentType.Block);
1004      }
1005      else
1006      {
1007        NextLineIndent.Push(IndentType.Empty);
1008      }
1009    }
1010
1011    static readonly string[] caseDefaultKeywords = {
1012      "case",
1013      "default"
1014    };
1015
1016    static readonly string[] breakContinueReturnGotoKeywords = {
1017      "break",
1018      "continue",
1019      "return",
1020      "goto"
1021    };
1022
1023    public override void CheckKeyword(string keyword)
1024    {
1025      if (caseDefaultKeywords.Contains(keyword) && Engine.isLineStartBeforeWordToken)
1026      {
1027        ExitState();
1028        ChangeState<SwitchCaseState>();
1029      }
1030      else if (breakContinueReturnGotoKeywords.Contains(keyword) && Engine.isLineStartBeforeWordToken)
1031      {
1032        // OPTION: Engine.formattingOptions.IndentBreakStatements
1033        if (!Engine.formattingOptions.IndentBreakStatements)
1034        {
1035          ThisLineIndent = Parent.ThisLineIndent.Clone();
1036        }
1037      }
1038
1039      base.CheckKeyword(keyword);
1040    }
1041
1042
1043    public override void OnExit()
1044    {
1045      //Parent.OnExit();
1046    }
1047
1048    public override IndentState Clone(CSharpIndentEngine engine)
1049    {
1050      return new SwitchCaseState(this, engine);
1051    }
1052  }
1053
1054  #endregion
1055
1056  #region Parentheses body state
1057
1058  /// <summary>
1059  ///     Parentheses body state.
1060  /// </summary>
1061  /// <remarks>
1062  ///     Represents a block of code between ( and ).
1063  /// </remarks>
1064  public class ParenthesesBodyState : BracketsBodyBaseState
1065  {
1066    /// <summary>
1067    ///     True if any char has been pushed.
1068    /// </summary>
1069    public bool IsSomethingPushed;
1070
1071    public override char ClosedBracket
1072    {
1073      get { return ')'; }
1074    }
1075
1076    public ParenthesesBodyState()
1077    { }
1078
1079    public ParenthesesBodyState(ParenthesesBodyState prototype, CSharpIndentEngine engine)
1080      : base(prototype, engine)
1081    {
1082      IsSomethingPushed = prototype.IsSomethingPushed;
1083    }
1084
1085    public override void Push(char ch)
1086    {
1087      if (ch == Engine.newLineChar)
1088      {
1089        if (Engine.formattingOptions.AnonymousMethodBraceStyle == BraceStyle.EndOfLine ||
1090          Engine.formattingOptions.AnonymousMethodBraceStyle == BraceStyle.EndOfLineWithoutSpace) {
1091          if (NextLineIndent.PopIf(IndentType.Continuation)) {
1092            NextLineIndent.Push(IndentType.Block);
1093          }
1094        }
1095      }
1096      else if (!IsSomethingPushed)
1097      {
1098        // OPTION: CSharpFormattingOptions.AlignToFirstMethodCallArgument
1099        if (Engine.formattingOptions.AlignToFirstMethodCallArgument)
1100        {
1101          NextLineIndent.PopTry();
1102          // align the next line at the beginning of the open bracket
1103          NextLineIndent.ExtraSpaces = Math.Max(0, Engine.column - NextLineIndent.CurIndent - 1);
1104        }
1105      }
1106
1107      base.Push(ch);
1108      IsSomethingPushed = true;
1109    }
1110
1111    public override void InitializeState()
1112    {
1113      ThisLineIndent = Parent.ThisLineIndent.Clone();
1114      NextLineIndent = ThisLineIndent.Clone();
1115      NextLineIndent.Push(IndentType.Continuation);
1116    }
1117
1118    public override IndentState Clone(CSharpIndentEngine engine)
1119    {
1120      return new ParenthesesBodyState(this, engine);
1121    }
1122
1123    public override void OnExit()
1124    {
1125      if (Engine.isLineStart)
1126      {
1127        if (ThisLineIndent.ExtraSpaces > 0)
1128        {
1129          ThisLineIndent.ExtraSpaces--;
1130        }
1131        else
1132        {
1133          ThisLineIndent.PopTry();
1134        }
1135      }
1136
1137      base.OnExit();
1138    }
1139  }
1140
1141  #endregion
1142
1143  #region Square brackets body state
1144
1145  /// <summary>
1146  ///     Square brackets body state.
1147  /// </summary>
1148  /// <remarks>
1149  ///     Represents a block of code between [ and ].
1150  /// </remarks>
1151  public class SquareBracketsBodyState : BracketsBodyBaseState
1152  {
1153    /// <summary>
1154    ///     True if any char has been pushed.
1155    /// </summary>
1156    public bool IsSomethingPushed;
1157
1158    public override char ClosedBracket
1159    {
1160      get { return ']'; }
1161    }
1162
1163    public SquareBracketsBodyState()
1164    { }
1165
1166    public SquareBracketsBodyState(SquareBracketsBodyState prototype, CSharpIndentEngine engine)
1167      : base(prototype, engine)
1168    {
1169      IsSomethingPushed = prototype.IsSomethingPushed;
1170    }
1171
1172    public override void Push(char ch)
1173    {
1174      if (ch == Engine.newLineChar)
1175      {
1176        if (NextLineIndent.PopIf(IndentType.Continuation))
1177        {
1178          NextLineIndent.Push(IndentType.Block);
1179        }
1180      }
1181      else if (!IsSomethingPushed)
1182      {
1183        // OPTION: CSharpFormattingOptions.AlignToFirstIndexerArgument
1184        if (Engine.formattingOptions.AlignToFirstIndexerArgument)
1185        {
1186          NextLineIndent.PopTry();
1187          // align the next line at the beginning of the open bracket
1188          NextLineIndent.ExtraSpaces = Math.Max(0, Engine.column - NextLineIndent.CurIndent - 1);
1189        }
1190      }
1191
1192      base.Push(ch);
1193      IsSomethingPushed = true;
1194    }
1195
1196    public override void InitializeState()
1197    {
1198      ThisLineIndent = Parent.ThisLineIndent.Clone();
1199      NextLineIndent = ThisLineIndent.Clone();
1200      NextLineIndent.Push(IndentType.Continuation);
1201    }
1202
1203    public override IndentState Clone(CSharpIndentEngine engine)
1204    {
1205      return new SquareBracketsBodyState(this, engine);
1206    }
1207
1208    public override void OnExit()
1209    {
1210      if (Engine.isLineStart)
1211      {
1212        if (ThisLineIndent.ExtraSpaces > 0)
1213        {
1214          ThisLineIndent.ExtraSpaces--;
1215        }
1216        else
1217        {
1218          ThisLineIndent.PopTry();
1219        }
1220      }
1221
1222      base.OnExit();
1223    }
1224  }
1225
1226  #endregion
1227
1228  #endregion
1229
1230  #region PreProcessor state
1231
1232  /// <summary>
1233  ///     PreProcessor directive state.
1234  /// </summary>
1235  /// <remarks>
1236  ///     Activated when the '#' char is pushed and the
1237  ///     <see cref="CSharpIndentEngine.isLineStart"/> is true.
1238  /// </remarks>
1239  public class PreProcessorState : IndentState
1240  {
1241    /// <summary>
1242    ///     The type of the preprocessor directive.
1243    /// </summary>
1244    public PreProcessorDirective DirectiveType;
1245
1246    /// <summary>
1247    ///     If <see cref="DirectiveType"/> is set (not equal to 'None'), this
1248    ///     stores the expression of the directive.
1249    /// </summary>
1250    public StringBuilder DirectiveStatement;
1251
1252    public PreProcessorState()
1253    {
1254      DirectiveType = PreProcessorDirective.None;
1255      DirectiveStatement = new StringBuilder();
1256    }
1257
1258    public PreProcessorState(PreProcessorState prototype, CSharpIndentEngine engine)
1259      : base(prototype, engine)
1260    {
1261      DirectiveType = prototype.DirectiveType;
1262      DirectiveStatement = new StringBuilder(prototype.DirectiveStatement.ToString());
1263    }
1264
1265    public override void Push(char ch)
1266    {
1267      // HACK: if this change would be left for the CheckKeyword method, we will lose
1268      //       it if the next pushed char is newLineChar since ThisLineIndent will be
1269      //       immediately replaced with NextLineIndent. As this most likely will
1270      //       happen, we check for "endregion" on every push.
1271      if (Engine.wordToken.ToString() == "endregion")
1272      {
1273        CheckKeywordOnPush("endregion");
1274      }
1275
1276      base.Push(ch);
1277
1278      if (DirectiveType != PreProcessorDirective.None)
1279      {
1280        DirectiveStatement.Append(ch);
1281      }
1282
1283      if (ch == Engine.newLineChar)
1284      {
1285        ExitState();
1286        switch (DirectiveType)
1287        {
1288          case PreProcessorDirective.If:
1289            Engine.ifDirectiveEvalResults.Push(eval(DirectiveStatement.ToString()));
1290            if (Engine.ifDirectiveEvalResults.Peek())
1291            {
1292              // the if/elif directive is true -> continue with the previous state
1293            }
1294            else
1295            {
1296              // the if/elif directive is false -> change to a state that will
1297              // ignore any chars until #endif or #elif
1298              ChangeState<PreProcessorCommentState>();
1299            }
1300            break;
1301          case PreProcessorDirective.Elif:
1302            if (Engine.ifDirectiveEvalResults.Count > 0)
1303            {
1304              if (!Engine.ifDirectiveEvalResults.Peek())
1305              {
1306                Engine.ifDirectiveEvalResults.Pop();
1307                goto case PreProcessorDirective.If;
1308              }
1309            }
1310            // previous if was true -> comment
1311            ChangeState<PreProcessorCommentState>();
1312            break;
1313          case PreProcessorDirective.Else:
1314            if (Engine.ifDirectiveEvalResults.Count > 0 && Engine.ifDirectiveEvalResults.Peek())
1315            {
1316              // some if/elif directive was true -> change to a state that will
1317              // ignore any chars until #endif
1318              ChangeState<PreProcessorCommentState>();
1319            }
1320            else
1321            {
1322              // none if/elif directives were true -> continue with the previous state
1323            }
1324            break;
1325          case PreProcessorDirective.Define:
1326            var defineSymbol = DirectiveStatement.ToString().Trim();
1327            if (!Engine.conditionalSymbols.Contains(defineSymbol))
1328            {
1329              Engine.conditionalSymbols.Add(defineSymbol);
1330            }
1331            break;
1332          case PreProcessorDirective.Undef:
1333            var undefineSymbol = DirectiveStatement.ToString().Trim();
1334            if (Engine.conditionalSymbols.Contains(undefineSymbol))
1335            {
1336              Engine.conditionalSymbols.Remove(undefineSymbol);
1337            }
1338            break;
1339          case PreProcessorDirective.Endif:
1340            // marks the end of this block
1341            Engine.ifDirectiveEvalResults.Pop();
1342            Engine.ifDirectiveIndents.Pop();
1343            break;
1344          case PreProcessorDirective.Region:
1345          case PreProcessorDirective.Pragma:
1346          case PreProcessorDirective.Warning:
1347          case PreProcessorDirective.Error:
1348          case PreProcessorDirective.Line:
1349            // continue with the previous state
1350            break;
1351        }
1352      }
1353    }
1354
1355    public override void InitializeState()
1356    {
1357      // OPTION: IndentPreprocessorStatements
1358      if (Engine.formattingOptions.IndentPreprocessorDirectives)
1359      {
1360        if (Engine.ifDirectiveIndents.Count > 0)
1361        {
1362          ThisLineIndent = Engine.ifDirectiveIndents.Peek().Clone();
1363        }
1364        else
1365        {
1366          ThisLineIndent = Parent.ThisLineIndent.Clone();
1367        }
1368      }
1369      else
1370      {
1371        ThisLineIndent = new Indent(Engine.textEditorOptions);
1372      }
1373
1374      NextLineIndent = Parent.NextLineIndent.Clone();
1375    }
1376
1377    static readonly Dictionary<string, PreProcessorDirective> preProcessorDirectives = new Dictionary<string, PreProcessorDirective>
1378    {
1379      { "if", PreProcessorDirective.If },
1380      { "elif", PreProcessorDirective.Elif },
1381      { "else", PreProcessorDirective.Else },
1382      { "endif", PreProcessorDirective.Endif },
1383      { "region", PreProcessorDirective.Region },
1384      { "endregion", PreProcessorDirective.Endregion },
1385      { "pragma", PreProcessorDirective.Pragma },
1386      { "warning", PreProcessorDirective.Warning },
1387      { "error", PreProcessorDirective.Error },
1388      { "line", PreProcessorDirective.Line },
1389      { "define", PreProcessorDirective.Define },
1390      { "undef", PreProcessorDirective.Undef }
1391    };
1392
1393    public override void CheckKeywordOnPush(string keyword)
1394    {
1395      if (keyword == "endregion")
1396      {
1397        DirectiveType = PreProcessorDirective.Endregion;
1398        ThisLineIndent = Parent.NextLineIndent.Clone();
1399      }
1400    }
1401
1402    public override void CheckKeyword(string keyword)
1403    {
1404      // check if the directive type has already been set
1405      if (DirectiveType != PreProcessorDirective.None)
1406      {
1407        return;
1408      }
1409
1410      if (preProcessorDirectives.ContainsKey(keyword))
1411      {
1412        DirectiveType = preProcessorDirectives[keyword];
1413
1414        // adjust the indentation for the region directive
1415        if (DirectiveType == PreProcessorDirective.Region)
1416        {
1417          ThisLineIndent = Parent.NextLineIndent.Clone();
1418        }
1419        else if (DirectiveType == PreProcessorDirective.If)
1420        {
1421          Engine.ifDirectiveIndents.Push(ThisLineIndent.Clone());
1422        }
1423      }
1424    }
1425
1426    public override IndentState Clone(CSharpIndentEngine engine)
1427    {
1428      return new PreProcessorState(this, engine);
1429    }
1430
1431    /// <summary>
1432    ///     Types of preprocessor directives.
1433    /// </summary>
1434    public enum PreProcessorDirective
1435    {
1436      None,
1437      If,
1438      Elif,
1439      Else,
1440      Endif,
1441      Region,
1442      Endregion,
1443      Pragma,
1444      Warning,
1445      Error,
1446      Line,
1447      Define,
1448      Undef
1449    }
1450
1451    #region Pre processor evaluation (from cs-tokenizer.cs)
1452
1453    static bool is_identifier_start_character(int c)
1454    {
1455      return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Char.IsLetter((char)c);
1456    }
1457
1458    static bool is_identifier_part_character(char c)
1459    {
1460      if (c >= 'a' && c <= 'z')
1461        return true;
1462
1463      if (c >= 'A' && c <= 'Z')
1464        return true;
1465
1466      if (c == '_' || (c >= '0' && c <= '9'))
1467        return true;
1468
1469      if (c < 0x80)
1470        return false;
1471
1472      return Char.IsLetter(c) || Char.GetUnicodeCategory(c) == UnicodeCategory.ConnectorPunctuation;
1473    }
1474
1475    bool eval_val(string s)
1476    {
1477      if (s == "true")
1478        return true;
1479      if (s == "false")
1480        return false;
1481
1482      return Engine.conditionalSymbols != null && Engine.conditionalSymbols.Contains(s) ||
1483        Engine.customConditionalSymbols != null && Engine.customConditionalSymbols.Contains(s);
1484    }
1485
1486    bool pp_primary(ref string s)
1487    {
1488      s = s.Trim();
1489      int len = s.Length;
1490
1491      if (len > 0)
1492      {
1493        char c = s[0];
1494
1495        if (c == '(')
1496        {
1497          s = s.Substring(1);
1498          bool val = pp_expr(ref s, false);
1499          if (s.Length > 0 && s[0] == ')')
1500          {
1501            s = s.Substring(1);
1502            return val;
1503          }
1504          return false;
1505        }
1506
1507        if (is_identifier_start_character(c))
1508        {
1509          int j = 1;
1510
1511          while (j < len)
1512          {
1513            c = s[j];
1514
1515            if (is_identifier_part_character(c))
1516            {
1517              j++;
1518              continue;
1519            }
1520            bool v = eval_val(s.Substring(0, j));
1521            s = s.Substring(j);
1522            return v;
1523          }
1524          bool vv = eval_val(s);
1525          s = "";
1526          return vv;
1527        }
1528      }
1529      return false;
1530    }
1531
1532    bool pp_unary(ref string s)
1533    {
1534      s = s.Trim();
1535      int len = s.Length;
1536
1537      if (len > 0)
1538      {
1539        if (s[0] == '!')
1540        {
1541          if (len > 1 && s[1] == '=')
1542          {
1543            return false;
1544          }
1545          s = s.Substring(1);
1546          return !pp_primary(ref s);
1547        }
1548        else
1549          return pp_primary(ref s);
1550      }
1551      else
1552      {
1553        return false;
1554      }
1555    }
1556
1557    bool pp_eq(ref string s)
1558    {
1559      bool va = pp_unary(ref s);
1560
1561      s = s.Trim();
1562      int len = s.Length;
1563      if (len > 0)
1564      {
1565        if (s[0] == '=')
1566        {
1567          if (len > 2 && s[1] == '=')
1568          {
1569            s = s.Substring(2);
1570            return va == pp_unary(ref s);
1571          }
1572          else
1573          {
1574            return false;
1575          }
1576        }
1577        else if (s[0] == '!' && len > 1 && s[1] == '=')
1578        {
1579          s = s.Substring(2);
1580
1581          return va != pp_unary(ref s);
1582
1583        }
1584      }
1585
1586      return va;
1587
1588    }
1589
1590    bool pp_and(ref string s)
1591    {
1592      bool va = pp_eq(ref s);
1593
1594      s = s.Trim();
1595      int len = s.Length;
1596      if (len > 0)
1597      {
1598        if (s[0] == '&')
1599        {
1600          if (len > 2 && s[1] == '&')
1601          {
1602            s = s.Substring(2);
1603            return (va & pp_and(ref s));
1604          }
1605          else
1606          {
1607            return false;
1608          }
1609        }
1610      }
1611      return va;
1612    }
1613
1614    //
1615    // Evaluates an expression for `#if' or `#elif'
1616    //
1617    bool pp_expr(ref string s, bool isTerm)
1618    {
1619      bool va = pp_and(ref s);
1620      s = s.Trim();
1621      int len = s.Length;
1622      if (len > 0)
1623      {
1624        char c = s[0];
1625
1626        if (c == '|')
1627        {
1628          if (len > 2 && s[1] == '|')
1629          {
1630            s = s.Substring(2);
1631            return va | pp_expr(ref s, isTerm);
1632          }
1633          else
1634          {
1635
1636            return false;
1637          }
1638        }
1639        if (isTerm)
1640        {
1641          return false;
1642        }
1643      }
1644
1645      return va;
1646    }
1647
1648    bool eval(string s)
1649    {
1650      bool v = pp_expr(ref s, true);
1651      s = s.Trim();
1652      if (s.Length != 0)
1653      {
1654        return false;
1655      }
1656
1657      return v;
1658    }
1659
1660    #endregion
1661  }
1662
1663  #endregion
1664
1665  #region PreProcessorComment state
1666
1667  /// <summary>
1668  ///     PreProcessor comment state.
1669  /// </summary>
1670  /// <remarks>
1671  ///     Activates when the #if or #elif directive is false and ignores
1672  ///     all pushed chars until the next '#'.
1673  /// </remarks>
1674  public class PreProcessorCommentState : IndentState
1675  {
1676    public PreProcessorCommentState()
1677    { }
1678
1679    public PreProcessorCommentState(PreProcessorCommentState prototype, CSharpIndentEngine engine)
1680      : base(prototype, engine)
1681    { }
1682
1683    public override void Push(char ch)
1684    {
1685      base.Push(ch);
1686
1687      if (ch == '#' && Engine.isLineStart)
1688      {
1689        // TODO: Return back only on #if/#elif/#else/#endif
1690        // Ignore any of the other directives (especially #define/#undef)
1691        ExitState();
1692        ChangeState<PreProcessorState>();
1693      }
1694    }
1695
1696    public override void InitializeState()
1697    {
1698      if (Engine.formattingOptions.IndentPreprocessorDirectives &&
1699          Engine.ifDirectiveIndents.Count > 0)
1700      {
1701        ThisLineIndent = Engine.ifDirectiveIndents.Peek().Clone();
1702        NextLineIndent = ThisLineIndent.Clone();
1703      }
1704      else
1705      {
1706        ThisLineIndent = Parent.NextLineIndent.Clone();
1707        NextLineIndent = ThisLineIndent.Clone();
1708      }
1709    }
1710
1711    public override IndentState Clone(CSharpIndentEngine engine)
1712    {
1713      return new PreProcessorCommentState(this, engine);
1714    }
1715  }
1716
1717  #endregion
1718
1719  #region LineComment state
1720
1721  /// <summary>
1722  ///     Single-line comment state.
1723  /// </summary>
1724  public class LineCommentState : IndentState
1725  {
1726    /// <summary>
1727    ///     It's possible that this should be the DocComment state:
1728    ///         check if the first next pushed char is equal to '/'.
1729    /// </summary>
1730    public bool CheckForDocComment = true;
1731
1732    public LineCommentState()
1733    {
1734      /*      if (engine.formattingOptions.KeepCommentsAtFirstColumn && engine.column == 2)
1735        ThisLineIndent.Reset();*/
1736    }
1737
1738    public LineCommentState(LineCommentState prototype, CSharpIndentEngine engine)
1739      : base(prototype, engine)
1740    {
1741      CheckForDocComment = prototype.CheckForDocComment;
1742    }
1743
1744    public override void Push(char ch)
1745    {
1746      base.Push(ch);
1747
1748      if (ch == Engine.newLineChar)
1749      {
1750        // to handle cases like //\n/*
1751        // Otherwise line 2 would be treated as line comment.
1752        Engine.previousChar = '\0';
1753        ExitState();
1754      }
1755      else if (ch == '/' && CheckForDocComment)
1756      {
1757        // wrong state, should be DocComment.
1758        ExitState();
1759        ChangeState<DocCommentState>();
1760      }
1761
1762      CheckForDocComment = false;
1763    }
1764
1765    public override void InitializeState()
1766    {
1767      ThisLineIndent = Parent.ThisLineIndent.Clone();
1768      NextLineIndent = Parent.NextLineIndent.Clone();
1769    }
1770
1771    public override IndentState Clone(CSharpIndentEngine engine)
1772    {
1773      return new LineCommentState(this, engine);
1774    }
1775  }
1776
1777  #endregion
1778
1779  #region DocComment state
1780
1781  /// <summary>
1782  ///     XML documentation comment state.
1783  /// </summary>
1784  public class DocCommentState : IndentState
1785  {
1786    public DocCommentState()
1787    { }
1788
1789    public DocCommentState(DocCommentState prototype, CSharpIndentEngine engine)
1790      : base(prototype, engine)
1791    { }
1792
1793    public override void Push(char ch)
1794    {
1795      base.Push(ch);
1796
1797      if (ch == Engine.newLineChar)
1798      {
1799        ExitState();
1800      }
1801    }
1802
1803    public override void InitializeState()
1804    {
1805      ThisLineIndent = Parent.ThisLineIndent.Clone();
1806      NextLineIndent = Parent.NextLineIndent.Clone();
1807    }
1808
1809    public override IndentState Clone(CSharpIndentEngine engine)
1810    {
1811      return new DocCommentState(this, engine);
1812    }
1813  }
1814
1815  #endregion
1816
1817  #region MultiLineComment state
1818
1819  /// <summary>
1820  ///     Multi-line comment state.
1821  /// </summary>
1822  public class MultiLineCommentState : IndentState
1823  {
1824    /// <summary>
1825    ///     True if any char has been pushed to this state.
1826    /// </summary>
1827    /// <remarks>
1828    ///     Needed to resolve an issue when the first pushed char is '/'.
1829    ///     The state would falsely exit on this sequence of chars '/*/',
1830    ///     since it only checks if the last two chars are '/' and '*'.
1831    /// </remarks>
1832    public bool IsAnyCharPushed;
1833
1834    public MultiLineCommentState()
1835    { }
1836
1837    public MultiLineCommentState(MultiLineCommentState prototype, CSharpIndentEngine engine)
1838      : base(prototype, engine)
1839    {
1840      IsAnyCharPushed = prototype.IsAnyCharPushed;
1841    }
1842
1843    public override void Push(char ch)
1844    {
1845      base.Push(ch);
1846
1847      if (ch == '/' && Engine.previousChar == '*' && IsAnyCharPushed)
1848      {
1849        ExitState();
1850      }
1851
1852      IsAnyCharPushed = true;
1853    }
1854
1855    public override void InitializeState()
1856    {
1857      ThisLineIndent = Parent.ThisLineIndent.Clone();
1858      NextLineIndent = ThisLineIndent.Clone();
1859    }
1860
1861    public override IndentState Clone(CSharpIndentEngine engine)
1862    {
1863      return new MultiLineCommentState(this, engine);
1864    }
1865  }
1866
1867  #endregion
1868
1869  #region StringLiteral state
1870
1871  /// <summary>
1872  ///     StringLiteral state.
1873  /// </summary>
1874  public class StringLiteralState : IndentState
1875  {
1876    /// <summary>
1877    ///     True if the next char is escaped with '\'.
1878    /// </summary>
1879    public bool IsEscaped;
1880
1881    public StringLiteralState()
1882    { }
1883
1884    public StringLiteralState(StringLiteralState prototype, CSharpIndentEngine engine)
1885      : base(prototype, engine)
1886    {
1887      IsEscaped = prototype.IsEscaped;
1888    }
1889
1890    public override void Push(char ch)
1891    {
1892      base.Push(ch);
1893
1894      if (ch == Engine.newLineChar || (!IsEscaped && ch == '"')) {
1895        ExitState();
1896      } else {
1897        IsEscaped = ch == '\\' && !IsEscaped;
1898      }
1899    }
1900
1901    public override void InitializeState()
1902    {
1903      ThisLineIndent = Parent.ThisLineIndent.Clone();
1904      NextLineIndent = Parent.NextLineIndent.Clone();
1905    }
1906
1907    public override IndentState Clone(CSharpIndentEngine engine)
1908    {
1909      return new StringLiteralState(this, engine);
1910    }
1911  }
1912
1913  #endregion
1914
1915  #region Verbatim string state
1916
1917  /// <summary>
1918  ///     Verbatim string state.
1919  /// </summary>
1920  public class VerbatimStringState : IndentState
1921  {
1922    /// <summary>
1923    ///     True if there is an odd number of '"' in a row.
1924    /// </summary>
1925    public bool IsEscaped;
1926
1927    public VerbatimStringState()
1928    { }
1929
1930    public VerbatimStringState(VerbatimStringState prototype, CSharpIndentEngine engine)
1931      : base(prototype, engine)
1932    {
1933      IsEscaped = prototype.IsEscaped;
1934    }
1935
1936    public override void Push(char ch)
1937    {
1938      base.Push(ch);
1939
1940      if (IsEscaped && ch != '"')
1941      {
1942        ExitState();
1943        // the char has been pushed to the wrong state, push it back
1944        Engine.currentState.Push(ch);
1945      }
1946
1947      IsEscaped = ch == '"' && !IsEscaped;
1948    }
1949
1950    public override void InitializeState()
1951    {
1952      ThisLineIndent = Parent.ThisLineIndent.Clone();
1953      NextLineIndent = new Indent(Engine.textEditorOptions);
1954    }
1955
1956    public override IndentState Clone(CSharpIndentEngine engine)
1957    {
1958      return new VerbatimStringState(this, engine);
1959    }
1960  }
1961
1962  #endregion
1963
1964  #region Character state
1965
1966  /// <summary>
1967  ///     Character state.
1968  /// </summary>
1969  public class CharacterState : IndentState
1970  {
1971    /// <summary>
1972    ///     True if the next char is escaped with '\'.
1973    /// </summary>
1974    public bool IsEscaped;
1975
1976    public CharacterState()
1977    { }
1978
1979    public CharacterState(CharacterState prototype, CSharpIndentEngine engine)
1980      : base(prototype, engine)
1981    {
1982      IsEscaped = prototype.IsEscaped;
1983    }
1984
1985    public override void Push(char ch)
1986    {
1987      base.Push(ch);
1988
1989      if (ch == Engine.newLineChar)
1990      {
1991        ExitState();
1992      }
1993      else if (!IsEscaped && ch == '\'')
1994      {
1995        ExitState();
1996      }
1997
1998      IsEscaped = ch == '\\' && !IsEscaped;
1999    }
2000
2001    public override void InitializeState()
2002    {
2003      ThisLineIndent = Parent.ThisLineIndent.Clone();
2004      NextLineIndent = Parent.NextLineIndent.Clone();
2005    }
2006
2007    public override IndentState Clone(CSharpIndentEngine engine)
2008    {
2009      return new CharacterState(this, engine);
2010    }
2011  }
2012
2013  #endregion
2014}
Note: See TracBrowser for help on using the repository browser.