İzlenecek Yol: Ana Hat Oluşturma

Genişletmek veya daraltmak istediğiniz metin bölgesi türlerini tanımlayarak ana hat oluşturma gibi dil tabanlı özellikleri ayarlayın. Bir dil hizmeti bağlamında bölgeler tanımlayabilir veya kendi dosya adı uzantınızı ve içerik türünüzü tanımlayabilir ve bölge tanımını yalnızca bu türe uygulayabilir veya bölge tanımlarını mevcut bir içerik türüne ("metin" gibi) uygulayabilirsiniz. Bu kılavuzda, anahat bölgelerinin nasıl tanımlanacağı ve görüntüleneceği gösterilmektedir.

Yönetilen Genişletilebilirlik Çerçevesi (MEF) projesi oluşturma

MEF projesi oluşturmak için

  1. VSIX projesi oluşturma. Çözümü OutlineRegionTestolarak 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.

Ana hat oluşturma tagger'ı uygulama

Ana hat oluşturma bölgeleri bir tür etiketle (OutliningRegionTag ) işaretlenir. Bu etiket, standart ana hat oluşturma davranışını sağlar. Ana hatlı bölge genişletilebilir veya daraltılabilir. Ana hatlı bölge daraltılmışsa Artı işareti (+) veya genişletildiyse Eksi işareti (-) ile işaretlenir ve genişletilmiş bölge dikey çizgiyle ayrılmıştır.

Aşağıdaki adımlarda köşeli ayraçlar ([,]) ile sınırlandırılmış tüm bölgeler için ana hat oluşturma bölgeleri oluşturan bir etiketlemenin nasıl tanımlanacağı gösterilmektedir.

Ana hat oluşturma tagger'ı uygulamak için

  1. Bir sınıf dosyası ekleyin ve adını verin OutliningTagger.

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

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel.Composition;
    using Microsoft.VisualStudio.Text.Outlining;
    using Microsoft.VisualStudio.Text.Tagging;
    using Microsoft.VisualStudio.Utilities;
    using Microsoft.VisualStudio.Text;
    
  3. adlı OutliningTaggerbir sınıf oluşturun ve uygulamasını ITagger<T>sağlamayı seçin:

    internal sealed class OutliningTagger : ITagger<IOutliningRegionTag>
    
  4. Metin arabelleği ve anlık görüntüyü izlemek ve ana hat bölgeleri olarak etiketlenmesi gereken satır kümelerini biriktirmek için bazı alanlar ekleyin. Bu kod, ana hat bölgelerini temsil eden Region nesnelerinin (daha sonra tanımlanacak) listesini içerir.

    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;
    
  5. Alanları başlatan, arabelleği ayrıştıran ve olaya bir olay işleyicisi ekleyen bir etiket oluşturucu Changed ekleyin.

    public OutliningTagger(ITextBuffer buffer)
    {
        this.buffer = buffer;
        this.snapshot = buffer.CurrentSnapshot;
        this.regions = new List<Region>();
        this.ReParse();
        this.buffer.Changed += BufferChanged;
    }
    
  6. GetTags Etiket yayılmalarının örneğini oluşturan yöntemini uygulayın. Bu örnekte, yöntemine geçirilen içindeki span'ların NormalizedSpanCollection bitişik olduğu varsayılır, ancak her zaman böyle olmayabilir. Bu yöntem, ana hat oluşturma bölgelerinin her biri için yeni bir etiket aralığı oluşturur.

    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));
            }
        }
    }
    
  7. Bir TagsChanged olay işleyicisi bildirin.

    public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
    
  8. Metin arabelleği ayrıştırarak olaylara Changed yanıt veren bir BufferChanged olay işleyicisi ekleyin.

    void BufferChanged(object sender, TextContentChangedEventArgs e)
    {
        // If this isn't the most up-to-date version of the buffer, then ignore it for now (we'll eventually get another change event).
        if (e.After != buffer.CurrentSnapshot)
            return;
        this.ReParse();
    }
    
  9. Arabelleği ayrıştıran bir yöntem ekleyin. Burada verilen örnek yalnızca çizim içindir. Zaman uyumlu olarak arabelleği iç içe yerleştirilmiş ana hat bölgelerine ayrıştırır.

    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))));
        }
    }
    
  10. Aşağıdaki yardımcı yöntem, 1'in en soldaki ayraç çifti olması için ana hat düzeyini temsil eden bir tamsayı alır.

    static bool TryGetLevel(string text, int startIndex, out int level)
    {
        level = -1;
        if (text.Length > startIndex + 3)
        {
            if (int.TryParse(text.Substring(startIndex + 1), out level))
                return true;
        }
    
        return false;
    }
    
  11. Aşağıdaki yardımcı yöntem, bir Bölgeyi (bu makalenin ilerleyen bölümlerinde tanımlanmıştır) SnapshotSpan'a çevirir.

    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);
    }
    
  12. Aşağıdaki kod yalnızca çizim içindir. Bir ana hat bölgesinin başlangıcının satır numarasını ve uzaklığını ve varsa üst bölgeye başvuruyu içeren bir PartialRegion sınıfı tanımlar. Bu kod, ayrıştırıcının iç içe yerleştirilmiş ana hat bölgeleri ayarlamasını sağlar. Türetilmiş Region sınıfı, bir ana hat bölgesinin sonunun satır numarasına başvuru içerir.

    class PartialRegion
    {
        public int StartLine { get; set; }
        public int StartOffset { get; set; }
        public int Level { get; set; }
        public PartialRegion PartialParent { get; set; }
    }
    
    class Region : PartialRegion
    {
        public int EndLine { get; set; }
    }
    

Bir etiketleme sağlayıcısı uygulama

Etiketlemeniz için bir etiketleme sağlayıcısı dışarı aktarın. Etiketleme sağlayıcısı "metin" içerik türündeki bir arabellek için bir OutliningTagger oluşturur veya arabellekte zaten varsa bir OutliningTagger döndürür.

Bir etiketleme sağlayıcısı uygulamak için

  1. uygulayan ITaggerProvideradlı OutliningTaggerProvider bir sınıf oluşturun ve ContentType ve TagType öznitelikleriyle dışarı aktarın.

    [Export(typeof(ITaggerProvider))]
    [TagType(typeof(IOutliningRegionTag))]
    [ContentType("text")]
    internal sealed class OutliningTaggerProvider : ITaggerProvider
    
  2. CreateTagger arabellek özelliklerine bir OutliningTagger ekleyerek yöntemini uygulayın.

    public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
    {
        //create a single tagger for each buffer.
        Func<ITagger<T>> sc = delegate() { return new OutliningTagger(buffer) as ITagger<T>; };
        return buffer.Properties.GetOrCreateSingletonProperty<ITagger<T>>(sc);
    }
    

Kodu derleme ve test etme

Bu kodu test etmek için OutlineRegionTest çözümünü derleyin ve deneysel örnekte çalıştırın.

OutlineRegionTest çö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. Hem açılış köşeli ayraçlarını hem de kapatma köşeli ayraçlarını içeren bir metin yazın.

    [
       Hello
    ]
    
  4. Her iki köşeli ayraç içeren bir ana hat bölgesi olmalıdır. Ana hat bölgesini daraltmak için açık köşeli ayraç solundaki Eksi İşareti'ne tıklayabilmeniz gerekir. Bölge daraltıldığında, daraltılmış bölgenin solunda üç nokta simgesi (...) görünmelidir ve işaretçiyi üç noktanın üzerine getirdiğinizde metin vurgulama metnini içeren bir açılır pencere gösterilmelidir.