void TextViewVisualLinesChanged(object sender, EventArgs e) { foreach (FoldingMarginMarker m in markers) { RemoveVisualChild(m); } markers.Clear(); InvalidateVisual(); if (TextView != null && FoldingManager != null && TextView.VisualLinesValid) { foreach (VisualLine line in TextView.VisualLines) { FoldingSection fs = FoldingManager.GetNextFolding(line.FirstDocumentLine.Offset); if (fs == null) { continue; } if (fs.StartOffset <= line.LastDocumentLine.Offset + line.LastDocumentLine.Length) { FoldingMarginMarker m = new FoldingMarginMarker { IsExpanded = !fs.IsFolded, VisualLine = line, FoldingSection = fs }; markers.Add(m); AddVisualChild(m); m.IsMouseDirectlyOverChanged += delegate { InvalidateVisual(); }; InvalidateMeasure(); continue; } } } }
/// <summary> /// Gets the first offset greater or equal to <paramref name="startOffset"/> where a folded folding starts. /// Returns -1 if there are no foldings after <paramref name="startOffset"/>. /// </summary> public int GetNextFoldedFoldingStart(int startOffset) { FoldingSection fs = foldings.FindFirstSegmentWithStartAfter(startOffset); while (fs != null && !fs.IsFolded) { fs = foldings.GetNextSegment(fs); } return(fs != null ? fs.StartOffset : -1); }
/// <summary> /// Removes a folding section from this manager. /// </summary> public void RemoveFolding(FoldingSection fs) { if (fs == null) { throw new ArgumentNullException("fs"); } fs.IsFolded = false; foldings.Remove(fs); textView.Redraw(fs, DispatcherPriority.Normal); }
/// <summary> /// Gets all foldings that start exactly at <paramref name="startOffset"/>. /// </summary> public ReadOnlyCollection <FoldingSection> GetFoldingsAt(int startOffset) { List <FoldingSection> result = new List <FoldingSection>(); FoldingSection fs = foldings.FindFirstSegmentWithStartAfter(startOffset); while (fs != null && fs.StartOffset == startOffset) { result.Add(fs); fs = foldings.GetNextSegment(fs); } return(result.AsReadOnly()); }
/// <summary> /// Creates a folding for the specified text section. /// </summary> public FoldingSection CreateFolding(int startOffset, int endOffset) { if (startOffset >= endOffset) { throw new ArgumentException("startOffset must be less than endOffset"); } FoldingSection fs = new FoldingSection(this, startOffset, endOffset); foldings.Add(fs); textView.Redraw(fs, DispatcherPriority.Normal); return(fs); }
void OnDocumentChanged(DocumentChangeEventArgs e) { foldings.UpdateOffsets(e); FoldingSection s = foldings.FindFirstSegmentWithStartAfter(e.Offset); while (s != null && s.StartOffset == e.Offset) { FoldingSection next = foldings.GetNextSegment(s); if (s.Length == 0) { RemoveFolding(s); } s = next; } }
/// <summary> /// Updates the foldings in this <see cref="FoldingManager"/> using the given new foldings. /// This method will try to detect which new foldings correspond to which existing foldings; and will keep the state /// (<see cref="FoldingSection.IsFolded"/>) for existing foldings. /// </summary> /// <param name="newFoldings">The new set of foldings. These must be sorted by starting offset.</param> /// <param name="firstErrorOffset">The first position of a parse error. Existing foldings starting after /// this offset will be kept even if they don't appear in <paramref name="newFoldings"/>. /// Use -1 for this parameter if there were no parse errors.</param> public void UpdateFoldings(IEnumerable <NewFolding> newFoldings, int firstErrorOffset) { if (newFoldings == null) { throw new ArgumentNullException("newFoldings"); } if (firstErrorOffset < 0) { firstErrorOffset = int.MaxValue; } var oldFoldings = this.AllFoldings.ToArray(); int oldFoldingIndex = 0; int previousStartOffset = 0; // merge new foldings into old foldings so that sections keep being collapsed // both oldFoldings and newFoldings are sorted by start offset foreach (NewFolding newFolding in newFoldings) { // ensure newFoldings are sorted correctly if (newFolding.StartOffset < previousStartOffset) { throw new ArgumentException("newFoldings must be sorted by start offset"); } previousStartOffset = newFolding.StartOffset; if (newFolding.StartOffset == newFolding.EndOffset) { continue; // ignore zero-length foldings } // remove old foldings that were skipped while (oldFoldingIndex < oldFoldings.Length && newFolding.StartOffset > oldFoldings[oldFoldingIndex].StartOffset) { this.RemoveFolding(oldFoldings[oldFoldingIndex++]); } FoldingSection section; // reuse current folding if its matching: if (oldFoldingIndex < oldFoldings.Length && newFolding.StartOffset == oldFoldings[oldFoldingIndex].StartOffset) { section = oldFoldings[oldFoldingIndex++]; section.Length = newFolding.EndOffset - newFolding.StartOffset; } else { // no matching current folding; create a new one: section = this.CreateFolding(newFolding.StartOffset, newFolding.EndOffset); // auto-close #regions only when opening the document section.IsFolded = newFolding.DefaultClosed; section.Tag = newFolding; } section.Title = newFolding.Name; } // remove all outstanding old foldings: while (oldFoldingIndex < oldFoldings.Length) { FoldingSection oldSection = oldFoldings[oldFoldingIndex++]; if (oldSection.StartOffset >= firstErrorOffset) { break; } this.RemoveFolding(oldSection); } }