static VBNetFormattingStrategy()
 {
     statements = new List <VBStatement>();
     statements.Add(new VBStatement(@"^if.*?(then|\s+_)$", "^end ?if$", "End If", 1, Tokens.If));
     statements.Add(new VBStatement(@"\bclass\s+\w+\s*($|\(\s*Of)", "^end class$", "End Class", 1, Tokens.Class));
     statements.Add(new VBStatement(@"\bnamespace\s+\w+(\.\w+)*$", "^end namespace$", "End Namespace", 1, Tokens.Namespace));
     statements.Add(new VBStatement(@"\bmodule\s+\w+$", "^end module$", "End Module", 1, Tokens.Module));
     statements.Add(new VBStatement(@"\bstructure\s+\w+\s*($|\(\s*Of)", "^end structure$", "End Structure", 1, Tokens.Structure));
     statements.Add(new VBStatement(@"^while\s+", "^end while$", "End While", 1, Tokens.While));
     statements.Add(new VBStatement(@"^select case", "^end select$", "End Select", 1, Tokens.Select));
     statements.Add(new VBStatement(@"(?<!\b(delegate|mustoverride|declare(\s+(unicode|ansi|auto))?)\s+)\bsub\s+\w+", @"^end\s+sub$", "End Sub", 1, Tokens.Sub));
     statements.Add(new VBStatement(@"(?<!\bmustoverride (readonly |writeonly )?)\bproperty\s+\w+", @"^end\s+property$", "End Property", 1, Tokens.Property));
     statements.Add(new VBStatement(@"(?<!\b(delegate|mustoverride|declare(\s+(unicode|ansi|auto))?)\s+)\bfunction\s+\w+", @"^end\s+function$", "End Function", 1, Tokens.Function));
     statements.Add(new VBStatement(@"\boperator(\s*[\+\-\*\/\&\^\>\<\=\\]+\s*|\s+\w+\s*)\(", @"^end\s+operator$", "End Operator", 1, Tokens.Operator));
     statements.Add(new VBStatement(@"\bfor\s+.*?$", "^next( \\w+)?$", "Next", 1, Tokens.For));
     statements.Add(new VBStatement(@"^synclock\s+.*?$", "^end synclock$", "End SyncLock", 1, Tokens.SyncLock));
     statements.Add(new VBStatement(@"^get$", "^end get$", "End Get", 1, Tokens.Get));
     statements.Add(new VBStatement(@"^with\s+.*?$", "^end with$", "End With", 1, Tokens.With));
     statements.Add(new VBStatement(@"^set(\s*\(.*?\))?$", "^end set$", "End Set", 1, Tokens.Set));
     statements.Add(new VBStatement(@"^try$", "^end try$", "End Try", 1, Tokens.Try));
     statements.Add(new VBStatement(@"^do\s+.+?$", "^loop$", "Loop", 1, Tokens.Do));
     statements.Add(new VBStatement(@"^do$", "^loop .+?$", "Loop While ", 1, Tokens.Do));
     statements.Add(new VBStatement(@"\benum\s+\w+$", "^end enum$", "End Enum", 1, Tokens.Enum));
     interfaceStatement = new VBStatement(@"\binterface\s+\w+\s*($|\(\s*Of)", "^end interface$", "End Interface", 1, Tokens.Interface);
     statements.Add(interfaceStatement);
     statements.Add(new VBStatement(@"\busing\s+", "^end using$", "End Using", 1, Tokens.Using));
     statements.Add(new VBStatement(@"^#region\s+", "^#end region$", "#End Region", 0, -1));
 }
        static int FindEndStatementAroundOffset(IDocument document, int offset, out VBStatement statement)
        {
            IDocumentLine line = document.GetLineForOffset(offset);

            string interestingText = line.Text.TrimLine().Trim(' ', '\t');

            //LoggingService.Debug("text: '" + interestingText + "'");

            foreach (VBStatement s in VBNetFormattingStrategy.Statements)
            {
                Match match = Regex.Matches(interestingText, s.EndRegex, RegexOptions.Singleline | RegexOptions.IgnoreCase).OfType <Match>().FirstOrDefault();
                if (match != null)
                {
                    //LoggingService.DebugFormatted("Found end statement at offset {1}: {0}", s, offset);
                    statement = s;
                    int result = match.Index + (line.Length - line.Text.TrimStart(' ', '\t').Length) + line.Offset;
                    if (offset >= result && offset <= (result + match.Length))
                    {
                        return(result);
                    }
                }
            }

            statement = null;
            return(-1);
        }
        static Token GetClosestMissing(List <Token> missingEnds, VBStatement statement, ITextEditor editor, int lineNr)
        {
            Token closest = null;
            int   diff    = 0;

            foreach (Token t in missingEnds)
            {
                if (!IsSingleLine(t.Location.Line, editor))
                {
                    if (IsMatchingStatement(t, statement) && ((diff = lineNr - t.Location.Line) > 0))
                    {
                        if (closest == null)
                        {
                            closest = t;
                        }
                        else
                        {
                            if (diff < lineNr - closest.Location.Line)
                            {
                                closest = t;
                            }
                        }
                    }
                }
            }

            return(closest);
        }
        bool IsEndStatementNeeded(ITextEditor editor, ref VBStatement statement, int lineNr)
        {
            Stack <Token> tokens      = new Stack <Token>();
            List <Token>  missingEnds = new List <Token>();

            ILexer lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, new StringReader(editor.Document.Text));

            Token currentToken = null;
            Token prevToken    = null;

            while ((currentToken = lexer.NextToken()).Kind != Tokens.EOF)
            {
                if (prevToken == null)
                {
                    prevToken = currentToken;
                }
                if (IsBlockStart(lexer, currentToken, prevToken))
                {
                    if ((tokens.Count > 0 && tokens.Peek().Kind != Tokens.Interface) || IsDeclaration(currentToken.Kind))
                    {
                        tokens.Push(currentToken);
                    }
                }

                if (IsBlockEnd(currentToken, prevToken))
                {
                    while (tokens.Count > 0 && !IsMatchingEnd(tokens.Peek(), currentToken))
                    {
                        missingEnds.Add(tokens.Pop());
                    }

                    if (tokens.Count > 0)
                    {
                        if (IsMatchingEnd(tokens.Peek(), currentToken))
                        {
                            tokens.Pop();
                        }
                    }
                }

                prevToken = currentToken;
            }

            while (tokens.Count > 0)
            {
                missingEnds.Add(tokens.Pop());
            }

            if (missingEnds.Count > 0)
            {
                return(GetClosestMissing(missingEnds, statement, editor, lineNr) != null);
            }
            else
            {
                return(false);
            }
        }
        void DoInsertionOnLine(string terminator, IDocumentLine currentLine, IDocumentLine lineAbove, string textToReplace, ITextEditor editor, int lineNr)
        {
            string curLineText = currentLine.Text;

            if (Regex.IsMatch(textToReplace.Trim(), "^If .*[^_]$", RegexOptions.IgnoreCase))
            {
                if (!Regex.IsMatch(textToReplace, "\\bthen\\b", RegexOptions.IgnoreCase))
                {
                    string specialThen = "Then";                     // do special check in cases like If t = True' comment
                    if (editor.Document.GetCharAt(lineAbove.Offset + textToReplace.Length) == '\'')
                    {
                        specialThen += " ";
                    }
                    if (editor.Document.GetCharAt(lineAbove.Offset + textToReplace.Length - 1) != ' ')
                    {
                        specialThen = " " + specialThen;
                    }
                    editor.Document.Insert(lineAbove.Offset + textToReplace.Length, specialThen);
                    textToReplace += specialThen;
                }
            }

            // check #Region statements
            if (Regex.IsMatch(textToReplace.Trim(), "^#Region", RegexOptions.IgnoreCase) && LookForEndRegion(editor))
            {
                string indentation = DocumentUtilitites.GetWhitespaceAfter(editor.Document, lineAbove.Offset);
                textToReplace += indentation + "\r\n" + indentation + "#End Region";
                editor.Document.Replace(currentLine.Offset, currentLine.Length, textToReplace);
            }

            foreach (VBStatement statement_ in statements)
            {
                VBStatement statement = statement_;                     // allow passing statement byref
                if (Regex.IsMatch(textToReplace.Trim(), statement.StartRegex, RegexOptions.IgnoreCase))
                {
                    string indentation = DocumentUtilitites.GetWhitespaceAfter(editor.Document, lineAbove.Offset);
                    if (IsEndStatementNeeded(editor, ref statement, lineNr))
                    {
                        editor.Document.Replace(currentLine.Offset, currentLine.Length, terminator + indentation + statement.EndStatement);
                    }
                    if (!IsInsideInterface(editor, lineNr) || statement == interfaceStatement)
                    {
                        for (int i = 0; i < statement.IndentPlus; i++)
                        {
                            indentation += editor.Options.IndentationString;
                        }
                    }
                    editor.Document.Replace(currentLine.Offset, currentLine.Length, indentation + curLineText.Trim());
                    editor.Caret.Line   = currentLine.LineNumber;
                    editor.Caret.Column = indentation.Length;
                    return;
                }
            }
        }
        static bool IsMatchingStatement(Token token, VBStatement statement)
        {
            if (token.Kind == Tokens.For && statement.EndStatement == "Next")
            {
                return(true);
            }

            if (token.Kind == Tokens.Do && statement.EndStatement.StartsWith("Loop", StringComparison.OrdinalIgnoreCase))
            {
                return(true);
            }

            bool empty = !string.IsNullOrEmpty(token.Value);
            bool match = statement.EndStatement.IndexOf(token.Value, StringComparison.OrdinalIgnoreCase) != -1;

            return(empty && match);
        }
		static Token GetClosestMissing(List<Token> missingEnds, VBStatement statement, ITextEditor editor, int lineNr)
		{
			Token closest = null;
			int diff = 0;
			
			foreach (Token t in missingEnds) {
				if (!IsSingleLine(t.Location.Line, editor)) {
					if (IsMatchingStatement(t, statement) && ((diff = lineNr - t.Location.Line) > 0)) {
						if (closest == null) {
							closest = t;
						} else {
							if (diff < lineNr - closest.Location.Line)
								closest = t;
						}
					}
				}
			}
			
			return closest;
		}
		bool IsEndStatementNeeded(ITextEditor editor, ref VBStatement statement, int lineNr)
		{
			Stack<Token> tokens = new Stack<Token>();
			List<Token> missingEnds = new List<Token>();
			
			ILexer lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, new StringReader(editor.Document.Text));
			
			Token currentToken = null;
			Token prevToken = null;
			
			while ((currentToken = lexer.NextToken()).Kind != Tokens.EOF) {
				if (prevToken == null)
					prevToken = currentToken;
				
				if (IsBlockStart(lexer, currentToken, prevToken)) {
					if ((tokens.Count > 0 && tokens.Peek().Kind != Tokens.Interface) || IsDeclaration(currentToken.Kind))
						tokens.Push(currentToken);
				}
				
				if (IsBlockEnd(currentToken, prevToken)) {
					while (tokens.Count > 0 && !IsMatchingEnd(tokens.Peek(), currentToken)) {
						missingEnds.Add(tokens.Pop());
					}
					
					if (tokens.Count > 0) {
						if (IsMatchingEnd(tokens.Peek(), currentToken))
							tokens.Pop();
					}
				}
				
				prevToken = currentToken;
			}
			
			while (tokens.Count > 0)
				missingEnds.Add(tokens.Pop());
			
			if (missingEnds.Count > 0)
				return GetClosestMissing(missingEnds, statement, editor, lineNr) != null;
			else
				return false;
		}
		static int FindEndStatement(IDocument document, VBStatement statement)
		{
			return -1;
		}
		static int FindBeginStatement(IDocument document, VBStatement statement, Location endLocation, out int length)
		{
			ILexer lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, document.CreateReader());
			
			Token currentToken = null;
			Token prevToken = null;
			
			int lookFor = statement.StatementToken;
			Stack<Token> tokens = new Stack<Token>();
			
			if (statement.EndStatement == "Next") {
				lookFor = Tokens.For;
			}
			
			Token result = null;
//			Token firstModifier = null;
			
			while ((currentToken = lexer.NextToken()).Kind != Tokens.EOF) {
				if (prevToken == null)
					prevToken = currentToken;
				
//				if (IsModifier(currentToken)) {
//					if (firstModifier == null)
//						firstModifier = currentToken;
//				} else
//					firstModifier = null;
				
				
				if (VBNetFormattingStrategy.IsBlockStart(lexer, currentToken, prevToken)) {
					tokens.Push(currentToken);
				}
				if (VBNetFormattingStrategy.IsBlockEnd(currentToken, prevToken)) {
					while (tokens.Count > 0 && !VBNetFormattingStrategy.IsMatchingEnd(tokens.Peek(), currentToken))
						tokens.Pop();
					if (tokens.Count > 0) {
						Token t = tokens.Pop();
						if (currentToken.Location.Line == endLocation.Line) {
							result = t;
							break;
						}
					}
				}
				
				prevToken = currentToken;
			}
			
			if (result != null) {
				int endOffset = document.PositionToOffset(result.EndLocation.Line, result.EndLocation.Column);
				int offset = document.PositionToOffset(result.Location.Line, result.Location.Column);
				length = endOffset - offset;
				return offset;
			}
			
			length = 0;
			
			return -1;
		}
		static int FindEndStatementAroundOffset(IDocument document, int offset, out VBStatement statement)
		{
			IDocumentLine line = document.GetLineForOffset(offset);
			
			string interestingText = VBNetFormattingStrategy.TrimLine(line.Text).Trim(' ', '\t');
			
			//LoggingService.Debug("text: '" + interestingText + "'");
			
			foreach (VBStatement s in VBNetFormattingStrategy.Statements) {
				Match match = Regex.Matches(interestingText, s.EndRegex, RegexOptions.Singleline | RegexOptions.IgnoreCase).OfType<Match>().FirstOrDefault();
				if (match != null) {
					//LoggingService.DebugFormatted("Found end statement at offset {1}: {0}", s, offset);
					statement = s;
					int result = match.Index + (line.Length - line.Text.TrimStart(' ', '\t').Length) + line.Offset;
					if (offset >= result && offset <= (result + match.Length))
						return result;
				}
			}
			
			statement = null;
			return -1;
		}
		static int FindBeginStatementAroundOffset(IDocument document, int offset, out VBStatement statement, out int length)
		{
			length = 0;
			statement = null;
			return -1;
		}
 static int FindEndStatement(IDocument document, VBStatement statement)
 {
     return(-1);
 }
        static int FindBeginStatement(IDocument document, VBStatement statement, Location endLocation, out int length)
        {
            ILexer lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, document.CreateReader());

            Token currentToken = null;
            Token prevToken    = null;

            int           lookFor = statement.StatementToken;
            Stack <Token> tokens  = new Stack <Token>();

            if (statement.EndStatement == "Next")
            {
                lookFor = Tokens.For;
            }

            Token result = null;

//			Token firstModifier = null;

            while ((currentToken = lexer.NextToken()).Kind != Tokens.EOF)
            {
                if (prevToken == null)
                {
                    prevToken = currentToken;
                }

//				if (IsModifier(currentToken)) {
//					if (firstModifier == null)
//						firstModifier = currentToken;
//				} else
//					firstModifier = null;


                if (VBNetFormattingStrategy.IsBlockStart(lexer, currentToken, prevToken))
                {
                    tokens.Push(currentToken);
                }
                if (VBNetFormattingStrategy.IsBlockEnd(currentToken, prevToken))
                {
                    while (tokens.Count > 0 && !VBNetFormattingStrategy.IsMatchingEnd(tokens.Peek(), currentToken))
                    {
                        tokens.Pop();
                    }
                    if (tokens.Count > 0)
                    {
                        Token t = tokens.Pop();
                        if (currentToken.Location.Line == endLocation.Line)
                        {
                            result = t;
                            break;
                        }
                    }
                }

                prevToken = currentToken;
            }

            if (result != null)
            {
                int endOffset = document.PositionToOffset(result.EndLocation.Line, result.EndLocation.Column);
                int offset    = document.PositionToOffset(result.Location.Line, result.Location.Column);
                length = endOffset - offset;
                return(offset);
            }

            length = 0;

            return(-1);
        }
		static bool IsMatchingStatement(Token token, VBStatement statement)
		{
			if (token.Kind == Tokens.For && statement.EndStatement == "Next")
				return true;
			
			if (token.Kind == Tokens.Do && statement.EndStatement.StartsWith("Loop", StringComparison.OrdinalIgnoreCase))
				return true;
			
			bool empty = !string.IsNullOrEmpty(token.Value);
			bool match = statement.EndStatement.IndexOf(token.Value, StringComparison.OrdinalIgnoreCase) != -1;
			
			return empty && match;
		}
		static VBNetFormattingStrategy()
		{
			statements = new List<VBStatement>();
			statements.Add(new VBStatement(@"^if.*?(then|\s+_)$", "^end ?if$", "End If", 1, Tokens.If));
			statements.Add(new VBStatement(@"\bclass\s+\w+\s*($|\(\s*Of)", "^end class$", "End Class", 1, Tokens.Class));
			statements.Add(new VBStatement(@"\bnamespace\s+\w+(\.\w+)*$", "^end namespace$", "End Namespace", 1, Tokens.Namespace));
			statements.Add(new VBStatement(@"\bmodule\s+\w+$", "^end module$", "End Module", 1, Tokens.Module));
			statements.Add(new VBStatement(@"\bstructure\s+\w+\s*($|\(\s*Of)", "^end structure$", "End Structure", 1, Tokens.Structure));
			statements.Add(new VBStatement(@"^while\s+", "^end while$", "End While", 1, Tokens.While));
			statements.Add(new VBStatement(@"^select case", "^end select$", "End Select", 1, Tokens.Select));
			statements.Add(new VBStatement(@"(?<!\b(delegate|mustoverride|declare(\s+(unicode|ansi|auto))?)\s+)\bsub\s+\w+", @"^end\s+sub$", "End Sub", 1, Tokens.Sub));
			statements.Add(new VBStatement(@"(?<!\bmustoverride (readonly |writeonly )?)\bproperty\s+\w+", @"^end\s+property$", "End Property", 1, Tokens.Property));
			statements.Add(new VBStatement(@"(?<!\b(delegate|mustoverride|declare(\s+(unicode|ansi|auto))?)\s+)\bfunction\s+\w+", @"^end\s+function$", "End Function", 1, Tokens.Function));
			statements.Add(new VBStatement(@"\boperator(\s*[\+\-\*\/\&\^\>\<\=\\]+\s*|\s+\w+\s*)\(", @"^end\s+operator$", "End Operator", 1, Tokens.Operator));
			statements.Add(new VBStatement(@"\bfor\s+.*?$", "^next( \\w+)?$", "Next", 1, Tokens.For));
			statements.Add(new VBStatement(@"^synclock\s+.*?$", "^end synclock$", "End SyncLock", 1, Tokens.SyncLock));
			statements.Add(new VBStatement(@"^get$", "^end get$", "End Get", 1, Tokens.Get));
			statements.Add(new VBStatement(@"^with\s+.*?$", "^end with$", "End With", 1, Tokens.With));
			statements.Add(new VBStatement(@"^set(\s*\(.*?\))?$", "^end set$", "End Set", 1, Tokens.Set));
			statements.Add(new VBStatement(@"^try$", "^end try$", "End Try", 1, Tokens.Try));
			statements.Add(new VBStatement(@"^do\s+.+?$", "^loop$", "Loop", 1, Tokens.Do));
			statements.Add(new VBStatement(@"^do$", "^loop .+?$", "Loop While ", 1, Tokens.Do));
			statements.Add(new VBStatement(@"\benum\s+\w+$", "^end enum$", "End Enum", 1, Tokens.Enum));
			interfaceStatement = new VBStatement(@"\binterface\s+\w+\s*($|\(\s*Of)", "^end interface$", "End Interface", 1, Tokens.Interface);
			statements.Add(interfaceStatement);
			statements.Add(new VBStatement(@"\busing\s+", "^end using$", "End Using", 1, Tokens.Using));
			statements.Add(new VBStatement(@"^#region\s+", "^#end region$", "#End Region", 0, -1));
		}
 static int FindBeginStatementAroundOffset(IDocument document, int offset, out VBStatement statement, out int length)
 {
     length    = 0;
     statement = null;
     return(-1);
 }