提交 98f38d5e 编写于 作者: J Jason Malinowski 提交者: GitHub

Merge pull request #14016 from vslsnap/merge-dev15-preview-5-into-master20160923-150132

Merge dev15-preview-5 into master
...@@ -284,6 +284,7 @@ ...@@ -284,6 +284,7 @@
or '%(FileName)' == 'Microsoft.VisualStudio.Shell.Interop.12.0' or '%(FileName)' == 'Microsoft.VisualStudio.Shell.Interop.12.0'
or '%(FileName)' == 'Microsoft.VisualStudio.Shell.Interop.12.1.DesignTime' or '%(FileName)' == 'Microsoft.VisualStudio.Shell.Interop.12.1.DesignTime'
or '%(FileName)' == 'Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime' or '%(FileName)' == 'Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime'
or '%(FileName)' == 'Microsoft.VisualStudio.Shell.Interop.15.0.DesignTime'
or '%(Filename)' == 'Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime' or '%(Filename)' == 'Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime'
or '%(FileName)' == 'Microsoft.VisualStudio.TextManager.Interop.12.1.DesignTime' or '%(FileName)' == 'Microsoft.VisualStudio.TextManager.Interop.12.1.DesignTime'
or '%(FileName)' == 'Microsoft.Internal.VisualStudio.Shell.Interop.14.0.DesignTime' or '%(FileName)' == 'Microsoft.Internal.VisualStudio.Shell.Interop.14.0.DesignTime'
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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.Diagnostics;
using System.IO; using System.IO;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim; using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop; using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Legacy;
using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService
...@@ -16,6 +19,8 @@ internal partial class CSharpLanguageService : ICSharpProjectHost ...@@ -16,6 +19,8 @@ internal partial class CSharpLanguageService : ICSharpProjectHost
public void BindToProject(ICSharpProjectRoot projectRoot, IVsHierarchy hierarchy) public void BindToProject(ICSharpProjectRoot projectRoot, IVsHierarchy hierarchy)
{ {
var projectName = Path.GetFileName(projectRoot.GetFullProjectName()); // GetFullProjectName returns the path to the project file w/o the extension? var projectName = Path.GetFileName(projectRoot.GetFullProjectName()); // GetFullProjectName returns the path to the project file w/o the extension?
Workspace.ProjectTracker.TryDisconnectExistingDeferredProject(hierarchy, projectName);
var project = new CSharpProjectShimWithServices( var project = new CSharpProjectShimWithServices(
projectRoot, projectRoot,
this.Workspace.ProjectTracker, this.Workspace.ProjectTracker,
......
...@@ -64,24 +64,7 @@ public void OnSourceFileAdded(string filename) ...@@ -64,24 +64,7 @@ public void OnSourceFileAdded(string filename)
// ? SourceCodeKind.Script // ? SourceCodeKind.Script
// : SourceCodeKind.Regular; // : SourceCodeKind.Regular;
var sourceCodeKind = SourceCodeKind.Regular; var sourceCodeKind = SourceCodeKind.Regular;
AddFile(filename, sourceCodeKind);
IVsHierarchy foundHierarchy;
uint itemId;
if (ErrorHandler.Succeeded(_projectRoot.GetHierarchyAndItemID(filename, out foundHierarchy, out itemId)))
{
Debug.Assert(foundHierarchy == this.Hierarchy);
}
else
{
// Unfortunately, the project system does pass us some files which aren't part of
// the project as far as the hierarchy and itemid are concerned. We'll just used
// VSITEMID.Nil for them.
foundHierarchy = null;
itemId = (uint)VSConstants.VSITEMID.Nil;
}
AddFile(filename, sourceCodeKind, itemId);
} }
public void OnSourceFileRemoved(string filename) public void OnSourceFileRemoved(string filename)
......
...@@ -46,9 +46,7 @@ public void AddRemoveProjectAndMetadataReference_CPS() ...@@ -46,9 +46,7 @@ public void AddRemoveProjectAndMetadataReference_CPS()
((IWorkspaceProjectContext)project4).BinOutputPath = @"C:\project4.dll"; ((IWorkspaceProjectContext)project4).BinOutputPath = @"C:\project4.dll";
Assert.Equal(@"C:\project4.dll", project4.BinOutputPath); Assert.Equal(@"C:\project4.dll", project4.BinOutputPath);
// This is currently broken by https://github.com/dotnet/roslyn/issues/12707 Assert.True(project3.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project4.Id));
// Reverse the below assert to Assert.True once the above bug is fixed.
Assert.False(project3.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project4.Id));
// Remove project reference // Remove project reference
project3.RemoveProjectReference(project1); project3.RemoveProjectReference(project1);
......
...@@ -78,11 +78,9 @@ public static bool TryGetImageListAndIndex(this VisualStudioWorkspaceImpl worksp ...@@ -78,11 +78,9 @@ public static bool TryGetImageListAndIndex(this VisualStudioWorkspaceImpl worksp
public static bool TryGetImageListAndIndex(this VisualStudioWorkspaceImpl workspace, IVsImageService2 imageService, ProjectId id, out IntPtr imageList, out ushort index) public static bool TryGetImageListAndIndex(this VisualStudioWorkspaceImpl workspace, IVsImageService2 imageService, ProjectId id, out IntPtr imageList, out ushort index)
{ {
var project = workspace.GetHostProject(id); var hierarchy = workspace.GetHostProject(id)?.Hierarchy;
if (project != null) if (hierarchy != null)
{ {
var hierarchy = project.Hierarchy;
return TryGetImageListAndIndex(hierarchy, imageService, VSConstants.VSITEMID_ROOT, out imageList, out index); return TryGetImageListAndIndex(hierarchy, imageService, VSConstants.VSITEMID_ROOT, out imageList, out index);
} }
......
...@@ -187,6 +187,9 @@ internal abstract partial class AbstractProject : ForegroundThreadAffinitizedObj ...@@ -187,6 +187,9 @@ internal abstract partial class AbstractProject : ForegroundThreadAffinitizedObj
private ICommandLineParserService CommandLineParserService { get; } private ICommandLineParserService CommandLineParserService { get; }
/// <summary>
/// The <see cref="IVsHierarchy"/> for this project. NOTE: May be null in Deferred Project Load cases.
/// </summary>
public IVsHierarchy Hierarchy { get; } public IVsHierarchy Hierarchy { get; }
/// <summary> /// <summary>
...@@ -892,15 +895,19 @@ private static void OnAdditionalDocumentUpdatedOnDisk(object sender, EventArgs e ...@@ -892,15 +895,19 @@ private static void OnAdditionalDocumentUpdatedOnDisk(object sender, EventArgs e
} }
} }
protected void AddFile(string filename, SourceCodeKind sourceCodeKind, Func<IVisualStudioHostDocument, bool> getIsCurrentContext, IReadOnlyList<string> folderNames) protected void AddFile(
string filename,
SourceCodeKind sourceCodeKind,
Func<IVisualStudioHostDocument, bool> getIsCurrentContext,
Func<uint, IReadOnlyList<string>> getFolderNames)
{ {
// We can currently be on a background thread. // We can currently be on a background thread.
// So, hookup the handlers when creating the standard text document, as we might receive these handler notifications on the UI thread. // So, hookup the handlers when creating the standard text document, as we might receive these handler notifications on the UI thread.
var document = this.DocumentProvider.TryGetDocumentForFile( var document = this.DocumentProvider.TryGetDocumentForFile(
this, this,
folderNames,
filePath: filename, filePath: filename,
sourceCodeKind: sourceCodeKind, sourceCodeKind: sourceCodeKind,
getFolderNames: getFolderNames,
canUseTextBuffer: CanUseTextBuffer, canUseTextBuffer: CanUseTextBuffer,
updatedOnDiskHandler: s_documentUpdatedOnDiskEventHandler, updatedOnDiskHandler: s_documentUpdatedOnDiskEventHandler,
openedHandler: s_documentOpenedEventHandler, openedHandler: s_documentOpenedEventHandler,
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host;
using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{ {
...@@ -125,9 +126,9 @@ public void AddAdditionalFile(string additionalFilePath, Func<IVisualStudioHostD ...@@ -125,9 +126,9 @@ public void AddAdditionalFile(string additionalFilePath, Func<IVisualStudioHostD
{ {
var document = this.DocumentProvider.TryGetDocumentForFile( var document = this.DocumentProvider.TryGetDocumentForFile(
this, this,
ImmutableArray<string>.Empty,
filePath: additionalFilePath, filePath: additionalFilePath,
sourceCodeKind: SourceCodeKind.Regular, sourceCodeKind: SourceCodeKind.Regular,
getFolderNames: _ => SpecializedCollections.EmptyReadOnlyList<string>(),
canUseTextBuffer: _ => true, canUseTextBuffer: _ => true,
updatedOnDiskHandler: s_additionalDocumentUpdatedOnDiskEventHandler, updatedOnDiskHandler: s_additionalDocumentUpdatedOnDiskEventHandler,
openedHandler: s_additionalDocumentOpenedEventHandler, openedHandler: s_additionalDocumentOpenedEventHandler,
......
...@@ -19,7 +19,7 @@ internal interface IWorkspaceProjectContextFactory ...@@ -19,7 +19,7 @@ internal interface IWorkspaceProjectContextFactory
/// <param name="projectDisplayName">Display name for the project.</param> /// <param name="projectDisplayName">Display name for the project.</param>
/// <param name="projectFilePath">Full path to the project file for the project.</param> /// <param name="projectFilePath">Full path to the project file for the project.</param>
/// <param name="projectGuid">Project guid.</param> /// <param name="projectGuid">Project guid.</param>
/// <param name="hierarchy"><see cref="IVsHierarchy"/> for the project.</param> /// <param name="hierarchy"><see cref="IVsHierarchy"/> for the project, an be null in deferred project load cases.</param>
/// <param name="binOutputPath">Initial project binary output path.</param> /// <param name="binOutputPath">Initial project binary output path.</param>
IWorkspaceProjectContext CreateProjectContext(string languageName, string projectDisplayName, string projectFilePath, Guid projectGuid, object hierarchy, string binOutputPath); IWorkspaceProjectContext CreateProjectContext(string languageName, string projectDisplayName, string projectFilePath, Guid projectGuid, object hierarchy, string binOutputPath);
} }
......
...@@ -45,11 +45,15 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS ...@@ -45,11 +45,15 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS
public event EventHandler<bool> Opened; public event EventHandler<bool> Opened;
public event EventHandler<bool> Closing; public event EventHandler<bool> Closing;
/// <summary>
/// Creates a <see cref="StandardTextDocument"/>.
/// <para>Note: getFolderNames maps from a VSITEMID to the folders this document should be contained in.</para>
/// </summary>
public StandardTextDocument( public StandardTextDocument(
DocumentProvider documentProvider, DocumentProvider documentProvider,
IVisualStudioHostProject project, IVisualStudioHostProject project,
DocumentKey documentKey, DocumentKey documentKey,
IReadOnlyList<string> folderNames, Func<uint, IReadOnlyList<string>> getFolderNames,
SourceCodeKind sourceCodeKind, SourceCodeKind sourceCodeKind,
ITextUndoHistoryRegistry textUndoHistoryRegistry, ITextUndoHistoryRegistry textUndoHistoryRegistry,
IVsFileChangeEx fileChangeService, IVsFileChangeEx fileChangeService,
...@@ -63,13 +67,17 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS ...@@ -63,13 +67,17 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS
this.Project = project; this.Project = project;
this.Id = id ?? DocumentId.CreateNewId(project.Id, documentKey.Moniker); this.Id = id ?? DocumentId.CreateNewId(project.Id, documentKey.Moniker);
this.Folders = folderNames; _itemMoniker = documentKey.Moniker;
var itemid = this.GetItemId();
this.Folders = itemid == (uint)VSConstants.VSITEMID.Nil
? SpecializedCollections.EmptyReadOnlyList<string>()
: getFolderNames(itemid);
_documentProvider = documentProvider; _documentProvider = documentProvider;
this.Key = documentKey; this.Key = documentKey;
this.SourceCodeKind = sourceCodeKind; this.SourceCodeKind = sourceCodeKind;
_itemMoniker = documentKey.Moniker;
_textUndoHistoryRegistry = textUndoHistoryRegistry; _textUndoHistoryRegistry = textUndoHistoryRegistry;
_fileChangeTracker = new FileChangeTracker(fileChangeService, this.FilePath); _fileChangeTracker = new FileChangeTracker(fileChangeService, this.FilePath);
_fileChangeTracker.UpdatedOnDisk += OnUpdatedOnDisk; _fileChangeTracker.UpdatedOnDisk += OnUpdatedOnDisk;
...@@ -251,7 +259,7 @@ public uint GetItemId() ...@@ -251,7 +259,7 @@ public uint GetItemId()
{ {
AssertIsForeground(); AssertIsForeground();
if (_itemMoniker == null) if (_itemMoniker == null || Project.Hierarchy == null)
{ {
return (uint)VSConstants.VSITEMID.Nil; return (uint)VSConstants.VSITEMID.Nil;
} }
......
...@@ -95,10 +95,10 @@ internal abstract partial class DocumentProvider : ForegroundThreadAffinitizedOb ...@@ -95,10 +95,10 @@ internal abstract partial class DocumentProvider : ForegroundThreadAffinitizedOb
/// </summary> /// </summary>
public IVisualStudioHostDocument TryGetDocumentForFile( public IVisualStudioHostDocument TryGetDocumentForFile(
IVisualStudioHostProject hostProject, IVisualStudioHostProject hostProject,
IReadOnlyList<string> folderNames,
string filePath, string filePath,
SourceCodeKind sourceCodeKind, SourceCodeKind sourceCodeKind,
Func<ITextBuffer, bool> canUseTextBuffer, Func<ITextBuffer, bool> canUseTextBuffer,
Func<uint, IReadOnlyList<string>> getFolderNames,
EventHandler updatedOnDiskHandler = null, EventHandler updatedOnDiskHandler = null,
EventHandler<bool> openedHandler = null, EventHandler<bool> openedHandler = null,
EventHandler<bool> closingHandler = null) EventHandler<bool> closingHandler = null)
...@@ -163,7 +163,7 @@ internal abstract partial class DocumentProvider : ForegroundThreadAffinitizedOb ...@@ -163,7 +163,7 @@ internal abstract partial class DocumentProvider : ForegroundThreadAffinitizedOb
this, this,
hostProject, hostProject,
documentKey, documentKey,
folderNames, getFolderNames,
sourceCodeKind, sourceCodeKind,
_textUndoHistoryRegistry, _textUndoHistoryRegistry,
_fileChangeService, _fileChangeService,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal interface IDeferredProjectWorkspaceService : IWorkspaceService
{
bool IsDeferredProjectLoadEnabled { get; }
/// <summary>
/// Returns a mapping of project file path to information about that project.
/// </summary>
Task<IReadOnlyDictionary<string, DeferredProjectInformation>> GetDeferredProjectInfoForConfigurationAsync(
string solutionConfiguration,
CancellationToken cancellationToken);
}
internal struct DeferredProjectInformation
{
public DeferredProjectInformation(
string targetPath,
ImmutableArray<string> commandLineArgs,
ImmutableArray<string> referencedProjectFilePaths)
{
TargetPath = targetPath;
CommandLineArguments = commandLineArgs;
ReferencedProjectFilePaths = referencedProjectFilePaths;
}
/// <summary>
/// The full path to the binary this project would create if built *by msbuild*.
/// May be different than the /out argument in <see cref="CommandLineArguments"/>.
/// </summary>
public string TargetPath { get; }
/// <summary>
/// The set of command line arguments that can be used to build this project.
/// </summary>
public ImmutableArray<string> CommandLineArguments { get; }
/// <summary>
/// The paths to referenced projects.
/// </summary>
public ImmutableArray<string> ReferencedProjectFilePaths { get; }
}
}
...@@ -15,6 +15,9 @@ internal interface IVisualStudioHostProject ...@@ -15,6 +15,9 @@ internal interface IVisualStudioHostProject
ProjectId Id { get; } ProjectId Id { get; }
string Language { get; } string Language { get; }
/// <summary>
/// The <see cref="IVsHierarchy"/> for this project. NOTE: May be null in Deferred Project Load cases.
/// </summary>
IVsHierarchy Hierarchy { get; } IVsHierarchy Hierarchy { get; }
Guid Guid { get; } Guid Guid { get; }
......
...@@ -45,8 +45,11 @@ internal abstract partial class AbstractLegacyProject : AbstractProject ...@@ -45,8 +45,11 @@ internal abstract partial class AbstractLegacyProject : AbstractProject
hostDiagnosticUpdateSourceOpt: hostDiagnosticUpdateSourceOpt, hostDiagnosticUpdateSourceOpt: hostDiagnosticUpdateSourceOpt,
commandLineParserServiceOpt: commandLineParserServiceOpt) commandLineParserServiceOpt: commandLineParserServiceOpt)
{ {
ConnectHierarchyEvents(); if (Hierarchy != null)
this.IsWebSite = GetIsWebsiteProject(hierarchy); {
ConnectHierarchyEvents();
this.IsWebSite = GetIsWebsiteProject(Hierarchy);
}
// Initialize command line arguments. // Initialize command line arguments.
base.SetArguments(commandLine: string.Empty); base.SetArguments(commandLine: string.Empty);
...@@ -65,10 +68,10 @@ public override void Disconnect() ...@@ -65,10 +68,10 @@ public override void Disconnect()
DisconnectHierarchyEvents(); DisconnectHierarchyEvents();
} }
protected void AddFile(string filename, SourceCodeKind sourceCodeKind, uint itemId) protected void AddFile(string filename, SourceCodeKind sourceCodeKind)
{ {
Func<IVisualStudioHostDocument, bool> getIsCurrentContext = document => LinkedFileUtilities.IsCurrentContextHierarchy(document, RunningDocumentTable); Func<IVisualStudioHostDocument, bool> getIsCurrentContext = document => LinkedFileUtilities.IsCurrentContextHierarchy(document, RunningDocumentTable);
AddFile(filename, sourceCodeKind, getIsCurrentContext, GetFolderNames(itemId)); AddFile(filename, sourceCodeKind, getIsCurrentContext, GetFolderNames);
} }
protected void SetOutputPathAndRelatedData(string objOutputPath) protected void SetOutputPathAndRelatedData(string objOutputPath)
...@@ -124,7 +127,7 @@ private static string GetProjectDisplayName(IVsHierarchy hierarchy) ...@@ -124,7 +127,7 @@ private static string GetProjectDisplayName(IVsHierarchy hierarchy)
return hierarchy.TryGetName(out name) ? name : null; return hierarchy.TryGetName(out name) ? name : null;
} }
private static string GetProjectFilePath(IVsHierarchy hierarchy) internal static string GetProjectFilePath(IVsHierarchy hierarchy)
{ {
string filePath; string filePath;
return ErrorHandler.Succeeded(((IVsProject3)hierarchy).GetMkDocument((uint)VSConstants.VSITEMID.Root, out filePath)) ? filePath : null; return ErrorHandler.Succeeded(((IVsProject3)hierarchy).GetMkDocument((uint)VSConstants.VSITEMID.Root, out filePath)) ? filePath : null;
......
...@@ -311,9 +311,9 @@ private void AttachToDocument(uint docCookie, string moniker) ...@@ -311,9 +311,9 @@ private void AttachToDocument(uint docCookie, string moniker)
// Now try to find the document. We accept any text buffer, since we've already verified it's an appropriate file in ShouldIncludeFile. // Now try to find the document. We accept any text buffer, since we've already verified it's an appropriate file in ShouldIncludeFile.
var document = _documentProvider.TryGetDocumentForFile( var document = _documentProvider.TryGetDocumentForFile(
hostProject, hostProject,
ImmutableArray<string>.Empty,
moniker, moniker,
parseOptions.Kind, parseOptions.Kind,
getFolderNames: _ => SpecializedCollections.EmptyReadOnlyList<string>(),
canUseTextBuffer: _ => true); canUseTextBuffer: _ => true);
// If the buffer has not yet been initialized, we won't get a document. // If the buffer has not yet been initialized, we won't get a document.
......
...@@ -3,13 +3,19 @@ ...@@ -3,13 +3,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Threading; using System.Windows.Threading;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host;
using Microsoft.Internal.VisualStudio.Shell.Interop; using Microsoft.Internal.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Legacy;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -29,6 +35,8 @@ internal sealed partial class VisualStudioProjectTracker : ForegroundThreadAffin ...@@ -29,6 +35,8 @@ internal sealed partial class VisualStudioProjectTracker : ForegroundThreadAffin
#region Mutable fields accessed only from foreground thread - don't need locking for access (all accessing methods must have AssertIsForeground). #region Mutable fields accessed only from foreground thread - don't need locking for access (all accessing methods must have AssertIsForeground).
private readonly List<WorkspaceHostState> _workspaceHosts; private readonly List<WorkspaceHostState> _workspaceHosts;
private readonly HostWorkspaceServices _workspaceServices;
/// <summary> /// <summary>
/// The list of projects loaded in this batch between <see cref="IVsSolutionLoadEvents.OnBeforeLoadProjectBatch" /> and /// The list of projects loaded in this batch between <see cref="IVsSolutionLoadEvents.OnBeforeLoadProjectBatch" /> and
/// <see cref="IVsSolutionLoadEvents.OnAfterLoadProjectBatch(bool)"/>. /// <see cref="IVsSolutionLoadEvents.OnAfterLoadProjectBatch(bool)"/>.
...@@ -100,7 +108,7 @@ void IVisualStudioHostProjectContainer.NotifyNonDocumentOpenedForProject(IVisual ...@@ -100,7 +108,7 @@ void IVisualStudioHostProjectContainer.NotifyNonDocumentOpenedForProject(IVisual
StartPushingToWorkspaceAndNotifyOfOpenDocuments(SpecializedCollections.SingletonEnumerable(abstractProject)); StartPushingToWorkspaceAndNotifyOfOpenDocuments(SpecializedCollections.SingletonEnumerable(abstractProject));
} }
public VisualStudioProjectTracker(IServiceProvider serviceProvider) public VisualStudioProjectTracker(IServiceProvider serviceProvider, HostWorkspaceServices workspaceServices)
: base(assertIsForeground: true) : base(assertIsForeground: true)
{ {
_projectMap = new Dictionary<ProjectId, AbstractProject>(); _projectMap = new Dictionary<ProjectId, AbstractProject>();
...@@ -108,6 +116,7 @@ public VisualStudioProjectTracker(IServiceProvider serviceProvider) ...@@ -108,6 +116,7 @@ public VisualStudioProjectTracker(IServiceProvider serviceProvider)
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_workspaceHosts = new List<WorkspaceHostState>(capacity: 1); _workspaceHosts = new List<WorkspaceHostState>(capacity: 1);
_workspaceServices = workspaceServices;
_vsSolution = (IVsSolution)serviceProvider.GetService(typeof(SVsSolution)); _vsSolution = (IVsSolution)serviceProvider.GetService(typeof(SVsSolution));
_runningDocumentTable = (IVsRunningDocumentTable4)serviceProvider.GetService(typeof(SVsRunningDocumentTable)); _runningDocumentTable = (IVsRunningDocumentTable4)serviceProvider.GetService(typeof(SVsRunningDocumentTable));
...@@ -565,5 +574,21 @@ internal void RemoveProjectByBinPath(string filePath, AbstractProject project) ...@@ -565,5 +574,21 @@ internal void RemoveProjectByBinPath(string filePath, AbstractProject project)
} }
} }
} }
internal void TryDisconnectExistingDeferredProject(IVsHierarchy hierarchy, string projectName)
{
var projectPath = AbstractLegacyProject.GetProjectFilePath(hierarchy);
var projectId = GetOrCreateProjectIdForPath(projectPath, projectName);
// If we created a project for this while in deferred project load mode, let's close it
// now that we're being asked to make a "real" project for it, so that we'll prefer the
// "real" project
if (_workspaceServices.GetService<IDeferredProjectWorkspaceService>()?.IsDeferredProjectLoadEnabled == true)
{
var existingProject = GetProject(projectId);
Debug.Assert(existingProject is IWorkspaceProjectContext);
existingProject?.Disconnect();
}
}
} }
} }
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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.Linq;
using System.Threading;
using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -19,6 +21,14 @@ int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) ...@@ -19,6 +21,14 @@ int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)
int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution) int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution)
{ {
AssertIsForeground();
var deferredProjectWorkspaceService = _workspaceServices.GetService<IDeferredProjectWorkspaceService>();
if (deferredProjectWorkspaceService?.IsDeferredProjectLoadEnabled == true)
{
LoadSolutionFromMSBuildAsync(deferredProjectWorkspaceService, _solutionParsingCancellationTokenSource.Token).FireAndForget();
}
return VSConstants.S_OK; return VSConstants.S_OK;
} }
...@@ -60,6 +70,11 @@ int IVsSolutionEvents.OnBeforeCloseSolution(object pUnkReserved) ...@@ -60,6 +70,11 @@ int IVsSolutionEvents.OnBeforeCloseSolution(object pUnkReserved)
_solutionLoadComplete = false; _solutionLoadComplete = false;
// Cancel any background solution parsing. NOTE: This means that that work needs to
// check the token periodically, and whenever resuming from an "await"
_solutionParsingCancellationTokenSource.Cancel();
_solutionParsingCancellationTokenSource = new CancellationTokenSource();
return VSConstants.S_OK; return VSConstants.S_OK;
} }
...@@ -67,6 +82,17 @@ int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved) ...@@ -67,6 +82,17 @@ int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved)
{ {
AssertIsForeground(); AssertIsForeground();
var deferredProjectWorkspaceService = _workspaceServices.GetService<IDeferredProjectWorkspaceService>();
if (deferredProjectWorkspaceService?.IsDeferredProjectLoadEnabled == true)
{
// Copy to avoid modifying the collection while enumerating
var loadedProjects = ImmutableProjects.ToList();
foreach (var p in loadedProjects)
{
p.Disconnect();
}
}
lock (_gate) lock (_gate)
{ {
Contract.ThrowIfFalse(_projectMap.Count == 0); Contract.ThrowIfFalse(_projectMap.Count == 0);
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{ {
internal partial class VisualStudioProjectTracker : IVsSolutionLoadEvents internal partial class VisualStudioProjectTracker : IVsSolutionLoadEvents
{ {
// Temporary for prototyping purposes
private IVsOutputWindowPane _pane;
/// <summary>
/// Used to cancel our background solution parse if we get a solution close event from VS.
/// </summary>
private CancellationTokenSource _solutionParsingCancellationTokenSource = new CancellationTokenSource();
int IVsSolutionLoadEvents.OnBeforeOpenSolution(string pszSolutionFilename) int IVsSolutionLoadEvents.OnBeforeOpenSolution(string pszSolutionFilename)
{ {
return VSConstants.S_OK; return VSConstants.S_OK;
} }
private async Task LoadSolutionFromMSBuildAsync(
IDeferredProjectWorkspaceService deferredProjectWorkspaceService,
CancellationToken cancellationToken)
{
AssertIsForeground();
InitializeOutputPane();
// Continue on the UI thread for these operations, since we are touching the VisualStudioWorkspace, etc.
await PopulateWorkspaceFromDeferredProjectInfoAsync(deferredProjectWorkspaceService, cancellationToken).ConfigureAwait(true);
}
[Conditional("DEBUG")]
private void InitializeOutputPane()
{
var outputWindow = (IVsOutputWindow)_serviceProvider.GetService(typeof(SVsOutputWindow));
var paneGuid = new Guid("07aaa8e9-d776-47d6-a1be-5ce00332d74d");
if (ErrorHandler.Succeeded(outputWindow.CreatePane(ref paneGuid, "Roslyn DPL Status", fInitVisible: 1, fClearWithSolution: 1)) &&
ErrorHandler.Succeeded(outputWindow.GetPane(ref paneGuid, out _pane)) && _pane != null)
{
_pane.Activate();
}
}
int IVsSolutionLoadEvents.OnBeforeBackgroundSolutionLoadBegins() int IVsSolutionLoadEvents.OnBeforeBackgroundSolutionLoadBegins()
{ {
return VSConstants.S_OK; return VSConstants.S_OK;
...@@ -51,6 +95,228 @@ int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete() ...@@ -51,6 +95,228 @@ int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete()
{ {
AssertIsForeground(); AssertIsForeground();
// In Non-DPL scenarios, this indicates that ASL is complete, and we should push any
// remaining information we have to the Workspace. If DPL is enabled, this is never
// called.
FinishLoad();
return VSConstants.S_OK;
}
private async Task PopulateWorkspaceFromDeferredProjectInfoAsync(
IDeferredProjectWorkspaceService deferredProjectWorkspaceService,
CancellationToken cancellationToken)
{
// NOTE: We need to check cancellationToken after each await, in case the user has
// already closed the solution.
AssertIsForeground();
var componentModel = _serviceProvider.GetService(typeof(SComponentModel)) as IComponentModel;
var workspaceProjectContextFactory = componentModel.GetService<IWorkspaceProjectContextFactory>();
var dte = _serviceProvider.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
var solutionConfig = (EnvDTE80.SolutionConfiguration2)dte.Solution.SolutionBuild.ActiveConfiguration;
OutputToOutputWindow($"Getting project information - start");
var start = DateTimeOffset.UtcNow;
// Capture the context so that we come back on the UI thread, and do the actual project creation there.
var projectInfos = await deferredProjectWorkspaceService.GetDeferredProjectInfoForConfigurationAsync(
$"{solutionConfig.Name}|{solutionConfig.PlatformName}",
cancellationToken).ConfigureAwait(true);
AssertIsForeground();
cancellationToken.ThrowIfCancellationRequested();
OutputToOutputWindow($"Getting project information - done (took {DateTimeOffset.UtcNow - start})");
OutputToOutputWindow($"Creating projects - start");
start = DateTimeOffset.UtcNow;
var targetPathsToProjectPaths = BuildTargetPathMap(projectInfos);
foreach (var projectFilename in projectInfos.Keys)
{
cancellationToken.ThrowIfCancellationRequested();
GetOrCreateProjectFromArgumentsAndReferences(
workspaceProjectContextFactory,
projectFilename,
projectInfos,
targetPathsToProjectPaths);
}
OutputToOutputWindow($"Creating projects - done (took {DateTimeOffset.UtcNow - start})");
OutputToOutputWindow($"Pushing to workspace - start");
start = DateTimeOffset.UtcNow;
FinishLoad();
OutputToOutputWindow($"Pushing to workspace - done (took {DateTimeOffset.UtcNow - start})");
}
private static ImmutableDictionary<string, string> BuildTargetPathMap(IReadOnlyDictionary<string, DeferredProjectInformation> projectInfos)
{
var builder = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var item in projectInfos)
{
var targetPath = item.Value.TargetPath;
if (targetPath != null)
{
if (!builder.ContainsKey(targetPath))
{
builder[targetPath] = item.Key;
}
else
{
Debug.Fail($"Already have a target path of '{item.Value.TargetPath}', with value '{builder[item.Value.TargetPath]}'.");
}
}
}
return builder.ToImmutable();
}
[Conditional("DEBUG")]
private void OutputToOutputWindow(string message)
{
_pane?.OutputString(message + Environment.NewLine);
}
private AbstractProject GetOrCreateProjectFromArgumentsAndReferences(
IWorkspaceProjectContextFactory workspaceProjectContextFactory,
string projectFilename,
IReadOnlyDictionary<string, DeferredProjectInformation> allProjectInfos,
IReadOnlyDictionary<string, string> targetPathsToProjectPaths)
{
var languageName = GetLanguageOfProject(projectFilename);
if (languageName == null)
{
return null;
}
DeferredProjectInformation projectInfo;
if (!allProjectInfos.TryGetValue(projectFilename, out projectInfo))
{
// This could happen if we were called recursively about a dangling P2P reference
// that isn't actually in the solution.
return null;
}
var commandLineParser = _workspaceServices.GetLanguageServices(languageName).GetService<ICommandLineParserService>();
var projectDirectory = Path.GetDirectoryName(projectFilename);
var commandLineArguments = commandLineParser.Parse(
projectInfo.CommandLineArguments,
projectDirectory,
isInteractive: false,
sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory());
// TODO: Should come from sln file?
var projectName = Path.GetFileNameWithoutExtension(projectFilename);
var projectId = GetOrCreateProjectIdForPath(projectFilename, projectName);
// See if we've already created this project and we're now in a recursive call to
// hook up a P2P ref.
AbstractProject project;
if (_projectMap.TryGetValue(projectId, out project))
{
return project;
}
OutputToOutputWindow($"\tCreating '{projectName}':\t{commandLineArguments.SourceFiles.Length} source files,\t{commandLineArguments.MetadataReferences.Length} references.");
var solution5 = _serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution5;
var projectGuid = solution5.GetGuidOfProjectFile(projectFilename);
var projectContext = workspaceProjectContextFactory.CreateProjectContext(
languageName,
projectName,
projectFilename,
projectGuid: projectGuid,
hierarchy: null,
binOutputPath: projectInfo.TargetPath);
projectContext.SetOptions(projectInfo.CommandLineArguments.Join(" "));
foreach (var sourceFile in commandLineArguments.SourceFiles)
{
projectContext.AddSourceFile(sourceFile.Path);
}
foreach (var sourceFile in commandLineArguments.AdditionalFiles)
{
projectContext.AddAdditionalFile(sourceFile.Path);
}
var addedProjectReferences = new HashSet<string>();
foreach (var projectReferencePath in projectInfo.ReferencedProjectFilePaths)
{
var referencedProject = ImmutableProjects.SingleOrDefault(p => StringComparer.OrdinalIgnoreCase.Equals(p.ProjectFilePath, projectReferencePath));
if (referencedProject == null)
{
referencedProject = GetOrCreateProjectFromArgumentsAndReferences(
workspaceProjectContextFactory,
projectReferencePath,
allProjectInfos,
targetPathsToProjectPaths);
}
var referencedProjectContext = referencedProject as IWorkspaceProjectContext;
if (referencedProjectContext != null)
{
// TODO: Can we get the properties from corresponding metadata reference in
// commandLineArguments?
addedProjectReferences.Add(projectReferencePath);
projectContext.AddProjectReference(
referencedProjectContext,
new MetadataReferenceProperties());
}
else if (referencedProject != null)
{
// This project was already created by the regular project system. See if we
// can find the matching project somehow.
var existingReferenceOutputPath = referencedProject?.BinOutputPath;
if (existingReferenceOutputPath != null)
{
addedProjectReferences.Add(projectReferencePath);
projectContext.AddMetadataReference(
existingReferenceOutputPath,
new MetadataReferenceProperties());
}
}
else
{
// We don't know how to create this project. Another language or something?
OutputToOutputWindow($"Failed to create a project for '{projectReferencePath}'.");
}
}
foreach (var reference in commandLineArguments.MetadataReferences)
{
string possibleProjectReference;
if (targetPathsToProjectPaths.TryGetValue(reference.Reference, out possibleProjectReference) &&
addedProjectReferences.Contains(possibleProjectReference))
{
// We already added a P2P reference for this, we don't need to add the file reference too.
continue;
}
projectContext.AddMetadataReference(reference.Reference, reference.Properties);
}
foreach (var reference in commandLineArguments.AnalyzerReferences)
{
projectContext.AddAnalyzerReference(reference.FilePath);
}
return (AbstractProject)projectContext;
}
private static string GetLanguageOfProject(string projectFilename)
{
switch (Path.GetExtension(projectFilename))
{
case ".csproj":
return LanguageNames.CSharp;
case ".vbproj":
return LanguageNames.VisualBasic;
default:
return null;
};
}
private void FinishLoad()
{
// We are now completely done, so let's simply ensure all projects are added. // We are now completely done, so let's simply ensure all projects are added.
StartPushingToWorkspaceAndNotifyOfOpenDocuments_Foreground(this.ImmutableProjects); StartPushingToWorkspaceAndNotifyOfOpenDocuments_Foreground(this.ImmutableProjects);
...@@ -59,8 +325,6 @@ int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete() ...@@ -59,8 +325,6 @@ int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete()
// Check that the set of analyzers is complete and consistent. // Check that the set of analyzers is complete and consistent.
GetAnalyzerDependencyCheckingService()?.CheckForConflictsAsync(); GetAnalyzerDependencyCheckingService()?.CheckForConflictsAsync();
return VSConstants.S_OK;
} }
private AnalyzerDependencyCheckingService GetAnalyzerDependencyCheckingService() private AnalyzerDependencyCheckingService GetAnalyzerDependencyCheckingService()
......
...@@ -79,7 +79,7 @@ internal static HostServices CreateHostServices(SVsServiceProvider serviceProvid ...@@ -79,7 +79,7 @@ internal static HostServices CreateHostServices(SVsServiceProvider serviceProvid
protected void InitializeStandardVisualStudioWorkspace(SVsServiceProvider serviceProvider, SaveEventsService saveEventsService) protected void InitializeStandardVisualStudioWorkspace(SVsServiceProvider serviceProvider, SaveEventsService saveEventsService)
{ {
var projectTracker = new VisualStudioProjectTracker(serviceProvider); var projectTracker = new VisualStudioProjectTracker(serviceProvider, this.Services);
// Ensure the document tracking service is initialized on the UI thread // Ensure the document tracking service is initialized on the UI thread
var documentTrackingService = this.Services.GetService<IDocumentTrackingService>(); var documentTrackingService = this.Services.GetService<IDocumentTrackingService>();
...@@ -145,8 +145,36 @@ private bool TryGetHostProject(ProjectId projectId, out IVisualStudioHostProject ...@@ -145,8 +145,36 @@ private bool TryGetHostProject(ProjectId projectId, out IVisualStudioHostProject
Microsoft.CodeAnalysis.Solution newSolution, Microsoft.CodeAnalysis.Solution newSolution,
IProgressTracker progressTracker) IProgressTracker progressTracker)
{ {
var projectChanges = newSolution.GetChanges(this.CurrentSolution).GetProjectChanges().ToList();
var projectsToLoad = new HashSet<Guid>();
foreach (var pc in projectChanges)
{
if (pc.GetAddedAdditionalDocuments().Any() ||
pc.GetAddedAnalyzerReferences().Any() ||
pc.GetAddedDocuments().Any() ||
pc.GetAddedMetadataReferences().Any() ||
pc.GetAddedProjectReferences().Any() ||
pc.GetRemovedAdditionalDocuments().Any() ||
pc.GetRemovedAnalyzerReferences().Any() ||
pc.GetRemovedDocuments().Any() ||
pc.GetRemovedMetadataReferences().Any() ||
pc.GetRemovedProjectReferences().Any())
{
projectsToLoad.Add(GetHostProject(pc.ProjectId).Guid);
}
}
if (projectsToLoad.Any())
{
var vsSolution4 = GetVsService(typeof(SVsSolution)) as IVsSolution4;
vsSolution4.EnsureProjectsAreLoaded(
(uint)projectsToLoad.Count,
projectsToLoad.ToArray(),
(uint)__VSBSLFLAGS.VSBSLFLAGS_None);
}
// first make sure we can edit the document we will be updating (check them out from source control, etc) // first make sure we can edit the document we will be updating (check them out from source control, etc)
var changedDocs = newSolution.GetChanges(this.CurrentSolution).GetProjectChanges().SelectMany(pd => pd.GetChangedDocuments()).ToList(); var changedDocs = projectChanges.SelectMany(pd => pd.GetChangedDocuments()).ToList();
if (changedDocs.Count > 0) if (changedDocs.Count > 0)
{ {
this.EnsureEditableDocuments(changedDocs); this.EnsureEditableDocuments(changedDocs);
......
...@@ -90,6 +90,7 @@ ...@@ -90,6 +90,7 @@
<Compile Include="Implementation\Preview\ReferenceChange.ProjectReferenceChange.cs" /> <Compile Include="Implementation\Preview\ReferenceChange.ProjectReferenceChange.cs" />
<Compile Include="Implementation\Preview\ReferenceChange.cs" /> <Compile Include="Implementation\Preview\ReferenceChange.cs" />
<Compile Include="Implementation\ProjectSystem\CPS\ICodeModelFactory.cs" /> <Compile Include="Implementation\ProjectSystem\CPS\ICodeModelFactory.cs" />
<Compile Include="Implementation\ProjectSystem\IDeferredProjectWorkspaceService.cs" />
<Compile Include="Implementation\ProjectSystem\Legacy\AbstractLegacyProject_ICompilerOptionsHostObject.cs" /> <Compile Include="Implementation\ProjectSystem\Legacy\AbstractLegacyProject_ICompilerOptionsHostObject.cs" />
<Compile Include="Implementation\ProjectSystem\Legacy\AbstractLegacyProject_IIntellisenseBuildTarget.cs" /> <Compile Include="Implementation\ProjectSystem\Legacy\AbstractLegacyProject_IIntellisenseBuildTarget.cs" />
<Compile Include="Implementation\ProjectSystem\CPS\IWorkspaceProjectContextFactory.cs" /> <Compile Include="Implementation\ProjectSystem\CPS\IWorkspaceProjectContextFactory.cs" />
......
...@@ -66,7 +66,7 @@ internal CodeModelProjectCache GetCodeModelCache() ...@@ -66,7 +66,7 @@ internal CodeModelProjectCache GetCodeModelCache()
public bool TryGetCachedFileCodeModel(string fileName, out ComHandle<EnvDTE80.FileCodeModel2, FileCodeModel> fileCodeModelHandle) public bool TryGetCachedFileCodeModel(string fileName, out ComHandle<EnvDTE80.FileCodeModel2, FileCodeModel> fileCodeModelHandle)
{ {
var handle = GetCodeModelCache().GetComHandleForFileCodeModel(fileName); var handle = GetCodeModelCache()?.GetComHandleForFileCodeModel(fileName);
fileCodeModelHandle = handle != null fileCodeModelHandle = handle != null
? handle.Value ? handle.Value
......
...@@ -43,13 +43,22 @@ internal sealed partial class CPSProject : AbstractProject ...@@ -43,13 +43,22 @@ internal sealed partial class CPSProject : AbstractProject
// We might we invoked from a background thread, so schedule the disconnect on foreground task scheduler. // We might we invoked from a background thread, so schedule the disconnect on foreground task scheduler.
public sealed override void Disconnect() public sealed override void Disconnect()
{ {
InvokeBelowInputPriority(() => if (IsForeground())
{ {
// clear code model cache and shutdown instances, if any exists. DisconnectCore();
_projectCodeModel?.OnProjectClosed(); }
else
{
InvokeBelowInputPriority(DisconnectCore);
}
}
private void DisconnectCore()
{
// clear code model cache and shutdown instances, if any exists.
_projectCodeModel?.OnProjectClosed();
base.Disconnect(); base.Disconnect();
});
} }
} }
} }
...@@ -59,13 +59,9 @@ internal static CPSProject CreateCPSProject(VisualStudioProjectTracker projectTr ...@@ -59,13 +59,9 @@ internal static CPSProject CreateCPSProject(VisualStudioProjectTracker projectTr
object hierarchy, object hierarchy,
string binOutputPath) string binOutputPath)
{ {
Contract.ThrowIfNull(hierarchy); // NOTE: It is acceptable for hierarchy to be null in Deferred Project Load scenarios.
var vsHierarchy = hierarchy as IVsHierarchy; var vsHierarchy = hierarchy as IVsHierarchy;
if (vsHierarchy == null)
{
throw new ArgumentException(nameof(hierarchy));
}
Func<ProjectId, IVsReportExternalErrors> getExternalErrorReporter = id => GetExternalErrorReporter(id, languageName); Func<ProjectId, IVsReportExternalErrors> getExternalErrorReporter = id => GetExternalErrorReporter(id, languageName);
return new CPSProject(_visualStudioWorkspace.ProjectTracker, getExternalErrorReporter, projectDisplayName, projectFilePath, return new CPSProject(_visualStudioWorkspace.ProjectTracker, getExternalErrorReporter, projectDisplayName, projectFilePath,
vsHierarchy, languageName, projectGuid, binOutputPath, _serviceProvider, _visualStudioWorkspace, _hostDiagnosticUpdateSource, vsHierarchy, languageName, projectGuid, binOutputPath, _serviceProvider, _visualStudioWorkspace, _hostDiagnosticUpdateSource,
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -108,29 +109,14 @@ public new void RemoveMetadataReference(string referencePath) ...@@ -108,29 +109,14 @@ public new void RemoveMetadataReference(string referencePath)
public void AddProjectReference(IWorkspaceProjectContext project, MetadataReferenceProperties properties) public void AddProjectReference(IWorkspaceProjectContext project, MetadataReferenceProperties properties)
{ {
var abstractProject = GetAbstractProject(project); var abstractProject = GetAbstractProject(project);
AddProjectReference(new ProjectReference(abstractProject.Id, properties.Aliases, properties.EmbedInteropTypes));
// AbstractProject and ProjectTracker track project references using the project bin output path.
// Setting the command line arguments should have already set the output file name and folder.
// We fetch this output path to add the reference.
var referencedProject = this.ProjectTracker.GetProject(abstractProject.Id);
var binPathOpt = referencedProject.BinOutputPath;
if (!string.IsNullOrEmpty(binPathOpt))
{
AddMetadataReferenceAndTryConvertingToProjectReferenceIfPossible(binPathOpt, properties);
}
} }
public void RemoveProjectReference(IWorkspaceProjectContext project) public void RemoveProjectReference(IWorkspaceProjectContext project)
{ {
var referencedProject = GetAbstractProject(project); var referencedProject = GetAbstractProject(project);
var projectReference = GetCurrentProjectReferences().Single(p => p.ProjectId == referencedProject.Id);
// AbstractProject and ProjectTracker track project references using the project bin output path. RemoveProjectReference(projectReference);
// We fetch this output path to remove the reference.
var binPathOpt = referencedProject.BinOutputPath;
if (!string.IsNullOrEmpty(binPathOpt))
{
base.RemoveMetadataReference(binPathOpt);
}
} }
private AbstractProject GetAbstractProject(IWorkspaceProjectContext project) private AbstractProject GetAbstractProject(IWorkspaceProjectContext project)
...@@ -148,7 +134,7 @@ private AbstractProject GetAbstractProject(IWorkspaceProjectContext project) ...@@ -148,7 +134,7 @@ private AbstractProject GetAbstractProject(IWorkspaceProjectContext project)
#region Files #region Files
public void AddSourceFile(string filePath, bool isInCurrentContext = true, IEnumerable<string> folderNames = null, SourceCodeKind sourceCodeKind = SourceCodeKind.Regular) public void AddSourceFile(string filePath, bool isInCurrentContext = true, IEnumerable<string> folderNames = null, SourceCodeKind sourceCodeKind = SourceCodeKind.Regular)
{ {
AddFile(filePath, sourceCodeKind, getIsCurrentContext: _ => isInCurrentContext, folderNames: folderNames.ToImmutableArrayOrEmpty()); AddFile(filePath, sourceCodeKind, _ => isInCurrentContext, _ => folderNames.ToImmutableArrayOrEmpty());
} }
public void RemoveSourceFile(string filePath) public void RemoveSourceFile(string filePath)
......
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Workspace.Extensions;
using Microsoft.VisualStudio.Workspace.VSIntegration;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem
{
[ExportWorkspaceService(typeof(IDeferredProjectWorkspaceService)), Shared]
internal class DeferredProjectWorkspaceService : IDeferredProjectWorkspaceService
{
private readonly IVsSolutionWorkspaceService _solutionWorkspaceService;
private readonly IVsSolution7 _solution7;
[ImportingConstructor]
public DeferredProjectWorkspaceService(SVsServiceProvider serviceProvider)
{
_solutionWorkspaceService = serviceProvider.GetService(typeof(SVsSolutionWorkspaceService)) as IVsSolutionWorkspaceService;
_solution7 = serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution7;
}
public bool IsDeferredProjectLoadEnabled => _solution7?.IsSolutionLoadDeferred() == true;
public async Task<IReadOnlyDictionary<string, DeferredProjectInformation>> GetDeferredProjectInfoForConfigurationAsync(
string solutionConfiguration,
CancellationToken cancellationToken)
{
var commandLineInfos = await _solutionWorkspaceService.GetManagedCommandLineInfoAsync(
solutionConfiguration, cancellationToken).ConfigureAwait(false);
// NOTE: Anycode gives us the project references as if they were command line arguments with
// "/ProjectReference:" prepended. Strip that off here.
var result = ImmutableDictionary.CreateRange(
commandLineInfos.Select(kvp => KeyValuePair.Create(
kvp.Key,
new DeferredProjectInformation(
kvp.Value.TargetPath,
kvp.Value.CommandLineArgs,
kvp.Value.ProjectReferences.Select(p => p.Substring("/ProjectReference:".Length)).ToImmutableArray()))));
return result;
}
}
}
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
namespace Microsoft.VisualStudio.LanguageServices.Remote namespace Microsoft.VisualStudio.LanguageServices.Remote
{ {
using Workspace = Microsoft.CodeAnalysis.Workspace;
[ExportWorkspaceService(typeof(IRemoteHostClientFactory)), Shared] [ExportWorkspaceService(typeof(IRemoteHostClientFactory)), Shared]
internal class RemoteHostClientFactory : IRemoteHostClientFactory internal class RemoteHostClientFactory : IRemoteHostClientFactory
{ {
...@@ -28,4 +30,4 @@ public Task<RemoteHostClient> CreateAsync(Workspace workspace, CancellationToken ...@@ -28,4 +30,4 @@ public Task<RemoteHostClient> CreateAsync(Workspace workspace, CancellationToken
} }
} }
} }
} }
\ No newline at end of file
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
namespace Microsoft.VisualStudio.LanguageServices.Remote namespace Microsoft.VisualStudio.LanguageServices.Remote
{ {
using Workspace = Microsoft.CodeAnalysis.Workspace;
internal partial class ServiceHubRemoteHostClient : RemoteHostClient internal partial class ServiceHubRemoteHostClient : RemoteHostClient
{ {
private readonly HubClient _hubClient; private readonly HubClient _hubClient;
......
...@@ -106,6 +106,7 @@ ...@@ -106,6 +106,7 @@
<Compile Include="FindReferences\StreamingFindReferencesPresenter.TableDataSourceFindReferencesContext.cs" /> <Compile Include="FindReferences\StreamingFindReferencesPresenter.TableDataSourceFindReferencesContext.cs" />
<Compile Include="FindReferences\StreamingFindReferencesPresenter.TableEntriesSnapshot.cs" /> <Compile Include="FindReferences\StreamingFindReferencesPresenter.TableEntriesSnapshot.cs" />
<Compile Include="FindReferences\TaggedTextAndHighlightSpan.cs" /> <Compile Include="FindReferences\TaggedTextAndHighlightSpan.cs" />
<Compile Include="ProjectSystem\DeferredProjectWorkspaceService.cs" />
<Compile Include="Remote\JsonRpcClient.cs" /> <Compile Include="Remote\JsonRpcClient.cs" />
<Compile Include="Remote\JsonRpcSession.cs" /> <Compile Include="Remote\JsonRpcSession.cs" />
<Compile Include="Remote\RemoteHostClientFactory.cs" /> <Compile Include="Remote\RemoteHostClientFactory.cs" />
...@@ -130,4 +131,4 @@ ...@@ -130,4 +131,4 @@
<InternalsVisibleToTest Include="Roslyn.VisualStudio.Next.UnitTests" /> <InternalsVisibleToTest Include="Roslyn.VisualStudio.Next.UnitTests" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\..\build\Targets\VSL.Imports.targets" /> <Import Project="..\..\..\..\build\Targets\VSL.Imports.targets" />
</Project> </Project>
\ No newline at end of file
...@@ -12,9 +12,11 @@ ...@@ -12,9 +12,11 @@
}, },
"Microsoft.ServiceHub.Client": "1.0.103-rc", "Microsoft.ServiceHub.Client": "1.0.103-rc",
"Newtonsoft.Json": "8.0.3", "Newtonsoft.Json": "8.0.3",
"RoslynDependencies.Microsoft.VisualStudio.Shell.Interop.15.0.DesignTime": "15.0.25718-Preview5",
"RoslynDependencies.Microsoft.VisualStudio.Workspace": "14.0.983-pre-ge167e81694",
"System.Collections.Immutable": "1.2.0" "System.Collections.Immutable": "1.2.0"
}, },
"frameworks": { "frameworks": {
"net46": { } "net46": { }
} }
} }
\ No newline at end of file
...@@ -38,8 +38,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr ...@@ -38,8 +38,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr
_monitorSelectionMock = New MockShellMonitorSelection(solutionIsFullyLoaded) _monitorSelectionMock = New MockShellMonitorSelection(solutionIsFullyLoaded)
_serviceProvider = New MockServiceProvider(_monitorSelectionMock) _serviceProvider = New MockServiceProvider(_monitorSelectionMock)
_projectTracker = New VisualStudioProjectTracker(_serviceProvider)
_workspace = New TestWorkspace() _workspace = New TestWorkspace()
_projectTracker = New VisualStudioProjectTracker(_serviceProvider, _workspace.Services)
Dim metadataReferenceProvider = New VisualStudioMetadataReferenceManager(_serviceProvider, _workspace.Services.GetService(Of ITemporaryStorageService)()) Dim metadataReferenceProvider = New VisualStudioMetadataReferenceManager(_serviceProvider, _workspace.Services.GetService(Of ITemporaryStorageService)())
Dim ruleSetFileProvider = New VisualStudioRuleSetManager( Dim ruleSetFileProvider = New VisualStudioRuleSetManager(
DirectCast(_serviceProvider.GetService(GetType(SVsFileChangeEx)), IVsFileChangeEx), DirectCast(_serviceProvider.GetService(GetType(SVsFileChangeEx)), IVsFileChangeEx),
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeLens;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Remote.Diagnostics;
using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions;
using Microsoft.VisualStudio.LanguageServices.Remote;
namespace Microsoft.VisualStudio.LanguageServices.CodeLens
{
[ExportWorkspaceService(typeof(ICodeLensReferencesService), layer: ServiceLayer.Host), Shared]
internal sealed class RemoteCodeLensReferencesService : ICodeLensReferencesService
{
public async Task<ReferenceCount> GetReferenceCountAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, int maxSearchResults,
CancellationToken cancellationToken)
{
var remoteHostClient = await solution.Workspace.Services.GetService<IRemoteHostClientService>().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
if (remoteHostClient == null)
{
// remote host is not running. this can happen if remote host is disabled.
return await CodeLensReferencesServiceFactory.Instance.GetReferenceCountAsync(solution, documentId, syntaxNode, maxSearchResults, cancellationToken).ConfigureAwait(false);
}
// TODO: send telemetry on session
using (var session = await remoteHostClient.CreateCodeAnalysisServiceSessionAsync(solution, cancellationToken).ConfigureAwait(false))
{
return await session.InvokeAsync<ReferenceCount>(WellKnownServiceHubServices.CodeAnalysisService_GetReferenceCountAsync, new CodeLensArguments(documentId, syntaxNode), maxSearchResults).ConfigureAwait(false);
}
}
public async Task<IEnumerable<ReferenceLocationDescriptor>> FindReferenceLocationsAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode,
CancellationToken cancellationToken)
{
var remoteHostClient = await solution.Workspace.Services.GetService<IRemoteHostClientService>().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
if (remoteHostClient == null)
{
// remote host is not running. this can happen if remote host is disabled.
return await CodeLensReferencesServiceFactory.Instance.FindReferenceLocationsAsync(solution, documentId, syntaxNode, cancellationToken).ConfigureAwait(false);
}
// TODO: send telemetry on session
using (var session = await remoteHostClient.CreateCodeAnalysisServiceSessionAsync(solution, cancellationToken).ConfigureAwait(false))
{
return await session.InvokeAsync<IEnumerable<ReferenceLocationDescriptor>>(WellKnownServiceHubServices.CodeAnalysisService_FindReferenceLocationsAsync, new CodeLensArguments(documentId, syntaxNode)).ConfigureAwait(false);
}
}
public async Task<IEnumerable<ReferenceMethodDescriptor>> FindReferenceMethodsAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode,
CancellationToken cancellationToken)
{
var remoteHostClient = await solution.Workspace.Services.GetService<IRemoteHostClientService>().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
if (remoteHostClient == null)
{
// remote host is not running. this can happen if remote host is disabled.
return await CodeLensReferencesServiceFactory.Instance.FindReferenceMethodsAsync(solution, documentId, syntaxNode, cancellationToken).ConfigureAwait(false);
}
// TODO: send telemetry on session
using (var session = await remoteHostClient.CreateCodeAnalysisServiceSessionAsync(solution, cancellationToken).ConfigureAwait(false))
{
return await session.InvokeAsync<IEnumerable<ReferenceMethodDescriptor>>(WellKnownServiceHubServices.CodeAnalysisService_FindReferenceMethodsAsync, new CodeLensArguments(documentId, syntaxNode)).ConfigureAwait(false);
}
}
public async Task<string> GetFullyQualifiedName(Solution solution, DocumentId documentId, SyntaxNode syntaxNode,
CancellationToken cancellationToken)
{
var remoteHostClient = await solution.Workspace.Services.GetService<IRemoteHostClientService>().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
if (remoteHostClient == null)
{
// remote host is not running. this can happen if remote host is disabled.
return await CodeLensReferencesServiceFactory.Instance.GetFullyQualifiedName(solution, documentId, syntaxNode, cancellationToken).ConfigureAwait(false);
}
// TODO: send telemetry on session
using (var session = await remoteHostClient.CreateCodeAnalysisServiceSessionAsync(solution, cancellationToken).ConfigureAwait(false))
{
return await session.InvokeAsync<string>(WellKnownServiceHubServices.CodeAnalysisService_GetFullyQualifiedName, new CodeLensArguments(documentId, syntaxNode)).ConfigureAwait(false);
}
}
}
}
...@@ -4,7 +4,9 @@ Imports Microsoft.CodeAnalysis ...@@ -4,7 +4,9 @@ Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host
Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem Imports Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
Imports Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Legacy
Imports Microsoft.VisualStudio.LanguageServices.Implementation.TaskList Imports Microsoft.VisualStudio.LanguageServices.Implementation.TaskList
Imports Microsoft.VisualStudio.LanguageServices.ProjectSystem
Imports Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim Imports Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim
Imports Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim.Interop Imports Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim.Interop
Imports Microsoft.VisualStudio.Shell.Interop Imports Microsoft.VisualStudio.Shell.Interop
...@@ -19,17 +21,19 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic ...@@ -19,17 +21,19 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic
End Function End Function
Public Function CreateProject(wszName As String, punkProject As Object, pProjHier As IVsHierarchy, pVbCompilerHost As IVbCompilerHost) As IVbCompilerProject Implements IVbCompiler.CreateProject Public Function CreateProject(wszName As String, punkProject As Object, pProjHier As IVsHierarchy, pVbCompilerHost As IVbCompilerHost) As IVbCompilerProject Implements IVbCompiler.CreateProject
Dim visualStudioWorkspace = ComponentModel.GetService(Of VisualStudioWorkspaceImpl)()
Dim hostDiagnosticUpdateSource = ComponentModel.GetService(Of HostDiagnosticUpdateSource)() Dim hostDiagnosticUpdateSource = ComponentModel.GetService(Of HostDiagnosticUpdateSource)()
Workspace.ProjectTracker.TryDisconnectExistingDeferredProject(pProjHier, wszName)
Return New VisualBasicProjectShimWithServices( Return New VisualBasicProjectShimWithServices(
visualStudioWorkspace.ProjectTracker, Workspace.ProjectTracker,
pVbCompilerHost, pVbCompilerHost,
wszName, wszName,
pProjHier, pProjHier,
Me, Me,
visualStudioWorkspace, Workspace,
hostDiagnosticUpdateSource, hostDiagnosticUpdateSource,
commandLineParserServiceOpt:=visualStudioWorkspace.Services.GetLanguageServices(LanguageNames.VisualBasic).GetService(Of ICommandLineParserService)) commandLineParserServiceOpt:=Workspace.Services.GetLanguageServices(LanguageNames.VisualBasic).GetService(Of ICommandLineParserService))
End Function End Function
Public Function IsValidIdentifier(wszIdentifier As String) As Boolean Implements IVbCompiler.IsValidIdentifier Public Function IsValidIdentifier(wszIdentifier As String) As Boolean Implements IVbCompiler.IsValidIdentifier
......
...@@ -104,7 +104,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim ...@@ -104,7 +104,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim
' We trust the project system to only tell us about files that we can use. ' We trust the project system to only tell us about files that we can use.
Dim canUseTextBuffer As Func(Of ITextBuffer, Boolean) = Function(t) True Dim canUseTextBuffer As Func(Of ITextBuffer, Boolean) = Function(t) True
MyBase.AddFile(wszFileName, SourceCodeKind.Regular, itemid) MyBase.AddFile(wszFileName, SourceCodeKind.Regular)
Catch e As Exception When FilterException(e) Catch e As Exception When FilterException(e)
Throw ExceptionUtilities.Unreachable Throw ExceptionUtilities.Unreachable
End Try End Try
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudio.Utilities;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Xaml namespace Microsoft.VisualStudio.LanguageServices.Xaml
{ {
...@@ -142,7 +143,9 @@ private AbstractProject GetXamlProject(IVsHierarchy hierarchy) ...@@ -142,7 +143,9 @@ private AbstractProject GetXamlProject(IVsHierarchy hierarchy)
private bool TryCreateXamlDocument(AbstractProject project, string filePath, out IVisualStudioHostDocument vsDocument) private bool TryCreateXamlDocument(AbstractProject project, string filePath, out IVisualStudioHostDocument vsDocument)
{ {
vsDocument = _vsWorkspace.ProjectTracker.DocumentProvider.TryGetDocumentForFile( vsDocument = _vsWorkspace.ProjectTracker.DocumentProvider.TryGetDocumentForFile(
project, ImmutableArray<string>.Empty, filePath, SourceCodeKind.Regular, tb => tb.ContentType.IsOfType(ContentTypeNames.XamlContentType)); project, filePath, SourceCodeKind.Regular,
tb => tb.ContentType.IsOfType(ContentTypeNames.XamlContentType),
_ => SpecializedCollections.EmptyReadOnlyList<string>());
return vsDocument != null; return vsDocument != null;
} }
......
...@@ -14,6 +14,21 @@ namespace Roslyn.Utilities ...@@ -14,6 +14,21 @@ namespace Roslyn.Utilities
[SuppressMessage("ApiDesign", "CA1068", Justification = "Matching TPL Signatures")] [SuppressMessage("ApiDesign", "CA1068", Justification = "Matching TPL Signatures")]
internal static partial class TaskExtensions internal static partial class TaskExtensions
{ {
/// <summary>
/// Use to explicitly indicate that you are not waiting for a task to complete
/// Observes the exceptions from it.
/// </summary>
public static async void FireAndForget(this Task task)
{
try
{
await task.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
}
}
public static T WaitAndGetResult<T>(this Task<T> task, CancellationToken cancellationToken) public static T WaitAndGetResult<T>(this Task<T> task, CancellationToken cancellationToken)
{ {
#if DEBUG #if DEBUG
......
...@@ -26,4 +26,4 @@ internal static class RoslynServices ...@@ -26,4 +26,4 @@ internal static class RoslynServices
public static readonly SolutionService SolutionService = new SolutionService(); public static readonly SolutionService SolutionService = new SolutionService();
public static readonly CompilationService CompilationService = new CompilationService(); public static readonly CompilationService CompilationService = new CompilationService();
} }
} }
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册