/// <summary> /// Gets the type of code at offset.<br/> /// 0 = Code,<br/> /// 1 = Comment,<br/> /// 2 = String<br/> /// Block comments and multiline strings are not supported. /// </summary> static int GetStartType(TextDocument document, int linestart, int offset) { bool inString = false; bool inChar = false; bool verbatim = false; int result = 0; for (int i = linestart; i < offset; i++) { switch (document.GetCharAt(i)) { case '/': if (!inString && !inChar && i + 1 < document.TextLength) { if (document.GetCharAt(i + 1) == '/') { result = 1; } } break; case '"': case '\'': if (!inChar) { if (inString && verbatim) { if (i + 1 < document.TextLength && document.GetCharAt(i + 1) == '"') { ++i; // skip escaped quote inString = false; // let the string go on } else { verbatim = false; } } else if (!inString && i > 0 && document.GetCharAt(i - 1) == '@') { verbatim = true; } inString = !inString; } break; case '\\': if ((inString && !verbatim) || inChar) ++i; // skip next character break; } } return (inString || inChar) ? 2 : result; }
/// <summary> /// Sets the total line length and checks the delimiter. /// This method can cause line to be deleted when it contains a single '\n' character /// and the previous line ends with '\r'. /// </summary> /// <returns>Usually returns <paramref name="line"/>, but if line was deleted due to /// the "\r\n" merge, returns the previous line.</returns> DocumentLine SetLineLength(DocumentLine line, int newTotalLength) { // changedLines.Add(line); // deletedOrChangedLines.Add(line); int delta = newTotalLength - line.TotalLength; if (delta != 0) { foreach (ILineTracker lt in lineTrackers) { lt.SetLineLength(line, newTotalLength); } line.TotalLength = newTotalLength; DocumentLineTree.UpdateAfterChildrenChange(line); } // determine new DelimiterLength if (newTotalLength == 0) { line.DelimiterLength = 0; } else { int lineOffset = line.Offset; char lastChar = document.GetCharAt(lineOffset + newTotalLength - 1); if (lastChar == '\r') { line.DelimiterLength = 1; } else if (lastChar == '\n') { if (newTotalLength >= 2 && document.GetCharAt(lineOffset + newTotalLength - 2) == '\r') { line.DelimiterLength = 2; } else if (newTotalLength == 1 && lineOffset > 0 && document.GetCharAt(lineOffset - 1) == '\r') { // we need to join this line with the previous line DocumentLine previousLine = line.PreviousLine; RemoveLine(line); return(SetLineLength(previousLine, previousLine.TotalLength + 1)); } else { line.DelimiterLength = 1; } } else { line.DelimiterLength = 0; } } return(line); }
public BracketSearchResult SearchBracket(TextDocument document, int offset) { if (offset > 0) { var c = document.GetCharAt(offset - 1); int index = openingBrackets.IndexOf(c); int otherOffset = -1; if (index > -1) otherOffset = SearchBracketForward(document, offset, openingBrackets[index], closingBrackets[index]); index = closingBrackets.IndexOf(c); if (index > -1) otherOffset = SearchBracketBackward(document, offset - 2, openingBrackets[index], closingBrackets[index]); if (otherOffset > -1) { var result = new BracketSearchResult( Math.Min(offset - 1, otherOffset), Math.Max(offset - 1, otherOffset)); SearchDefinition(document, result); return result; } } return null; }
public void RawlyIndentLine(string indentString, ICSharpCode.AvalonEdit.Document.TextDocument document, DocumentLine line) { if (!_doBeginUpdateManually) { document.BeginUpdate(); } // 1) int prevInd = 0; int curOff = line.Offset; if (curOff < document.TextLength) { char curChar = '\0'; while (curOff < document.TextLength && ((curChar = document.GetCharAt(curOff)) == ' ' || curChar == '\t')) { prevInd++; curOff++; } document.Remove(line.Offset, prevInd); } document.Insert(line.Offset, indentString); if (!_doBeginUpdateManually) { document.EndUpdate(); } }
public BracketSearchResult SearchBracket(TextDocument document, int offset) { BracketSearchResult result; if (offset > 0) { var charAt = document.GetCharAt(offset - 1); var num = "([{".IndexOf(charAt); var num2 = -1; if (num > -1) { num2 = SearchBracketForward(document, offset, "([{"[num], ")]}"[num]); } num = ")]}".IndexOf(charAt); if (num > -1) { num2 = SearchBracketBackward(document, offset - 2, "([{"[num], ")]}"[num]); } if (num2 > -1) { result = new BracketSearchResult(Math.Min(offset - 1, num2), 1, Math.Max(offset - 1, num2), 1); return result; } } result = null; return result; }
public override int GetFirstInterestedOffset(int startOffset) { ICSharpCode.AvalonEdit.Document.TextDocument document = CurrentContext.Document; int firstOffset = CurrentContext.VisualLine.FirstDocumentLine.Offset; if (firstOffset == startOffset) { return(firstOffset); } else { return(-1); } int endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset; for (int i = startOffset; i < endOffset; i++) { char c = document.GetCharAt(i); if (c == ':') { return(i + 1); } } return(-1); }
public void RawlyIndentLine(int tabsToInsert, TextDocument document, DocumentLine line) { if (!_doBeginUpdateManually) document.BeginUpdate(); /* * 1) Remove old indentation * 2) Insert new one */ // 1) int prevInd = 0; int curOff = line.Offset; if (curOff < document.TextLength) { char curChar = '\0'; while (curOff < document.TextLength && ((curChar = document.GetCharAt(curOff)) == ' ' || curChar == '\t')) { prevInd++; curOff++; } document.Remove(line.Offset, prevInd); } // 2) string indentString = ""; for (int i = 0; i < tabsToInsert; i++) indentString += dEditor.Editor.Options.IndentationString; document.Insert(line.Offset, indentString); if (!_doBeginUpdateManually) document.EndUpdate(); }
private void CreateMethodFold(TextDocument textDocument) { // TODO: Find a way to fold just functions brackets. this.startOffsets.Push(this.currentOffset); this.bracketSearcher.OpeningBrackets = "{"; this.bracketSearcher.ClosingBrackets = "}"; BracketSearchResult result = this.bracketSearcher.SearchBracket(textDocument, this.currentOffset + 1); if (result != null) { int startOffset = startOffsets.Pop(); // Skip empty spaces. for(int i = startOffset - 1; i > 0; i--) { char character = textDocument.GetCharAt(i); if (char.IsWhiteSpace(character) == false && character != '\n' && character != '\r') { startOffset = i + 1; break; } } this.foldings.Foldings.Add(new NewFolding(startOffset, result.ClosingBracketOffset + 1) { Name = "..." }); } }
private void CreateCommentFold(TextDocument textDocument) { this.startOffsets.Push(this.currentOffset - 1); for (; this.currentOffset < textDocument.TextLength; this.currentOffset++) { char character = textDocument.GetCharAt(this.currentOffset); if (character == '*' && textDocument.GetCharAt(++this.currentOffset) == '/') { this.currentOffset++; foldings.Foldings.Add(new NewFolding(startOffsets.Pop(), this.currentOffset) { Name = "/* ..." }); break; } } }
static int ScanLineStart(TextDocument document, int offset) { for (int i = offset - 1; i > 0; --i) { if (document.GetCharAt(i) == '\n') return i + 1; } return 0; }
/// <summary> /// Gets the offset of the first non-whitespace character. /// </summary> /// <param name="document">The document.</param> /// <param name="offset">The offset where to start the search.</param> /// <returns> /// The offset of the first non-whitespace at or after <paramref name="offset"/>. /// <see cref="TextDocument.TextLength"/> is returned if no non-whitespace is found. (Line /// breaks do not count as whitespace.) /// </returns> internal static int FindFirstNonWhitespace(TextDocument document, int offset) { while (offset < document.TextLength && char.IsWhiteSpace(document.GetCharAt(offset))) { ++offset; } return(offset); }
/// <summary> /// Gets the offset of the first non-whitespace character. /// </summary> /// <param name="document">The document.</param> /// <param name="offset">The offset where to start the search.</param> /// <param name="searchBackwards"> /// If set to <see langword="true"/>, the document is searched in backwards direction (= /// from <paramref name="offset"/> to start of text document. /// </param> /// <returns> /// <para> /// If <paramref name="searchBackwards"/> is <see langword="false"/>: /// The offset of the first non-whitespace at or after <paramref name="offset" />. /// <see cref="TextDocument.TextLength" /> is returned if no non-whitespace is found. (Line /// breaks do not count as whitespace.) /// </para> /// <para> /// If <paramref name="searchBackwards"/> is <see langword="true"/>: /// The offset of the first non-whitespace at or before <paramref name="offset" />. /// 0 is returned if no non-whitespace is found. (Line breaks do not count as whitespace.) /// </para> /// </returns> internal static int FindFirstNonWhitespace(TextDocument document, int offset, bool searchBackwards) { if (searchBackwards) { while (offset > 0 && char.IsWhiteSpace(document.GetCharAt(offset))) { --offset; } } else { while (offset < document.TextLength && char.IsWhiteSpace(document.GetCharAt(offset))) { ++offset; } } return(offset); }
void SearchDefinition(TextDocument document, BracketSearchResult result) { if (document.GetCharAt(result.OpeningOffset) != '{') return; // get line var documentLine = document.GetLineByOffset(result.OpeningOffset); while (documentLine != null && IsBracketOnly(document, documentLine)) documentLine = documentLine.PreviousLine; if (documentLine != null) { result.DefinitionHeaderOffset = documentLine.Offset; result.DefinitionHeaderLength = documentLine.Length; } }
/// <summary> /// Determines whether a line of a document is empty (no characters or whitespace). /// </summary> /// <param name="document">The document.</param> /// <param name="line">The line.</param> /// <returns> /// <see langword="true"/> if line is empty of filled with whitespace; otherwise, /// <see langword="false"/>. /// </returns> internal static bool IsEmptyLine(TextDocument document, DocumentLine line) { int lineOffset = line.Offset; int startOffset = lineOffset; int endOffset = lineOffset + line.Length; for (int i = startOffset; i < endOffset; ++i) { char ch = document.GetCharAt(i); if (!char.IsWhiteSpace(ch)) { return(false); } } return(true); }
/// <summary> /// Checks whether a region (offset + length) matches a given word. /// </summary> /// <param name="document">The document.</param> /// <param name="offset">The offset.</param> /// <param name="length">The length.</param> /// <param name="word">The word.</param> /// <returns><see langword="true"/> if region matches word.</returns> /// <remarks> /// The comparison is case-sensitive. /// </remarks> internal static bool CompareSegment(TextDocument document, int offset, int length, string word) { if (length != word.Length || document.TextLength < offset + length) { return(false); } for (int i = 0; i < length; ++i) { if (document.GetCharAt(offset + i) != word[i]) { return(false); } } return(true); }
private void codeEditor_TextEntered(object sender, TextCompositionEventArgs e) { if (e.Text == ".") { ICSharpCode.AvalonEdit.Editing.TextArea area = (sender as ICSharpCode.AvalonEdit.Editing.TextArea); ICSharpCode.AvalonEdit.Document.TextDocument doc = area.Document; int startoff = area.Caret.Offset - 1; int off = area.Caret.Offset - 2; while (true) { char o = doc.GetCharAt(off); if (!char.IsLetter(o)) { break; } if (off == 0) { break; } off--; } string prevToken = doc.GetText(off, startoff - off); // Open code completion after the user has pressed dot: completionWindow = new CompletionWindow(CodeEditor.TextArea); completionWindow.Margin = new Thickness(0); IList <ICompletionData> data = completionWindow.CompletionList.CompletionData; try { List <string> items = CompletionDatabase.completionDatabase[prevToken]; foreach (string item in items) { data.Add(new CompletionData(item)); } completionWindow.Show(); completionWindow.Closed += delegate { completionWindow = null; }; }catch (Exception) { } } }
public void RawlyIndentLine(int tabsToInsert, ICSharpCode.AvalonEdit.Document.TextDocument document, DocumentLine line) { if (!_doBeginUpdateManually) { document.BeginUpdate(); } /* * 1) Remove old indentation * 2) Insert new one */ // 1) int prevInd = 0; int curOff = line.Offset; if (curOff < document.TextLength) { char curChar = '\0'; while (curOff < document.TextLength && ((curChar = document.GetCharAt(curOff)) == ' ' || curChar == '\t')) { prevInd++; curOff++; } document.Remove(line.Offset, prevInd); } // 2) string indentString = ""; for (int i = 0; i < tabsToInsert; i++) { indentString += dEditor.Editor.Options.IndentationString; } document.Insert(line.Offset, indentString); if (!_doBeginUpdateManually) { document.EndUpdate(); } }
/// <summary> /// Finds and marks all regions. /// </summary> /// <param name="document">The document.</param> /// <param name="offset">The offset where the search starts.</param> /// <param name="folds">The fold markers.</param> /// <returns>The index of the next character after the all regions.</returns> /// <remarks> /// This method returns when it finds a "#endregion" string that does not have /// a "#region" statement after <paramref name="offset"/>. In this case it /// returns the index of the next character after the "#endregion" statement. /// </remarks> private static int FindAndMarkRegions(TextDocument document, int offset, List<NewFolding> folds) { if (offset >= document.TextLength) return offset; while (offset < document.TextLength) { char c = document.GetCharAt(offset); switch (c) { case '/': // Skip comments offset = SkipComment(document, offset); break; case '#': string word = TextUtilities.GetIdentifierAt(document, offset + 1); if (word == "region") { offset = MarkRegion(document, offset, folds); } else if (word == "endregion") { return offset + "endregion".Length + 1; } else { offset++; } break; default: // Skip to next word int endOfIdentifier = TextUtilities.FindEndOfIdentifier(document, offset); if (endOfIdentifier > 0) offset = endOfIdentifier + 1; else ++offset; break; } } return offset; }
/// <summary> /// Checks whether a region (offset + length) matches a given word. /// </summary> /// <param name="document">The document.</param> /// <param name="offset">The offset.</param> /// <param name="length">The length.</param> /// <param name="word">The word.</param> /// <param name="caseSensitive"> /// If set to <see langword="true"/> the comparison is case-sensitive. /// </param> /// <returns><see langword="true"/> if region matches word.</returns> internal static bool CompareSegment(TextDocument document, int offset, int length, string word, bool caseSensitive) { if (caseSensitive) { return(CompareSegment(document, offset, length, word)); } if (length != word.Length || document.TextLength < offset + length) { return(false); } for (int i = 0; i < length; ++i) { if (char.ToUpper(document.GetCharAt(offset + i)) != char.ToUpper(word[i])) { return(false); } } return(true); }
public static char GetPreviousChar(TextDocument doc, int offset) { if (offset - 1 == 0) return '\0'; return doc.GetCharAt(offset - 1); }
int SearchBracketBackward(TextDocument document, int offset, char openBracket, char closingBracket) { if (offset + 1 >= document.TextLength) return -1; // this method parses a c# document backwards to find the matching bracket // first try "quick find" - find the matching bracket if there is no string/comment in the way int quickResult = QuickSearchBracketBackward(document, offset, openBracket, closingBracket); if (quickResult >= 0) return quickResult; // we need to parse the line from the beginning, so get the line start position int linestart = ScanLineStart(document, offset + 1); // I don't see any possibility to parse a C# document backwards... // We have to do it forwards and push all bracket positions on a stack. Stack<int> bracketStack = new Stack<int>(); bool blockComment = false; bool lineComment = false; bool inChar = false; bool inString = false; bool verbatim = false; for(int i = 0; i <= offset; ++i) { char ch = document.GetCharAt(i); switch (ch) { case '\r': case '\n': lineComment = false; inChar = false; if (!verbatim) inString = false; break; case '/': if (blockComment) { Debug.Assert(i > 0); if (document.GetCharAt(i - 1) == '*') { blockComment = false; } } if (!inString && !inChar && i + 1 < document.TextLength) { if (!blockComment && document.GetCharAt(i + 1) == '/') { lineComment = true; } if (!lineComment && document.GetCharAt(i + 1) == '*') { blockComment = true; } } break; case '"': if (!(inChar || lineComment || blockComment)) { if (inString && verbatim) { if (i + 1 < document.TextLength && document.GetCharAt(i + 1) == '"') { ++i; // skip escaped quote inString = false; // let the string go } else { verbatim = false; } } else if (!inString && offset > 0 && document.GetCharAt(i - 1) == '@') { verbatim = true; } inString = !inString; } break; case '\'': if (!(inString || lineComment || blockComment)) { inChar = !inChar; } break; case '\\': if ((inString && !verbatim) || inChar) ++i; // skip next character break; default : if (ch == openBracket) { if (!(inString || inChar || lineComment || blockComment)) { bracketStack.Push(i); } } else if (ch == closingBracket) { if (!(inString || inChar || lineComment || blockComment)) { if (bracketStack.Count > 0) bracketStack.Pop(); } } break; } } if (bracketStack.Count > 0) return (int)bracketStack.Pop(); return -1; }
/// <summary> /// Marks all code blocks (namespaces, classes, methods, etc.) in the document. /// </summary> /// <param name="document">The document.</param> /// <param name="foldMarkers">The fold markers.</param> private static void MarkBlocks(TextDocument document, ICollection<NewFolding> foldMarkers) { int offset = 0; while (offset < document.TextLength) { switch (document.GetCharAt(offset)) { case '/': offset = SkipComment(document, offset); break; case '{': offset = MarkMethod(document, offset, foldMarkers); break; default: offset = MarkBlock(document, offset, foldMarkers); break; } } }
/// <summary> /// Marks the region that starts at the given offset. /// </summary> /// <param name="document">The document.</param> /// <param name="offset">The offset.</param> /// <param name="folds">The fold markers.</param> /// <returns>The index of the next character after the region.</returns> private static int MarkRegion(TextDocument document, int offset, List<NewFolding> folds) { if (offset >= document.TextLength) return offset; if (document.GetCharAt(offset) == '#') { int startOffset = offset; offset++; string word = TextUtilities.GetIdentifierAt(document, offset); if (word == "region") { offset += "region".Length; // Find label var line = document.GetLineByOffset(offset); int lineEnd = line.Offset + line.Length; int labelLength = lineEnd - offset; string label = document.GetText(offset, labelLength); label = label.Trim(); if (label.Length == 0) label = "#region"; // Find and mark subregions offset = FindAndMarkRegions(document, lineEnd, folds); if (offset <= document.TextLength) { AddFold(document, folds, startOffset, offset, label); offset++; } } } else { offset++; } return offset; }
/// <summary> /// Marks all code blocks (namespaces, classes, methods, etc.) in the document. /// </summary> /// <param name="document">The document.</param> /// <param name="foldMarkers">The fold markers.</param> private static void MarkBlocks(TextDocument document, ICollection<NewFolding> foldMarkers) { int offset = 0; while (offset < document.TextLength) { switch (document.GetCharAt(offset)) { case '/': offset = SkipComment(document, offset); break; case 'c': offset = MarkBlock("class", document, offset, foldMarkers); break; case 'e': offset = MarkBlock("enum", document, offset, foldMarkers); break; case 'i': offset = MarkBlock("interface", document, offset, foldMarkers); break; case 'n': offset = MarkBlock("namespace", document, offset, foldMarkers); break; case 's': offset = MarkBlock("struct", document, offset, foldMarkers); break; case '{': offset = MarkMethod(document, offset, foldMarkers); break; default: int endOfIdentifier = TextUtilities.FindEndOfIdentifier(document, offset); if (endOfIdentifier > 0) offset = endOfIdentifier + 1; else ++offset; break; } } }
public void InsertBold(int start, int length, TextDocument document) { var chs = document.GetCharAt(start); var che = document.GetCharAt(start + length - 1); document.Insert(start + length - 1, che.ToString()); document.Replace(start + length, 1, "]"); //trick to keep anchors document.Insert(start + 1, chs.ToString()); document.Replace(start, 1, "["); //trick to keep anchors Blocks.Add(new TextBlockBold() { OriginallyLength = length + 2, OriginallyOffset = start, MyAnchor = new AnchorSegment(document, start, length + 2) }); }
/// <summary> /// Gets the offset of the first non-whitespace character. /// </summary> /// <param name="document">The document.</param> /// <param name="offset">The offset where to start the search.</param> /// <returns> /// The offset of the first non-whitespace at or after <paramref name="offset"/>. /// <see cref="TextDocument.TextLength"/> is returned if no non-whitespace is found. (Line /// breaks do not count as whitespace.) /// </returns> internal static int FindFirstNonWhitespace(TextDocument document, int offset) { while (offset < document.TextLength && char.IsWhiteSpace(document.GetCharAt(offset))) ++offset; return offset; }
/// <summary> /// Gets the offset of the first non-whitespace character. /// </summary> /// <param name="document">The document.</param> /// <param name="offset">The offset where to start the search.</param> /// <param name="searchBackwards"> /// If set to <see langword="true"/>, the document is searched in backwards direction (= /// from <paramref name="offset"/> to start of text document. /// </param> /// <returns> /// <para> /// If <paramref name="searchBackwards"/> is <see langword="false"/>: /// The offset of the first non-whitespace at or after <paramref name="offset" />. /// <see cref="TextDocument.TextLength" /> is returned if no non-whitespace is found. (Line /// breaks do not count as whitespace.) /// </para> /// <para> /// If <paramref name="searchBackwards"/> is <see langword="true"/>: /// The offset of the first non-whitespace at or before <paramref name="offset" />. /// 0 is returned if no non-whitespace is found. (Line breaks do not count as whitespace.) /// </para> /// </returns> internal static int FindFirstNonWhitespace(TextDocument document, int offset, bool searchBackwards) { if (searchBackwards) { while (offset > 0 && char.IsWhiteSpace(document.GetCharAt(offset))) --offset; } else { while (offset < document.TextLength && char.IsWhiteSpace(document.GetCharAt(offset))) ++offset; } return offset; }
/// <summary> /// Gets the expression before a given offset. /// </summary> /// <param name="document">The document.</param> /// <param name="initialOffset">The initial offset.</param> /// <returns>The expression.</returns> /// <remarks> /// This method returns the expression before a specified offset. /// That method is used in code completion to determine the expression before /// the caret. The expression can be passed to a parser to resolve the type /// or similar. /// </remarks> internal static string GetExpressionBeforeOffset(TextDocument document, int initialOffset) { int offset = initialOffset; while (offset - 1 > 0) { switch (document.GetCharAt(offset - 1)) { case '\n': case '\r': case '}': goto done; //offset = FindOpeningBracket(document, offset - 2, '{','}'); //break; case ']': offset = FindOpeningBracket(document, offset - 2, '[', ']'); break; case ')': offset = FindOpeningBracket(document, offset - 2, '(', ')'); break; case '.': --offset; break; case '"': if (offset < initialOffset - 1) { return null; } return "\"\""; case '\'': if (offset < initialOffset - 1) { return null; } return "'a'"; case '>': if (document.GetCharAt(offset - 2) == '-') { offset -= 2; break; } goto done; default: if (char.IsWhiteSpace(document.GetCharAt(offset - 1))) { --offset; break; } int start = offset - 1; if (!IsIdentifierPart(document.GetCharAt(start))) { goto done; } while (start > 0 && IsIdentifierPart(document.GetCharAt(start - 1))) { --start; } string word = document.GetText(start, offset - start).Trim(); switch (word) { case "ref": case "out": case "in": case "return": case "throw": case "case": goto done; } if (word.Length > 0 && !IsIdentifierPart(word[0])) { goto done; } offset = start; break; } } done: // simple exit fails when : is inside comment line or any other character // we have to check if we got several ids in resulting line, which usually happens when // id. is typed on next line after comment one // Would be better if lexer would parse properly such expressions. However this will cause // modifications in this area too - to get full comment line and remove it afterwards if (offset < 0) return string.Empty; string resText = document.GetText(offset, initialOffset - offset).Trim(); int pos = resText.LastIndexOf('\n'); if (pos >= 0) { offset += pos + 1; // whitespaces and tabs, which might be inside, will be skipped by trim below } string expression = document.GetText(offset, initialOffset - offset).Trim(); return expression; }
public void GetCharAt0EmptyDocument() { document.GetCharAt(0); }
/// <summary> /// Determines whether a line of a document is empty (no characters or whitespace). /// </summary> /// <param name="document">The document.</param> /// <param name="line">The line.</param> /// <returns> /// <see langword="true"/> if line is empty of filled with whitespace; otherwise, /// <see langword="false"/>. /// </returns> internal static bool IsEmptyLine(TextDocument document, DocumentLine line) { int lineOffset = line.Offset; int startOffset = lineOffset; int endOffset = lineOffset + line.Length; for (int i = startOffset; i < endOffset; ++i) { char ch = document.GetCharAt(i); if (!char.IsWhiteSpace(ch)) return false; } return true; }
/// <summary> /// Gets the expression before a given offset. /// </summary> /// <param name="document">The document.</param> /// <param name="initialOffset">The initial offset.</param> /// <returns>The expression.</returns> /// <remarks> /// This method returns the expression before a specified offset. /// That method is used in code completion to determine the expression before /// the caret. The expression can be passed to a parser to resolve the type /// or similar. /// </remarks> internal static string GetExpressionBeforeOffset(TextDocument document, int initialOffset) { int offset = initialOffset; while (offset - 1 > 0) { switch (document.GetCharAt(offset - 1)) { case '\n': case '\r': case '}': goto done; //offset = FindOpeningBracket(document, offset - 2, '{','}'); //break; case ']': offset = FindOpeningBracket(document, offset - 2, '[', ']'); break; case ')': offset = FindOpeningBracket(document, offset - 2, '(', ')'); break; case '.': --offset; break; case '"': if (offset < initialOffset - 1) { return(null); } return("\"\""); case '\'': if (offset < initialOffset - 1) { return(null); } return("'a'"); case '>': if (document.GetCharAt(offset - 2) == '-') { offset -= 2; break; } goto done; default: if (char.IsWhiteSpace(document.GetCharAt(offset - 1))) { --offset; break; } int start = offset - 1; if (!IsIdentifierPart(document.GetCharAt(start))) { goto done; } while (start > 0 && IsIdentifierPart(document.GetCharAt(start - 1))) { --start; } string word = document.GetText(start, offset - start).Trim(); switch (word) { case "ref": case "out": case "in": case "return": case "throw": case "case": goto done; } if (word.Length > 0 && !IsIdentifierPart(word[0])) { goto done; } offset = start; break; } } done: // simple exit fails when : is inside comment line or any other character // we have to check if we got several ids in resulting line, which usually happens when // id. is typed on next line after comment one // Would be better if lexer would parse properly such expressions. However this will cause // modifications in this area too - to get full comment line and remove it afterwards if (offset < 0) { return(string.Empty); } string resText = document.GetText(offset, initialOffset - offset).Trim(); int pos = resText.LastIndexOf('\n'); if (pos >= 0) { offset += pos + 1; // whitespaces and tabs, which might be inside, will be skipped by trim below } string expression = document.GetText(offset, initialOffset - offset).Trim(); return(expression); }
private void CreateNewFoldings(TextDocument textDocument) { for (this.currentOffset = 0; this.currentOffset < textDocument.TextLength; this.currentOffset++) { char character = textDocument.GetCharAt(this.currentOffset); if (character == '{') { this.CreateMethodFold(textDocument); } else if (character == '/' && ++this.currentOffset < textDocument.TextLength && textDocument.GetCharAt(this.currentOffset) == '*') { this.CreateCommentFold(textDocument); } } this.foldings.Foldings.Sort((first, second) => first.StartOffset.CompareTo(second.StartOffset)); }
private IEnumerable<NewFolding> CreateNewFoldings(TextDocument document) { var newFoldings = new List<NewFolding>(); var startOffsets = new Stack<int>(); var titles = new Stack<string>(); var lastNewLineOffset = 0; for (var i = 0; i < document.TextLength; i++) { var c = document.GetCharAt(i); if (c == '#') { // see if the next word is 'region', or 'endregion' var directive = new StringBuilder(); var j = i + 1; while (j < document.TextLength && !Char.IsWhiteSpace(document.GetCharAt(j))) { directive.Append(document.GetCharAt(j++)); } if (directive.ToString().Equals("region")) { j++; var nameBuilder = new StringBuilder(); while (j < document.TextLength && !Char.IsWhiteSpace(document.GetCharAt(j))) { nameBuilder.Append(document.GetCharAt(j++)); } titles.Push(nameBuilder.ToString()); // go to next newline (if there is one), otherwise we don't start an offset while(j < document.TextLength) { c = document.GetCharAt(j++); if (c != '\n' && c != '\r') continue; lastNewLineOffset = j; startOffsets.Push(i); break; } } else if (directive.ToString().Equals("endregion") && startOffsets.Count > 0) { var startOffset = startOffsets.Pop(); var name = titles.Pop(); // don't fold if opening and closing brace are on the same line if (startOffset < lastNewLineOffset) { newFoldings.Add(new NewFolding(startOffset, j) {Name = name} ); } } i = j; } else if (c == '\n' || c == '\r') { lastNewLineOffset = i + 1; } } lastNewLineOffset = 0; const char openingBrace = '{'; const char closingBrace = '}'; for (var i = 0; i < document.TextLength; i++) { var c = document.GetCharAt(i); if (c == openingBrace) { startOffsets.Push(i); } else if (c == closingBrace && startOffsets.Count > 0) { var startOffset = startOffsets.Pop(); // don't fold if opening and closing brace are on the same line if (startOffset < lastNewLineOffset) { newFoldings.Add(new NewFolding(startOffset, i + 1)); } } else if (c == '\n' || c == '\r') { lastNewLineOffset = i + 1; } } newFoldings.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset)); return newFoldings; }
/// <summary> /// Checks whether a region (offset + length) matches a given word. /// </summary> /// <param name="document">The document.</param> /// <param name="offset">The offset.</param> /// <param name="length">The length.</param> /// <param name="word">The word.</param> /// <param name="caseSensitive"> /// If set to <see langword="true"/> the comparison is case-sensitive. /// </param> /// <returns><see langword="true"/> if region matches word.</returns> internal static bool CompareSegment(TextDocument document, int offset, int length, string word, bool caseSensitive) { if (caseSensitive) return CompareSegment(document, offset, length, word); if (length != word.Length || document.TextLength < offset + length) return false; for (int i = 0; i < length; ++i) if (char.ToUpper(document.GetCharAt(offset + i)) != char.ToUpper(word[i])) return false; return true; }
/// <summary> /// Marks the block that starts at the current offset. /// </summary> /// <param name="name">The identifier of the block (e.g. "class", "struct").</param> /// <param name="document">The document.</param> /// <param name="offset">The offset of the identifier.</param> /// <param name="foldMarkers">The fold markers.</param> /// <returns>The index of the next character after the block.</returns> private static int MarkBlock(string name, TextDocument document, int offset, ICollection<NewFolding> foldMarkers) { if (offset >= document.TextLength) return offset; string word = TextUtilities.GetIdentifierAt(document, offset); if (word == name) { offset += word.Length; while (offset < document.TextLength) { char c = document.GetCharAt(offset); if (c == '}' || c == ';') { offset++; break; } if (c == '{') { int startOffset = offset; while (Char.IsWhiteSpace(document.GetCharAt(startOffset - 1))) startOffset--; int offsetOfClosingBracket = TextUtilities.FindClosingBracket(document, offset + 1, '{', '}'); if (offsetOfClosingBracket > 0) { AddFold(document, foldMarkers, startOffset, offsetOfClosingBracket + 1, "{...}"); // Skip to offset after '{'. offset++; break; } } offset++; } } else { // Skip to next word offset += word.Length; } return offset; }
/// <summary> /// Access the document like a char array /// (first char at index 0 at the beginning of document) /// </summary> public char CharAt(int offset) { return(_avalonEditTextDocument.GetCharAt(offset)); }
/// <summary> /// Marks the method whose block starts at the given offset. /// </summary> /// <param name="document">The document.</param> /// <param name="offset">The offset of the method body ('{').</param> /// <param name="folds">The fold markers.</param> /// <returns>The index of the next character after the method.</returns> private static int MarkMethod(TextDocument document, int offset, ICollection<NewFolding> folds) { if (offset >= document.TextLength) return offset; int startOffset = offset; while (startOffset - 1 > 0 && Char.IsWhiteSpace(document.GetCharAt(startOffset - 1))) startOffset--; int offsetOfClosingBracket = TextUtilities.FindClosingBracket(document, offset + 1, '{', '}'); if (offsetOfClosingBracket > 0) { // Check whether next character is ';' int offsetOfNextCharacter = TextUtilities.FindFirstNonWhitespace(document, offsetOfClosingBracket + 1); if (offsetOfNextCharacter < document.TextLength && document.GetCharAt(offsetOfNextCharacter) == ';') return offset + 1; AddFold(document, folds, startOffset, offsetOfClosingBracket + 1, "{...}"); // Skip to offset after '}'. (Ignore nested blocks.) offset = offsetOfClosingBracket + 1; return offset; } return offset + 1; }
int SearchBracketForward(TextDocument document, int offset, char openBracket, char closingBracket) { bool inString = false; bool inChar = false; bool verbatim = false; bool lineComment = false; bool blockComment = false; if (offset < 0) return -1; // first try "quick find" - find the matching bracket if there is no string/comment in the way int quickResult = QuickSearchBracketForward(document, offset, openBracket, closingBracket); if (quickResult >= 0) return quickResult; // we need to parse the line from the beginning, so get the line start position int linestart = ScanLineStart(document, offset); int brackets = 1; while (offset < document.TextLength) { char ch = document.GetCharAt(offset); switch (ch) { case '\r': case '\n': lineComment = false; inChar = false; if (!verbatim) inString = false; break; case '/': if (blockComment) { Debug.Assert(offset > 0); if (document.GetCharAt(offset - 1) == '*') { blockComment = false; } } if (!inString && !inChar && offset + 1 < document.TextLength) { if (!blockComment && document.GetCharAt(offset + 1) == '/') { lineComment = true; } if (!lineComment && document.GetCharAt(offset + 1) == '*') { blockComment = true; } } break; case '"': if (!(inChar || lineComment || blockComment)) { if (inString && verbatim) { if (offset + 1 < document.TextLength && document.GetCharAt(offset + 1) == '"') { ++offset; // skip escaped quote inString = false; // let the string go } else { verbatim = false; } } else if (!inString && offset > 0 && document.GetCharAt(offset - 1) == '@') { verbatim = true; } inString = !inString; } break; case '\'': if (!(inString || lineComment || blockComment)) { inChar = !inChar; } break; case '\\': if ((inString && !verbatim) || inChar) ++offset; // skip next character break; default : if (ch == openBracket) { if (!(inString || inChar || lineComment || blockComment)) { ++brackets; } } else if (ch == closingBracket) { if (!(inString || inChar || lineComment || blockComment)) { --brackets; if (brackets == 0) { return offset; } } } break; } ++offset; } return -1; }
/// <summary> /// Skips any comments that start at the current offset. /// </summary> /// <param name="document">The document.</param> /// <param name="offset">The offset.</param> /// <returns>The index of the next character after the comments.</returns> private static int SkipComment(TextDocument document, int offset) { if (offset >= document.TextLength - 1) return offset + 1; char current = document.GetCharAt(offset); char next = document.GetCharAt(offset + 1); if (current == '/' && next == '/') { // Skip line comment "//" var line = document.GetLineByOffset(offset); int offsetOfNextLine = line.Offset + line.TotalLength; return offsetOfNextLine; } if (current == '/' && next == '*') { // Skip block comment "/* ... */" offset += 2; while (offset + 1 < document.TextLength) { if (document.GetCharAt(offset) == '*' && document.GetCharAt(offset + 1) == '/') { offset = offset + 2; break; } offset++; } return offset; } return offset + 1; }
int QuickSearchBracketForward(TextDocument document, int offset, char openBracket, char closingBracket) { int brackets = 1; // try "quick find" - find the matching bracket if there is no string/comment in the way for (int i = offset; i < document.TextLength; ++i) { char ch = document.GetCharAt(i); if (ch == openBracket) { ++brackets; } else if (ch == closingBracket) { --brackets; if (brackets == 0) return i; } else if (ch == '"') { break; } else if (ch == '\'') { break; } else if (ch == '/' && i > 0) { if (document.GetCharAt(i - 1) == '/') break; } else if (ch == '*' && i > 0) { if (document.GetCharAt(i - 1) == '/') break; } } return -1; }
public char this[int o] { get { return(document.GetCharAt(o)); } }
/// <summary> /// Checks whether a region (offset + length) matches a given word. /// </summary> /// <param name="document">The document.</param> /// <param name="offset">The offset.</param> /// <param name="length">The length.</param> /// <param name="word">The word.</param> /// <returns><see langword="true"/> if region matches word.</returns> /// <remarks> /// The comparison is case-sensitive. /// </remarks> internal static bool CompareSegment(TextDocument document, int offset, int length, string word) { if (length != word.Length || document.TextLength < offset + length) return false; for (int i = 0; i < length; ++i) if (document.GetCharAt(offset + i) != word[i]) return false; return true; }
public void GetCharAt0EmptyDocument() { Assert.Throws <ArgumentOutOfRangeException>(() => document.GetCharAt(0)); }
private static int FindPrevWordStart(TextDocument document, int offset, IList<char> allowedChars) { for (var num = offset - 1; num != -1; num--) { var charAt = document.GetCharAt(num); if (!IsWordPart(charAt) && !allowedChars.Contains(charAt)) { return num + 1; } } return 0; }