Návod: Zobrazení složených závorek
Implementujte funkce založené na jazyce, například párování závorek, definováním složených závorek, které chcete spárovat, a přidáním značky textové značky do shodných závorek, když je stříška na jedné závorkách. Složené závorky můžete definovat v kontextu jazyka, definovat vlastní příponu názvu souboru a typ obsahu a použít značky jenom na tento typ nebo použít značky u existujícího typu obsahu (například "text"). Následující návod ukazuje, jak použít složené závorky odpovídající značky pro typ obsahu "text".
Vytvoření projektu MEF (Managed Extensibility Framework)
Vytvoření projektu MEF
Vytvořte projekt klasifikátoru editoru. Pojmenujte řešení
BraceMatchingTest
.Přidejte do projektu šablonu položky klasifikátoru editoru. Další informace najdete v tématu Vytvoření rozšíření pomocí šablony položky editoru.
Odstraňte existující soubory třídy.
Implementace závorky odpovídajícího taggeru
Pokud chcete získat efekt zvýraznění závorek, který se podobá efektu, který se používá v sadě Visual Studio, můžete implementovat tagger typu TextMarkerTag. Následující kód ukazuje, jak definovat tagger pro páry závorek na libovolné úrovni vnoření. V tomto příkladu jsou dvojice závorek [] a {} jsou definovány v konstruktoru taggeru, ale v implementaci celého jazyka by se příslušné dvojice závorek definovaly ve specifikaci jazyka.
Implementace závorky odpovídajícího taggeru
Přidejte soubor třídy a pojmenujte ho BraceMatching.
Naimportujte následující obory názvů.
Definujte třídu
BraceMatchingTagger
, která dědí z ITagger<T> typu TextMarkerTag.Přidejte vlastnosti pro textové zobrazení, zdrojovou vyrovnávací paměť, aktuální bod snímku a také sadu párů závorek.
V konstruktoru taggeru nastavte vlastnosti a přihlaste se k odběru událostí PositionChanged změny zobrazení a LayoutChanged. V tomto příkladu jsou pro ilustrativní účely definovány také odpovídající dvojice v konstruktoru.
internal BraceMatchingTagger(ITextView view, ITextBuffer sourceBuffer) { //here the keys are the open braces, and the values are the close braces m_braceList = new Dictionary<char, char>(); m_braceList.Add('{', '}'); m_braceList.Add('[', ']'); m_braceList.Add('(', ')'); this.View = view; this.SourceBuffer = sourceBuffer; this.CurrentChar = null; this.View.Caret.PositionChanged += CaretPositionChanged; this.View.LayoutChanged += ViewLayoutChanged; }
V rámci ITagger<T> implementace deklarujte událost TagsChanged.
Obslužné rutiny událostí aktualizují aktuální pozici kurzoru
CurrentChar
vlastnosti a vyvolat TagsChanged událost.void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) { if (e.NewSnapshot != e.OldSnapshot) //make sure that there has really been a change { UpdateAtCaretPosition(View.Caret.Position); } } void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) { UpdateAtCaretPosition(e.NewPosition); } void UpdateAtCaretPosition(CaretPosition caretPosition) { CurrentChar = caretPosition.Point.GetPoint(SourceBuffer, caretPosition.Affinity); if (!CurrentChar.HasValue) return; var tempEvent = TagsChanged; if (tempEvent != null) tempEvent(this, new SnapshotSpanEventArgs(new SnapshotSpan(SourceBuffer.CurrentSnapshot, 0, SourceBuffer.CurrentSnapshot.Length))); }
Implementujte metodu GetTags pro porovnání složených závorek buď v případě, že aktuální znak je otevřená složená závorka, nebo pokud je předchozí znak blízkou složenou závorkou, jako v sadě Visual Studio. Když se najde shoda, vytvoří tato metoda instanci dvou značek, jednu pro otevřenou složenou závorku a druhou pro blízkou složenou závorku.
public IEnumerable<ITagSpan<TextMarkerTag>> GetTags(NormalizedSnapshotSpanCollection spans) { if (spans.Count == 0) //there is no content in the buffer yield break; //don't do anything if the current SnapshotPoint is not initialized or at the end of the buffer if (!CurrentChar.HasValue || CurrentChar.Value.Position >= CurrentChar.Value.Snapshot.Length) yield break; //hold on to a snapshot of the current character SnapshotPoint currentChar = CurrentChar.Value; //if the requested snapshot isn't the same as the one the brace is on, translate our spans to the expected snapshot if (spans[0].Snapshot != currentChar.Snapshot) { currentChar = currentChar.TranslateTo(spans[0].Snapshot, PointTrackingMode.Positive); } //get the current char and the previous char char currentText = currentChar.GetChar(); SnapshotPoint lastChar = currentChar == 0 ? currentChar : currentChar - 1; //if currentChar is 0 (beginning of buffer), don't move it back char lastText = lastChar.GetChar(); SnapshotSpan pairSpan = new SnapshotSpan(); if (m_braceList.ContainsKey(currentText)) //the key is the open brace { char closeChar; m_braceList.TryGetValue(currentText, out closeChar); if (BraceMatchingTagger.FindMatchingCloseChar(currentChar, currentText, closeChar, View.TextViewLines.Count, out pairSpan) == true) { yield return new TagSpan<TextMarkerTag>(new SnapshotSpan(currentChar, 1), new TextMarkerTag("blue")); yield return new TagSpan<TextMarkerTag>(pairSpan, new TextMarkerTag("blue")); } } else if (m_braceList.ContainsValue(lastText)) //the value is the close brace, which is the *previous* character { var open = from n in m_braceList where n.Value.Equals(lastText) select n.Key; if (BraceMatchingTagger.FindMatchingOpenChar(lastChar, (char)open.ElementAt<char>(0), lastText, View.TextViewLines.Count, out pairSpan) == true) { yield return new TagSpan<TextMarkerTag>(new SnapshotSpan(lastChar, 1), new TextMarkerTag("blue")); yield return new TagSpan<TextMarkerTag>(pairSpan, new TextMarkerTag("blue")); } } }
Následující privátní metody najdou odpovídající složenou závorku na libovolné úrovni vnoření. První metoda najde blízký znak, který odpovídá otevřenému znaku:
private static bool FindMatchingCloseChar(SnapshotPoint startPoint, char open, char close, int maxLines, out SnapshotSpan pairSpan) { pairSpan = new SnapshotSpan(startPoint.Snapshot, 1, 1); ITextSnapshotLine line = startPoint.GetContainingLine(); string lineText = line.GetText(); int lineNumber = line.LineNumber; int offset = startPoint.Position - line.Start.Position + 1; int stopLineNumber = startPoint.Snapshot.LineCount - 1; if (maxLines > 0) stopLineNumber = Math.Min(stopLineNumber, lineNumber + maxLines); int openCount = 0; while (true) { //walk the entire line while (offset < line.Length) { char currentChar = lineText[offset]; if (currentChar == close) //found the close character { if (openCount > 0) { openCount--; } else //found the matching close { pairSpan = new SnapshotSpan(startPoint.Snapshot, line.Start + offset, 1); return true; } } else if (currentChar == open) // this is another open { openCount++; } offset++; } //move on to the next line if (++lineNumber > stopLineNumber) break; line = line.Snapshot.GetLineFromLineNumber(lineNumber); lineText = line.GetText(); offset = 0; } return false; }
Následující pomocná metoda najde otevřený znak, který odpovídá úzkému znaku:
private static bool FindMatchingOpenChar(SnapshotPoint startPoint, char open, char close, int maxLines, out SnapshotSpan pairSpan) { pairSpan = new SnapshotSpan(startPoint, startPoint); ITextSnapshotLine line = startPoint.GetContainingLine(); int lineNumber = line.LineNumber; int offset = startPoint - line.Start - 1; //move the offset to the character before this one //if the offset is negative, move to the previous line if (offset < 0) { line = line.Snapshot.GetLineFromLineNumber(--lineNumber); offset = line.Length - 1; } string lineText = line.GetText(); int stopLineNumber = 0; if (maxLines > 0) stopLineNumber = Math.Max(stopLineNumber, lineNumber - maxLines); int closeCount = 0; while (true) { // Walk the entire line while (offset >= 0) { char currentChar = lineText[offset]; if (currentChar == open) { if (closeCount > 0) { closeCount--; } else // We've found the open character { pairSpan = new SnapshotSpan(line.Start + offset, 1); //we just want the character itself return true; } } else if (currentChar == close) { closeCount++; } offset--; } // Move to the previous line if (--lineNumber < stopLineNumber) break; line = line.Snapshot.GetLineFromLineNumber(lineNumber); lineText = line.GetText(); offset = line.Length - 1; } return false; }
Implementace závorky odpovídajícího poskytovateli taggeru
Kromě implementace taggeru musíte také implementovat a exportovat poskytovatele taggeru. V tomto případě je typ obsahu poskytovatele text. Porovnávání závorek se tedy zobrazí ve všech typech textových souborů, ale úplná implementace použije složenou závorku pouze na určitý typ obsahu.
Implementace závorky odpovídajícího poskytovateli taggeru
Deklarujte zprostředkovatele taggeru, který dědí z IViewTaggerProvider, pojmenujte jej BraceMatchingTaggerProvider a exportujte ho s textem ContentTypeAttribute a TagTypeAttribute z TextMarkerTag.
Implementujte metodu CreateTagger pro vytvoření instance BraceMatchingTagger.
Sestavení a otestování kódu
Pokud chcete tento kód otestovat, sestavte řešení BraceMatchingTest a spusťte ho v experimentální instanci.
Sestavení a testování řešení BraceMatchingTest
Sestavte řešení.
Když tento projekt spustíte v ladicím programu, spustí se druhá instance sady Visual Studio.
Vytvořte textový soubor a zadejte nějaký text, který obsahuje odpovídající složené závorky.
hello { goodbye} {} {hello}
Když umístíte kurzor před otevřenou složenou závorku, měla by se zvýraznit obě složená závorka i odpovídající složená závorka. Když umístíte kurzor těsně za závorkou, zvýrazní se obě složené závorky i odpovídající otevřená složená závorka.