Skip to content

Commit

Permalink
Merge pull request dotnet#15914 from CyrusNajmabadi/deriveFromEditorTYpe
Browse files Browse the repository at this point in the history
Subclass the editor's BlockTag so we can pick up some heuristic changes they're making on their end.
  • Loading branch information
CyrusNajmabadi authored Dec 16, 2016
2 parents 4545838 + c2c7f23 commit cf0df39
Showing 7 changed files with 260 additions and 200 deletions.
1 change: 1 addition & 0 deletions src/EditorFeatures/Core/EditorFeatures.csproj
Original file line number Diff line number Diff line change
@@ -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" />
165 changes: 165 additions & 0 deletions src/EditorFeatures/Core/Implementation/Structure/BlockTagState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// 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));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
// 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;
}
}
1 change: 1 addition & 0 deletions src/EditorFeatures/Next/EditorFeatures.Next.csproj
Original file line number Diff line number Diff line change
@@ -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" />
5 changes: 2 additions & 3 deletions src/EditorFeatures/Next/Structure/BlockContextProvider.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,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;
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);
}

Loading

0 comments on commit cf0df39

Please sign in to comment.