提交 cf0df399 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #15914 from CyrusNajmabadi/deriveFromEditorTYpe

Subclass the editor's BlockTag so we can pick up some heuristic changes they're making on their end.
......@@ -109,6 +109,7 @@
<Compile Include="FindUsages\IFindUsagesContext.cs" />
<Compile Include="FindUsages\IFindUsagesService.cs" />
<Compile Include="FindUsages\SimpleFindUsagesContext.cs" />
<Compile Include="Implementation\Structure\BlockTagState.cs" />
<Compile Include="Tags\ExportImageMonikerServiceAttribute.cs" />
<Compile Include="Implementation\NavigateTo\AbstractNavigateToItemDisplay.cs" />
<Compile Include="CommandArgs.cs" />
......
// 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.Collections.Generic;
using System.Windows.Media;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Structure;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Projection;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.Structure
{
/// <summary>
/// Shared state object we use for <see cref="RoslynOutliningRegionTag"/> and RoslynBlockTag
/// (in EditorFeatures.Next).
/// </summary>
internal struct BlockTagState
{
private const string OutliningRegionTextViewRole = nameof(OutliningRegionTextViewRole);
private const string Ellipsis = "...";
private const int MaxPreviewText = 1000;
private readonly ITextEditorFactoryService _textEditorFactoryService;
private readonly IProjectionBufferFactoryService _projectionBufferFactoryService;
private readonly IEditorOptionsFactoryService _editorOptionsFactoryService;
private readonly ITextBuffer _subjectBuffer;
private readonly ITrackingSpan _hintSpan;
public bool IsDefaultCollapsed => BlockSpan.IsDefaultCollapsed;
public bool IsImplementation => BlockSpan.AutoCollapse;
public object CollapsedForm => BlockSpan.BannerText;
public readonly BlockSpan BlockSpan;
public BlockTagState(
ITextEditorFactoryService textEditorFactoryService,
IProjectionBufferFactoryService projectionBufferFactoryService,
IEditorOptionsFactoryService editorOptionsFactoryService,
ITextSnapshot snapshot,
BlockSpan blockSpan)
{
_textEditorFactoryService = textEditorFactoryService;
_projectionBufferFactoryService = projectionBufferFactoryService;
_editorOptionsFactoryService = editorOptionsFactoryService;
_subjectBuffer = snapshot.TextBuffer;
BlockSpan = blockSpan;
_hintSpan = snapshot.CreateTrackingSpan(BlockSpan.HintSpan.ToSpan(), SpanTrackingMode.EdgeExclusive);
}
public override bool Equals(object obj)
=> obj is BlockTagState s && Equals(s);
public bool Equals(BlockTagState tag)
=> IsImplementation == tag.IsImplementation &&
Equals(this.CollapsedForm, tag.CollapsedForm);
public override int GetHashCode()
=> Hash.Combine(IsImplementation,
EqualityComparer<object>.Default.GetHashCode(this.CollapsedForm));
public object CollapsedHintForm
=> new ViewHostingControl(CreateElisionBufferView, CreateElisionBuffer);
private IWpfTextView CreateElisionBufferView(ITextBuffer finalBuffer)
=> CreateShrunkenTextView(_textEditorFactoryService, finalBuffer);
internal static IWpfTextView CreateShrunkenTextView(
ITextEditorFactoryService textEditorFactoryService,
ITextBuffer finalBuffer)
{
var roles = textEditorFactoryService.CreateTextViewRoleSet(OutliningRegionTextViewRole);
var view = textEditorFactoryService.CreateTextView(finalBuffer, roles);
view.Background = Brushes.Transparent;
view.SizeToFit();
// Zoom out a bit to shrink the text.
view.ZoomLevel *= 0.75;
return view;
}
private ITextBuffer CreateElisionBuffer()
{
// Remove any starting whitespace.
var span = TrimStartingNewlines(_hintSpan.GetSpan(_subjectBuffer.CurrentSnapshot));
// Trim the length if it's too long.
var shortSpan = span;
if (span.Length > MaxPreviewText)
{
shortSpan = ComputeShortSpan(span);
}
// Create an elision buffer for that span, also trimming the
// leading whitespace.
var elisionBuffer = CreateElisionBufferWithoutIndentation(_subjectBuffer, shortSpan);
var finalBuffer = elisionBuffer;
// If we trimmed the length, then make a projection buffer that
// has the above elision buffer and follows it with "..."
if (span.Length != shortSpan.Length)
{
finalBuffer = CreateTrimmedProjectionBuffer(elisionBuffer);
}
return finalBuffer;
}
private ITextBuffer CreateTrimmedProjectionBuffer(ITextBuffer elisionBuffer)
{
// The elision buffer is too long. We've already trimmed it, but now we want to add
// a "..." to it. We do that by creating a projection of both the elision buffer and
// a new text buffer wrapping the ellipsis.
var elisionSpan = elisionBuffer.CurrentSnapshot.GetFullSpan();
var sourceSpans = new List<object>()
{
elisionSpan.Snapshot.CreateTrackingSpan(elisionSpan, SpanTrackingMode.EdgeExclusive),
Ellipsis
};
var projectionBuffer = _projectionBufferFactoryService.CreateProjectionBuffer(
projectionEditResolver: null,
sourceSpans: sourceSpans,
options: ProjectionBufferOptions.None);
return projectionBuffer;
}
private Span ComputeShortSpan(Span span)
{
var endIndex = span.Start + MaxPreviewText;
var line = _subjectBuffer.CurrentSnapshot.GetLineFromPosition(endIndex);
return Span.FromBounds(span.Start, line.EndIncludingLineBreak);
}
private Span TrimStartingNewlines(Span span)
{
while (span.Length > 1 && char.IsWhiteSpace(_subjectBuffer.CurrentSnapshot[span.Start]))
{
span = new Span(span.Start + 1, span.Length - 1);
}
return span;
}
private ITextBuffer CreateElisionBufferWithoutIndentation(
ITextBuffer dataBuffer, Span shortHintSpan)
{
return _projectionBufferFactoryService.CreateElisionBufferWithoutIndentation(
_editorOptionsFactoryService.GlobalOptions,
contentType: null,
exposedSpans: new SnapshotSpan(dataBuffer.CurrentSnapshot, shortHintSpan));
}
}
}
\ No newline at end of file
// 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.Collections.Generic;
using System.Windows.Media;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Structure;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Projection;
using Microsoft.VisualStudio.Text.Tagging;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.Structure
{
......@@ -19,150 +13,35 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Structure
// we are collapsing.
internal class RoslynOutliningRegionTag : IOutliningRegionTag
{
private const string OutliningRegionTextViewRole = nameof(OutliningRegionTextViewRole);
private const string Ellipsis = "...";
private const int MaxPreviewText = 1000;
private readonly ITextEditorFactoryService _textEditorFactoryService;
private readonly IProjectionBufferFactoryService _projectionBufferFactoryService;
private readonly IEditorOptionsFactoryService _editorOptionsFactoryService;
private readonly ITextBuffer _subjectBuffer;
private readonly ITrackingSpan _hintSpan;
public bool IsDefaultCollapsed => BlockSpan.IsDefaultCollapsed;
public bool IsImplementation => BlockSpan.AutoCollapse;
public object CollapsedForm => BlockSpan.BannerText;
protected readonly BlockSpan BlockSpan;
private readonly BlockTagState _state;
public RoslynOutliningRegionTag(
ITextEditorFactoryService textEditorFactoryService,
IProjectionBufferFactoryService projectionBufferFactoryService,
IEditorOptionsFactoryService editorOptionsFactoryService,
ITextSnapshot snapshot,
BlockSpan outliningSpan)
BlockSpan blockSpan)
{
_textEditorFactoryService = textEditorFactoryService;
_projectionBufferFactoryService = projectionBufferFactoryService;
_editorOptionsFactoryService = editorOptionsFactoryService;
_subjectBuffer = snapshot.TextBuffer;
BlockSpan = outliningSpan;
_hintSpan = snapshot.CreateTrackingSpan(BlockSpan.HintSpan.ToSpan(), SpanTrackingMode.EdgeExclusive);
_state = new BlockTagState(
textEditorFactoryService, projectionBufferFactoryService,
editorOptionsFactoryService, snapshot, blockSpan);
}
public override bool Equals(object obj)
public override bool Equals(object obj)
=> Equals(obj as RoslynOutliningRegionTag);
public bool Equals(RoslynOutliningRegionTag tag)
=> tag != null &&
IsImplementation == tag.IsImplementation &&
Equals(this.CollapsedForm, tag.CollapsedForm);
public override int GetHashCode()
=> Hash.Combine(IsImplementation,
EqualityComparer<object>.Default.GetHashCode(this.CollapsedForm));
public object CollapsedHintForm =>
new ViewHostingControl(CreateElisionBufferView, CreateElisionBuffer);
private IWpfTextView CreateElisionBufferView(ITextBuffer finalBuffer)
{
return CreateShrunkenTextView(_textEditorFactoryService, finalBuffer);
}
internal static IWpfTextView CreateShrunkenTextView(
ITextEditorFactoryService textEditorFactoryService,
ITextBuffer finalBuffer)
{
var roles = textEditorFactoryService.CreateTextViewRoleSet(OutliningRegionTextViewRole);
var view = textEditorFactoryService.CreateTextView(finalBuffer, roles);
view.Background = Brushes.Transparent;
view.SizeToFit();
// Zoom out a bit to shrink the text.
view.ZoomLevel *= 0.75;
return view;
}
=> tag != null && _state.Equals(tag._state);
private ITextBuffer CreateElisionBuffer()
{
// Remove any starting whitespace.
var span = TrimStartingNewlines(_hintSpan.GetSpan(_subjectBuffer.CurrentSnapshot));
// Trim the length if it's too long.
var shortSpan = span;
if (span.Length > MaxPreviewText)
{
shortSpan = ComputeShortSpan(span);
}
// Create an elision buffer for that span, also trimming the
// leading whitespace.
var elisionBuffer = CreateElisionBufferWithoutIndentation(_subjectBuffer, shortSpan);
var finalBuffer = elisionBuffer;
// If we trimmed the length, then make a projection buffer that
// has the above elision buffer and follows it with "..."
if (span.Length != shortSpan.Length)
{
finalBuffer = CreateTrimmedProjectionBuffer(elisionBuffer);
}
return finalBuffer;
}
public override int GetHashCode()
=> _state.GetHashCode();
private ITextBuffer CreateTrimmedProjectionBuffer(ITextBuffer elisionBuffer)
{
// The elision buffer is too long. We've already trimmed it, but now we want to add
// a "..." to it. We do that by creating a projection of both the elision buffer and
// a new text buffer wrapping the ellipsis.
var elisionSpan = elisionBuffer.CurrentSnapshot.GetFullSpan();
public object CollapsedForm => _state.CollapsedForm;
var sourceSpans = new List<object>()
{
elisionSpan.Snapshot.CreateTrackingSpan(elisionSpan, SpanTrackingMode.EdgeExclusive),
Ellipsis
};
public object CollapsedHintForm => _state.CollapsedHintForm;
var projectionBuffer = _projectionBufferFactoryService.CreateProjectionBuffer(
projectionEditResolver: null,
sourceSpans: sourceSpans,
options: ProjectionBufferOptions.None);
public bool IsDefaultCollapsed => _state.IsDefaultCollapsed;
return projectionBuffer;
}
private Span ComputeShortSpan(Span span)
{
var endIndex = span.Start + MaxPreviewText;
var line = _subjectBuffer.CurrentSnapshot.GetLineFromPosition(endIndex);
return Span.FromBounds(span.Start, line.EndIncludingLineBreak);
}
private Span TrimStartingNewlines(Span span)
{
while (span.Length > 1 && char.IsWhiteSpace(_subjectBuffer.CurrentSnapshot[span.Start]))
{
span = new Span(span.Start + 1, span.Length - 1);
}
return span;
}
private ITextBuffer CreateElisionBufferWithoutIndentation(
ITextBuffer dataBuffer, Span shortHintSpan)
{
return _projectionBufferFactoryService.CreateElisionBufferWithoutIndentation(
_editorOptionsFactoryService.GlobalOptions,
contentType: null,
exposedSpans: new SnapshotSpan(dataBuffer.CurrentSnapshot, shortHintSpan));
}
public bool IsImplementation => _state.IsImplementation;
}
}
\ No newline at end of file
......@@ -66,6 +66,7 @@
<Compile Include="Options\EditorConfigDocumentOptionsProvider.DocumentOptions.cs" />
<Compile Include="Options\EditorConfigDocumentOptionsProvider.EmptyCodingConventionContext.cs" />
<Compile Include="Structure\BlockContextProvider.cs" />
<Compile Include="Structure\RoslynBlockTag.cs" />
<Compile Include="Structure\VisualStudio15StructureTaggerProvider.cs" />
<Compile Include="Options\EditorConfigDocumentOptionsProviderFactory.cs" />
<Compile Include="Options\EditorConfigDocumentOptionsProvider.cs" />
......
// 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;
using System.Threading;
......@@ -62,7 +61,7 @@ public void Dispose()
public Task<IBlockContext> GetBlockContextAsync(
IBlockTag blockTag, ITextView view, CancellationToken token)
{
if (blockTag is RoslynOutliningRegionTag)
if (blockTag is RoslynBlockTag)
{
var result = new RoslynBlockContext(_provider, blockTag, view);
return Task.FromResult<IBlockContext>(result);
......@@ -104,7 +103,7 @@ private object CreateContent()
private IWpfTextView CreateElisionBufferView(ITextBuffer finalBuffer)
{
return RoslynOutliningRegionTag.CreateShrunkenTextView(
return BlockTagState.CreateShrunkenTextView(
_provider._textEditorFactoryService, finalBuffer);
}
......
// 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 Microsoft.CodeAnalysis.Editor.Implementation.Structure;
using Microsoft.CodeAnalysis.Structure;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Projection;
using Microsoft.VisualStudio.Text.Tagging;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Structure
{
internal class RoslynBlockTag : BlockTag
{
private readonly BlockTagState _state;
public override int Level { get; }
public RoslynBlockTag(
ITextEditorFactoryService textEditorFactoryService,
IProjectionBufferFactoryService projectionBufferFactoryService,
IEditorOptionsFactoryService editorOptionsFactoryService,
IBlockTag parent,
ITextSnapshot snapshot,
BlockSpan blockSpan) :
base(span: blockSpan.TextSpan.ToSnapshotSpan(snapshot),
statementSpan: blockSpan.HintSpan.ToSnapshotSpan(snapshot),
parent: parent,
type: blockSpan.Type,
isCollapsible: blockSpan.IsCollapsible,
isDefaultCollapsed: blockSpan.IsDefaultCollapsed,
isImplementation: blockSpan.AutoCollapse,
collapsedForm: null,
collapsedHintForm: null)
{
_state = new BlockTagState(
textEditorFactoryService, projectionBufferFactoryService,
editorOptionsFactoryService, snapshot, blockSpan);
Level = parent == null ? 0 : parent.Level + 1;
}
public override object CollapsedForm => _state.CollapsedForm;
public override object CollapsedHintForm => _state.CollapsedHintForm;
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)
{
return _state.Equals(tag._state) &&
IsCollapsible == tag.IsCollapsible &&
Level == tag.Level &&
Type == tag.Type &&
StatementSpan == tag.StatementSpan &&
Span == tag.Span;
}
public override int GetHashCode()
{
return Hash.Combine(base.GetHashCode(),
Hash.Combine(this.IsCollapsible,
Hash.Combine(this.Level,
Hash.Combine(this.Type,
Hash.Combine(this.StatementSpan.GetHashCode(), this.Span.GetHashCode())))));
}
}
}
\ No newline at end of file
......@@ -6,14 +6,11 @@
using Microsoft.CodeAnalysis.Editor.Implementation.Structure;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Structure;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Adornments;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Projection;
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Utilities;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Structure
{
......@@ -44,65 +41,5 @@ internal partial class VisualStudio15StructureTaggerProvider :
this.EditorOptionsFactoryService,
parentTag, snapshot, region);
}
private class RoslynBlockTag : RoslynOutliningRegionTag, IBlockTag
{
public IBlockTag Parent { get; }
public int Level { get; }
public SnapshotSpan Span { get; }
public SnapshotSpan StatementSpan { get; }
public string Type => BlockSpan.Type;
public bool IsCollapsible => BlockSpan.IsCollapsible;
public RoslynBlockTag(
ITextEditorFactoryService textEditorFactoryService,
IProjectionBufferFactoryService projectionBufferFactoryService,
IEditorOptionsFactoryService editorOptionsFactoryService,
IBlockTag parent,
ITextSnapshot snapshot,
BlockSpan blockSpan) :
base(textEditorFactoryService,
projectionBufferFactoryService,
editorOptionsFactoryService,
snapshot, blockSpan)
{
Parent = parent;
Level = parent == null ? 0 : parent.Level + 1;
Span = blockSpan.TextSpan.ToSnapshotSpan(snapshot);
StatementSpan = blockSpan.HintSpan.ToSnapshotSpan(snapshot);
}
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)
{
return base.Equals(tag) &&
IsCollapsible == tag.IsCollapsible &&
Level == tag.Level &&
Type == tag.Type &&
StatementSpan == tag.StatementSpan &&
Span == tag.Span;
}
public override int GetHashCode()
{
return Hash.Combine(base.GetHashCode(),
Hash.Combine(this.IsCollapsible,
Hash.Combine(this.Level,
Hash.Combine(this.Type,
Hash.Combine(this.StatementSpan.GetHashCode(), this.Span.GetHashCode())))));
}
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册