public override bool Equals(object obj)
        {
            AutoCompleteEditHandler other = obj as AutoCompleteEditHandler;

            return(base.Equals(obj) &&
                   other != null &&
                   String.Equals(other.AutoCompleteString, AutoCompleteString, StringComparison.Ordinal) &&
                   AutoCompleteAtEndOfSpan == other.AutoCompleteAtEndOfSpan);
        }
        protected virtual void SectionDirective()
        {
            bool nested = Context.IsWithin(BlockType.Section);
            bool errorReported = false;

            // Set the block and span type
            Context.CurrentBlock.Type = BlockType.Section;

            // Verify we're on "section" and accept
            AssertDirective(SyntaxConstants.CSharp.SectionKeyword);
            AcceptAndMoveNext();

            if (nested)
            {
                Context.OnError(CurrentLocation, String.Format(CultureInfo.CurrentCulture, RazorResources.ParseError_Sections_Cannot_Be_Nested, RazorResources.SectionExample_CS));
                errorReported = true;
            }

            IEnumerable<CSharpSymbol> ws = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: false));

            // Get the section name
            string sectionName = String.Empty;
            if (!Required(CSharpSymbolType.Identifier,
                          errorIfNotFound: true,
                          errorBase: RazorResources.ParseError_Unexpected_Character_At_Section_Name_Start))
            {
                if (!errorReported)
                {
                    errorReported = true;
                }

                PutCurrentBack();
                PutBack(ws);
                AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: false));
            }
            else
            {
                Accept(ws);
                sectionName = CurrentSymbol.Content;
                AcceptAndMoveNext();
            }
            Context.CurrentBlock.CodeGenerator = new SectionCodeGenerator(sectionName);

            SourceLocation errorLocation = CurrentLocation;
            ws = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: false));

            // Get the starting brace
            bool sawStartingBrace = At(CSharpSymbolType.LeftBrace);
            if (!sawStartingBrace)
            {
                if (!errorReported)
                {
                    errorReported = true;
                    Context.OnError(errorLocation, RazorResources.ParseError_MissingOpenBraceAfterSection);
                }

                PutCurrentBack();
                PutBack(ws);
                AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: false));
                Optional(CSharpSymbolType.NewLine);
                Output(SpanKind.MetaCode);
                CompleteBlock();
                return;
            }
            else
            {
                Accept(ws);
            }

            // Set up edit handler
            AutoCompleteEditHandler editHandler = new AutoCompleteEditHandler(Language.TokenizeString) { AutoCompleteAtEndOfSpan = true };

            Span.EditHandler = editHandler;
            Span.Accept(CurrentSymbol);

            // Output Metacode then switch to section parser
            Output(SpanKind.MetaCode);
            SectionBlock("{", "}", caseSensitive: true);

            Span.CodeGenerator = SpanCodeGenerator.Null;
            // Check for the terminating "}"
            if (!Optional(CSharpSymbolType.RightBrace))
            {
                editHandler.AutoCompleteString = "}";
                Context.OnError(CurrentLocation,
                                RazorResources.ParseError_Expected_X,
                                Language.GetSample(CSharpSymbolType.RightBrace));
            }
            else
            {
                Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None;
            }
            CompleteBlock(insertMarkerIfNecessary: false, captureWhitespaceToEndOfLine: true);
            Output(SpanKind.MetaCode);
            return;
        }
        protected virtual void FunctionsDirective()
        {
            // Set the block type
            Context.CurrentBlock.Type = BlockType.Functions;

            // Verify we're on "functions" and accept
            AssertDirective(SyntaxConstants.CSharp.FunctionsKeyword);
            Block block = new Block(CurrentSymbol);
            AcceptAndMoveNext();

            AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: false));

            if (!At(CSharpSymbolType.LeftBrace))
            {
                Context.OnError(CurrentLocation,
                                RazorResources.ParseError_Expected_X,
                                Language.GetSample(CSharpSymbolType.LeftBrace));
                CompleteBlock();
                Output(SpanKind.MetaCode);
                return;
            }
            else
            {
                Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None;
            }

            // Capture start point and continue
            SourceLocation blockStart = CurrentLocation;
            AcceptAndMoveNext();

            // Output what we've seen and continue
            Output(SpanKind.MetaCode);

            AutoCompleteEditHandler editHandler = new AutoCompleteEditHandler(Language.TokenizeString);
            Span.EditHandler = editHandler;

            Balance(BalancingModes.NoErrorOnFailure, CSharpSymbolType.LeftBrace, CSharpSymbolType.RightBrace, blockStart);
            Span.CodeGenerator = new TypeMemberCodeGenerator();
            if (!At(CSharpSymbolType.RightBrace))
            {
                editHandler.AutoCompleteString = "}";
                Context.OnError(block.Start, RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, block.Name, "}", "{");
                CompleteBlock();
                Output(SpanKind.Code);
            }
            else
            {
                Output(SpanKind.Code);
                Assert(CSharpSymbolType.RightBrace);
                Span.CodeGenerator = SpanCodeGenerator.Null;
                Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None;
                AcceptAndMoveNext();
                CompleteBlock();
                Output(SpanKind.MetaCode);
            }
        }
        protected virtual void HelperDirective()
        {
            bool nested = Context.IsWithin(BlockType.Helper);

            // Set the block and span type
            Context.CurrentBlock.Type = BlockType.Helper;

            // Verify we're on "helper" and accept
            AssertDirective(SyntaxConstants.CSharp.HelperKeyword);
            Block block = new Block(CurrentSymbol.Content.ToString().ToLowerInvariant(), CurrentLocation);
            AcceptAndMoveNext();

            if (nested)
            {
                Context.OnError(CurrentLocation, RazorResources.ParseError_Helpers_Cannot_Be_Nested);
            }

            // Accept a single whitespace character if present, if not, we should stop now
            if (!At(CSharpSymbolType.WhiteSpace))
            {
                string error;
                if (At(CSharpSymbolType.NewLine))
                {
                    error = RazorResources.ErrorComponent_Newline;
                }
                else if (EndOfFile)
                {
                    error = RazorResources.ErrorComponent_EndOfFile;
                }
                else
                {
                    error = String.Format(CultureInfo.CurrentCulture, RazorResources.ErrorComponent_Character, CurrentSymbol.Content);
                }

                Context.OnError(
                    CurrentLocation,
                    RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start,
                    error);
                PutCurrentBack();
                Output(SpanKind.MetaCode);
                return;
            }

            CSharpSymbol remainingWs = AcceptSingleWhiteSpaceCharacter();

            // Output metacode and continue
            Output(SpanKind.MetaCode);
            if (remainingWs != null)
            {
                Accept(remainingWs);
            }
            AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true)); // Don't accept newlines.

            // Expecting an identifier (helper name)
            bool errorReported = !Required(CSharpSymbolType.Identifier, errorIfNotFound: true, errorBase: RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start);
            if (!errorReported)
            {
                Assert(CSharpSymbolType.Identifier);
                AcceptAndMoveNext();
            }

            AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));

            // Expecting parameter list start: "("
            SourceLocation bracketErrorPos = CurrentLocation;
            if (!Optional(CSharpSymbolType.LeftParenthesis))
            {
                if (!errorReported)
                {
                    errorReported = true;
                    Context.OnError(
                        CurrentLocation,
                        RazorResources.ParseError_MissingCharAfterHelperName,
                        "(");
                }
            }
            else
            {
                SourceLocation bracketStart = CurrentLocation;
                if (!Balance(BalancingModes.NoErrorOnFailure,
                             CSharpSymbolType.LeftParenthesis,
                             CSharpSymbolType.RightParenthesis,
                             bracketStart))
                {
                    errorReported = true;
                    Context.OnError(
                        bracketErrorPos,
                        RazorResources.ParseError_UnterminatedHelperParameterList);
                }
                Optional(CSharpSymbolType.RightParenthesis);
            }

            int bookmark = CurrentLocation.AbsoluteIndex;
            IEnumerable<CSharpSymbol> ws = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));

            // Expecting a "{"
            SourceLocation errorLocation = CurrentLocation;
            bool headerComplete = At(CSharpSymbolType.LeftBrace);
            if (headerComplete)
            {
                Accept(ws);
                AcceptAndMoveNext();
            }
            else
            {
                Context.Source.Position = bookmark;
                NextToken();
                AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
                if (!errorReported)
                {
                    Context.OnError(
                        errorLocation,
                        RazorResources.ParseError_MissingCharAfterHelperParameters,
                        Language.GetSample(CSharpSymbolType.LeftBrace));
                }
            }

            // Grab the signature and build the code generator
            AddMarkerSymbolIfNecessary();
            LocationTagged<string> signature = Span.GetContent();
            HelperCodeGenerator blockGen = new HelperCodeGenerator(signature, headerComplete);
            Context.CurrentBlock.CodeGenerator = blockGen;

            // The block will generate appropriate code, 
            Span.CodeGenerator = SpanCodeGenerator.Null;

            if (!headerComplete)
            {
                CompleteBlock();
                Output(SpanKind.Code);
                return;
            }
            else
            {
                Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None;
                Output(SpanKind.Code);
            }

            // We're valid, so parse the nested block
            AutoCompleteEditHandler bodyEditHandler = new AutoCompleteEditHandler(Language.TokenizeString);
            using (PushSpanConfig(DefaultSpanConfig))
            {
                using (Context.StartBlock(BlockType.Statement))
                {
                    Span.EditHandler = bodyEditHandler;
                    CodeBlock(false, block);
                    CompleteBlock(insertMarkerIfNecessary: true);
                    Output(SpanKind.Code);
                }
            }
            Initialize(Span);

            EnsureCurrent();

            Span.CodeGenerator = SpanCodeGenerator.Null; // The block will generate the footer code.
            if (!Optional(CSharpSymbolType.RightBrace))
            {
                // The } is missing, so set the initial signature span to use it as an autocomplete string
                bodyEditHandler.AutoCompleteString = "}";

                // Need to be able to accept anything to properly handle the autocomplete
                bodyEditHandler.AcceptedCharacters = AcceptedCharacters.Any;
            }
            else
            {
                blockGen.Footer = Span.GetContent();
                Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None;
            }
            CompleteBlock();
            Output(SpanKind.Code);
        }