From 4924d709471fa72ace07747f0b37131e592abd05 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 20 Mar 2018 01:02:19 -0500 Subject: [PATCH] Place ITextBufferCloneService in ITextBuffer.Properties instead of separate tracking in SnapshotSourceText --- .../Workspaces/EditorTextFactoryService.cs | 36 +---- .../SourceTextContainerExtensionsTests.cs | 2 +- .../Test/Workspaces/TextFactoryTests.cs | 123 ++++++++---------- .../Text/Extensions.SnapshotSourceText.cs | 48 +++---- .../Text/Extensions.TextBufferContainer.cs | 29 ++--- src/EditorFeatures/Text/Extensions.cs | 8 +- .../TextBufferCloneServiceFactory.cs | 10 +- .../VisualStudioWorkspaceImpl.cs | 17 +++ 8 files changed, 118 insertions(+), 155 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/EditorTextFactoryService.cs b/src/EditorFeatures/Core/Implementation/Workspaces/EditorTextFactoryService.cs index 633cf33a69f..2023d3542eb 100644 --- a/src/EditorFeatures/Core/Implementation/Workspaces/EditorTextFactoryService.cs +++ b/src/EditorFeatures/Core/Implementation/Workspaces/EditorTextFactoryService.cs @@ -13,18 +13,20 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces { + [ExportWorkspaceService(typeof(ITextFactoryService), ServiceLayer.Editor), Shared] internal class EditorTextFactoryService : ITextFactoryService { - private readonly Workspace _workspace; + private readonly ITextBufferCloneService _textBufferCloneService; private readonly ITextBufferFactoryService _textBufferFactory; private readonly IContentType _unknownContentType; + [ImportingConstructor] public EditorTextFactoryService( - Workspace workspace, + ITextBufferCloneService textBufferCloneService, ITextBufferFactoryService textBufferFactoryService, IContentTypeRegistryService contentTypeRegistryService) { - _workspace = workspace; + _textBufferCloneService = textBufferCloneService; _textBufferFactory = textBufferFactoryService; _unknownContentType = contentTypeRegistryService.UnknownContentType; } @@ -69,7 +71,7 @@ public SourceText CreateText(TextReader reader, Encoding encoding, CancellationT var buffer = CreateTextBuffer(reader, cancellationToken); // use the given encoding as it is. - return buffer.CurrentSnapshot.AsRoslynText(_workspace, encoding); + return buffer.CurrentSnapshot.AsRoslynText(_textBufferCloneService, encoding); } private ITextBuffer CreateTextBuffer(TextReader reader, CancellationToken cancellationToken = default) @@ -85,31 +87,7 @@ private SourceText CreateTextInternal(Stream stream, Encoding encoding, Cancella using (var reader = new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true)) { var buffer = CreateTextBuffer(reader, cancellationToken); - return buffer.CurrentSnapshot.AsRoslynText(_workspace, reader.CurrentEncoding ?? Encoding.UTF8); - } - } - - [ExportWorkspaceServiceFactory(typeof(ITextFactoryService), ServiceLayer.Editor), Shared] - private sealed class Factory : IWorkspaceServiceFactory - { - private readonly ITextBufferFactoryService _textBufferFactoryService; - private readonly IContentTypeRegistryService _contentTypeRegistryService; - - [ImportingConstructor] - public Factory( - ITextBufferFactoryService textBufferFactoryService, - IContentTypeRegistryService contentTypeRegistryService) - { - _textBufferFactoryService = textBufferFactoryService; - _contentTypeRegistryService = contentTypeRegistryService; - } - - public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - { - return new EditorTextFactoryService( - workspaceServices.Workspace, - _textBufferFactoryService, - _contentTypeRegistryService); + return buffer.CurrentSnapshot.AsRoslynText(_textBufferCloneService, reader.CurrentEncoding ?? Encoding.UTF8); } } } diff --git a/src/EditorFeatures/Test/Extensions/SourceTextContainerExtensionsTests.cs b/src/EditorFeatures/Test/Extensions/SourceTextContainerExtensionsTests.cs index 6dcc15a6bd7..b1c083cdc1e 100644 --- a/src/EditorFeatures/Test/Extensions/SourceTextContainerExtensionsTests.cs +++ b/src/EditorFeatures/Test/Extensions/SourceTextContainerExtensionsTests.cs @@ -33,7 +33,7 @@ public void GetBufferTextFromTextContainerDoesNotThrow() bufferMock.SetupGet(x => x.CurrentSnapshot).Returns(textSnapshotMock.Object); bufferMock.SetupGet(x => x.Properties).Returns(new VisualStudio.Utilities.PropertyCollection()); - var textContainer = Text.Extensions.TextBufferContainer.From(workspace: null, bufferMock.Object); + var textContainer = Text.Extensions.TextBufferContainer.From(bufferMock.Object); Text.Extensions.GetTextBuffer(textContainer); } diff --git a/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs b/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs index 264a43f6479..42ccf708acc 100644 --- a/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs +++ b/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs @@ -1,11 +1,13 @@ // 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.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Implementation.Workspaces; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Utilities; using Moq; @@ -21,108 +23,86 @@ public class TextFactoryTests [Fact, WorkItem(1038018, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1038018"), WorkItem(1041792, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1041792")] public void TestCreateTextFallsBackToSystemDefaultEncoding() { - using (var workspace = new TestWorkspace()) - { - TestCreateTextInferredEncoding( - workspace, - _nonUTF8StringBytes, - defaultEncoding: null, - expectedEncoding: Encoding.Default); - } + TestCreateTextInferredEncoding( + _nonUTF8StringBytes, + defaultEncoding: null, + expectedEncoding: Encoding.Default); } [Fact, WorkItem(1038018, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1038018")] public void TestCreateTextFallsBackToUTF8Encoding() { - using (var workspace = new TestWorkspace()) - { - TestCreateTextInferredEncoding( - workspace, - new ASCIIEncoding().GetBytes("Test"), - defaultEncoding: null, - expectedEncoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true)); - } + TestCreateTextInferredEncoding( + new ASCIIEncoding().GetBytes("Test"), + defaultEncoding: null, + expectedEncoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true)); } [Fact, WorkItem(1038018, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1038018")] public void TestCreateTextFallsBackToProvidedDefaultEncoding() { - using (var workspace = new TestWorkspace()) - { - TestCreateTextInferredEncoding( - workspace, - new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetBytes("Test"), - defaultEncoding: Encoding.GetEncoding(1254), - expectedEncoding: Encoding.GetEncoding(1254)); - } + TestCreateTextInferredEncoding( + new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetBytes("Test"), + defaultEncoding: Encoding.GetEncoding(1254), + expectedEncoding: Encoding.GetEncoding(1254)); } [Fact, WorkItem(1038018, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1038018")] public void TestCreateTextUsesByteOrderMarkIfPresent() { - using (var workspace = new TestWorkspace()) - { - TestCreateTextInferredEncoding( - workspace, - Encoding.UTF8.GetPreamble().Concat(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetBytes("Test")).ToArray(), - defaultEncoding: Encoding.GetEncoding(1254), - expectedEncoding: Encoding.UTF8); - } + TestCreateTextInferredEncoding( + Encoding.UTF8.GetPreamble().Concat(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetBytes("Test")).ToArray(), + defaultEncoding: Encoding.GetEncoding(1254), + expectedEncoding: Encoding.UTF8); } [Fact] public async Task TestCreateFromTemporaryStorage() { - using (var workspace = new TestWorkspace()) - { - var textFactory = CreateMockTextFactoryService(workspace); - var temporaryStorageService = new TemporaryStorageServiceFactory.TemporaryStorageService(textFactory); + var textFactory = CreateMockTextFactoryService(); + var temporaryStorageService = new TemporaryStorageServiceFactory.TemporaryStorageService(textFactory); - var text = Text.SourceText.From("Hello, World!"); + var text = Text.SourceText.From("Hello, World!"); - // Create a temporary storage location - using (var temporaryStorage = temporaryStorageService.CreateTemporaryTextStorage(System.Threading.CancellationToken.None)) - { - // Write text into it - await temporaryStorage.WriteTextAsync(text); + // Create a temporary storage location + using (var temporaryStorage = temporaryStorageService.CreateTemporaryTextStorage(System.Threading.CancellationToken.None)) + { + // Write text into it + await temporaryStorage.WriteTextAsync(text); - // Read text back from it - var text2 = await temporaryStorage.ReadTextAsync(); + // Read text back from it + var text2 = await temporaryStorage.ReadTextAsync(); - Assert.NotSame(text, text2); - Assert.Equal(text.ToString(), text2.ToString()); - Assert.Equal(text2.Encoding, null); - } + Assert.NotSame(text, text2); + Assert.Equal(text.ToString(), text2.ToString()); + Assert.Equal(text2.Encoding, null); } } [Fact] public async Task TestCreateFromTemporaryStorageWithEncoding() { - using (var workspace = new TestWorkspace()) - { - var textFactory = CreateMockTextFactoryService(workspace); - var temporaryStorageService = new TemporaryStorageServiceFactory.TemporaryStorageService(textFactory); + var textFactory = CreateMockTextFactoryService(); + var temporaryStorageService = new TemporaryStorageServiceFactory.TemporaryStorageService(textFactory); - var text = Text.SourceText.From("Hello, World!", Encoding.ASCII); + var text = Text.SourceText.From("Hello, World!", Encoding.ASCII); - // Create a temporary storage location - using (var temporaryStorage = temporaryStorageService.CreateTemporaryTextStorage(System.Threading.CancellationToken.None)) - { - // Write text into it - await temporaryStorage.WriteTextAsync(text); + // Create a temporary storage location + using (var temporaryStorage = temporaryStorageService.CreateTemporaryTextStorage(System.Threading.CancellationToken.None)) + { + // Write text into it + await temporaryStorage.WriteTextAsync(text); - // Read text back from it - var text2 = await temporaryStorage.ReadTextAsync(); + // Read text back from it + var text2 = await temporaryStorage.ReadTextAsync(); - Assert.NotSame(text, text2); - Assert.Equal(text.ToString(), text2.ToString()); - Assert.Equal(text2.Encoding, Encoding.ASCII); - } + Assert.NotSame(text, text2); + Assert.Equal(text.ToString(), text2.ToString()); + Assert.Equal(text2.Encoding, Encoding.ASCII); } } - private EditorTextFactoryService CreateMockTextFactoryService(Workspace workspace) + private EditorTextFactoryService CreateMockTextFactoryService() { var mockTextBufferFactoryService = new Mock(); mockTextBufferFactoryService @@ -143,17 +123,24 @@ private EditorTextFactoryService CreateMockTextFactoryService(Workspace workspac return mockTextBuffer.Object; }); - return new EditorTextFactoryService(workspace, mockTextBufferFactoryService.Object, new Mock().Object); + return new EditorTextFactoryService(new FakeTextBufferCloneService(), mockTextBufferFactoryService.Object, new Mock().Object); } - private void TestCreateTextInferredEncoding(Workspace workspace, byte[] bytes, Encoding defaultEncoding, Encoding expectedEncoding) + private void TestCreateTextInferredEncoding(byte[] bytes, Encoding defaultEncoding, Encoding expectedEncoding) { - var factory = CreateMockTextFactoryService(workspace); + var factory = CreateMockTextFactoryService(); using (var stream = new MemoryStream(bytes)) { var text = factory.CreateText(stream, defaultEncoding); Assert.Equal(expectedEncoding, text.Encoding); } } + + private class FakeTextBufferCloneService : ITextBufferCloneService + { + public ITextBuffer Clone(SnapshotSpan span) => throw new NotImplementedException(); + + public ITextBuffer Clone(ITextImage textImage) => throw new NotImplementedException(); + } } } diff --git a/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs b/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs index e12c19e793d..08935142713 100644 --- a/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs +++ b/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs @@ -24,36 +24,36 @@ private class SnapshotSourceText : SourceText { private static readonly Func s_textLog = (v1, v2) => string.Format("FullRange : from {0} to {1}", v1, v2); - private readonly Workspace _workspace; - /// /// The backing the SourceText instance /// public readonly ITextImage TextImage; + private readonly ITextBufferCloneService _textBufferCloneServiceOpt; + private readonly Encoding _encodingOpt; private readonly TextBufferContainer _containerOpt; - private SnapshotSourceText(Workspace workspace, ITextSnapshot editorSnapshot) : - this(workspace, editorSnapshot, TextBufferContainer.From(workspace, editorSnapshot.TextBuffer)) + private SnapshotSourceText(ITextBufferCloneService textBufferCloneServiceOpt, ITextSnapshot editorSnapshot) : + this(textBufferCloneServiceOpt, editorSnapshot, TextBufferContainer.From(editorSnapshot.TextBuffer)) { } - private SnapshotSourceText(Workspace workspace, ITextSnapshot editorSnapshot, TextBufferContainer container) + private SnapshotSourceText(ITextBufferCloneService textBufferCloneServiceOpt, ITextSnapshot editorSnapshot, TextBufferContainer container) { Contract.ThrowIfNull(editorSnapshot); - _workspace = workspace; + _textBufferCloneServiceOpt = textBufferCloneServiceOpt; this.TextImage = RecordReverseMapAndGetImage(editorSnapshot); _encodingOpt = editorSnapshot.TextBuffer.GetEncodingOrUTF8(); _containerOpt = container; } - public SnapshotSourceText(Workspace workspace, ITextImage textImage, Encoding encodingOpt, TextBufferContainer containerOpt) + public SnapshotSourceText(ITextBufferCloneService textBufferCloneServiceOpt, ITextImage textImage, Encoding encodingOpt, TextBufferContainer containerOpt) { Contract.ThrowIfNull(textImage); - _workspace = workspace; + _textBufferCloneServiceOpt = textBufferCloneServiceOpt; this.TextImage = textImage; _encodingOpt = encodingOpt; _containerOpt = containerOpt; @@ -70,9 +70,7 @@ public SnapshotSourceText(Workspace workspace, ITextImage textImage, Encoding en /// private static readonly ConditionalWeakTable> s_textImageToEditorSnapshotMap = new ConditionalWeakTable>(); - internal Workspace Workspace => _workspace; - - public static SourceText From(Workspace workspace, ITextSnapshot editorSnapshot) + public static SourceText From(ITextBufferCloneService textBufferCloneServiceOpt, ITextSnapshot editorSnapshot) { if (editorSnapshot == null) { @@ -82,8 +80,8 @@ public static SourceText From(Workspace workspace, ITextSnapshot editorSnapshot) if (!s_textSnapshotMap.TryGetValue(editorSnapshot, out var snapshot)) { // Avoid capturing `workspace` on the fast path - var tempWorkspace = workspace; - snapshot = s_textSnapshotMap.GetValue(editorSnapshot, s => new SnapshotSourceText(tempWorkspace, s)); + var tempTextBufferCloneServiceOpt = textBufferCloneServiceOpt; + snapshot = s_textSnapshotMap.GetValue(editorSnapshot, s => new SnapshotSourceText(tempTextBufferCloneServiceOpt, s)); } return snapshot; @@ -92,7 +90,7 @@ public static SourceText From(Workspace workspace, ITextSnapshot editorSnapshot) /// /// This only exist to break circular dependency on creating buffer. nobody except extension itself should use it /// - internal static SourceText From(Workspace workspace, ITextSnapshot editorSnapshot, TextBufferContainer container) + internal static SourceText From(ITextBufferCloneService textBufferCloneServiceOpt, ITextSnapshot editorSnapshot, TextBufferContainer container) { if (editorSnapshot == null) { @@ -100,7 +98,7 @@ internal static SourceText From(Workspace workspace, ITextSnapshot editorSnapsho } Contract.ThrowIfFalse(editorSnapshot.TextBuffer == container.GetTextBuffer()); - return s_textSnapshotMap.GetValue(editorSnapshot, s => new SnapshotSourceText(workspace, s, container)); + return s_textSnapshotMap.GetValue(editorSnapshot, s => new SnapshotSourceText(textBufferCloneServiceOpt, s, container)); } public override Encoding Encoding @@ -111,14 +109,6 @@ public override Encoding Encoding public ITextSnapshot TryFindEditorSnapshot() => TryFindEditorSnapshot(this.TextImage); - protected ITextBufferCloneService TextBufferFactory - { - get - { - return _workspace?.Services.GetService(); - } - } - public override SourceTextContainer Container { get @@ -213,7 +203,7 @@ public override SourceText WithChanges(IEnumerable changes) } // check whether we can use text buffer factory - var factory = TextBufferFactory; + var factory = _textBufferCloneServiceOpt; if (factory == null) { // if we can't get the factory, use the default implementation @@ -236,7 +226,7 @@ public override SourceText WithChanges(IEnumerable changes) } return new ChangedSourceText( - workspace: _workspace, + textBufferCloneServiceOpt: _textBufferCloneServiceOpt, baseText: this, baseSnapshot: ((ITextSnapshot2)baseSnapshot).TextImage, currentSnapshot: ((ITextSnapshot2)buffer.CurrentSnapshot).TextImage); @@ -282,8 +272,8 @@ private static ITextSnapshot TryFindEditorSnapshot(ITextImage textImage) /// internal sealed class ClosedSnapshotSourceText : SnapshotSourceText { - public ClosedSnapshotSourceText(Workspace workspace, ITextImage textImage, Encoding encodingOpt) - : base(workspace, textImage, encodingOpt, containerOpt: null) + public ClosedSnapshotSourceText(ITextBufferCloneService textBufferCloneServiceOpt, ITextImage textImage, Encoding encodingOpt) + : base(textBufferCloneServiceOpt, textImage, encodingOpt, containerOpt: null) { } } @@ -296,8 +286,8 @@ private class ChangedSourceText : SnapshotSourceText private readonly SnapshotSourceText _baseText; private readonly ITextImage _baseSnapshot; - public ChangedSourceText(Workspace workspace, SnapshotSourceText baseText, ITextImage baseSnapshot, ITextImage currentSnapshot) - : base(workspace, currentSnapshot, baseText.Encoding, containerOpt: null) + public ChangedSourceText(ITextBufferCloneService textBufferCloneServiceOpt, SnapshotSourceText baseText, ITextImage baseSnapshot, ITextImage currentSnapshot) + : base(textBufferCloneServiceOpt, currentSnapshot, baseText.Encoding, containerOpt: null) { _baseText = baseText; _baseSnapshot = baseSnapshot; diff --git a/src/EditorFeatures/Text/Extensions.TextBufferContainer.cs b/src/EditorFeatures/Text/Extensions.TextBufferContainer.cs index fd8110e3fad..bcf2caa7416 100644 --- a/src/EditorFeatures/Text/Extensions.TextBufferContainer.cs +++ b/src/EditorFeatures/Text/Extensions.TextBufferContainer.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using System.Linq; using System.Runtime.CompilerServices; -using System.Text; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; @@ -19,49 +18,39 @@ internal class TextBufferContainer : SourceTextContainer { private readonly WeakReference _weakEditorBuffer; private readonly object _gate = new object(); + private readonly ITextBufferCloneService _textBufferCloneServiceOpt; private event EventHandler EtextChanged; private SourceText _currentText; - private TextBufferContainer(Workspace workspace, ITextBuffer editorBuffer) + private TextBufferContainer(ITextBuffer editorBuffer) { Contract.ThrowIfNull(editorBuffer); _weakEditorBuffer = new WeakReference(editorBuffer); - _currentText = SnapshotSourceText.From(workspace, editorBuffer.CurrentSnapshot, this); + editorBuffer.Properties.TryGetProperty(typeof(ITextBufferCloneService), out _textBufferCloneServiceOpt); + _currentText = SnapshotSourceText.From(_textBufferCloneServiceOpt, editorBuffer.CurrentSnapshot, this); } /// /// A weak map of all Editor ITextBuffers and their associated SourceTextContainer /// private static readonly ConditionalWeakTable s_textContainerMap = new ConditionalWeakTable(); - private static readonly ConditionalWeakTable.CreateValueCallback s_createContainerWithoutWorkspaceCallback = CreateContainer; + private static readonly ConditionalWeakTable.CreateValueCallback s_createContainerCallback = CreateContainer; - public static TextBufferContainer From(Workspace workspace, ITextBuffer buffer) + public static TextBufferContainer From(ITextBuffer buffer) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } - if (workspace is null) - { - return s_textContainerMap.GetValue(buffer, s_createContainerWithoutWorkspaceCallback); - } - - if (!s_textContainerMap.TryGetValue(buffer, out var container)) - { - // Avoid capturing 'workspace' on the fast path - var workspaceCopy = workspace; - container = s_textContainerMap.GetValue(buffer, key => new TextBufferContainer(workspace, key)); - } - - return container; + return s_textContainerMap.GetValue(buffer, s_createContainerCallback); } private static TextBufferContainer CreateContainer(ITextBuffer editorBuffer) { - return new TextBufferContainer(workspace: null, editorBuffer); + return new TextBufferContainer(editorBuffer); } public ITextBuffer TryFindEditorTextBuffer() @@ -122,7 +111,7 @@ private void OnTextContentChanged(object sender, TextContentChangedEventArgs arg // this should convert given editor snapshots to roslyn forked snapshots var oldText = (SnapshotSourceText)args.Before.AsText(); - var newText = SnapshotSourceText.From(oldText.Workspace, args.After); + var newText = SnapshotSourceText.From(_textBufferCloneServiceOpt, args.After); _currentText = newText; var changes = ImmutableArray.CreateRange(args.Changes.Select(c => new TextChangeRange(new TextSpan(c.OldSpan.Start, c.OldSpan.Length), c.NewLength))); diff --git a/src/EditorFeatures/Text/Extensions.cs b/src/EditorFeatures/Text/Extensions.cs index 0ab197ebf56..9d3df1ab9db 100644 --- a/src/EditorFeatures/Text/Extensions.cs +++ b/src/EditorFeatures/Text/Extensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Text public static partial class Extensions { public static SourceTextContainer AsTextContainer(this ITextBuffer buffer) - => TextBufferContainer.From(workspace: null, buffer); + => TextBufferContainer.From(buffer); public static ITextBuffer GetTextBuffer(this SourceTextContainer textContainer) => TryGetTextBuffer(textContainer) ?? throw new ArgumentException(TextEditorResources.textContainer_is_not_a_SourceTextContainer_that_was_created_from_an_ITextBuffer, nameof(textContainer)); @@ -37,10 +37,10 @@ internal static TextLine AsTextLine(this ITextSnapshotLine line) => line.Snapshot.AsText().Lines[line.LineNumber]; public static SourceText AsText(this ITextSnapshot textSnapshot) - => SnapshotSourceText.From(workspace: null, textSnapshot); + => SnapshotSourceText.From(textBufferCloneServiceOpt: null, textSnapshot); - internal static SourceText AsRoslynText(this ITextSnapshot textSnapshot, Workspace workspace, Encoding encoding) - => new SnapshotSourceText.ClosedSnapshotSourceText(workspace, ((ITextSnapshot2)textSnapshot).TextImage, encoding); + internal static SourceText AsRoslynText(this ITextSnapshot textSnapshot, ITextBufferCloneService textBufferCloneServiceOpt, Encoding encoding) + => new SnapshotSourceText.ClosedSnapshotSourceText(textBufferCloneServiceOpt, ((ITextSnapshot2)textSnapshot).TextImage, encoding); /// /// Gets the workspace corresponding to the text buffer. diff --git a/src/EditorFeatures/Text/Implementation/TextBufferFactoryService/TextBufferCloneServiceFactory.cs b/src/EditorFeatures/Text/Implementation/TextBufferFactoryService/TextBufferCloneServiceFactory.cs index 91f1d100865..02cca1ba0b1 100644 --- a/src/EditorFeatures/Text/Implementation/TextBufferFactoryService/TextBufferCloneServiceFactory.cs +++ b/src/EditorFeatures/Text/Implementation/TextBufferFactoryService/TextBufferCloneServiceFactory.cs @@ -16,9 +16,9 @@ internal class TextBufferCloneServiceFactory : IWorkspaceServiceFactory [ImportingConstructor] public TextBufferCloneServiceFactory( ITextBufferFactoryService textBufferFactoryService, - IContentTypeRegistryService contentTypeRegistry) + IContentTypeRegistryService contentTypeRegistryService) { - _singleton = new TextBufferCloneService((ITextBufferFactoryService3)textBufferFactoryService, contentTypeRegistry.UnknownContentType); + _singleton = new TextBufferCloneService((ITextBufferFactoryService3)textBufferFactoryService, contentTypeRegistryService); } public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) @@ -26,15 +26,17 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) return _singleton; } + [Export(typeof(ITextBufferCloneService)), Shared] private class TextBufferCloneService : ITextBufferCloneService { private readonly ITextBufferFactoryService3 _textBufferFactoryService; private readonly IContentType _unknownContentType; - public TextBufferCloneService(ITextBufferFactoryService3 textBufferFactoryService, IContentType unknownContentType) + [ImportingConstructor] + public TextBufferCloneService(ITextBufferFactoryService3 textBufferFactoryService, IContentTypeRegistryService contentTypeRegistryService) { _textBufferFactoryService = textBufferFactoryService; - _unknownContentType = unknownContentType; + _unknownContentType = contentTypeRegistryService.UnknownContentType; } public ITextBuffer Clone(SnapshotSpan span) diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs index f6b4dc37ff0..8b9636cb13a 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -22,6 +22,7 @@ using Microsoft.VisualStudio.LanguageServices.Utilities; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; using Roslyn.Utilities; using VSLangProj; using VSLangProj140; @@ -37,6 +38,9 @@ internal abstract partial class VisualStudioWorkspaceImpl : VisualStudioWorkspac private static readonly IntPtr s_docDataExisting_Unknown = new IntPtr(-1); private const string AppCodeFolderName = "App_Code"; + private readonly ITextBufferFactoryService _textBufferFactoryService; + private readonly ITextBufferCloneService _textBufferCloneService; + // document worker coordinator private ISolutionCrawlerRegistrationService _registrationService; @@ -58,6 +62,9 @@ public VisualStudioWorkspaceImpl(ExportProvider exportProvider) MefV1HostServices.Create(exportProvider), backgroundWork: WorkspaceBackgroundWork.ParseAndCompile) { + _textBufferCloneService = exportProvider.GetExportedValue(); + _textBufferFactoryService = exportProvider.GetExportedValue(); + _textBufferFactoryService.TextBufferCreated += AddTextBufferCloneServiceToBuffer; exportProvider.GetExportedValue().Register(this); } @@ -213,6 +220,11 @@ protected override bool CanApplyParseOptionChange(ParseOptions oldOptions, Parse return newOptions == updated; } + private void AddTextBufferCloneServiceToBuffer(object sender, TextBufferCreatedEventArgs e) + { + e.TextBuffer.Properties.AddProperty(typeof(ITextBufferCloneService), _textBufferCloneService); + } + public override bool CanApplyChange(ApplyChangesKind feature) { switch (feature) @@ -1081,6 +1093,11 @@ internal void StopSolutionCrawler() protected override void Dispose(bool finalize) { + if (!finalize) + { + _textBufferFactoryService.TextBufferCreated -= AddTextBufferCloneServiceToBuffer; + } + // workspace is going away. unregister this workspace from work coordinator StopSolutionCrawler(); -- GitLab