İ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

  1. Düzenleyici Sınıflandırıcı projesi oluşturun. Çözümü BraceMatchingTestolarak adlandırın.

  2. 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.

  3. 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

  1. Sınıf dosyasını ekleyin ve BraceMatching olarak adlandırnın.

  2. Aşağıdaki ad alanlarını içeri aktarın.

    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Text.Tagging;
    using Microsoft.VisualStudio.Utilities;
    
  3. türünden ITagger<T> TextMarkerTagdevralan bir sınıf BraceMatchingTagger tanımlayın.

    internal class BraceMatchingTagger : ITagger<TextMarkerTag>
    
  4. 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.

    ITextView View { get; set; }
    ITextBuffer SourceBuffer { get; set; }
    SnapshotPoint? CurrentChar { get; set; }
    private Dictionary<char, char> m_braceList;
    
  5. 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;
    }
    
  6. Uygulamanın bir parçası ITagger<T> olarak TagsChanged olayını bildirin.

    public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
    
  7. 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)));
    }
    
  8. 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"));
            }
        }
    }
    
  9. 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;
    }
    
  10. 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

  1. öğ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.

    [Export(typeof(IViewTaggerProvider))]
    [ContentType("text")]
    [TagType(typeof(TextMarkerTag))]
    internal class BraceMatchingTaggerProvider : IViewTaggerProvider
    
  2. CreateTagger Bir BraceMatchingTagger örneği oluşturmak için yöntemini uygulayın.

    public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag
    {
        if (textView == null)
            return null;
    
        //provide highlighting only on the top-level buffer
        if (textView.TextBuffer != buffer)
            return null;
    
        return new BraceMatchingTagger(textView, buffer) as ITagger<T>;
    }
    

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

  1. Çözümü oluşturun.

  2. Bu projeyi hata ayıklayıcıda çalıştırdığınızda, Visual Studio'nun ikinci bir örneği başlatılır.

  3. Bir metin dosyası oluşturun ve eşleşen ayraçlar içeren bir metin yazın.

    hello {
    goodbye}
    
    {}
    
    {hello}
    
  4. Ş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.