internal HeightTreeNode(DocumentLine documentLine, double height) { this.documentLine = documentLine; this.totalCount = 1; this.lineNode = new HeightTreeLineNode(height); this.totalHeight = height; }
internal VisualLine(TextView textView, DocumentLine firstDocumentLine) { Debug.Assert(textView != null); Debug.Assert(firstDocumentLine != null); this.textView = textView; this.Document = textView.Document; this.FirstDocumentLine = firstDocumentLine; }
void ILineTracker.BeforeRemoveLine(DocumentLine line) { ILineTracker targetTracker = targetObject.Target as ILineTracker; if (targetTracker != null) targetTracker.BeforeRemoveLine(line); else Deregister(); }
void ILineTracker.LineInserted(DocumentLine insertionPos, DocumentLine newLine) { ILineTracker targetTracker = targetObject.Target as ILineTracker; if (targetTracker != null) targetTracker.LineInserted(insertionPos, newLine); else Deregister(); }
internal CollapsedLineSection(HeightTree heightTree, DocumentLine start, DocumentLine end) { this.heightTree = heightTree; this.start = start; this.end = end; #if DEBUG unchecked { this.ID = " #" + (nextId++); } #endif }
/// <inheritdoc cref="IIndentationStrategy.IndentLine"/> public override void IndentLine(TextDocument document, DocumentLine line) { int lineNr = line.LineNumber; TextDocumentAccessor acc = new TextDocumentAccessor(document, lineNr, lineNr); Indent(acc, false); string t = acc.Text; if (t.Length == 0) { // use AutoIndentation for new lines in comments / verbatim strings. base.IndentLine(document, line); } }
/// <inheritdoc/> public virtual void IndentLine(TextDocument document, DocumentLine line) { if (document == null) throw new ArgumentNullException("document"); if (line == null) throw new ArgumentNullException("line"); DocumentLine previousLine = line.PreviousLine; if (previousLine != null) { ISegment indentationSegment = TextUtilities.GetWhitespaceAfter(document, previousLine.Offset); string indentation = document.GetText(indentationSegment); // copy indentation to line indentationSegment = TextUtilities.GetWhitespaceAfter(document, line.Offset); document.Replace(indentationSegment, indentation); } }
void CheckIsInSection(CollapsedLineSection cs, DocumentLine line) { HeightTreeNode node = GetNode(line); if (node.lineNode.collapsedSections != null && node.lineNode.collapsedSections.Contains(cs)) return; while (node != null) { if (node.collapsedSections != null && node.collapsedSections.Contains(cs)) return; node = node.parent; } throw new InvalidOperationException(cs + " not found for line " + line); }
HeightTreeNode InsertAfter(HeightTreeNode node, DocumentLine newLine) { HeightTreeNode newNode = new HeightTreeNode(newLine, defaultLineHeight); if (node.right == null) { if (node.lineNode.collapsedSections != null) { // we are inserting directly after node - so copy all collapsedSections // that do not end at node. foreach (CollapsedLineSection cs in node.lineNode.collapsedSections) { if (cs.End != node.documentLine) newNode.AddDirectlyCollapsed(cs); } } InsertAsRight(node, newNode); } else { node = node.right.LeftMost; if (node.lineNode.collapsedSections != null) { // we are inserting directly before node - so copy all collapsedSections // that do not start at node. foreach (CollapsedLineSection cs in node.lineNode.collapsedSections) { if (cs.Start != node.documentLine) newNode.AddDirectlyCollapsed(cs); } } InsertAsLeft(node, newNode); } return newNode; }
HeightTreeNode GetNode(DocumentLine ls) { return GetNodeByIndex(ls.LineNumber - 1); }
void ILineTracker.BeforeRemoveLine(DocumentLine line) { CheckIsHighlighting(); int number = line.LineNumber; storedSpanStacks.RemoveAt(number); isValid.RemoveAt(number); if (number < isValid.Count) { isValid[number] = false; if (number < firstInvalidLine) firstInvalidLine = number; } }
static void CopyWholeLine(TextArea textArea, DocumentLine line) { ISegment wholeLine = new SimpleSegment(line.Offset, line.TotalLength); string text = textArea.Document.GetText(wholeLine); // Ensure we use the appropriate newline sequence for the OS text = TextUtilities.NormalizeNewLines(text, Environment.NewLine); DataObject data = new DataObject(text); // Also copy text in HTML format to clipboard - good for pasting text into Word // or to the SharpDevelop forums. IHighlighter highlighter = textArea.GetService(typeof(IHighlighter)) as IHighlighter; HtmlClipboard.SetHtml(data, HtmlClipboard.CreateHtmlFragment(textArea.Document, highlighter, wholeLine, new HtmlOptions(textArea.Options))); MemoryStream lineSelected = new MemoryStream(1); lineSelected.WriteByte(1); data.SetData(LineSelectedType, lineSelected, false); try { Clipboard.SetDataObject(data, true); } catch (ExternalException) { // Apparently this exception sometimes happens randomly. // The MS controls just ignore it, so we'll do the same. return; } textArea.OnTextCopied(new TextEventArgs(text)); }
protected override void OnHighlightStateChanged(DocumentLine line, int lineNumber) { base.OnHighlightStateChanged(line, lineNumber); if (colorizer.lineNumberBeingColorized != lineNumber) { // Ignore notifications for any line except the one we're interested in. // This improves the performance as Redraw() can take quite some time when called repeatedly // while scanning the document (above the visible area) for highlighting changes. return; } if (textView.Document != this.Document) { // May happen if document on text view was changed but some user code is still using the // existing IHighlighter instance. return; } // The user may have inserted "/*" into the current line, and so far only that line got redrawn. // So when the highlighting state is changed, we issue a redraw for the line immediately below. // If the highlighting state change applies to the lines below, too, the construction of each line // will invalidate the next line, and the construction pass will regenerate all lines. Debug.WriteLine("OnHighlightStateChanged forces redraw of line " + (lineNumber + 1)); // If the VisualLine construction is in progress, we have to avoid sending redraw commands for // anything above the line currently being constructed. // It takes some explanation to see why this cannot happen. // VisualLines always get constructed from top to bottom. // Each VisualLine construction calls into the highlighter and thus forces an update of the // highlighting state for all lines up to the one being constructed. // To guarantee that we don't redraw lines we just constructed, we need to show that when // a VisualLine is being reused, the highlighting state at that location is still up-to-date. // This isn't exactly trivial and the initial implementation was incorrect in the presence of external document changes // (e.g. split view). // For the first line in the view, the TextView.VisualLineConstructionStarting event is used to check that the // highlighting state is up-to-date. If it isn't, this method will be executed, and it'll mark the first line // in the view as requiring a redraw. This is safely possible because that event occurs before any lines are reused. // Once we take care of the first visual line, we won't get in trouble with other lines due to the top-to-bottom // construction process. // We'll prove that: if line N is being reused, then the highlighting state is up-to-date until (end of) line N-1. // Start of induction: the first line in view is reused only if the highlighting state was up-to-date // until line N-1 (no change detected in VisualLineConstructionStarting event). // Induction step: // If another line N+1 is being reused, then either // a) the previous line (the visual line containing document line N) was newly constructed // or b) the previous line was reused // In case a, the construction updated the highlighting state. This means the stack at end of line N is up-to-date. // In case b, the highlighting state at N-1 was up-to-date, and the text of line N was not changed. // (if the text was changed, the line could not have been reused). // From this follows that the highlighting state at N is still up-to-date. // The above proof holds even in the presence of folding: folding only ever hides text in the middle of a visual line. // Our Colorize-override ensures that the highlighting state is always updated for the LastDocumentLine, // so it will always invalidate the next visual line when a folded line is constructed // and the highlighting stack has changed. textView.Redraw(line.NextLine, DispatcherPriority.Normal); /* * Meta-comment: "why does this have to be so complicated?" * * The problem is that I want to re-highlight only on-demand and incrementally; * and at the same time only repaint changed lines. * So the highlighter and the VisualLine construction both have to run in a single pass. * The highlighter must take care that it never touches already constructed visual lines; * if it detects that something must be redrawn because the highlighting state changed, * it must do so early enough in the construction process. * But doing it too early means it doesn't have the information necessary to re-highlight and redraw only the desired parts. */ }
/// <summary> /// Is called when the highlighting state at the end of the specified line has changed. /// </summary> /// <remarks>This callback must not call HighlightLine or InvalidateHighlighting. /// It may call GetSpanStack, but only for the changed line and lines above. /// This method must not modify the document.</remarks> protected virtual void OnHighlightStateChanged(DocumentLine line, int lineNumber) { }
void ILineTracker.SetLineLength(DocumentLine line, int newTotalLength) { ILineTracker targetTracker = targetObject.Target as ILineTracker; if (targetTracker != null) targetTracker.SetLineLength(line, newTotalLength); else Deregister(); }
public double GetHeight(DocumentLine line) { return GetNode(line).lineNode.height; }
/// <summary> /// Collapses the specified text section. /// Runtime: O(log n) /// </summary> public CollapsedLineSection CollapseText(DocumentLine start, DocumentLine end) { if (!document.Lines.Contains(start)) throw new ArgumentException("Line is not part of this document", "start"); if (!document.Lines.Contains(end)) throw new ArgumentException("Line is not part of this document", "end"); int length = end.LineNumber - start.LineNumber + 1; if (length < 0) throw new ArgumentException("start must be a line before end"); CollapsedLineSection section = new CollapsedLineSection(this, start, end); AddCollapsedSection(section, length); #if DEBUG CheckProperties(); #endif return section; }
void HighlightLineInternal(DocumentLine line) { lineStartOffset = line.Offset; lineText = document.GetText(line.Offset, line.Length); position = 0; ResetColorStack(); HighlightingRuleSet currentRuleSet = this.CurrentRuleSet; Stack<Match[]> storedMatchArrays = new Stack<Match[]>(); Match[] matches = AllocateMatchArray(currentRuleSet.Spans.Count); Match endSpanMatch = null; while (true) { for (int i = 0; i < matches.Length; i++) { if (matches[i] == null || (matches[i].Success && matches[i].Index < position)) matches[i] = currentRuleSet.Spans[i].StartExpression.Match(lineText, position); } if (endSpanMatch == null && !spanStack.IsEmpty) endSpanMatch = spanStack.Peek().EndExpression.Match(lineText, position); Match firstMatch = Minimum(matches, endSpanMatch); if (firstMatch == null) break; HighlightNonSpans(firstMatch.Index); Debug.Assert(position == firstMatch.Index); if (firstMatch == endSpanMatch) { HighlightingSpan poppedSpan = spanStack.Peek(); if (!poppedSpan.SpanColorIncludesEnd) PopColor(); // pop SpanColor PushColor(poppedSpan.EndColor); position = firstMatch.Index + firstMatch.Length; PopColor(); // pop EndColor if (poppedSpan.SpanColorIncludesEnd) PopColor(); // pop SpanColor spanStack = spanStack.Pop(); currentRuleSet = this.CurrentRuleSet; //FreeMatchArray(matches); if (storedMatchArrays.Count > 0) { matches = storedMatchArrays.Pop(); int index = currentRuleSet.Spans.IndexOf(poppedSpan); Debug.Assert(index >= 0 && index < matches.Length); if (matches[index].Index == position) { throw new InvalidOperationException( "A highlighting span matched 0 characters, which would cause an endless loop.\n" + "Change the highlighting definition so that either the start or the end regex matches at least one character.\n" + "Start regex: " + poppedSpan.StartExpression + "\n" + "End regex: " + poppedSpan.EndExpression); } } else { matches = AllocateMatchArray(currentRuleSet.Spans.Count); } } else { int index = Array.IndexOf(matches, firstMatch); Debug.Assert(index >= 0); HighlightingSpan newSpan = currentRuleSet.Spans[index]; spanStack = spanStack.Push(newSpan); currentRuleSet = this.CurrentRuleSet; storedMatchArrays.Push(matches); matches = AllocateMatchArray(currentRuleSet.Spans.Count); if (newSpan.SpanColorIncludesStart) PushColor(newSpan.SpanColor); PushColor(newSpan.StartColor); position = firstMatch.Index + firstMatch.Length; PopColor(); if (!newSpan.SpanColorIncludesStart) PushColor(newSpan.SpanColor); } endSpanMatch = null; } HighlightNonSpans(line.Length); PopAllColors(); }
void HighlightLineAndUpdateTreeList(DocumentLine line, int lineNumber) { //Debug.WriteLine("Highlight line " + lineNumber + (highlightedLine != null ? "" : " (span stack only)")); spanStack = storedSpanStacks[lineNumber - 1]; HighlightLineInternal(line); if (!EqualSpanStacks(spanStack, storedSpanStacks[lineNumber])) { isValid[lineNumber] = true; //Debug.WriteLine("Span stack in line " + lineNumber + " changed from " + storedSpanStacks[lineNumber] + " to " + spanStack); storedSpanStacks[lineNumber] = spanStack; if (lineNumber + 1 < isValid.Count) { isValid[lineNumber + 1] = false; firstInvalidLine = lineNumber + 1; } else { firstInvalidLine = int.MaxValue; } OnHighlightStateChanged(line, lineNumber); } else if (firstInvalidLine == lineNumber) { isValid[lineNumber] = true; firstInvalidLine = isValid.IndexOf(false); if (firstInvalidLine < 0) firstInvalidLine = int.MaxValue; } }
void ILineTracker.LineInserted(DocumentLine insertionPos, DocumentLine newLine) { CheckIsHighlighting(); Debug.Assert(insertionPos.LineNumber + 1 == newLine.LineNumber); int lineNumber = newLine.LineNumber; storedSpanStacks.Insert(lineNumber, null); isValid.Insert(lineNumber, false); if (lineNumber < firstInvalidLine) firstInvalidLine = lineNumber; }
public double GetVisualPosition(DocumentLine line) { return GetVisualPositionFromNode(GetNode(line)); }
void ILineTracker.SetLineLength(DocumentLine line, int newTotalLength) { CheckIsHighlighting(); int number = line.LineNumber; isValid[number] = false; if (number < firstInvalidLine) firstInvalidLine = number; }
/// <summary> /// Creates a new VisualLineConstructionStartEventArgs instance. /// </summary> public VisualLineConstructionStartEventArgs(DocumentLine firstLineInView) { if (firstLineInView == null) throw new ArgumentNullException("firstLineInView"); this.FirstLineInView = firstLineInView; }
void ILineTracker.BeforeRemoveLine(DocumentLine line) { HeightTreeNode node = GetNode(line); if (node.lineNode.collapsedSections != null) { foreach (CollapsedLineSection cs in node.lineNode.collapsedSections.ToArray()) { if (cs.Start == line && cs.End == line) { cs.Start = null; cs.End = null; } else if (cs.Start == line) { Uncollapse(cs); cs.Start = line.NextLine; AddCollapsedSection(cs, cs.End.LineNumber - cs.Start.LineNumber + 1); } else if (cs.End == line) { Uncollapse(cs); cs.End = line.PreviousLine; AddCollapsedSection(cs, cs.End.LineNumber - cs.Start.LineNumber + 1); } } } BeginRemoval(); RemoveNode(node); // clear collapsedSections from removed line: prevent damage if removed line is in "nodesToCheckForMerging" node.lineNode.collapsedSections = null; EndRemoval(); }
/// <inheritdoc/> protected override void ColorizeLine(DocumentLine line) { IHighlighter highlighter = CurrentContext.TextView.Services.GetService(typeof(IHighlighter)) as IHighlighter; if (highlighter != null) { lineNumberBeingColorized = line.LineNumber; HighlightedLine hl = highlighter.HighlightLine(lineNumberBeingColorized); lineNumberBeingColorized = 0; foreach (HighlightedSection section in hl.Sections) { if (IsEmptyColor(section.Color)) continue; ChangeLinePart(section.Offset, section.Offset + section.Length, visualLineElement => ApplyColorToElement(visualLineElement, section.Color)); } } this.lastColorizedLine = line; }
// void ILineTracker.AfterRemoveLine(DocumentLine line) // { // // } void ILineTracker.LineInserted(DocumentLine insertionPos, DocumentLine newLine) { InsertAfter(GetNode(insertionPos), newLine); #if DEBUG CheckProperties(); #endif }
/// <inheritdoc/> protected override void Colorize(ITextRunConstructionContext context) { this.lastColorizedLine = null; base.Colorize(context); if (this.lastColorizedLine != context.VisualLine.LastDocumentLine) { IHighlighter highlighter = context.TextView.Services.GetService(typeof(IHighlighter)) as IHighlighter; if (highlighter != null) { // In some cases, it is possible that we didn't highlight the last document line within the visual line // (e.g. when the line ends with a fold marker). // But even if we didn't highlight it, we'll have to update the highlighting state for it so that the // proof inside TextViewDocumentHighlighter.OnHighlightStateChanged holds. lineNumberBeingColorized = context.VisualLine.LastDocumentLine.LineNumber; highlighter.GetSpanStack(lineNumberBeingColorized); lineNumberBeingColorized = 0; } } this.lastColorizedLine = null; }
public void SetHeight(DocumentLine line, double val) { var node = GetNode(line); node.lineNode.height = val; UpdateAfterChildrenChange(node); }
void ILineTracker.SetLineLength(DocumentLine ls, int newTotalLength) { }
public HighlightedLine HighlightLine(DocumentLine line) { if (!document.Lines.Contains(line)) throw new ArgumentException("The specified line does not belong to the document."); return HighlightLine(line.LineNumber); }