public void Rebuild() { // keep the first document line var ls = documentLineTree.GetByNumber(1); // but mark all other lines as deleted, and detach them from the other nodes for (var line = ls.NextLine; line != null; line = line.NextLine) { line.isDeleted = true; line.parent = line.left = line.right = null; } // Reset the first line to detach it from the deleted lines ls.ResetLine(); var ds = NewLineFinder.NextNewLine(document, 0); var lines = new List<DocumentLine>(); var lastDelimiterEnd = 0; while (ds != SimpleSegment.Invalid) { ls.TotalLength = ds.Offset + ds.Length - lastDelimiterEnd; ls.DelimiterLength = ds.Length; lastDelimiterEnd = ds.Offset + ds.Length; lines.Add(ls); ls = new DocumentLine(document); ds = NewLineFinder.NextNewLine(document, lastDelimiterEnd); } ls.TotalLength = document.TextLength - lastDelimiterEnd; lines.Add(ls); documentLineTree.RebuildTree(lines); foreach (var lineTracker in lineTrackers) lineTracker.RebuildDocument(); }
void ILineTracker.LineInserted(DocumentLine insertionPos, DocumentLine newLine) { var targetTracker = targetObject.Target as ILineTracker; if (targetTracker != null) targetTracker.LineInserted(insertionPos, newLine); else Deregister(); }
void ILineTracker.SetLineLength(DocumentLine line, int newTotalLength) { var targetTracker = targetObject.Target as ILineTracker; if (targetTracker != null) targetTracker.SetLineLength(line, newTotalLength); else Deregister(); }
void ILineTracker.BeforeRemoveLine(DocumentLine line) { var targetTracker = targetObject.Target as ILineTracker; if (targetTracker != null) targetTracker.BeforeRemoveLine(line); else Deregister(); }
/// <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> private DocumentLine SetLineLength(DocumentLine line, int newTotalLength) { // changedLines.Add(line); // deletedOrChangedLines.Add(line); var delta = newTotalLength - line.TotalLength; if (delta != 0) { foreach (var lt in lineTrackers) lt.SetLineLength(line, newTotalLength); line.TotalLength = newTotalLength; DocumentLineTree.UpdateAfterChildrenChange(line); } // determine new DelimiterLength if (newTotalLength == 0) { line.DelimiterLength = 0; } else { var lineOffset = line.Offset; var 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 var previousLine = line.PreviousLine; RemoveLine(line); return SetLineLength(previousLine, previousLine.TotalLength + 1); } else { line.DelimiterLength = 1; } } else { line.DelimiterLength = 0; } } return line; }
/// <inheritdoc cref="IIndentationStrategy.IndentLine" /> public override int IndentLine(TextDocument document, DocumentLine line, int caretOffset) { var lineNr = line.LineNumber; var acc = new TextDocumentAccessor(document, lineNr, lineNr); var result = Indent(acc, false, caretOffset); var t = acc.Text; if (t.Length == 0) { // use AutoIndentation for new lines in comments / verbatim strings. return base.IndentLine(document, line, caretOffset); } return result; }
/// <inheritdoc /> public virtual int IndentLine(TextDocument document, DocumentLine line, int caretOffset) { if (document == null) throw new ArgumentNullException("document"); if (line == null) throw new ArgumentNullException("line"); var previousLine = line.PreviousLine; if (previousLine != null) { var indentationSegment = TextUtilities.GetWhitespaceAfter(document, previousLine.Offset); var indentation = document.GetText(indentationSegment); // copy indentation to line indentationSegment = TextUtilities.GetWhitespaceAfter(document, line.Offset); document.Replace(indentationSegment, indentation); } return caretOffset; }
/// <inheritdoc cref="IIndentationStrategy.IndentLine" /> public override int IndentLine(TextDocument document, DocumentLine line, int caretIndex) { if (line == null) { return caretIndex; } var lineNr = line.LineNumber; var acc = new TextDocumentAccessor(document, lineNr, lineNr); var leadingWhiteSpaceBefore = TextUtilities.GetLeadingWhitespace(document, line).Length; var result = Indent(acc, false, caretIndex); var t = acc.Text; result = caretIndex + TextUtilities.GetLeadingWhitespace(document, line).Length - leadingWhiteSpaceBefore; if (t.Length == 0) { // use AutoIndentation for new lines in comments / verbatim strings. return base.IndentLine(document, line, caretIndex); } return result; }
public static ISegment GetTrailingWhitespace(TextDocument document, DocumentLine documentLine) { if (documentLine == null) throw new ArgumentNullException("documentLine"); var segment = GetWhitespaceBefore(document, documentLine.EndOffset); // If the whole line consists of whitespace, we consider all of it as leading whitespace, // so return an empty segment as trailing whitespace. if (segment.Offset == documentLine.Offset) return new SimpleSegment(documentLine.EndOffset, 0); return segment; }
public static ISegment GetLeadingWhitespace(TextDocument document, DocumentLine documentLine) { if (documentLine == null) throw new ArgumentNullException("documentLine"); return GetWhitespaceAfter(document, documentLine.Offset); }
/// <inheritdoc /> public bool MoveNext() { if (lineDirty) { doc.Replace(line, text); lineDirty = false; } ++LineNumber; if (LineNumber > maxLine) return false; line = doc.GetLineByNumber(LineNumber); text = doc.GetText(line); return true; }
// optimization note: I tried packing color and isDeleted into a single byte field, but that // actually increased the memory requirements. The JIT packs two bools and a byte (delimiterSize) // into a single DWORD, but two bytes get each their own DWORD. Three bytes end up in the same DWORD, so // apparently the JIT only optimizes for memory when there are at least three small fields. // Currently, DocumentLine takes 36 bytes on x86 (8 byte object overhead, 3 pointers, 3 ints, and another DWORD // for the small fields). // TODO: a possible optimization would be to combine 'totalLength' and the small fields into a single uint. // delimiterSize takes only two bits, the two bools take another two bits; so there's still // 28 bits left for totalLength. 268435455 characters per line should be enough for everyone :) /// <summary> /// Resets the line to enable its reuse after a document rebuild. /// </summary> internal void ResetLine() { totalLength = delimiterLength = 0; isDeleted = color = false; left = right = parent = null; }
private DocumentLine InsertLineAfter(DocumentLine line, int length) { var newLine = documentLineTree.InsertLineAfter(line, length); foreach (var lt in lineTrackers) lt.LineInserted(line, newLine); return newLine; }
private void RemoveLine(DocumentLine lineToRemove) { foreach (var lt in lineTrackers) lt.BeforeRemoveLine(lineToRemove); documentLineTree.RemoveLine(lineToRemove); // foreach (ILineTracker lt in lineTracker) // lt.AfterRemoveLine(lineToRemove); // deletedLines.Add(lineToRemove); // deletedOrChangedLines.Add(lineToRemove); }