public void Reformat(IDocumentAccessor doc, IndentationSettings settings)
        {
            Init();

            while (doc.MoveNext())
                Step(doc, settings);
        }
        /// <summary>
        /// Performs indentation using the specified document accessor.
        /// </summary>
        /// <param name="document">Object used for accessing the document line-by-line</param>
        /// <param name="indentationString">The string used for indentation.</param>
        /// <param name="keepEmptyLines">Specifies whether empty lines should be kept</param>
        private void Indent(IDocumentAccessor document, string indentationString, bool keepEmptyLines)
        {
            if (document == null)
                throw new ArgumentNullException(nameof(document));

            IndentationSettings settings = new IndentationSettings();
            settings.IndentString = indentationString;
            settings.LeaveEmptyLines = keepEmptyLines;

            CSharpIndentationReformatter r = new CSharpIndentationReformatter();
            r.Reformat(document, settings);
        }
        private void Step(IDocumentAccessor doc, IndentationSettings settings)
        {
            string line = doc.Text;
            if (settings.LeaveEmptyLines && line.Length == 0)
            {
                // Leave empty lines empty.
                _lineCommentStart = 0;
                return;
            }

            line = line.TrimStart();
            if (line.Length == 0)
            {
                _lineCommentStart = 0;
                if (_inBlockComment || _inVerbatimString)
                {
                    // Examples:
                    //
                    //  /* comment
                    //                      <-- HERE
                    //     comment
                    //  */
                    //
                    //  string s = @"text
                    //                      <-- HERE
                    //  text";
                    return;
                }

                _indent.Clear();
                _indent.Append(_block.InnerIndent);
                _indent.Append(Repeat(settings.IndentString, _block.OneLineBlock));
                if (_block.Continuation)
                {
                    // Example:
                    //
                    //   method(
                    //                      <-- HERE
                    //
                    _indent.Append(settings.IndentString);
                }

                // Apply indentation to current line.
                if (doc.Text.Length != _indent.Length) // Check length first to avoid unnecessary ToString().
                {
                    string text = _indent.ToString();
                    // ReSharper disable once RedundantCheckBeforeAssignment
                    if (doc.Text != text)
                        doc.Text = text;
                }

                return;
            }

            if (TrimEnd(doc))
                line = doc.Text.TrimStart();

            // oldBlock is the block at the start of the line.
            // _block is the block at the current character.
            Block oldBlock = _block;

            bool startInComment = _inBlockComment;
            bool startInString = _inVerbatimString;
            bool inLineComment = false;
            int lastLineCommentStart = _lineCommentStart;
            _lineCommentStart = 0;
            bool inString = _inVerbatimString;
            bool inChar = false;
            bool isEscapeChar = false;

            char lastRealChar = '\n'; // The last non-comment character.

            #region ----- Parse line character by character. -----

            char previousChar;
            char currentChar = ' ';
            char nextChar = line[0];
            for (int i = 0; i < line.Length; i++)
            {
                if (inLineComment)
                {
                    // Cancel parsing current line.
                    break;
                }

                previousChar = currentChar;
                currentChar = nextChar;
                if (i + 1 < line.Length)
                    nextChar = line[i + 1];
                else
                    nextChar = '\n';

                // Skip escape characters.
                if (isEscapeChar)
                {
                    // Example:
                    //
                    //   char c = '\t';
                    //              ^
                    //             HERE
                    //
                    isEscapeChar = false;
                    continue;
                }

                // ----- Check for comment, preprocessor directive, string, character.
                switch (currentChar)
                {
                    // Check for preprocessor directive.
                    case '#':
                        if (!(_inBlockComment || inString || inChar))
                        {
                            inLineComment = true;
                        }
                        break;

                    // Check for comment.
                    case '/':
                        if (_inBlockComment && previousChar == '*')
                            _inBlockComment = false;

                        if (!inString && !inChar)
                        {
                            if (!_inBlockComment && nextChar == '/')
                            {
                                inLineComment = true;
                                _lineCommentStart = i;
                            }
                            if (!inLineComment && nextChar == '*')
                            {
                                _inBlockComment = true;
                            }
                        }
                        break;

                    // Check for string.
                    case '"':
                        if (!(_inBlockComment || inLineComment || inChar))
                        {
                            inString = !inString;
                            if (!inString && _inVerbatimString)
                            {
                                if (nextChar == '"')
                                {
                                    // Example:
                                    //
                                    //   string s = @"Printing ""double quotation"" ...";
                                    //                         ^
                                    //                        HERE
                                    //
                                    isEscapeChar = true; // Skip escaped quote.
                                    inString = true;
                                }
                                else
                                {
                                    // Example:
                                    //
                                    //   string s = @"Printing ""double quotation"" ...";
                                    //                                                 ^
                                    //                                                HERE
                                    //
                                    _inVerbatimString = false;
                                }
                            }
                            else if (inString)
                            {
                                // Example:
                                //
                                //   string s = "Text";
                                //              ^
                                //             HERE
                                //
                                //   string s = @"Printing ""double quotation"" ...";
                                //               ^
                                //              HERE
                                //
                                _inVerbatimString = (previousChar == '@');
                            }
                            else
                            {
                                // Example:
                                //
                                //   string s = "Text";
                                //                   ^
                                //                  HERE
                                //
                                _inVerbatimString = false;
                            }
                        }
                        break;

                    // Check for character.
                    case '\'':
                        if (!(_inBlockComment || inLineComment || inString))
                        {
                            inChar = !inChar;
                        }
                        break;

                    // Check for escape character.
                    case '\\':
                        if ((inString && !_inVerbatimString) || inChar)
                            isEscapeChar = true; // Skip next character at start of loop.
                        break;
                }

                Debug.Assert(!_inVerbatimString || _inVerbatimString && inString, "When _inVerbatimString is set, inString needs to be set.");

                // At this point the following variables are set:
                //   _inDirective, _inBlockComment,
                //   inLineComment, lineCommentStart,
                //   _inVerbatimString, _inString,
                //   inChar, _isEscapeChar

                if (_inBlockComment || inLineComment || inString || inChar)
                {
                    // Store last word before directive/comment/string/char and continue
                    // with next character.
                    if (_wordBuilder.Length > 0)
                    {
                        _block.LastWord = _wordBuilder.ToString();
                        _wordBuilder.Clear();
                    }

                    continue;
                }

                if (!Char.IsWhiteSpace(currentChar) && currentChar != '[' && currentChar != '/')
                {
                    if (_block.Bracket == '{')
                    {
                        // The current line potentially contains a statement. If the statement
                        // is not completed in this line, it the next line is a continuation.
                        _block.Continuation = true;
                    }
                }

                if (Char.IsLetterOrDigit(currentChar))
                {
                    _wordBuilder.Append(currentChar);
                }
                else
                {
                    if (_wordBuilder.Length > 0)
                    {
                        _block.LastWord = _wordBuilder.ToString();
                        _wordBuilder.Clear();
                    }
                }

                // ----- Push/pop the blocks.
                switch (currentChar)
                {
                    case '{':
                        _block.ResetOneLineBlock();
                        _blocks.Push(_block);
                        _block.StartLine = doc.LineNumber;
                        if (_block.LastWord == "switch")
                        {
                            _block.Indent(settings.IndentString + settings.IndentString);
                            /* oldBlock refers to the previous line, not the previous block
                             * The block we want is not available anymore because it was never pushed.
                             * } else if (oldBlock.OneLineBlock) {
                            // Inside a one-line-block is another statement
                            // with a full block: indent the inner full block
                            // by one additional level
                            block.Indent(settings, settings.IndentString + settings.IndentString);
                            block.OuterIndent += settings.IndentString;
                            // Indent current line if it starts with the '{' character
                            if (i == 0) {
                              oldBlock.InnerIndent += settings.IndentString;
                            }*/
                        }
                        else
                        {
                            if (i == line.Length - 1)
                            {
                                // Example:
                                //
                                //   if (condition) {
                                //                  ^
                                //                 HERE
                                //
                                _block.Indent(settings.IndentString);
                            }
                            else
                            {
                                // Example:
                                //
                                //   char[] array = { value, value,
                                //                  ^
                                //                 HERE
                                //
                                // Align subsequent lines with first value.
                                _block.Indent(new string(' ', i + 2));
                            }
                        }
                        _block.Bracket = '{';
                        break;

                    case '}':
                        while (_block.Bracket != '{')
                        {
                            if (_blocks.Count == 0)
                                break;
                            _block = _blocks.Pop();
                        }
                        if (_blocks.Count == 0)
                            break;
                        _block = _blocks.Pop();
                        _block.Continuation = false;
                        _block.ResetOneLineBlock();
                        break;

                    case '(':
                    case '[':
                        _blocks.Push(_block);
                        if (_block.StartLine == doc.LineNumber)
                        {
                            // Example:
                            //
                            //   ???
                            //
                            _block.InnerIndent = _block.OuterIndent;
                        }
                        else
                        {
                            _block.StartLine = doc.LineNumber;
                        }

                        _indent.Clear();

                        // New block may be part of line continuation.
                        if (oldBlock.Continuation)
                            _indent.Append(settings.IndentString);

                        // New block may be part to one-line-block.
                        _indent.Append(Repeat(settings.IndentString, oldBlock.OneLineBlock));

                        if (i == line.Length - 1)
                        {
                            // Example:
                            //
                            //   method(
                            //         ^
                            //        HERE
                            //
                            _indent.Append(settings.IndentString);
                        }
                        else
                        {
                            // Example:
                            //
                            //   method(param,
                            //         ^
                            //        HERE
                            //
                            // Align subsequent lines with first parameter.
                            _indent.Append(' ', i + 1);
                        }

                        _block.Indent(_indent.ToString());
                        _block.Bracket = currentChar;
                        break;

                    case ')':
                        if (_blocks.Count == 0)
                            break;
                        if (_block.Bracket == '(')
                        {
                            // Example:
                            //
                            //   method(param);
                            //               ^
                            //              HERE
                            //
                            _block = _blocks.Pop();

                            if (IsSingleStatementKeyword(_block.LastWord))
                            {
                                // Example:
                                //
                                //   if (condition)
                                //                ^
                                //               HERE
                                _block.Continuation = false;
                            }
                        }
                        break;

                    case ']':
                        if (_blocks.Count == 0)
                            break;
                        if (_block.Bracket == '[')
                        {
                            // Example:
                            //
                            //   array[index]
                            //              ^
                            //             HERE
                            _block = _blocks.Pop();
                        }
                        break;

                    case ';':
                    case ',':
                        // Example:
                        //
                        //   statement;
                        //            ^
                        //           HERE
                        _block.Continuation = false;
                        _block.ResetOneLineBlock();
                        break;
                    case ':':
                        if (_block.LastWord == "case"
                            || line.StartsWith("case ", StringComparison.Ordinal)
                            || line.StartsWith(_block.LastWord + ":", StringComparison.Ordinal))
                        {
                            // Examples:
                            //
                            //   case 1:
                            //         ^
                            //        HERE
                            //
                            //   label:
                            //        ^
                            //       HERE
                            _block.Continuation = false;
                            _block.ResetOneLineBlock();
                        }
                        break;
                }

                if (!Char.IsWhiteSpace(currentChar))
                {
                    // Register this character as last non-comment characater.
                    lastRealChar = currentChar;
                }
            }
            #endregion

            // At this point the line is parsed.

            if (_wordBuilder.Length > 0)
            {
                _block.LastWord = _wordBuilder.ToString();
                _wordBuilder.Clear();
            }

            if (startInComment && line[0] != '*')
                return;
            if (startInString)
                return;
            if (doc.Text.StartsWith("//\t", StringComparison.Ordinal) || doc.Text == "//")
                return;

            #region ----- Build indentation string. -----

            // Note: Line continuations, one-line-blocks, and multiline block comments
            // are not handled here. They are handled explicitly when the indentation
            // is applied.

            _indent.Clear();
            if (line[0] == '}')
            {
                // Example:
                //
                //   {
                //     statement;
                //     statement;
                //   }                    <-- HERE
                //
                _indent.Append(oldBlock.OuterIndent);
                oldBlock.ResetOneLineBlock();
                oldBlock.Continuation = false;
            }
            else
            {
                // Example:
                //
                //   {
                //     statement;
                //     statement;         <-- HERE
                //   }
                //
                _indent.Append(oldBlock.InnerIndent);
            }

            if (_indent.Length > 0 && oldBlock.Bracket == '(' && line[0] == ')')
            {
                // Example:
                //
                //   Method(param,
                //          param
                //         );             <-- HERE
                //
                _indent.Remove(_indent.Length - 1, 1);
            }
            else if (_indent.Length > 0 && oldBlock.Bracket == '[' && line[0] == ']')
            {
                // Example:
                //
                //   array[index0,
                //         index1
                //        ];             <-- HERE
                //
                _indent.Remove(_indent.Length - 1, 1);
            }

            if (line[0] == ':')
            {
                // Example:
                //
                //   ???
                //
                oldBlock.Continuation = true;
            }
            else if (lastRealChar == ':' && _indent.Length >= settings.IndentString.Length)
            {
                if (_block.LastWord == "case"
                    || line.StartsWith("case ", StringComparison.Ordinal)
                    || line.StartsWith(_block.LastWord + ":", StringComparison.Ordinal))
                {
                    // Examples:
                    //
                    //   switch (variable)
                    //   {
                    //     case 1:          <-- HERE
                    //       statement;
                    //   }
                    //
                    //   label:             <-- HERE
                    //     statement;
                    _indent.Remove(_indent.Length - settings.IndentString.Length, settings.IndentString.Length);
                }
            }
            else if (lastRealChar == ')')
            {
                if (IsSingleStatementKeyword(_block.LastWord))
                {
                    // Example:
                    //
                    //   if (condition)     <--- HERE
                    //
                    _block.OneLineBlock++;
                }
            }
            else if (lastRealChar == 'e' && _block.LastWord == "else")
            {
                // Example:
                //
                //   if (condition)
                //     statement;
                //   else                 <-- HERE
                //     statement;
                //
                // PreviousOneLineBlock stores the indentation level used by the previous
                // if-branch. Use the same indentation on the following else-branch.
                _block.OneLineBlock = Math.Max(1, _block.PreviousOneLineBlock);
                _block.Continuation = false;
                oldBlock.OneLineBlock = _block.OneLineBlock - 1;
            }
            #endregion

            #region ----- Apply indentation. -----

            if (doc.IsReadOnly)
            {
                // ----- Read-only line. (Not in selected text region.)
                // We can't change the current line, but we should accept the existing
                // indentation if possible (=if the current statement is not a multiline
                // statement).
                if (!oldBlock.Continuation
                    && oldBlock.OneLineBlock == 0
                    && oldBlock.StartLine == _block.StartLine
                    && _block.StartLine < doc.LineNumber
                    && lastRealChar != ':')
                {
                    // Use indent StringBuilder to get the indentation of the current line.
                    _indent.Clear();
                    line = doc.Text; // get untrimmed line
                    for (int i = 0; i < line.Length; ++i)
                    {
                        if (!Char.IsWhiteSpace(line[i]))
                            break;
                        _indent.Append(line[i]);
                    }

                    // /* */ multiline block comments have an extra space - do not count it
                    // for the block's indentation. (The extra space is applied explicitly below.)
                    if (startInComment && _indent.Length > 0 && _indent[_indent.Length - 1] == ' ')
                    {
                        _indent.Length -= 1;
                    }

                    _block.InnerIndent = _indent.ToString();
                }
            }
            else
            {
                // ----- Reformat current line.
                if (line[0] == '#')
                {
                    // Do not indent preprocessor directives.
                    _indent.Clear();
                }
                else if (lastLineCommentStart > 0
                         && line.StartsWith("//", StringComparison.Ordinal)
                         && (line.Length <= 2 || char.IsWhiteSpace(line[2]))) // Ignore commented code such as "//statement;".
                {
                    // Special treatment to align dangling comments.
                    // Example:
                    //
                    //   statement;  // comment
                    //               // comment            <-- HERE
                    //               // comment
                    _indent.Append(' ', lastLineCommentStart);
                    _lineCommentStart = lastLineCommentStart;
                }
                else if (line[0] != '{')
                {
                    // Handle line continuation.
                    if (line[0] != ')' && oldBlock.Continuation && oldBlock.Bracket == '{')
                    {
                        // Variant #1: Reformat line. (Overrides user-defined indentation.)
                        //_indent.Append(settings.IndentString);

                        // Variant #2: Ignore line. (Keep any user-defined indentation.)
                        return;
                    }

                    // Handle one-line-blocks.
                    _indent.Append(Repeat(settings.IndentString, oldBlock.OneLineBlock));
                }

                // Handle multiline block comments.
                if (startInComment)
                {
                    Debug.Assert(line[0] == '*', "Other cases should have been handled above.");

                    // This is a multiline block comment.
                    // Example:
                    //
                    //   /* comment
                    //    * comment         <-- HERE
                    //
                    // Add ' ' to align the '*' characters.
                    _indent.Append(' ');
                }

                // Check whether line already has correct indentation to avoid unnecessary change.
                if (_indent.Length != (doc.Text.Length - line.Length)
                    || Char.IsWhiteSpace(doc.Text[_indent.Length])
                    || !doc.Text.StartsWith(_indent.ToString(), StringComparison.Ordinal))
                {
                    doc.Text = _indent.Append(line).ToString();
                }
            }
            #endregion
        }