// TODO: Can we refactor FindMatchingCloseChar and FindMatchingOpenChar into a single method? static bool FindMatchingCloseChar(BlockSpan scriptBlock, IClassifier classifier, SnapshotPoint startPoint, char open, char close, int maxLines, ref bool maxLinesReached, out SnapshotSpan pairSpan) { var isHTML = startPoint.Snapshot.ContentType.TypeName.Equals("HTML", StringComparison.OrdinalIgnoreCase); pairSpan = new SnapshotSpan(startPoint.Snapshot, 1, 1); var line = startPoint.GetContainingLine(); var lineText = line.GetText(); var lineNumber = line.LineNumber; var offset = startPoint.Position - line.Start.Position + 1; var endOfScriptBlockReached = false; var stopLineNumber = startPoint.Snapshot.LineCount - 1; if (maxLines > 0) stopLineNumber = Math.Min(stopLineNumber, lineNumber + maxLines); #if DEBUG var startTicks = DateTime.Now.Ticks; Util.Log("Looking for matching closing brace '{0}'", close); #endif var openCount = 0; while (true) { // walk the entire line while (offset < line.Length) { // Check if we've hit the end of the script block if (scriptBlock.End < line.Start + offset) { // We've hit the end of a script block endOfScriptBlockReached = true; break; } char currentChar = lineText[offset]; var currentCharPoint = line.Start + offset; if (currentChar == close) // found the close character { if (JScriptEditorUtil.IsClassifiedAs(classifier, currentCharPoint, JScriptClassifications.Comment, JScriptClassifications.String, JScriptClassifications.Operator)) { #if DEBUG Util.Log("Candidate closing brace found but ignored because it was a comment, string or operator"); #endif } else if (openCount > 0) { openCount--; } else // found the matching close { #if DEBUG Util.Log("Matching closing brace '{0}' found after {1}ms", close, TimeSpan.FromTicks(DateTime.Now.Ticks - startTicks).TotalMilliseconds); #endif pairSpan = new SnapshotSpan(startPoint.Snapshot, currentCharPoint, 1); return true; } } else if (currentChar == open) // this is another open { if (JScriptEditorUtil.IsClassifiedAs(classifier, currentCharPoint, JScriptClassifications.Comment, JScriptClassifications.String, JScriptClassifications.Operator)) { #if DEBUG Util.Log("New opening brace found but ignored because it was a comment, string or operator"); #endif } else { openCount++; } } offset++; } if (endOfScriptBlockReached) break; // move on to the next line if (++lineNumber > stopLineNumber) { #if DEBUG Util.Log("Reached max lines ({0}), stopped looking at line {1} after {2}ms", maxLines, stopLineNumber, TimeSpan.FromTicks(DateTime.Now.Ticks - startTicks).TotalMilliseconds); #endif // Set maxLinesReached flag only if we actually hit max lines limit, not just end of buffer maxLinesReached = stopLineNumber >= maxLines; break; } // Move to next line line = line.Snapshot.GetLineFromLineNumber(lineNumber); lineText = line.GetText(); offset = 0; } #if DEBUG Util.Log("Matching closing brace '{0}' was not found. Open count: {1} after {2}ms", close, openCount, TimeSpan.FromTicks(DateTime.Now.Ticks - startTicks).TotalMilliseconds); #endif return false; }
static bool FindMatchingOpenChar(BlockSpan scriptBlock, IClassifier classifier, SnapshotPoint startPoint, char open, char close, int maxLines, ref bool maxLinesReached, out SnapshotSpan pairSpan) { var isHTML = startPoint.Snapshot.ContentType.TypeName.Equals("HTML", StringComparison.OrdinalIgnoreCase); pairSpan = new SnapshotSpan(startPoint, startPoint); ITextSnapshotLine line = startPoint.GetContainingLine(); var lineNumber = line.LineNumber; var offset = startPoint - line.Start - 1; // move the offset to the character before this one var startOfScriptBlockReached = false; // if the offset is negative, move to the previous line if (offset < 0) { line = line.Snapshot.GetLineFromLineNumber(--lineNumber); offset = line.Length - 1; } var lineText = line.GetText(); var stopLineNumber = 0; var nominalStopLineNumber = lineNumber - maxLines; if (maxLines > 0) stopLineNumber = Math.Max(stopLineNumber, nominalStopLineNumber); #if DEBUG var startTicks = DateTime.Now.Ticks; Util.Log("Looking for matching opening brace '{0}'", open); #endif var closeCount = 0; Debug.Assert(scriptBlock != null); while (true) { // Walk the entire line while (offset >= 0) { // Check if we've hit the start of the script block if (scriptBlock.Start > line.Start + offset) { // We've hit the end of a script block startOfScriptBlockReached = true; break; } char currentChar = lineText[offset]; var currentCharPoint = line.Start + offset; if (currentChar == open) // found the open character { if (JScriptEditorUtil.IsClassifiedAs(classifier, currentCharPoint, JScriptClassifications.Comment, JScriptClassifications.String, JScriptClassifications.Operator)) { #if DEBUG Util.Log("Candidate opening brace found but ignored because it was a comment, string or operator"); #endif } else if (closeCount > 0) { closeCount--; } else // We've found the matching open character { #if DEBUG Util.Log("Matching opening brace '{0}' found in {1}ms", open, TimeSpan.FromTicks(DateTime.Now.Ticks - startTicks).TotalMilliseconds); #endif pairSpan = new SnapshotSpan(currentCharPoint, 1); // we just want the character itself return true; } } else if (currentChar == close) { if (JScriptEditorUtil.IsClassifiedAs(classifier, currentCharPoint, JScriptClassifications.Comment, JScriptClassifications.String, JScriptClassifications.Operator)) { #if DEBUG Util.Log("New closing brace found but ignored because it was a comment, string or operator"); #endif } else { closeCount++; } } offset--; } if (startOfScriptBlockReached) break; // Move to the previous line if (--lineNumber < stopLineNumber) { // Set maxLinesReached flag only if we actually hit max lines limit, not just start of buffer maxLinesReached = stopLineNumber == nominalStopLineNumber; #if DEBUG Util.Log("Reached max lines ({0}, stopped looking at line {1} after {2}ms", maxLines, stopLineNumber, TimeSpan.FromTicks(DateTime.Now.Ticks - startTicks).TotalMilliseconds); #endif break; } line = line.Snapshot.GetLineFromLineNumber(lineNumber); lineText = line.GetText(); offset = line.Length - 1; } #if DEBUG Util.Log("Matching opening brace '{0}' was not found. Open count: {1} after {2}ms", open, closeCount, TimeSpan.FromTicks(DateTime.Now.Ticks - startTicks).TotalMilliseconds); #endif return false; }