diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageService/AbstractLanguageService`2.cs b/src/VisualStudio/Core/Def/Implementation/LanguageService/AbstractLanguageService`2.cs index 24ecf55621fa1aa1746f332fc054d5bd15668aaa..9b6bc174d99b34d79d066e454b8d75fc8a7a0126 100644 --- a/src/VisualStudio/Core/Def/Implementation/LanguageService/AbstractLanguageService`2.cs +++ b/src/VisualStudio/Core/Def/Implementation/LanguageService/AbstractLanguageService`2.cs @@ -397,7 +397,7 @@ private void UninitializeDebugMode() IVsHierarchy hierarchy, uint itemid) { return new ContainedLanguage( - bufferCoordinator, this.Package.ComponentModel, project, hierarchy, itemid, + bufferCoordinator, this.Package.ComponentModel, project, hierarchy, itemid, projectTrackerOpt: null, project.Id, (TLanguageService)this); } } diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs index 0b3abca65ef6177087fb46a9600dcad17403263d..7de9debb39ab34fc7333f49077ef8b7ef9ab2468 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs @@ -64,6 +64,7 @@ internal abstract partial class AbstractProject : ForegroundThreadAffinitizedObj Hierarchy = hierarchy; Guid = projectGuid; Language = language; + ProjectTracker = projectTracker; _visualStudioWorkspace = workspace; this.DisplayName = hierarchy != null && hierarchy.TryGetName(out var name) ? name : projectSystemName; @@ -112,9 +113,12 @@ protected virtual string GetOutputFilePath() internal HostDiagnosticUpdateSource HostDiagnosticUpdateSource { get; } - public virtual ProjectId Id => VisualStudioProject.Id; + public virtual ProjectId Id => VisualStudioProject?.Id ?? ExplicitId; + + internal ProjectId ExplicitId { get; set; } public string Language { get; } + public VisualStudioProjectTracker ProjectTracker { get; } /// /// The for this project. NOTE: May be null in Deferred Project Load cases. diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/DocumentProvider.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/DocumentProvider.cs index 99d885fb93feb194d7cab3ba51890c06389cfa7a..8b5fdc1692f006eba5f6dd062b9efabf37587f20 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/DocumentProvider.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/DocumentProvider.cs @@ -9,9 +9,14 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem { - [Obsolete("This is a compatibility shim for TypeScript; please do not use it.")] + [Obsolete("This is a compatibility shim for TypeScript and Live Share; please do not use it.")] internal sealed class DocumentProvider { + [Obsolete("This is a compatibility shim for Live Share; please do not use it.")] + public DocumentProvider(VisualStudioProjectTracker projectTracker, IServiceProvider serviceProvider, VisualStudioDocumentTrackingService documentTrackingService) + { + } + [Obsolete("This overload is a compatibility shim for TypeScript; please do not use it.")] public IVisualStudioHostDocument TryGetDocumentForFile( AbstractProject hostProject, diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProjectTracker.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProjectTracker.cs index b031f8b85adc72b50ac26b84ae6d3cd7ee0edf4d..f0208ed74f636ce4db4c189f4816a5cde8a3b5c1 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProjectTracker.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProjectTracker.cs @@ -3,10 +3,12 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; +using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Extensions; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem { @@ -16,22 +18,53 @@ internal sealed partial class VisualStudioProjectTracker private readonly VisualStudioProjectFactory _projectFactory; internal IThreadingContext ThreadingContext { get; } - internal ImmutableArray ImmutableProjects => ImmutableArray.Empty; + [Obsolete("This is a compatibility shim for Live Share; please do not use it.")] + internal ImmutableArray ImmutableProjects => _projects.Values.ToImmutableArray(); internal HostWorkspaceServices WorkspaceServices => _workspace.Services; - [Obsolete("This is a compatibility shim for TypeScript; please do not use it.")] - private readonly Dictionary _typeScriptProjects = new Dictionary(); + [Obsolete("This is a compatibility shim for TypeScript and Live Share; please do not use it.")] + private readonly Dictionary _projects = new Dictionary(); + [Obsolete("This is a compatibility shim; please do not use it.")] public VisualStudioProjectTracker(Workspace workspace, VisualStudioProjectFactory projectFactory, IThreadingContext threadingContext) { _workspace = workspace; _projectFactory = projectFactory; ThreadingContext = threadingContext; + DocumentProvider = new DocumentProvider(this, null, null); + } + + [Obsolete("This is a compatibility shim for Live Share; please do not use it.")] + public VisualStudioProjectTracker(IServiceProvider serviceProvider, Workspace workspace) + { + _workspace = workspace; + ThreadingContext = serviceProvider.GetMefService(); + + // This is used by Live Share to target their own workspace which is not the standard VS workspace; as a result this shouldn't have a project factory + // at all, because that's only targeting the VisualStudioWorkspace. + _projectFactory = null; + + // We don't set DocumentProvider, because Live Share creates their own and then sets it later. } [Obsolete("This is a compatibility shim for TypeScript; please do not use it.")] - public DocumentProvider DocumentProvider => new DocumentProvider(); + public DocumentProvider DocumentProvider { get; set; } + + public Workspace Workspace => _workspace; + + [Obsolete("This is a compatibility shim for Live Share; please do not use it.")] + public void InitializeProviders(DocumentProvider documentProvider, VisualStudioMetadataReferenceManager metadataReferenceProvider, VisualStudioRuleSetManager ruleSetFileProvider) + { + DocumentProvider = documentProvider; + } + + [Obsolete("This is a compatibility shim for Live Share; please do not use it.")] + public void StartPushingToWorkspaceAndNotifyOfOpenDocuments(IEnumerable projects) + { + // This shim doesn't expect to get actual things passed to it + Debug.Assert(!projects.Any()); + } /* @@ -62,7 +95,7 @@ public ProjectId GetOrCreateProjectIdForPath(string filePath, string projectDisp public AbstractProject GetProject(ProjectId projectId) { // HACK: if we have a TypeScript project, they expect to return the real thing deriving from AbstractProject - if (_typeScriptProjects.TryGetValue(projectId, out var typeScriptProject)) + if (_projects.TryGetValue(projectId, out var typeScriptProject)) { return typeScriptProject; } @@ -110,29 +143,45 @@ public StubProject(VisualStudioProjectTracker projectTracker, Project project) public override ProjectId Id => _id; } - [Obsolete("This is a compatibility shim for TypeScript; please do not use it.")] + [Obsolete("This is a compatibility shim for TypeScript and Live Share; please do not use it.")] public void AddProject(AbstractProject project) { - project.VisualStudioProject = _projectFactory.CreateAndAddToWorkspace(project.ProjectSystemName, project.Language); - project.UpdateVisualStudioProjectProperties(); - - _typeScriptProjects[project.Id] = project; + if (_projectFactory != null) + { + project.VisualStudioProject = _projectFactory.CreateAndAddToWorkspace(project.ProjectSystemName, project.Language); + project.UpdateVisualStudioProjectProperties(); + } + else + { + // We don't have an ID, so make something up + project.ExplicitId = ProjectId.CreateNewId(project.ProjectSystemName); + Workspace.OnProjectAdded(ProjectInfo.Create(project.ExplicitId, VersionStamp.Create(), project.ProjectSystemName, project.ProjectSystemName, project.Language)); + } + + _projects[project.Id] = project; } [Obsolete("This is a compatibility shim for TypeScript; please do not use it.")] public bool ContainsProject(AbstractProject project) { // This will be set as long as the project has been added and not since removed - return project.VisualStudioProject != null; + return _projects.Values.Contains(project); } [Obsolete("This is a compatibility shim for TypeScript; please do not use it.")] public void RemoveProject(AbstractProject project) { - _typeScriptProjects.Remove(project.Id); + _projects.Remove(project.Id); - project.VisualStudioProject.RemoveFromWorkspace(); - project.VisualStudioProject = null; + if (project.ExplicitId != null) + { + Workspace.OnProjectRemoved(project.ExplicitId); + } + else + { + project.VisualStudioProject.RemoveFromWorkspace(); + project.VisualStudioProject = null; + } } } } diff --git a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs index 81d962253fe9ff971a151b0aceea752504b420f4..c7d213fad9c5618d2f299f69a19a90d0829ddce1 100644 --- a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs +++ b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs @@ -68,7 +68,7 @@ public static ContainedDocument TryGetContainedDocument(DocumentId id) } private readonly IComponentModel _componentModel; - private readonly VisualStudioWorkspace _workspace; + private readonly Workspace _workspace; private readonly ITextDifferencingSelectorService _differenceSelectorService; private readonly HostType _hostType; private readonly ReiteratedVersionSnapshotTracker _snapshotTracker; @@ -89,7 +89,7 @@ public static ContainedDocument TryGetContainedDocument(DocumentId id) ITextBuffer subjectBuffer, ITextBuffer dataBuffer, IVsTextBufferCoordinator bufferCoordinator, - VisualStudioWorkspace workspace, + Workspace workspace, VisualStudioProject project, IVsHierarchy hierarchy, uint itemId, @@ -179,7 +179,8 @@ public void Dispose() public DocumentId FindProjectDocumentIdWithItemId(uint itemidInsertionPoint) { - var hierarchy = _workspace.GetHierarchy(_project.Id); + // We cast to VisualStudioWorkspace because the expectation is this isn't being used in Live Share workspaces + var hierarchy = ((VisualStudioWorkspace)_workspace).GetHierarchy(_project.Id); foreach (var document in _workspace.CurrentSolution.GetProject(_project.Id).Documents) { @@ -194,7 +195,8 @@ public DocumentId FindProjectDocumentIdWithItemId(uint itemidInsertionPoint) public uint FindItemIdOfDocument(Document document) { - var hierarchy = _workspace.GetHierarchy(_project.Id); + // We cast to VisualStudioWorkspace because the expectation is this isn't being used in Live Share workspaces + var hierarchy = ((VisualStudioWorkspace)_workspace).GetHierarchy(_project.Id); return hierarchy.TryGetItemId(_workspace.CurrentSolution.GetDocument(document.Id).FilePath); } diff --git a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguage.cs b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguage.cs index 6a16ae35eda727332f498bbf44d60b8b89b14959..c79ce43b165109e91207ae74212385a748a94e1a 100644 --- a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguage.cs +++ b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguage.cs @@ -31,7 +31,7 @@ internal partial class ContainedLanguage private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService; private readonly TLanguageService _languageService; - protected readonly VisualStudioWorkspace Workspace; + protected readonly Workspace Workspace; protected readonly IComponentModel ComponentModel; public VisualStudioProject Project { get; } @@ -69,7 +69,7 @@ internal partial class ContainedLanguage // flickering. private ITagAggregator _bufferTagAggregator; - [Obsolete("This is a compatibility shim for TypeScript; please do not use it.")] + [Obsolete("This is a compatibility shim for TypeScript and Live Share; please do not use it.")] public ContainedLanguage( IVsTextBufferCoordinator bufferCoordinator, IComponentModel componentModel, @@ -85,6 +85,8 @@ internal partial class ContainedLanguage project.VisualStudioProject, hierarchy, itemid, + project.ProjectTracker, + project.Id, languageService, vbHelperFormattingRule) { @@ -105,17 +107,21 @@ internal partial class ContainedLanguage project.VisualStudioProject, hierarchy, itemid, + projectTrackerOpt: null, + project.VisualStudioProject.Id, languageService, vbHelperFormattingRule) { } - public ContainedLanguage( + internal ContainedLanguage( IVsTextBufferCoordinator bufferCoordinator, IComponentModel componentModel, VisualStudioProject project, IVsHierarchy hierarchy, uint itemid, + VisualStudioProjectTracker projectTrackerOpt, + ProjectId projectId, TLanguageService languageService, IFormattingRule vbHelperFormattingRule = null) { @@ -124,7 +130,7 @@ internal partial class ContainedLanguage this.Project = project; _languageService = languageService; - this.Workspace = componentModel.GetService(); + this.Workspace = projectTrackerOpt?.Workspace ?? componentModel.GetService(); _editorAdaptersFactoryService = componentModel.GetService(); _diagnosticAnalyzerService = componentModel.GetService(); @@ -137,7 +143,7 @@ internal partial class ContainedLanguage // Get the ITextBuffer for the primary buffer Marshal.ThrowExceptionForHR(bufferCoordinator.GetPrimaryBuffer(out var primaryTextLines)); DataBuffer = _editorAdaptersFactoryService.GetDataBuffer((IVsTextBuffer)primaryTextLines); - + // Create our tagger var bufferTagAggregatorFactory = ComponentModel.GetService(); _bufferTagAggregator = bufferTagAggregatorFactory.CreateTagAggregator(SubjectBuffer); @@ -153,7 +159,20 @@ internal partial class ContainedLanguage } } - var documentId = this.Project.AddSourceTextContainer(SubjectBuffer.AsTextContainer(), filePath); + DocumentId documentId; + + if (this.Project != null) + { + documentId = this.Project.AddSourceTextContainer(SubjectBuffer.AsTextContainer(), filePath); + } + else + { + documentId = DocumentId.CreateNewId(projectId, $"{nameof(ContainedDocument)}: {filePath}"); + + // We must jam a document into an existing workspace, which we'll assume is safe to do with OnDocumentAdded + Workspace.OnDocumentAdded(DocumentInfo.Create(documentId, filePath, filePath: filePath)); + Workspace.OnDocumentOpened(documentId, SubjectBuffer.AsTextContainer()); + } this.ContainedDocument = new ContainedDocument( componentModel.GetService(), @@ -175,7 +194,19 @@ internal partial class ContainedLanguage private void OnDisconnect() { this.DataBuffer.Changed -= OnDataBufferChanged; - this.Project.RemoveSourceTextContainer(SubjectBuffer.AsTextContainer()); + + if (this.Project != null) + { + this.Project.RemoveSourceTextContainer(SubjectBuffer.AsTextContainer()); + } + else + { + // It's possible the host of the workspace might have already removed the entire project + if (Workspace.CurrentSolution.ContainsDocument(ContainedDocument.Id)) + { + Workspace.OnDocumentRemoved(ContainedDocument.Id); + } + } this.ContainedDocument.Dispose(); } diff --git a/src/VisualStudio/VisualBasic/Impl/Venus/VisualBasicContainedLanguage.vb b/src/VisualStudio/VisualBasic/Impl/Venus/VisualBasicContainedLanguage.vb index d053c2bc506bd3a56873300041847cfcce8b4a0a..0581671158025cc6920dd23f534481ee3fab3213 100644 --- a/src/VisualStudio/VisualBasic/Impl/Venus/VisualBasicContainedLanguage.vb +++ b/src/VisualStudio/VisualBasic/Impl/Venus/VisualBasicContainedLanguage.vb @@ -28,7 +28,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Venus itemid As UInteger, languageService As VisualBasicLanguageService, sourceCodeKind As SourceCodeKind) - MyBase.New(bufferCoordinator, componentModel, project, hierarchy, itemid, languageService, VisualBasicHelperFormattingRule.Instance) + MyBase.New(bufferCoordinator, componentModel, project, hierarchy, itemid, projectTrackerOpt:=Nothing, project.Id, languageService, VisualBasicHelperFormattingRule.Instance) End Sub Public Function AddStaticEventBinding(pszClassName As String,