public static bool getXmlMatchedTagsPos(Scintilla scintilla, ref XmlMatchedTagsPos xmlTags, bool prevChar) { bool tagFound = false; int caret = scintilla.NativeInterface.GetCurrentPos(); if (prevChar) { if (caret > 0) { caret--; } } int searchStartPoint = caret; int styleAt = -1; // search back for the previous open angle bracket. // Keep looking whilst the angle bracked found is inside an XML attribute. FindResult openFound = new FindResult(); do { openFound = findText("<", searchStartPoint, 0, 0, scintilla); styleAt = scintilla.NativeInterface.GetStyleAt(openFound.start); searchStartPoint = openFound.start - 1; } while (openFound.success && (styleAt == Constants.SCE_H_DOUBLESTRING || styleAt == Constants.SCE_H_SINGLESTRING) && searchStartPoint > 0); if (openFound.success && styleAt != Constants.SCE_H_CDATA) { FindResult closeFound = new FindResult(); searchStartPoint = openFound.start; do { closeFound = findText(">", searchStartPoint, caret, 0, scintilla); styleAt = scintilla.NativeInterface.GetStyleAt(closeFound.start); searchStartPoint = closeFound.end; } while (closeFound.success && (styleAt == Constants.SCE_H_DOUBLESTRING || styleAt == Constants.SCE_H_SINGLESTRING) && searchStartPoint <= caret); if (!closeFound.success) { char nextChar = scintilla.NativeInterface.GetCharAt(openFound.start + 1); if (nextChar == '/') { xmlTags.tagCloseStart = openFound.start; int docLength = scintilla.NativeInterface.GetLength(); FindResult endCloseTag = findText(">", caret, docLength, 0, scintilla); if (endCloseTag.success) { xmlTags.tagCloseEnd = endCloseTag.end; } // Find the tag name. int position = openFound.start + 2; string tagName = ""; nextChar = scintilla.NativeInterface.GetCharAt(position); while (position < docLength && !nextChar.IsWhiteSpace() && nextChar != '/' && nextChar != '>' && nextChar != '"' && nextChar != '\'') { tagName += nextChar; // tagName; position++; nextChar = scintilla.NativeInterface.GetCharAt(position); } if (!String.IsNullOrEmpty(tagName)) { /* Now we need to find the open tag. The logic here is that we search for "<TAGNAME", * then check the next character - if it's one of '>', ' ', '\"' then we know we've found * a relevant tag. * We then need to check if either * a) this tag is a self-closed tag - e.g. <TAGNAME attrib="value" /> * or b) this tag has another closing tag after it and before our closing tag * e.g. <TAGNAME attrib="value">some text</TAGNAME></TAGNA|ME> * (cursor represented by |) * If it's either of the above, then we continue searching, but only up to the * the point of the last find. (So in the (b) example above, we'd only search backwards * from the first "<TAGNAME...", as we know there's a close tag for the opened tag. * * NOTE:: NEED TO CHECK THE ROTTEN CASE: *********************************************************** * <TAGNAME attrib="value"><TAGNAME>something</TAGNAME></TAGNAME></TAGNA|ME> * Maybe count all closing tags between start point and start of our end tag.??? */ int currentEndPoint = xmlTags.tagCloseStart; int openTagsRemaining = 1; FindResult nextOpenTag = new FindResult(); do { nextOpenTag = findOpenTag(tagName, currentEndPoint, 0, scintilla); if (nextOpenTag.success) { openTagsRemaining--; FindResult inbetweenCloseTag = new FindResult(); int currentStartPosition = nextOpenTag.end; int closeTagsFound = 0; bool forwardSearch = (currentStartPosition < currentEndPoint); do { inbetweenCloseTag = findCloseTag(tagName, currentStartPosition, currentEndPoint, scintilla); if (inbetweenCloseTag.success) { closeTagsFound++; if (forwardSearch) { currentStartPosition = inbetweenCloseTag.end; } else { currentStartPosition = inbetweenCloseTag.start - 1; } } } while (inbetweenCloseTag.success); if (closeTagsFound == 0 && openTagsRemaining == 0) { xmlTags.tagOpenStart = nextOpenTag.start; xmlTags.tagOpenEnd = nextOpenTag.end + 1; xmlTags.tagNameEnd = nextOpenTag.start + tagName.Length + 1; tagFound = true; } else { openTagsRemaining += closeTagsFound; currentEndPoint = nextOpenTag.start; } } } while (!tagFound && openTagsRemaining > 0 && nextOpenTag.success); } } else { // Cursor in open tag int position = openFound.start + 1; int docLength = scintilla.NativeInterface.GetLength(); xmlTags.tagOpenStart = openFound.start; string tagName = ""; nextChar = scintilla.NativeInterface.GetCharAt(position); while (position < docLength && !nextChar.IsWhiteSpace() && nextChar != '/' && nextChar != '>' && nextChar != '"' && nextChar != '\'') { tagName = tagName + nextChar; position++; nextChar = scintilla.NativeInterface.GetCharAt(position); } if (!String.IsNullOrEmpty(tagName)) { xmlTags.tagNameEnd = openFound.start + tagName.Length + 1; int closeAnglePosition = findCloseAngle(position, docLength, scintilla); if (closeAnglePosition != -1) { xmlTags.tagOpenEnd = closeAnglePosition + 1; if (scintilla.NativeInterface.GetCharAt(closeAnglePosition - 1) == '/') { xmlTags.tagCloseEnd = -1; xmlTags.tagCloseStart = -1; tagFound = true; } else { int currentStartPosition = xmlTags.tagOpenEnd; int closeTagsRemaining = 1; FindResult nextCloseTag = new FindResult(); do { nextCloseTag = findCloseTag(tagName, currentStartPosition, docLength, scintilla); if (nextCloseTag.success) { closeTagsRemaining--; FindResult inbetweenOpenTag = new FindResult(); int currentEndPosition = nextCloseTag.start; int openTagsFound = 0; do { inbetweenOpenTag = findOpenTag(tagName, currentStartPosition, currentEndPosition, scintilla); if (inbetweenOpenTag.success) { openTagsFound++; currentStartPosition = inbetweenOpenTag.end; } } while (inbetweenOpenTag.success); if (openTagsFound == 0 && closeTagsRemaining == 0) { xmlTags.tagCloseStart = nextCloseTag.start; xmlTags.tagCloseEnd = nextCloseTag.end + 1; tagFound = true; } else { closeTagsRemaining += openTagsFound; currentStartPosition = nextCloseTag.end; } } } while (!tagFound && closeTagsRemaining > 0 && nextCloseTag.success); } } } } } } return(tagFound); }
/// <summary> /// If Smart Indenting is enabled, this delegate will be added to the CharAdded multicast event. /// </summary> internal void CheckSmartIndent(char ch) { char newline = (Scintilla.EndOfLine.Mode == EndOfLineMode.CR) ? '\r' : '\n'; switch (SmartIndentType) { case SmartIndent.None: return; case SmartIndent.Simple: if (ch == newline) { Line curLine = Scintilla.Lines.Current; curLine.Indentation = curLine.Previous.Indentation; Scintilla.CurrentPos = curLine.IndentPosition; } break; case SmartIndent.XML: if (ch == '}' || ch == ')' || ch == ']') { int styleBit = getPrevStyle(Scintilla.CurrentPos); if (handleWithCpp(styleBit)) { handleCpp(ch, newline); } } if (ch == newline) { int cpos = Scintilla.CurrentPos; int origPos = cpos; if (Scintilla.EndOfLine.Mode == EndOfLineMode.Crlf) { cpos -= 2; } else { cpos -= 1; } int styleBit = getPrevStyle(cpos); if (handleWithCpp(styleBit)) { handleCpp(ch, newline); } else { Line curLine = Scintilla.Lines.FromPosition(cpos); string line = curLine.Text; int col = Scintilla.GetColumn(cpos); int i = 0; int openCnt = 0; int closeCount = 0; bool inTag = false; string tagName = ""; bool buildingTag = false; while (i < col && i < line.Length) { if (line[i] == '<') { if (curLine.Length > i) { if (line[i + 1] != '/' && line[i + 1] != '!') { buildingTag = true; inTag = true; } else { closeCount++; inTag = false; } } } if (inTag && line[i] != '<') { if (line[i].IsWordChar() && buildingTag) { tagName += line[i]; } if (!line[i].IsWordChar()) { buildingTag = false; } if (line[i] == '>' && inTag) { if (i > 0) { if (line[i - 1] != '/') { // Ignore br. May be others to ignore. if (tagName.ToLowerInvariant() != "br") { openCnt++; } inTag = false; } } } } i++; } if (openCnt > closeCount) { cpos = Scintilla.CurrentPos; string lnBreak = ""; if (Scintilla.EndOfLine.Mode == EndOfLineMode.CR) { lnBreak = "\r"; } else if (Scintilla.EndOfLine.Mode == EndOfLineMode.LF) { lnBreak = "\n"; } else { lnBreak = "\r\n"; } Scintilla.UndoRedo.BeginUndoAction(); // Grab the current line indent int lineIndent = curLine.Indentation; // add in a line break. This also puts cursor on the next line but only // if auto closing html brackets. if (Scintilla.AutoClose.AutoCloseHtmlTags) { Scintilla.Selection.Text = lnBreak; } // Indent the next line back to the current line. Scintilla.Lines.Current.Indentation = curLine.Indentation; // Jump back to the original position (The new line which we want indented further Scintilla.Selection.Start = origPos; Scintilla.Selection.End = origPos; // Set indentation. Scintilla.Lines.Current.Indentation = curLine.Indentation + TabWidth; Scintilla.CurrentPos = Scintilla.Lines.Current.EndPosition; Scintilla.UndoRedo.EndUndoAction(); } else { // maintain indentation Scintilla.UndoRedo.BeginUndoAction(); Scintilla.Lines.Current.Indentation = curLine.Indentation; Scintilla.CurrentPos = Scintilla.Lines.Current.EndPosition; Scintilla.UndoRedo.EndUndoAction(); } } } if (ch == '>') { XmlMatchedTagsPos xmlTag = new XmlMatchedTagsPos(); if (MatchingTag.getXmlMatchedTagsPos(Scintilla, ref xmlTag, true)) { int indentSize = Scintilla.NativeInterface.GetLineIndentation(Scintilla.NativeInterface.LineFromPosition(xmlTag.tagOpenStart)); Scintilla.NativeInterface.SetLineIndentation(Scintilla.Lines.Current.Number, indentSize); } } break; case SmartIndent.CPP: case SmartIndent.CPP2: handleCpp(ch, newline); break; } }