Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Richten Sie sprachbasierte Features ein, z. B. Gliedern, indem Sie die Arten von Textbereichen definieren, die Sie erweitern oder reduzieren möchten. Sie können Regionen im Kontext eines Sprachdiensts definieren oder eigene Dateinamenerweiterung und Inhaltstyp definieren und die Regionsdefinition nur auf diesen Typ anwenden oder die Regionsdefinition auf einen vorhandenen Inhaltstyp anwenden (z. B. "Text"). In dieser exemplarischen Vorgehensweise wird gezeigt, wie Sie Bereiche definieren und anzeigen.
Erstellen eines Projekts für verwaltetes Erweiterbarkeitsframework (MEF)
So erstellen Sie ein MEF-Projekt
Erstellen eines VSIX-Projekts Nennen Sie die Projektmappe
OutlineRegionTest
.Fügen Sie dem Projekt eine Elementvorlage für Editorklassifizierer hinzu. Weitere Informationen finden Sie unter Erstellen einer Erweiterung mit einer Editorelementvorlage.
Löschen Sie die vorhandenen Klassendateien.
Implementieren eines Gliedertags
Die Gliederung von Regionen wird durch eine Art von Tag (OutliningRegionTag) gekennzeichnet. Dieses Tag stellt das standardmäßige Gliederverhalten bereit. Der umrissierte Bereich kann erweitert oder reduziert werden. Der umrandete Bereich wird durch ein Pluszeichen (+) markiert, wenn er reduziert ist oder ein Minuszeichen (-) wenn er erweitert wird, und der erweiterte Bereich wird durch eine vertikale Linie abgegrenzt.
Die folgenden Schritte zeigen, wie Sie einen Tagger definieren, der Klammern für alle regionen erstellt, die durch die Klammern ([,]) getrennt sind.
So implementieren Sie einen Gliedertagger
Fügen Sie eine Klassendatei hinzu, und nennen Sie sie
OutliningTagger
.Importieren Sie die folgenden Namespaces.
Erstellen Sie eine Klasse mit dem Namen
OutliningTagger
, und implementieren Sie ITagger<T>folgendes:Fügen Sie einige Felder hinzu, um den Textpuffer nachzuverfolgen und Momentaufnahme und die Zeilensätze anzusammeln, die als Rahmenbereiche markiert werden sollen. Dieser Code enthält eine Liste von Region-Objekten (die später definiert werden sollen), die die Gliederungsbereiche darstellen.
string startHide = "["; //the characters that start the outlining region string endHide = "]"; //the characters that end the outlining region string ellipsis = "..."; //the characters that are displayed when the region is collapsed string hoverText = "hover text"; //the contents of the tooltip for the collapsed span ITextBuffer buffer; ITextSnapshot snapshot; List<Region> regions;
Fügen Sie einen Tagger-Konstruktor hinzu, der die Felder initialisiert, den Puffer analysiert, und fügt dem Changed Ereignis einen Ereignishandler hinzu.
Implementieren Sie die Methode, mit der GetTags die Tagspanne instanziiert wird. In diesem Beispiel wird davon ausgegangen, dass die spannen in der NormalizedSpanCollection übergebenen Methode zusammenhängend sind, obwohl dies möglicherweise nicht immer der Fall ist. Diese Methode instanziiert eine neue Tagspanne für jeden der Gliederbereiche.
public IEnumerable<ITagSpan<IOutliningRegionTag>> GetTags(NormalizedSnapshotSpanCollection spans) { if (spans.Count == 0) yield break; List<Region> currentRegions = this.regions; ITextSnapshot currentSnapshot = this.snapshot; SnapshotSpan entire = new SnapshotSpan(spans[0].Start, spans[spans.Count - 1].End).TranslateTo(currentSnapshot, SpanTrackingMode.EdgeExclusive); int startLineNumber = entire.Start.GetContainingLine().LineNumber; int endLineNumber = entire.End.GetContainingLine().LineNumber; foreach (var region in currentRegions) { if (region.StartLine <= endLineNumber && region.EndLine >= startLineNumber) { var startLine = currentSnapshot.GetLineFromLineNumber(region.StartLine); var endLine = currentSnapshot.GetLineFromLineNumber(region.EndLine); //the region starts at the beginning of the "[", and goes until the *end* of the line that contains the "]". yield return new TagSpan<IOutliningRegionTag>( new SnapshotSpan(startLine.Start + region.StartOffset, endLine.End), new OutliningRegionTag(false, false, ellipsis, hoverText)); } } }
Deklarieren Sie einen
TagsChanged
Ereignishandler.Fügen Sie einen
BufferChanged
Ereignishandler hinzu, der auf Changed Ereignisse reagiert, indem Sie den Textpuffer analysieren.Fügen Sie eine Methode hinzu, die den Puffer analysiert. Das hier angegebene Beispiel dient nur zur Veranschaulichung. Er analysiert den Puffer synchron in geschachtelte Gliederbereiche.
void ReParse() { ITextSnapshot newSnapshot = buffer.CurrentSnapshot; List<Region> newRegions = new List<Region>(); //keep the current (deepest) partial region, which will have // references to any parent partial regions. PartialRegion currentRegion = null; foreach (var line in newSnapshot.Lines) { int regionStart = -1; string text = line.GetText(); //lines that contain a "[" denote the start of a new region. if ((regionStart = text.IndexOf(startHide, StringComparison.Ordinal)) != -1) { int currentLevel = (currentRegion != null) ? currentRegion.Level : 1; int newLevel; if (!TryGetLevel(text, regionStart, out newLevel)) newLevel = currentLevel + 1; //levels are the same and we have an existing region; //end the current region and start the next if (currentLevel == newLevel && currentRegion != null) { newRegions.Add(new Region() { Level = currentRegion.Level, StartLine = currentRegion.StartLine, StartOffset = currentRegion.StartOffset, EndLine = line.LineNumber }); currentRegion = new PartialRegion() { Level = newLevel, StartLine = line.LineNumber, StartOffset = regionStart, PartialParent = currentRegion.PartialParent }; } //this is a new (sub)region else { currentRegion = new PartialRegion() { Level = newLevel, StartLine = line.LineNumber, StartOffset = regionStart, PartialParent = currentRegion }; } } //lines that contain "]" denote the end of a region else if ((regionStart = text.IndexOf(endHide, StringComparison.Ordinal)) != -1) { int currentLevel = (currentRegion != null) ? currentRegion.Level : 1; int closingLevel; if (!TryGetLevel(text, regionStart, out closingLevel)) closingLevel = currentLevel; //the regions match if (currentRegion != null && currentLevel == closingLevel) { newRegions.Add(new Region() { Level = currentLevel, StartLine = currentRegion.StartLine, StartOffset = currentRegion.StartOffset, EndLine = line.LineNumber }); currentRegion = currentRegion.PartialParent; } } } //determine the changed span, and send a changed event with the new spans List<Span> oldSpans = new List<Span>(this.regions.Select(r => AsSnapshotSpan(r, this.snapshot) .TranslateTo(newSnapshot, SpanTrackingMode.EdgeExclusive) .Span)); List<Span> newSpans = new List<Span>(newRegions.Select(r => AsSnapshotSpan(r, newSnapshot).Span)); NormalizedSpanCollection oldSpanCollection = new NormalizedSpanCollection(oldSpans); NormalizedSpanCollection newSpanCollection = new NormalizedSpanCollection(newSpans); //the changed regions are regions that appear in one set or the other, but not both. NormalizedSpanCollection removed = NormalizedSpanCollection.Difference(oldSpanCollection, newSpanCollection); int changeStart = int.MaxValue; int changeEnd = -1; if (removed.Count > 0) { changeStart = removed[0].Start; changeEnd = removed[removed.Count - 1].End; } if (newSpans.Count > 0) { changeStart = Math.Min(changeStart, newSpans[0].Start); changeEnd = Math.Max(changeEnd, newSpans[newSpans.Count - 1].End); } this.snapshot = newSnapshot; this.regions = newRegions; if (changeStart <= changeEnd) { ITextSnapshot snap = this.snapshot; if (this.TagsChanged != null) this.TagsChanged(this, new SnapshotSpanEventArgs( new SnapshotSpan(this.snapshot, Span.FromBounds(changeStart, changeEnd)))); } }
Die folgende Hilfsmethode ruft eine ganze Zahl ab, die die Ebene der Gliederung darstellt, sodass 1 das äußerst linke geschweifte Paar ist.
Mit der folgenden Hilfsmethode wird eine Region (weiter unten in diesem Artikel definiert) in einen SnapshotSpan übersetzt.
static SnapshotSpan AsSnapshotSpan(Region region, ITextSnapshot snapshot) { var startLine = snapshot.GetLineFromLineNumber(region.StartLine); var endLine = (region.StartLine == region.EndLine) ? startLine : snapshot.GetLineFromLineNumber(region.EndLine); return new SnapshotSpan(startLine.Start + region.StartOffset, endLine.End); }
Der folgende Code dient nur zur Veranschaulichung. Sie definiert eine PartialRegion-Klasse, die die Zeilennummer und den Offset des Anfangs eines Gliederbereichs und einen Verweis auf den übergeordneten Bereich (sofern vorhanden) enthält. Mit diesem Code kann der Parser geschachtelte Gliederungsbereiche einrichten. Eine abgeleitete Region-Klasse enthält einen Verweis auf die Zeilennummer des Endes eines Gliederbereichs.
Implementieren eines Taggeranbieters
Exportieren Sie einen Tagger-Anbieter für Ihren Tagger. Der Taggeranbieter erstellt einen OutliningTagger
Puffer des Inhaltstyps "Text" oder gibt einen OutliningTagger
Wert zurück, wenn der Puffer bereits über einen verfügt.
So implementieren Sie einen Taggeranbieter
Erstellen Sie eine Klasse mit dem Namen
OutliningTaggerProvider
, die sie implementiert ITaggerProvider, und exportieren Sie sie mit den Attributen ContentType und TagType.Implementieren Sie die CreateTagger Methode, indem Sie die Eigenschaften des Puffers hinzufügen
OutliningTagger
.
Erstellen und Testen des Codes
Um diesen Code zu testen, erstellen Sie die OutlineRegionTest-Lösung, und führen Sie sie in der experimentellen Instanz aus.
So erstellen und testen Sie die OutlineRegionTest-Lösung
Erstellen Sie die Projektmappe.
Wenn Sie dieses Projekt im Debugger ausführen, wird eine zweite Instanz von Visual Studio gestartet.
Erstellen einer Textdatei Geben Sie Text ein, der sowohl die öffnenden Klammern als auch die schließenden Klammern enthält.
[ Hello ]
Es sollte eine Klammernregion vorhanden sein, die beide Klammern enthält. Sie sollten links neben der geöffneten Klammer auf das Minuszeichen klicken können, um den Rahmenbereich zu reduzieren. Wenn der Bereich reduziert ist, sollte das Auslassungszeichen (...) links neben dem reduzierten Bereich angezeigt werden, und ein Popup mit dem Textzeigertext sollte angezeigt werden, wenn Sie den Mauszeiger über die Auslassungspunkte bewegen.