diff --git a/docs/Breaking API Changes.md b/docs/Breaking API Changes.md index 72b8a95c02c85..cfdf98d31c450 100644 --- a/docs/Breaking API Changes.md +++ b/docs/Breaking API Changes.md @@ -64,10 +64,3 @@ Roslyn does not support implementing completion for arbitrary languages. ### `Microsoft.CodeAnalysis.CodeStyle.NotificationOption` is now immutable All property setters now throw an exception. - -# Version 4.4.0 - -`Workspace.OnWorkspaceFailed` is no longer called when an error occurs while reading source file content from disk. - -The `Workspace` and `DocumentId` parameters of `TextLoader.LoadTextAndVersionAsync(Workspace, DocumentId, CancellationToken)` are deprecated. -The method now receives an instance of an immutable empty `Workspace` with default workspace services, and a fake `DocumentId`. diff --git a/eng/config/BannedSymbols.txt b/eng/config/BannedSymbols.txt index c3d693297ea10..054e6fb4c538a 100644 --- a/eng/config/BannedSymbols.txt +++ b/eng/config/BannedSymbols.txt @@ -34,5 +34,4 @@ M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAna M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAnalysis.Document,Microsoft.CodeAnalysis.SyntaxAnnotation,Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use overload that takes SimplifierOptions M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAnalysis.Document,Microsoft.CodeAnalysis.Text.TextSpan,Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use overload that takes SimplifierOptions M:Microsoft.CodeAnalysis.Simplification.Simplifier.ReduceAsync(Microsoft.CodeAnalysis.Document,System.Collections.Generic.IEnumerable{Microsoft.CodeAnalysis.Text.TextSpan},Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use overload that takes SimplifierOptions -M:Microsoft.CodeAnalysis.Editing.SyntaxEditor.#ctor(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.Host.HostWorkspaceServices); Use overload that takes HostSolutionServices instead -M:Microsoft.CodeAnalysis.FileTextLoader.#ctor(System.String,System.Text.Encoding); use WorkspaceFileTextLoader \ No newline at end of file +M:Microsoft.CodeAnalysis.Editing.SyntaxEditor.#ctor(Microsoft.CodeAnalysis.SyntaxNode,Microsoft.CodeAnalysis.Host.HostWorkspaceServices); Use overload that takes HostSolutionServices instead \ No newline at end of file diff --git a/src/EditorFeatures/Core/Interactive/InteractiveSession.cs b/src/EditorFeatures/Core/Interactive/InteractiveSession.cs index 0235a7801a086..26cad7274c8fa 100644 --- a/src/EditorFeatures/Core/Interactive/InteractiveSession.cs +++ b/src/EditorFeatures/Core/Interactive/InteractiveSession.cs @@ -233,7 +233,7 @@ private void AddSubmissionProjectNoLock(ITextBuffer submissionBuffer, string lan solution = initProject.Solution.AddDocument( DocumentId.CreateNewId(initializationScriptProjectId, debugName: initializationScriptPath), Path.GetFileName(initializationScriptPath), - new WorkspaceFileTextLoader(solution.Services, initializationScriptPath, defaultEncoding: null)); + new FileTextLoader(initializationScriptPath, defaultEncoding: null)); } var newSubmissionProject = CreateSubmissionProjectNoLock(solution, _currentSubmissionProjectId, _lastSuccessfulSubmissionProjectId, languageName, imports, references); diff --git a/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs b/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs index f3f931c48bbb7..287d60b9f9ead 100644 --- a/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs +++ b/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces { [ExportWorkspaceService(typeof(ITextFactoryService), ServiceLayer.Editor), Shared] - internal sealed class EditorTextFactoryService : ITextFactoryService + internal class EditorTextFactoryService : ITextFactoryService { private readonly ITextBufferCloneService _textBufferCloneService; private readonly ITextBufferFactoryService _textBufferFactory; @@ -37,7 +37,7 @@ public EditorTextFactoryService( private static readonly Encoding s_throwingUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); - public SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken) + public SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken = default) { // this API is for a case where user wants us to figure out encoding from the given stream. // if defaultEncoding is given, we will use it if we couldn't figure out encoding used in the stream ourselves. @@ -70,7 +70,7 @@ public SourceText CreateText(Stream stream, Encoding? defaultEncoding, Cancellat } } - public SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken) + public SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken = default) { // this API is for a case where user just wants to create a source text with explicit encoding. var buffer = CreateTextBuffer(reader); diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index f4d44fd95b6a0..6cff7e8ed9396 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -373,9 +373,9 @@ private static DocumentInfo CreateDesignTimeOnlyDocument(ProjectId projectId, st internal sealed class FailingTextLoader : TextLoader { - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) { - Assert.True(false, $"Content of document should never be loaded"); + Assert.True(false, $"Content of document {documentId} should never be loaded"); throw ExceptionUtilities.Unreachable; } } @@ -497,28 +497,28 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, encodingA), + loader: new FileTextLoader(sourceFileA.Path, encodingA), filePath: sourceFileA.Path)); var documentIdB = DocumentId.CreateNewId(projectP.Id, debugName: "B"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdB, name: "B", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB), + loader: new FileTextLoader(sourceFileB.Path, encodingB), filePath: sourceFileB.Path)); var documentIdC = DocumentId.CreateNewId(projectP.Id, debugName: "C"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdC, name: "C", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileC.Path, encodingC), + loader: new FileTextLoader(sourceFileC.Path, encodingC), filePath: sourceFileC.Path)); var documentIdE = DocumentId.CreateNewId(projectP.Id, debugName: "E"); solution = solution.AddDocument(DocumentInfo.Create( id: documentIdE, name: "E", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileE.Path, encodingE), + loader: new FileTextLoader(sourceFileE.Path, encodingE), filePath: sourceFileE.Path)); // check that are testing documents whose hash algorithm does not match the PDB (but the hash itself does): @@ -558,7 +558,7 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume // change content of B on disk again: sourceFileB.WriteAllText(sourceB3, encodingB); - solution = solution.WithDocumentTextLoader(documentIdB, new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB), PreservationMode.PreserveValue); + solution = solution.WithDocumentTextLoader(documentIdB, new FileTextLoader(sourceFileB.Path, encodingB), PreservationMode.PreserveValue); EnterBreakState(debuggingSession); @@ -4387,7 +4387,7 @@ public async Task MultiSession() solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), + loader: new FileTextLoader(sourceFileA.Path, Encoding.UTF8), filePath: sourceFileA.Path)); var tasks = Enumerable.Range(0, 10).Select(async i => @@ -4473,7 +4473,7 @@ public async Task WatchHotReloadServiceTest() solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), + loader: new FileTextLoader(sourceFileA.Path, Encoding.UTF8), filePath: sourceFileA.Path)); var hotReload = new WatchHotReloadService(workspace.Services, ImmutableArray.Create("Baseline", "AddDefinitionToExistingType", "NewTypeDefinition")); @@ -4540,7 +4540,7 @@ public async Task UnitTestingHotReloadServiceTest() solution = solution.AddDocument(DocumentInfo.Create( id: documentIdA, name: "A", - loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, Encoding.UTF8), + loader: new FileTextLoader(sourceFileA.Path, Encoding.UTF8), filePath: sourceFileA.Path)); var hotReload = new UnitTestingHotReloadService(workspace.Services); diff --git a/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs b/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs index 6d8daec08ab71..152ecfa34bf32 100644 --- a/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs +++ b/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using System.Text; -using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Implementation.Workspaces; using Microsoft.CodeAnalysis.Host; @@ -120,7 +119,7 @@ public async Task TestCreateFromTemporaryStorageWithEncoding() private static void TestCreateTextInferredEncoding(ITextFactoryService textFactoryService, byte[] bytes, Encoding? defaultEncoding, Encoding expectedEncoding) { using var stream = new MemoryStream(bytes); - var text = textFactoryService.CreateText(stream, defaultEncoding, CancellationToken.None); + var text = textFactoryService.CreateText(stream, defaultEncoding); Assert.Equal(expectedEncoding, text.Encoding); } } diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs index af07e370bad4b..5ac6703cd3872 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs @@ -209,7 +209,7 @@ internal TestDocumentLoader(TestHostDocument hostDocument, string text) _text = text; } - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(SourceText.From(_text), VersionStamp.Create(), _hostDocument.FilePath)); } diff --git a/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs index 62dfa0e2d7122..9a04b4b293bda 100644 --- a/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs @@ -13,7 +13,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.DecompiledSource; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PdbSourceDocument; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -279,7 +278,7 @@ private bool RemoveDocumentFromWorkspace(Workspace workspace, MetadataAsSourceGe var documentId = _openedDocumentIds.GetValueOrDefault(fileInfo); Contract.ThrowIfNull(documentId); - workspace.OnDocumentClosed(documentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, fileInfo.TemporaryFilePath, MetadataAsSourceGeneratedFileInfo.Encoding)); + workspace.OnDocumentClosed(documentId, new FileTextLoader(fileInfo.TemporaryFilePath, MetadataAsSourceGeneratedFileInfo.Encoding)); workspace.OnProjectRemoved(documentId.ProjectId); _openedDocumentIds = _openedDocumentIds.RemoveKey(fileInfo); diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs index ebf796543ad33..5729d27c551f0 100644 --- a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs +++ b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs @@ -82,7 +82,7 @@ public Tuple GetProjectInfoAndDocumentId(Workspace work generatedDocumentId, Path.GetFileName(TemporaryFilePath), filePath: TemporaryFilePath, - loader: loadFileFromDisk ? new WorkspaceFileTextLoader(workspace.Services.SolutionServices, TemporaryFilePath, Encoding) : null); + loader: loadFileFromDisk ? new FileTextLoader(TemporaryFilePath, Encoding) : null); var projectInfo = ProjectInfo.Create( projectId, diff --git a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs index 3d7619e3a25c3..ce26e8be52705 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs @@ -338,7 +338,7 @@ public bool TryRemoveDocumentFromWorkspace(Workspace workspace, string filePath) { if (_fileToDocumentInfoMap.TryGetValue(filePath, out var info)) { - workspace.OnDocumentClosed(info.DocumentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, filePath, info.Encoding)); + workspace.OnDocumentClosed(info.DocumentId, new FileTextLoader(filePath, info.Encoding)); return true; } diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs index 1b3f604429fc0..1cc824c529c35 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs @@ -93,7 +93,7 @@ public void TryRemoveMiscellaneousDocument(Uri uri) } } - private sealed class SourceTextLoader : TextLoader + private class SourceTextLoader : TextLoader { private readonly SourceText _sourceText; private readonly string _fileUri; @@ -104,7 +104,7 @@ public SourceTextLoader(SourceText sourceText, string fileUri) _fileUri = fileUri; } - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(_sourceText, VersionStamp.Create(), _fileUri)); } } diff --git a/src/Features/Lsif/Generator/CompilerInvocation.cs b/src/Features/Lsif/Generator/CompilerInvocation.cs index 843415ecff98f..17451f67d4886 100644 --- a/src/Features/Lsif/Generator/CompilerInvocation.cs +++ b/src/Features/Lsif/Generator/CompilerInvocation.cs @@ -119,7 +119,7 @@ DocumentInfo CreateDocumentInfo(string unmappedPath) DocumentId.CreateNewId(projectId, mappedPath), name: mappedPath, filePath: mappedPath, - loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding)); + loader: new FileTextLoader(mappedPath, parsedCommandLine.Encoding)); } } diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs index 47a498f3e13d3..66dd83152b58e 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; @@ -326,8 +325,7 @@ private async Task FormatDocumentCreatedFromTemplateAsync(IVsHierarchy hierarchy var documentId = DocumentId.CreateNewId(projectToAddTo.Id); - var fileLoader = new WorkspaceFileTextLoader(projectToAddTo.Solution.Services, filePath, defaultEncoding: null); - var forkedSolution = projectToAddTo.Solution.AddDocument(DocumentInfo.Create(documentId, filePath, loader: fileLoader, filePath: filePath)); + var forkedSolution = projectToAddTo.Solution.AddDocument(DocumentInfo.Create(documentId, filePath, loader: new FileTextLoader(filePath, defaultEncoding: null), filePath: filePath)); var addedDocument = forkedSolution.GetRequiredDocument(documentId); var globalOptions = _componentModel.GetService(); diff --git a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs index a3ddb4882dcca..d9d7f207046f5 100644 --- a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs +++ b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs @@ -45,17 +45,17 @@ public void CloseDocument(TextDocument document, SourceText text) } } - private sealed class PreviewTextLoader : TextLoader + private class PreviewTextLoader : TextLoader { private readonly SourceText _text; internal PreviewTextLoader(SourceText documentText) => _text = documentText; - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) - => Task.FromResult(LoadTextAndVersionSynchronously(cancellationToken)); + public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + => Task.FromResult(LoadTextAndVersionSynchronously(workspace, documentId, cancellationToken)); - internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) => TextAndVersion.Create(_text, VersionStamp.Create()); } } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs index 8f42af1a91d43..acde4682b3a7c 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs @@ -292,8 +292,7 @@ private ProjectInfo CreateProjectInfoForDocument(string filePath) var languageInformation = TryGetLanguageInformation(filePath); Contract.ThrowIfNull(languageInformation); - var loader = new WorkspaceFileTextLoader(Services.SolutionServices, filePath, defaultEncoding: null); - return MiscellaneousFileUtilities.CreateMiscellaneousProjectInfoForDocument(filePath, loader, languageInformation, Services.SolutionServices, _metadataReferences); + return MiscellaneousFileUtilities.CreateMiscellaneousProjectInfoForDocument(filePath, new FileTextLoader(filePath, defaultEncoding: null), languageInformation, Services.SolutionServices, _metadataReferences); } private void DetachFromDocument(string moniker) @@ -309,7 +308,7 @@ private void DetachFromDocument(string moniker) var document = this.CurrentSolution.GetProject(projectIdAndContainer.projectId).Documents.Single(); // We must close the document prior to deleting the project - OnDocumentClosed(document.Id, new WorkspaceFileTextLoader(Services.SolutionServices, document.FilePath, defaultEncoding: null)); + OnDocumentClosed(document.Id, new FileTextLoader(document.FilePath, defaultEncoding: null)); OnProjectRemoved(document.Project.Id); _monikersToProjectIdAndContainer.Remove(moniker); diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs index bc8f5db48e61a..b3c174ecbce2d 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs @@ -92,7 +92,7 @@ public DocumentId AddFile(string fullPath, SourceCodeKind sourceCodeKind, Immuta } var documentId = DocumentId.CreateNewId(_project.Id, fullPath); - var textLoader = new WorkspaceFileTextLoader(_project._workspace.Services.SolutionServices, fullPath, defaultEncoding: null); + var textLoader = new FileTextLoader(fullPath, defaultEncoding: null); var documentInfo = DocumentInfo.Create( documentId, FileNameUtilities.GetFileName(fullPath), @@ -401,7 +401,7 @@ public async ValueTask ProcessRegularFileChangesAsync(ImmutableSegmentedList d.Id == documentId)) { - documentsToChange.Add((documentId, new WorkspaceFileTextLoader(_project._workspace.Services.SolutionServices, filePath, defaultEncoding: null))); + documentsToChange.Add((documentId, new FileTextLoader(filePath, defaultEncoding: null))); } } } @@ -612,7 +612,7 @@ public SourceTextLoader(SourceTextContainer textContainer, string? filePath) _filePath = filePath; } - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(_textContainer.CurrentText, VersionStamp.Create(), _filePath)); } } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs index 5e5098f1e4fd6..9b28ed151b3e5 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.ComponentModelHost; @@ -318,20 +317,18 @@ private void TryClosingDocumentsForMoniker(string moniker) { if (w.IsDocumentOpen(documentId) && !_workspace._documentsNotFromFiles.Contains(documentId)) { - var loader = new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null); - if (w.CurrentSolution.ContainsDocument(documentId)) { - w.OnDocumentClosed(documentId, loader); + w.OnDocumentClosed(documentId, new FileTextLoader(moniker, defaultEncoding: null)); } else if (w.CurrentSolution.ContainsAdditionalDocument(documentId)) { - w.OnAdditionalDocumentClosed(documentId, loader); + w.OnAdditionalDocumentClosed(documentId, new FileTextLoader(moniker, defaultEncoding: null)); } else { Debug.Assert(w.CurrentSolution.ContainsAnalyzerConfigDocument(documentId)); - w.OnAnalyzerConfigDocumentClosed(documentId, loader); + w.OnAnalyzerConfigDocumentClosed(documentId, new FileTextLoader(moniker, defaultEncoding: null)); } } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/FileTextLoaderNoException.cs similarity index 71% rename from src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs rename to src/VisualStudio/LiveShare/Impl/Client/Projects/FileTextLoaderNoException.cs index 984b7d28617a5..5b6a7551133aa 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/FileTextLoaderNoException.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Client.Projects @@ -18,21 +17,20 @@ namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Client.Projects /// This is a FileTextLoader which no-ops if the file is not available on disk. This is the common case for /// Cascade and throwing exceptions slows down GetText operations significantly enough to have visible UX impact. /// - internal sealed class WorkspaceFileTextLoaderNoException : WorkspaceFileTextLoader + internal class FileTextLoaderNoException : FileTextLoader { - public WorkspaceFileTextLoaderNoException(SolutionServices services, string path, Encoding defaultEncoding) - : base(services, path, defaultEncoding) + public FileTextLoaderNoException(string path, Encoding defaultEncoding) : base(path, defaultEncoding) { } - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(CodeAnalysis.Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) { if (!File.Exists(Path)) { return Task.FromResult(TextAndVersion.Create(SourceText.From(""), VersionStamp.Create())); } - return base.LoadTextAndVersionAsync(cancellationToken); + return base.LoadTextAndVersionAsync(workspace, documentId, cancellationToken); } } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs index 1c97ff18ce64f..60b213a0c8066 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs @@ -51,5 +51,29 @@ public async Task> GetRemoteProjectInfosAsync(C return projectInfos; } + + public static ProjectInfo CreateProjectInfo(string projectName, string language, ImmutableArray files) + { + var projectId = ProjectId.CreateNewId(); + var docInfos = ImmutableArray.CreateBuilder(); + + foreach (var file in files) + { + var fileName = Path.GetFileNameWithoutExtension(file); + var docInfo = DocumentInfo.Create(DocumentId.CreateNewId(projectId), + fileName, + filePath: file, + loader: new FileTextLoaderNoException(file, null)); + docInfos.Add(docInfo); + } + + return ProjectInfo.Create( + projectId, + VersionStamp.Create(), + projectName, + projectName, + language, + documents: docInfos.ToImmutable()); + } } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs index 9372dc858b60c..fd319980dbf00 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs @@ -12,7 +12,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.LiveShare.LanguageServices; @@ -76,14 +75,14 @@ public async Task> GetRemoteProjectInfosAsync(Cancel .Where(f => !_secondaryBufferFileExtensions.Any(ext => f.LocalPath.EndsWith(ext))) .Select(f => lspClient.ProtocolConverter.FromProtocolUriAsync(f, false, cancellationToken)); var files = await Task.WhenAll(filesTasks).ConfigureAwait(false); - var projectInfo = CreateProjectInfo(project.Name, project.Language, files.Select(f => f.LocalPath).ToImmutableArray(), _remoteLanguageServiceWorkspace.Services.SolutionServices); + var projectInfo = CreateProjectInfo(project.Name, project.Language, files.Select(f => f.LocalPath).ToImmutableArray()); projectInfos.Add(projectInfo); } return projectInfos.ToImmutableArray(); } - private static ProjectInfo CreateProjectInfo(string projectName, string language, ImmutableArray files, SolutionServices services) + private static ProjectInfo CreateProjectInfo(string projectName, string language, ImmutableArray files) { var projectId = ProjectId.CreateNewId(); var docInfos = ImmutableArray.CreateBuilder(); @@ -94,7 +93,7 @@ private static ProjectInfo CreateProjectInfo(string projectName, string language var docInfo = DocumentInfo.Create(DocumentId.CreateNewId(projectId), fileName, filePath: file, - loader: new WorkspaceFileTextLoaderNoException(services, file, defaultEncoding: null)); + loader: new FileTextLoaderNoException(file, null)); docInfos.Add(docInfo); } diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index b2284268131dc..64c4184ab37b7 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.Options; @@ -336,12 +335,10 @@ private Document AddDocumentToProject(string filePath, string language, string p project = CurrentSolution.GetRequiredProject(projectInfo.Id); } - var docInfo = DocumentInfo.Create( - DocumentId.CreateNewId(project.Id), - name: Path.GetFileName(filePath), - loader: new WorkspaceFileTextLoader(project.Solution.Services, filePath, defaultEncoding: null), - filePath: filePath); - + var docInfo = DocumentInfo.Create(DocumentId.CreateNewId(project.Id), + name: Path.GetFileName(filePath), + loader: new FileTextLoader(filePath, null), + filePath: filePath); OnDocumentAdded(docInfo); return CurrentSolution.GetDocument(docInfo.Id)!; } @@ -374,7 +371,7 @@ public void NotifyOnDocumentClosing(string moniker) // check if the doc is part of the current Roslyn workspace before notifying Roslyn. if (CurrentSolution.ContainsProject(id.ProjectId)) { - OnDocumentClosed(id, new WorkspaceFileTextLoaderNoException(CurrentSolution.Services, moniker, defaultEncoding: null)); + OnDocumentClosed(id, new FileTextLoaderNoException(moniker, null)); _openedDocs = _openedDocs.Remove(moniker); } } diff --git a/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt index 791ccc8106fdb..8b137891791fe 100644 --- a/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt @@ -1,4 +1 @@ -virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Text.SourceText (forwarded, contained in Microsoft.CodeAnalysis.Workspaces) -override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task (forwarded, contained in Microsoft.CodeAnalysis.Workspaces) -*REMOVED*override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task (forwarded, contained in Microsoft.CodeAnalysis.Workspaces) -*REMOVED*abstract Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task (forwarded, contained in Microsoft.CodeAnalysis.Workspaces) + diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs index 5a47611921f87..e61eec56067ab 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs @@ -441,7 +441,7 @@ private IEnumerable ResolveAnalyzerReferences(CommandLineArgu return commandLineArgs.ResolveAnalyzerReferences(analyzerLoader).Distinct(AnalyzerReferencePathComparer.Instance); } - private ImmutableArray CreateDocumentInfos(IReadOnlyList documentFileInfos, ProjectId projectId, Encoding? encoding) + private static ImmutableArray CreateDocumentInfos(IReadOnlyList documentFileInfos, ProjectId projectId, Encoding? encoding) { var results = ImmutableArray.CreateBuilder(); @@ -454,7 +454,7 @@ private ImmutableArray CreateDocumentInfos(IReadOnlyList ResolveReferencesAsync(ProjectId id, Proj // First, gather all of the metadata references from the command-line arguments. var resolvedMetadataReferences = commandLineArgs.ResolveMetadataReferences( new WorkspaceMetadataFileReferenceResolver( - metadataService: _workspaceServices.GetRequiredService(), + metadataService: GetWorkspaceService(), pathResolver: new RelativePathResolver(commandLineArgs.ReferencePaths, commandLineArgs.BaseDirectory))); var builder = new ResolvedReferencesBuilder(resolvedMetadataReferences); diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs index c480a89edb51d..dba94bbc991dd 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs @@ -452,7 +452,7 @@ protected override void ApplyDocumentAdded(DocumentInfo info, SourceText text) var newDocumentInfo = info.WithName(fileName) .WithFilePath(fullPath) - .WithTextLoader(new WorkspaceFileTextLoader(Services.SolutionServices, fullPath, text.Encoding)); + .WithTextLoader(new FileTextLoader(fullPath, text.Encoding)); // add document to project file _applyChangesProjectFile.AddDocument(relativePath); diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 5538f16a6c4be..9d91665102568 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -29,9 +29,3 @@ Microsoft.CodeAnalysis.Workspace.RaiseTextDocumentOpenedEventAsync(Microsoft.Cod Microsoft.CodeAnalysis.Workspace.TextDocumentClosed -> System.EventHandler Microsoft.CodeAnalysis.Workspace.TextDocumentOpened -> System.EventHandler static Microsoft.CodeAnalysis.Editing.DeclarationModifiers.File.get -> Microsoft.CodeAnalysis.Editing.DeclarationModifiers -virtual Microsoft.CodeAnalysis.FileTextLoader.CreateText(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Text.SourceText -override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -*REMOVED*override Microsoft.CodeAnalysis.FileTextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -*REMOVED*abstract Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -virtual Microsoft.CodeAnalysis.TextLoader.LoadTextAndVersionAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task diff --git a/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs b/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs index 0152e4bab41fd..fb07382eded07 100644 --- a/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs +++ b/src/Workspaces/Core/Portable/Workspace/CommandLineProject.cs @@ -127,7 +127,7 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, name: name, folders: folders, sourceCodeKind: fileArg.IsScript ? SourceCodeKind.Script : SourceCodeKind.Regular, - loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), + loader: new FileTextLoader(absolutePath, commandLineArguments.Encoding), filePath: absolutePath); docs.Add(doc); @@ -154,7 +154,7 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, name: name, folders: folders, sourceCodeKind: SourceCodeKind.Regular, - loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), + loader: new FileTextLoader(absolutePath, commandLineArguments.Encoding), filePath: absolutePath); additionalDocs.Add(doc); diff --git a/src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs index 987c9f3950b39..40bd368fd8c6f 100644 --- a/src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs @@ -58,29 +58,18 @@ public FileTextLoader(string path, Encoding? defaultEncoding) internal sealed override string FilePath => Path; - /// - /// Creates from . - /// - /// Stream. - /// Obsolete. Null. - [Obsolete("Use CreateText(Stream, CancellationToken)")] - protected virtual SourceText CreateText(Stream stream, Workspace? workspace) - => EncodedStringText.Create(stream, DefaultEncoding); - - /// - /// Creates from . - /// - protected virtual SourceText CreateText(Stream stream, CancellationToken cancellationToken) -#pragma warning disable CS0618 // Type or member is obsolete - => CreateText(stream, workspace: null); -#pragma warning restore + protected virtual SourceText CreateText(Stream stream, Workspace workspace) + { + var factory = workspace.Services.GetRequiredService(); + return factory.CreateText(stream, DefaultEncoding); + } /// /// Load a text and a version of the document in the workspace. /// /// /// - public override async Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override async Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) { ValidateFileLength(Path); @@ -161,7 +150,7 @@ public override async Task LoadTextAndVersionAsync(CancellationT // we do this so that we asynchronously read from file. and this should allocate less for IDE case. // but probably not for command line case where it doesn't use more sophisticated services. using var readStream = await SerializableBytes.CreateReadableStreamAsync(stream, cancellationToken: cancellationToken).ConfigureAwait(false); - var text = CreateText(readStream, cancellationToken); + var text = CreateText(readStream, workspace); textAndVersion = TextAndVersion.Create(text, version, Path); } @@ -180,11 +169,11 @@ public override async Task LoadTextAndVersionAsync(CancellationT } /// - /// Load a text and a version of the document. + /// Load a text and a version of the document in the workspace. /// /// /// - internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) { ValidateFileLength(Path); @@ -196,7 +185,7 @@ internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationTok using (var stream = FileUtilities.RethrowExceptionsAsIOException(() => new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete, bufferSize: 4096, useAsync: false))) { var version = VersionStamp.Create(prevLastWriteTime); - var text = CreateText(stream, cancellationToken); + var text = CreateText(stream, workspace); textAndVersion = TextAndVersion.Create(text, version, Path); } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/ITextFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/ITextFactoryService.cs index 16b6f81221427..6f555c5abd674 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/ITextFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/ITextFactoryService.cs @@ -29,7 +29,7 @@ internal interface ITextFactoryService : IWorkspaceService /// is null and the stream appears to be a binary file. /// /// An IO error occurred while reading from the stream. - SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken); + SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken = default); /// /// Creates from a reader with given . @@ -38,7 +38,7 @@ internal interface ITextFactoryService : IWorkspaceService /// Specifies an encoding for the SourceText. /// it could be null. but if null is given, it won't be able to calculate checksum /// Cancellation token. - SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken); + SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken = default); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/TextFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/TextFactoryService.cs index 9643ae18bda1c..ae3a638b03f00 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/TextFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/TextFactory/TextFactoryService.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Host { [ExportWorkspaceService(typeof(ITextFactoryService), ServiceLayer.Default), Shared] - internal sealed class TextFactoryService : ITextFactoryService + internal class TextFactoryService : ITextFactoryService { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -22,13 +22,13 @@ public TextFactoryService() { } - public SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken) + public SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); return EncodedStringText.Create(stream, defaultEncoding); } - public SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken) + public SourceText CreateText(TextReader reader, Encoding? encoding, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionChanges.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionChanges.cs index 89a34ac2e36f6..6e2e2778682de 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionChanges.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionChanges.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis @@ -59,7 +60,8 @@ public IEnumerable GetRemovedProjects() public IEnumerable GetAddedAnalyzerReferences() { - var oldAnalyzerReferences = new HashSet(_oldSolution.AnalyzerReferences); + using var _ = PooledHashSet.GetInstance(out var oldAnalyzerReferences); + oldAnalyzerReferences.UnionWith(_oldSolution.AnalyzerReferences); foreach (var analyzerReference in _newSolution.AnalyzerReferences) { if (!oldAnalyzerReferences.Contains(analyzerReference)) @@ -71,7 +73,8 @@ public IEnumerable GetAddedAnalyzerReferences() public IEnumerable GetRemovedAnalyzerReferences() { - var newAnalyzerReferences = new HashSet(_newSolution.AnalyzerReferences); + using var _ = PooledHashSet.GetInstance(out var newAnalyzerReferences); + newAnalyzerReferences.UnionWith(_newSolution.AnalyzerReferences); foreach (var analyzerReference in _oldSolution.AnalyzerReferences) { if (!newAnalyzerReferences.Contains(analyzerReference)) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs index 3c1a4836d573e..ae98692975601 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs @@ -74,7 +74,7 @@ public TextDocumentState(DocumentInfo info, HostWorkspaceServices services) info.Attributes, sourceText: null, textAndVersionSource: info.TextLoader != null - ? CreateRecoverableText(info.TextLoader, services.SolutionServices) + ? CreateRecoverableText(info.TextLoader, info.Id, services) : CreateStrongText(TextAndVersion.Create(SourceText.From(string.Empty, Encoding.UTF8), VersionStamp.Default, info.FilePath))) { } @@ -87,9 +87,12 @@ public TextDocumentState(DocumentInfo info, HostWorkspaceServices services) protected static ValueSource CreateStrongText(TextAndVersion text) => new ConstantValueSource(text); - protected static ValueSource CreateStrongText(TextLoader loader) + protected static ValueSource CreateStrongText(TextLoader loader, DocumentId documentId, HostWorkspaceServices services) { - return new AsyncLazy(loader.LoadTextAsync, loader.LoadTextSynchronously, cacheResult: true); + return new AsyncLazy( + asynchronousComputeFunction: cancellationToken => loader.LoadTextAsync(services.Workspace, documentId, cancellationToken), + synchronousComputeFunction: cancellationToken => loader.LoadTextSynchronously(services.Workspace, documentId, cancellationToken), + cacheResult: true); } protected static ValueSource CreateRecoverableText(TextAndVersion text, SolutionServices services) @@ -107,8 +110,15 @@ protected static ValueSource CreateRecoverableText(TextAndVersio return result; } - protected static ValueSource CreateRecoverableText(TextLoader loader, SolutionServices services) - => new RecoverableTextAndVersion(new AsyncLazy(loader.LoadTextAsync, loader.LoadTextSynchronously, cacheResult: false), services); + protected static ValueSource CreateRecoverableText(TextLoader loader, DocumentId documentId, HostWorkspaceServices services) + { + return new RecoverableTextAndVersion( + new AsyncLazy( + asynchronousComputeFunction: cancellationToken => loader.LoadTextAsync(services.Workspace, documentId, cancellationToken), + synchronousComputeFunction: cancellationToken => loader.LoadTextSynchronously(services.Workspace, documentId, cancellationToken), + cacheResult: false), + services.SolutionServices); + } public ITemporaryTextStorageInternal? Storage => (TextAndVersionSource as RecoverableTextAndVersion)?.Storage; @@ -220,8 +230,8 @@ public TextDocumentState UpdateText(TextLoader loader, PreservationMode mode) { // don't blow up on non-text documents. var newTextSource = mode == PreservationMode.PreserveIdentity - ? CreateStrongText(loader) - : CreateRecoverableText(loader, solutionServices.SolutionServices); + ? CreateStrongText(loader, Id, solutionServices) + : CreateRecoverableText(loader, Id, solutionServices); return UpdateText(newTextSource, mode, incremental: false); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs index d76523abbbbac..b1895bb604f10 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs @@ -4,7 +4,6 @@ using System; using System.IO; -using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -30,22 +29,7 @@ public abstract class TextLoader /// /// /// - public virtual Task LoadTextAndVersionAsync(CancellationToken cancellationToken) -#pragma warning disable CS0618 // Type or member is obsolete - => LoadTextAndVersionAsync(workspace: null, documentId: null, cancellationToken); -#pragma warning restore CS0618 - - /// - /// Load a text and a version of the document. - /// - /// Obsolete. Null. - /// Obsolete. Null. - /// - /// - /// - [Obsolete("Use LoadTextAndVersionAsync(CancellationToken) instead")] - public virtual Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) - => throw new NotImplementedException("The API is obsolete, call LoadTextAndVersionAsync(CancellationToken) instead"); + public abstract Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken); /// /// Load a text and a version of the document in the workspace. @@ -53,13 +37,13 @@ public virtual Task LoadTextAndVersionAsync(Workspace? workspace /// /// /// - internal virtual TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) + internal virtual TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) { // this implementation exists in case a custom derived type does not have access to internals - return LoadTextAndVersionAsync(cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); + return LoadTextAndVersionAsync(workspace, documentId, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); } - internal async Task LoadTextAsync(CancellationToken cancellationToken) + internal async Task LoadTextAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) { var retries = 0; @@ -67,20 +51,20 @@ internal async Task LoadTextAsync(CancellationToken cancellation { try { - return await LoadTextAndVersionAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); + return await LoadTextAndVersionAsync(workspace, documentId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } catch (IOException e) { if (++retries > MaxRetries) { - return CreateFailedText(e.Message); + return CreateFailedText(workspace, documentId, e.Message); } // fall out to try again } catch (InvalidDataException e) { - return CreateFailedText(e.Message); + return CreateFailedText(workspace, documentId, e.Message); } // try again after a delay @@ -88,7 +72,7 @@ internal async Task LoadTextAsync(CancellationToken cancellation } } - internal TextAndVersion LoadTextSynchronously(CancellationToken cancellationToken) + internal TextAndVersion LoadTextSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) { var retries = 0; @@ -96,31 +80,32 @@ internal TextAndVersion LoadTextSynchronously(CancellationToken cancellationToke { try { - return LoadTextAndVersionSynchronously(cancellationToken); + return LoadTextAndVersionSynchronously(workspace, documentId, cancellationToken); } catch (IOException e) { if (++retries > MaxRetries) { - return CreateFailedText(e.Message); + return CreateFailedText(workspace, documentId, e.Message); } // fall out to try again } catch (InvalidDataException e) { - return CreateFailedText(e.Message); + return CreateFailedText(workspace, documentId, e.Message); } - cancellationToken.ThrowIfCancellationRequested(); - // try again after a delay Thread.Sleep(RetryDelay); } } - private TextAndVersion CreateFailedText(string message) + private TextAndVersion CreateFailedText(Workspace workspace, DocumentId documentId, string message) { + // Notify workspace for backwards compatibility. + workspace.OnWorkspaceFailed(new DocumentDiagnostic(WorkspaceDiagnosticKind.Failure, message, documentId)); + Location location; string display; @@ -129,7 +114,7 @@ private TextAndVersion CreateFailedText(string message) if (filePath == null) { location = Location.None; - display = ""; + display = documentId.ToString(); } else { @@ -179,10 +164,10 @@ private sealed class TextDocumentLoader : TextLoader internal TextDocumentLoader(TextAndVersion textAndVersion) => _textAndVersion = textAndVersion; - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) => Task.FromResult(_textAndVersion); - internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) => _textAndVersion; } @@ -199,10 +184,10 @@ internal TextContainerLoader(SourceTextContainer container, VersionStamp version _filePath = filePath; } - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) - => Task.FromResult(LoadTextAndVersionSynchronously(cancellationToken)); + public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + => Task.FromResult(LoadTextAndVersionSynchronously(workspace, documentId, cancellationToken)); - internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) => TextAndVersion.Create(_container.CurrentText, _version, _filePath); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index 2548c9cbe32b7..84d1aadaea591 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -372,7 +372,6 @@ protected virtual void Dispose(bool finalize) } #region Host API - /// /// Call this method to respond to a solution being opened in the host environment. /// diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs deleted file mode 100644 index ca312116d805b..0000000000000 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.IO; -using System.Text; -using System.Threading; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis -{ - /// - /// that uses workspace services (i.e. ) to load file content. - /// - [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] - internal class WorkspaceFileTextLoader : FileTextLoader - { - private readonly ITextFactoryService _textFactory; - - internal WorkspaceFileTextLoader(SolutionServices services, string path, Encoding? defaultEncoding) -#pragma warning disable RS0030 // Do not used banned APIs - : base(path, defaultEncoding) -#pragma warning restore - { - _textFactory = services.GetRequiredService(); - } - - protected override SourceText CreateText(Stream stream, CancellationToken cancellationToken) - => _textFactory.CreateText(stream, DefaultEncoding, cancellationToken); - } -} diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs index 57e0ec4a611b6..d9ad1487e10ba 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs @@ -462,7 +462,7 @@ internal void OnSourceGeneratedDocumentClosed(SourceGeneratedDocument document) } } - private sealed class ReuseVersionLoader : TextLoader + private class ReuseVersionLoader : TextLoader { // Capture DocumentState instead of Document so that we don't hold onto the old solution. private readonly DocumentState _oldDocumentState; @@ -474,7 +474,8 @@ public ReuseVersionLoader(DocumentState oldDocumentState, SourceText newText) _newText = newText; } - public override async Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override async Task LoadTextAndVersionAsync( + Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) { var oldText = await _oldDocumentState.GetTextAsync(cancellationToken).ConfigureAwait(false); var version = await _oldDocumentState.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); @@ -482,7 +483,7 @@ public override async Task LoadTextAndVersionAsync(CancellationT return GetProperTextAndVersion(oldText, _newText, version, _oldDocumentState.FilePath); } - internal override TextAndVersion LoadTextAndVersionSynchronously(CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) { var oldText = _oldDocumentState.GetTextSynchronously(cancellationToken); var version = _oldDocumentState.GetTextVersionSynchronously(cancellationToken); diff --git a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs index f470c4cc54d52..572bb50a9abf4 100644 --- a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs @@ -8,7 +8,6 @@ using System.Collections.Immutable; using System.IO; using System.Linq; -using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.UnitTests @@ -29,7 +28,7 @@ public void Create_Errors() [Fact] public void Create() { - var loader = new TestTextLoader("text"); + var loader = new FileTextLoader(Path.GetTempPath(), defaultEncoding: null); var id = DocumentId.CreateNewId(ProjectId.CreateNewId()); var info = DocumentInfo.Create( @@ -84,7 +83,7 @@ public void TestProperties() SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithId(value), opt => opt.Id, documentId, defaultThrows: true); SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithName(value), opt => opt.Name, "New", defaultThrows: true); SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithSourceCodeKind(value), opt => opt.SourceCodeKind, SourceCodeKind.Script); - SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithTextLoader(value), opt => opt.TextLoader, (TextLoader)new TestTextLoader("text")); + SolutionTestHelpers.TestProperty(instance, (old, value) => old.WithTextLoader(value), opt => opt.TextLoader, (TextLoader)new FileTextLoader(Path.GetTempPath(), defaultEncoding: null)); SolutionTestHelpers.TestListProperty(instance, (old, value) => old.WithFolders(value), opt => opt.Folders, "folder", allowDuplicates: true); } diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index 131038d8a0c9b..846b69a3b252e 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -1926,7 +1926,7 @@ public void TestDocumentChangedOnDiskIsNotObserved() var did = DocumentId.CreateNewId(pid); sol = sol.AddProject(pid, "goo", "goo.dll", LanguageNames.CSharp) - .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8)); + .AddDocument(did, "x", new FileTextLoader(file.Path, Encoding.UTF8)); var observedText = GetObservedText(sol, did, text1); @@ -1985,7 +1985,7 @@ public void TestGetLoadedTextAsync() using var workspace = CreateWorkspace(); var sol = workspace.CurrentSolution .AddProject(pid, "goo", "goo.dll", LanguageNames.CSharp) - .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8)); + .AddDocument(did, "x", new FileTextLoader(file.Path, Encoding.UTF8)); var doc = sol.GetDocument(did); @@ -2052,7 +2052,7 @@ public void TestGetSyntaxTreeFromLoadedTextAsync() using var workspace = CreateWorkspace(); var sol = workspace.CurrentSolution .AddProject(pid, "goo", "goo.dll", LanguageNames.CSharp) - .AddDocument(did, "x", new WorkspaceFileTextLoader(workspace.Services.SolutionServices, file.Path, Encoding.UTF8)); + .AddDocument(did, "x", new FileTextLoader(file.Path, Encoding.UTF8)); var doc = sol.GetDocument(did); var docTree = doc.GetSyntaxTreeAsync().Result; @@ -2567,11 +2567,17 @@ public async Task TestDocumentFileAccessFailureMissingFile() var workspace = new AdhocWorkspace(); var solution = workspace.CurrentSolution; + WorkspaceDiagnostic diagnosticFromEvent = null; + solution.Workspace.WorkspaceFailed += (sender, args) => + { + diagnosticFromEvent = args.Diagnostic; + }; + var pid = ProjectId.CreateNewId(); var did = DocumentId.CreateNewId(pid); solution = solution.AddProject(pid, "goo", "goo", LanguageNames.CSharp) - .AddDocument(did, "x", new WorkspaceFileTextLoader(solution.Services, @"C:\doesnotexist.cs", Encoding.UTF8)) + .AddDocument(did, "x", new FileTextLoader(@"C:\doesnotexist.cs", Encoding.UTF8)) .WithDocumentFilePath(did, "document path"); var doc = solution.GetDocument(did); @@ -2580,6 +2586,7 @@ public async Task TestDocumentFileAccessFailureMissingFile() var diagnostic = await doc.State.GetLoadDiagnosticAsync(CancellationToken.None).ConfigureAwait(false); Assert.Equal(@"C:\doesnotexist.cs: (0,0)-(0,0)", diagnostic.Location.GetLineSpan().ToString()); + Assert.Equal(WorkspaceDiagnosticKind.Failure, diagnosticFromEvent.Kind); Assert.Equal("", text.ToString()); // Verify invariant: The compilation is guaranteed to have a syntax tree for each document of the project (even if the contnet fails to load). @@ -2836,9 +2843,7 @@ public void TestProjectCompletenessWithMultipleProjects() private class TestSmallFileTextLoader : FileTextLoader { public TestSmallFileTextLoader(string path, Encoding encoding) -#pragma warning disable RS0030 // Do not used banned APIs : base(path, encoding) -#pragma warning restore { } @@ -2849,6 +2854,8 @@ public TestSmallFileTextLoader(string path, Encoding encoding) [Fact] public async Task TestMassiveFileSize() { + var workspace = new AdhocWorkspace(); + using var root = new TempRoot(); var file = root.CreateFile(prefix: "massiveFile", extension: ".cs").WriteAllText("hello"); @@ -2862,7 +2869,7 @@ public async Task TestMassiveFileSize() try { // test async one - var unused = await loader.LoadTextAndVersionAsync(CancellationToken.None); + var unused = await loader.LoadTextAndVersionAsync(workspace, DocumentId.CreateNewId(ProjectId.CreateNewId()), CancellationToken.None); } catch (InvalidDataException ex) { @@ -2876,7 +2883,7 @@ public async Task TestMassiveFileSize() try { // test sync one - var unused = loader.LoadTextAndVersionSynchronously(CancellationToken.None); + var unused = loader.LoadTextAndVersionSynchronously(workspace, DocumentId.CreateNewId(ProjectId.CreateNewId()), CancellationToken.None); } catch (InvalidDataException ex) { diff --git a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs index 9845f3a4f75c3..3b74790d6145a 100644 --- a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs +++ b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs @@ -18,7 +18,7 @@ internal class TestTextLoader : TextLoader public TestTextLoader(string text = "test") => _text = text; - public override Task LoadTextAndVersionAsync(CancellationToken cancellationToken) + public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(SourceText.From(_text), VersionStamp.Create())); } } diff --git a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs index 610f6b4b3d879..20de2f3eca292 100644 --- a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs @@ -2326,10 +2326,10 @@ public async Task TestLoadTextSync() var infos = await loader.LoadProjectInfoAsync(projectFullPath); var doc = infos[0].Documents[0]; - var tav = doc.TextLoader.LoadTextAndVersionSynchronously(CancellationToken.None); + var tav = doc.TextLoader.LoadTextAndVersionSynchronously(workspace, doc.Id, CancellationToken.None); var adoc = infos[0].AdditionalDocuments.First(a => a.Name == "XamlFile.xaml"); - var atav = adoc.TextLoader.LoadTextAndVersionSynchronously(CancellationToken.None); + var atav = adoc.TextLoader.LoadTextAndVersionSynchronously(workspace, adoc.Id, CancellationToken.None); Assert.Contains("Window", atav.Text.ToString(), StringComparison.Ordinal); }