forked from dotnet/roslyn
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request dotnet#15914 from CyrusNajmabadi/deriveFromEditorTYpe
Subclass the editor's BlockTag so we can pick up some heuristic changes they're making on their end.
- Loading branch information
Showing
7 changed files
with
260 additions
and
200 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
165 changes: 165 additions & 0 deletions
165
src/EditorFeatures/Core/Implementation/Structure/BlockTagState.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.