VisualStudio15StructureTaggerProvider.cs 4.8 KB
Newer Older
1 2 3 4 5
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
6
using Microsoft.CodeAnalysis.Editor.Implementation.Structure;
7
using Microsoft.CodeAnalysis.Shared.TestHooks;
8
using Microsoft.CodeAnalysis.Structure;
9 10
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text;
11
using Microsoft.VisualStudio.Text.Adornments;
12 13 14 15
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Projection;
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Utilities;
16
using Roslyn.Utilities;
17

18
namespace Microsoft.CodeAnalysis.Editor.Structure
19 20
{
    [Export(typeof(ITaggerProvider))]
C
CyrusNajmabadi 已提交
21
    [Export(typeof(VisualStudio15StructureTaggerProvider))]
22
    [TagType(typeof(IBlockTag))]
23
    [ContentType(ContentTypeNames.RoslynContentType)]
24
    internal partial class VisualStudio15StructureTaggerProvider :
25
        AbstractStructureTaggerProvider<IBlockTag>
26 27
    {
        [ImportingConstructor]
C
CyrusNajmabadi 已提交
28
        public VisualStudio15StructureTaggerProvider(
29 30 31 32 33 34 35 36 37
            IForegroundNotificationService notificationService,
            ITextEditorFactoryService textEditorFactoryService,
            IEditorOptionsFactoryService editorOptionsFactoryService,
            IProjectionBufferFactoryService projectionBufferFactoryService,
            [ImportMany] IEnumerable<Lazy<IAsynchronousOperationListener, FeatureMetadata>> asyncListeners)
                : base(notificationService, textEditorFactoryService, editorOptionsFactoryService, projectionBufferFactoryService, asyncListeners)
        {
        }

38 39
        protected override IBlockTag CreateTag(
            IBlockTag parentTag, ITextSnapshot snapshot, BlockSpan region)
40
        {
41 42 43 44 45
            return new RoslynBlockTag(
                this.TextEditorFactoryService,
                this.ProjectionBufferFactoryService,
                this.EditorOptionsFactoryService,
                parentTag, snapshot, region);
46 47
        }

48
        private class RoslynBlockTag : RoslynOutliningRegionTag, IBlockTag
49
        {
50
            public IBlockTag Parent { get; }
51 52 53 54
            public int Level { get; }
            public SnapshotSpan Span { get; }
            public SnapshotSpan StatementSpan { get; }

C
CyrusNajmabadi 已提交
55
            public string Type => BlockSpan.Type;
56
            public bool IsCollapsible => BlockSpan.IsCollapsible;
57

58 59 60 61
            public RoslynBlockTag(
                ITextEditorFactoryService textEditorFactoryService,
                IProjectionBufferFactoryService projectionBufferFactoryService,
                IEditorOptionsFactoryService editorOptionsFactoryService,
62
                IBlockTag parent,
63
                ITextSnapshot snapshot,
64
                BlockSpan blockSpan) :
65
                base(textEditorFactoryService,
C
CyrusNajmabadi 已提交
66 67 68
                     projectionBufferFactoryService,
                     editorOptionsFactoryService,
                     snapshot, blockSpan)
69 70
            {
                Parent = parent;
71
                Level = parent == null ? 0 : parent.Level + 1;
72 73
                Span = blockSpan.TextSpan.ToSnapshotSpan(snapshot);
                StatementSpan = blockSpan.HintSpan.ToSnapshotSpan(snapshot);
74 75
            }

76 77 78 79 80 81 82 83 84 85 86 87 88 89
            public override bool Equals(object obj)
                => Equals(obj as RoslynBlockTag);

            /// <summary>
            /// This is only called if the spans for the tags were the same.  However, even if we 
            /// have the same span as the previous tag (taking into account span mapping) that 
            /// doesn't mean we can use the old block tag.  Specifically, the editor will look at
            /// other fields in the tags So we need to make sure that these values have not changed
            /// if we want to reuse the old block tag.  For example, perhaps the item's type changed
            /// (i.e. from class to struct).  It will have the same span, but might have a new 
            /// presentation as the 'Type' will be different.
            /// </summary>
            public bool Equals(RoslynBlockTag tag)
            {
C
CyrusNajmabadi 已提交
90 91 92 93 94 95
                return base.Equals(tag) &&
                       IsCollapsible == tag.IsCollapsible &&
                       Level == tag.Level &&
                       Type == tag.Type &&
                       StatementSpan == tag.StatementSpan &&
                       Span == tag.Span;
96 97 98 99
            }

            public override int GetHashCode()
            {
C
CyrusNajmabadi 已提交
100 101
                return Hash.Combine(base.GetHashCode(),
                       Hash.Combine(this.IsCollapsible,
102
                       Hash.Combine(this.Level,
103
                       Hash.Combine(this.Type, 
104
                       Hash.Combine(this.StatementSpan.GetHashCode(), this.Span.GetHashCode())))));
105
            }
106 107 108
        }
    }
}