İzlenecek yol: Eşleşen ayraçları görüntüleme
Eşleştirmek istediğiniz küme ayraçlarını tanımlayarak ve ayraç ayraçlardan birindeyken eşleşen ayraçlara metin işaretçisi etiketi ekleyerek ayraç eşleştirme gibi dil tabanlı özellikleri uygulayın. Ayraçları bir dil bağlamında tanımlayabilir, kendi dosya adı uzantınızı ve içerik türünüzü tanımlayabilir ve etiketleri yalnızca bu türe uygulayabilir veya mevcut içerik türüne ("metin" gibi) uygulayabilirsiniz. Aşağıdaki kılavuzda, "metin" içerik türüne ayraç eşleştirme etiketlerinin nasıl uygulanacağı gösterilmektedir.
Yönetilen Genişletilebilirlik Çerçevesi (MEF) projesi oluşturma
MEF projesi oluşturmak için
Düzenleyici Sınıflandırıcı projesi oluşturun. Çözümü
BraceMatchingTest
olarak adlandırın.Projeye bir Düzenleyici Sınıflandırıcısı öğesi şablonu ekleyin. Daha fazla bilgi için bkz . Düzenleyici öğesi şablonuyla uzantı oluşturma.
Varolan sınıf dosyalarını silin.
Küme ayracı eşleştirme tagger'ı uygulama
Visual Studio'da kullanılana benzer bir ayraç vurgulama efekti elde etmek için türünde TextMarkerTagbir etiket uygulayabilirsiniz. Aşağıdaki kod, küme ayracı çiftleri için herhangi bir iç içe yerleştirme düzeyinde küme ayracı tanımlamayı gösterir. Bu örnekte [] ve {} küme ayracı çiftleri, tagger oluşturucusunda tanımlanmıştır, ancak tam dil uygulamasında ilgili ayraç çiftleri dil belirtiminde tanımlanabilir.
Küme ayracı eşleştirme tagger'ı uygulamak için
Sınıf dosyasını ekleyin ve BraceMatching olarak adlandırnın.
Aşağıdaki ad alanlarını içeri aktarın.
türünden ITagger<T> TextMarkerTagdevralan bir sınıf
BraceMatchingTagger
tanımlayın.Metin görünümü, kaynak arabelleği, geçerli anlık görüntü noktası ve ayrıca bir küme ayracı çifti için özellikler ekleyin.
Tagger oluşturucusunda özellikleri ayarlayın ve görünüm değişikliği olaylarına PositionChanged ve LayoutChangedöğesine abone olun. Bu örnekte, açıklayıcı amaçlar için eşleşen çiftler de oluşturucuda tanımlanır.
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; }
Uygulamanın bir parçası ITagger<T> olarak TagsChanged olayını bildirin.
Olay işleyicileri özelliğin geçerli şapka işareti konumunu
CurrentChar
güncelleştirir ve TagsChanged olayını oluşturur.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))); }
Visual Studio'da GetTags olduğu gibi, geçerli karakter açık bir küme ayracı olduğunda veya önceki karakter yakın bir küme ayracı olduğunda küme ayraçlarını eşleştirmek için yöntemini uygulayın. Eşleşme bulunduğunda, bu yöntem biri açık küme ayracı, biri de kapanış ayracı için iki etiket oluşturur.
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")); } } }
Aşağıdaki özel yöntemler, eşleşen küme ayraçlarını herhangi bir iç içe yerleştirme düzeyinde bulur. İlk yöntem, açık karakterle eşleşen yakın karakteri bulur:
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; }
Aşağıdaki yardımcı yöntem, bir yakın karakterle eşleşen açık karakteri bulur:
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; }
Küme ayracı eşleşen bir etiketleme sağlayıcısı uygulama
Bir etiketleyici uygulamaya ek olarak, bir etiketleme sağlayıcısı da uygulamanız ve dışarı aktarmanız gerekir. Bu durumda, sağlayıcının içerik türü "metin"dir. Bu nedenle, tüm metin dosyası türlerinde ayraç eşleştirme görünür, ancak daha tam bir uygulama yalnızca belirli bir içerik türüne ayraç eşleştirmesi uygular.
Küme ayracı eşleşen bir etiketleme sağlayıcısı uygulamak için
öğesinden IViewTaggerProviderdevralan bir tagger sağlayıcısı bildirin, bunu BraceMatchingTaggerProvider olarak adlandırın ve bir "text" ve bir TagTypeAttribute ile dışarı ContentTypeAttribute aktarınTextMarkerTag.
CreateTagger Bir BraceMatchingTagger örneği oluşturmak için yöntemini uygulayın.
Kodu derleme ve test etme
Bu kodu test etmek için BraceMatchingTest çözümünü derleyin ve deneysel örnekte çalıştırın.
BraceMatchingTest çözümünü derlemek ve test etmek için
Çözümü oluşturun.
Bu projeyi hata ayıklayıcıda çalıştırdığınızda, Visual Studio'nun ikinci bir örneği başlatılır.
Bir metin dosyası oluşturun ve eşleşen ayraçlar içeren bir metin yazın.
hello { goodbye} {} {hello}
Şapka işaretini açık bir küme ayracından önce konumlandırdığınızda, hem bu küme ayracı hem de eşleşen kapanış ayracı vurgulanmalıdır. İmleci kapanış ayracından hemen sonra konumlandırdığınızda, hem bu ayraç hem de eşleşen açık küme ayracı vurgulanmalıdır.