未验证 提交 c20eae14 编写于 作者: J Jason Malinowski 提交者: GitHub

Merge pull request #26931 from jasonmalinowski/free-threaded-project-system-api

This produces a new free-threaded, well factored API for adding projects to the
VisualStudioWorkspace. The core type is the VisualStudioProject which is a
free-threaded API that you can use to push information over. CSharpProjectShim,
VisualBasicProject and CPSProject each have an instance of VisualStudioProject
that they push things through.

This also introduces a proper batching API: any consumer of VisualStudioProject
may call CreateBatchScope() which means all future operations are put a single
batch that will be applied at once in a single workspace change. Multiple calls
to CreateBatchScope just stack: it only applies when all scopes are closed. You
could think of the behavior as roughly analogous to an SQL transaction, except
with no rollback capabilities. A batch is only per-project: a batch started on
one project doesn't impact another project. There is no concept of a
cross-project batch, short of just starting two independent batches and
completing them at the same time (which creates two workspace changes.)

The inheritance model here is now smaller. CSharpProjectShim and
VisualBasicProject inherit from AbstractLegacyProject, but that's it. CPSProject
now inherits nothing, and AbstractProject is here purely for TypeScript
back-compat until they're moved onto the new APIs. The expectation is F# and
TypeScript both move to VisualStudioProject, which we make public in some form.
Then AbstractProject will go away.

AbstractLegacyProject (which is used for csproj and vbproj) still is tied to the
UI thread, but the ties should now be more limited and better understood. The
primary tangles right now are having to fetch a few things from IVsHierarchies,
namely some project properties and the folder structure. The plan for now is
AbstractLegacyProject-derived projects will still be called on the UI thread,
and we'll determine a new API to remove those remaining issues for them (or just
move them onto VisualStudioProject.)

The threading guarantee given to all consumers of VisualStudioProject is it can
be called from any thread, and no synchronous calls to the UI thread may be done
at that time. There are a few places where we asynchronously kick off work to
inspect the running document table, but those are async and should never block.

There is a lock hierarchy now instituted: each VisualStudioProject has a lock
which it takes when any method is called on it, this is to ensure
straightforward synchronization of it's data for things like batch
start/stopping, what's in the batch, etc. There is also a lock in
VisualStudioWorkspaceImpl that is acquired when actually modifying the
workspace, which is done through an internal ApplyChangeToWorkspace method so it
can be called from VisualStudioProject more easily. It is permissible for a
VisualStudioProject to call ApplyChangeToWorkspace to acquire the
VisualStudioWorkspaceImpl lock. It is _not_ permissible for a lock holder of
VisualStudioWorkspaceImpl's lock to acquire any project lock. To this end, some
project-to-project tracking information is held in VisualStudioWorkspaceImpl to
avoid mistakes -- although the data is a "per project" concept, putting it in
the project would cause people to take a project lock and risk deadlocks.
......@@ -134,6 +134,7 @@
<MicrosoftVisualStudioShellInterop100Version>10.0.30320</MicrosoftVisualStudioShellInterop100Version>
<MicrosoftVisualStudioShellInterop110Version>11.0.61031</MicrosoftVisualStudioShellInterop110Version>
<MicrosoftVisualStudioShellInterop121DesignTimeVersion>12.1.30328</MicrosoftVisualStudioShellInterop121DesignTimeVersion>
<MicrosoftVisualStudioShellInterop157DesignTimeVersion>15.7.1</MicrosoftVisualStudioShellInterop157DesignTimeVersion>
<MicrosoftVisualStudioShellInterop80Version>8.0.50728</MicrosoftVisualStudioShellInterop80Version>
<MicrosoftVisualStudioTelemetryVersion>15.8.27812-alpha</MicrosoftVisualStudioTelemetryVersion>
<MicrosoftVisualStudioTemplateWizardInterfaceVersion>8.0.0.0-alpha</MicrosoftVisualStudioTemplateWizardInterfaceVersion>
......
......@@ -404,9 +404,10 @@ public void TestRemoveOpenedDocument()
workspace.AddTestProject(project1);
workspace.OnDocumentOpened(document.Id, document.GetOpenTextContainer());
Assert.Throws<ArgumentException>(() => workspace.OnDocumentRemoved(document.Id));
workspace.OnDocumentRemoved(document.Id);
Assert.Empty(workspace.CurrentSolution.Projects.Single().Documents);
workspace.CloseDocument(document.Id);
workspace.OnProjectRemoved(project1.Id);
}
}
......
......@@ -94,8 +94,6 @@ private void OnWorkspaceRegistrationChanged(object sender, EventArgs e)
private void ConnectToWorkspace(Workspace workspace)
{
AssertIsForeground();
// If we disconnected before the workspace ever connected, just disregard
if (_disconnected)
{
......@@ -105,10 +103,28 @@ private void ConnectToWorkspace(Workspace workspace)
_workspace = workspace;
_workspace.WorkspaceChanged += this.OnWorkspaceChanged;
void connectToNewWorkspace()
{
// For the first time you open the file, we'll start immediately
StartModelUpdateAndSelectedItemUpdateTasks(modelUpdateDelay: 0, selectedItemUpdateDelay: 0, updateUIWhenDone: true);
}
if (IsForeground())
{
connectToNewWorkspace();
}
else
{
var asyncToken = _asyncListener.BeginAsyncOperation(nameof(ConnectToWorkspace));
Task.Run(async () =>
{
await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync();
connectToNewWorkspace();
}).CompletesAsyncOperation(asyncToken);
}
}
private void DisconnectFromWorkspace()
{
if (_workspace != null)
......
......@@ -2,6 +2,7 @@
using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop;
......@@ -16,16 +17,12 @@ 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 projectTracker = Workspace.GetProjectTrackerAndInitializeIfNecessary(SystemServiceProvider);
var project = new CSharpProjectShim(
projectRoot,
projectTracker,
id => new ProjectExternalErrorReporter(id, "CS", this.SystemServiceProvider),
projectName,
hierarchy,
this.SystemServiceProvider,
this.Workspace,
this.Package.ComponentModel.GetService<IThreadingContext>(),
this.HostDiagnosticUpdateSource,
this.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService<ICommandLineParserService>());
......
......@@ -49,8 +49,7 @@ public void BuildForEnc(ICSCompileProgress progress, ICSEncProjectServices encSe
public string GetOutputFileName()
{
// in-proc compiler, return the output we got set.
return this.ObjOutputPath;
return VisualStudioProject.IntermediateOutputFilePath;
}
public object CreateParser()
......
......@@ -12,7 +12,7 @@ public int GetOptionCount()
{
// NOTE: We return the length minus 1 to ensure that we're never called
// with LARGEST_OPTION_ID.
return _options.Length - 1;
return (int)CompilerOptions.LARGEST_OPTION_ID - 1;
}
public void GetOptionInfoAt(int index, out CompilerOptions optionID, out string switchName, out string switchDescription, out uint flags)
......@@ -27,23 +27,18 @@ public void GetOptionInfoAtEx(int index, out CompilerOptions optionID, out strin
public void ResetAllOptions()
{
_options[(int)CompilerOptions.OPTID_CCSYMBOLS] = string.Empty;
_options[(int)CompilerOptions.OPTID_KEYFILE] = string.Empty;
_options[(int)CompilerOptions.OPTID_NOWARNLIST] = string.Empty;
_options[(int)CompilerOptions.OPTID_WARNASERRORLIST] = string.Empty;
_options[(int)CompilerOptions.OPTID_WARNNOTASERRORLIST] = string.Empty;
_options[(int)CompilerOptions.OPTID_UNSAFE] = false;
_options[(int)CompilerOptions.OPTID_XML_DOCFILE] = string.Empty;
VisualStudioProjectOptionsProcessor[CompilerOptions.OPTID_CCSYMBOLS] = string.Empty;
VisualStudioProjectOptionsProcessor[CompilerOptions.OPTID_KEYFILE] = string.Empty;
VisualStudioProjectOptionsProcessor[CompilerOptions.OPTID_NOWARNLIST] = string.Empty;
VisualStudioProjectOptionsProcessor[CompilerOptions.OPTID_WARNASERRORLIST] = string.Empty;
VisualStudioProjectOptionsProcessor[CompilerOptions.OPTID_WARNNOTASERRORLIST] = string.Empty;
VisualStudioProjectOptionsProcessor[CompilerOptions.OPTID_UNSAFE] = false;
VisualStudioProjectOptionsProcessor[CompilerOptions.OPTID_XML_DOCFILE] = string.Empty;
}
public int SetOption(CompilerOptions optionID, HACK_VariantStructure value)
{
return SetOptionWithMarshaledValue(optionID, value.ConvertToObject());
}
public int SetOptionWithMarshaledValue(CompilerOptions optionID, object value)
{
SetOption(ref _options[(int)optionID], value);
VisualStudioProjectOptionsProcessor[optionID] = value.ConvertToObject();
if (optionID == CompilerOptions.OPTID_COMPATIBILITY)
{
......@@ -60,12 +55,7 @@ public int SetOptionWithMarshaledValue(CompilerOptions optionID, object value)
public void GetOption(CompilerOptions optionID, IntPtr variant)
{
if (optionID < 0 || optionID >= CompilerOptions.LARGEST_OPTION_ID)
{
throw new ArgumentOutOfRangeException(nameof(optionID));
}
Marshal.GetNativeVariantForObject(_options[(int)optionID], variant);
Marshal.GetNativeVariantForObject(VisualStudioProjectOptionsProcessor[optionID], variant);
}
public int CommitChanges(ref ICSError error)
......
// 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 Microsoft.CodeAnalysis;
using System.IO;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop;
namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim
......@@ -45,44 +45,17 @@ public void SetWin32Resource(string filename)
public void SetOutputFileName(string filename)
{
SetOutputPathAndRelatedData(filename);
}
VisualStudioProject.IntermediateOutputFilePath = filename;
public void SetOutputFileType(OutputFileType fileType)
{
OutputKind newOutputKind;
switch (fileType)
if (filename != null)
{
case OutputFileType.Console:
newOutputKind = OutputKind.ConsoleApplication;
break;
case OutputFileType.Windows:
newOutputKind = OutputKind.WindowsApplication;
break;
case OutputFileType.Library:
newOutputKind = OutputKind.DynamicallyLinkedLibrary;
break;
case OutputFileType.Module:
newOutputKind = OutputKind.NetModule;
break;
case OutputFileType.AppContainer:
newOutputKind = OutputKind.WindowsRuntimeApplication;
break;
case OutputFileType.WinMDObj:
newOutputKind = OutputKind.WindowsRuntimeMetadata;
break;
default:
throw new ArgumentException("fileType was not a valid OutputFileType", nameof(fileType));
VisualStudioProject.AssemblyName = Path.GetFileNameWithoutExtension(filename);
}
}
SetOption(ref _outputKind, newOutputKind);
public void SetOutputFileType(OutputFileType fileType)
{
VisualStudioProjectOptionsProcessor.SetOutputFileType(fileType);
}
public void SetImageBase(uint imageBase)
......@@ -92,7 +65,7 @@ public void SetImageBase(uint imageBase)
public void SetMainClass(string fullyQualifiedClassName)
{
SetOption(ref _mainTypeName, fullyQualifiedClassName);
VisualStudioProjectOptionsProcessor.SetMainTypeName(fullyQualifiedClassName);
}
public void SetWin32Icon(string iconFileName)
......@@ -124,14 +97,5 @@ public void SetWin32Manifest(string manifestFileName)
{
// This option is used only during emit. Since we no longer use our in-proc workspace to emit, we can ignore this value.
}
private void SetOption<T>(ref T value, T newValue)
{
if (!object.Equals(value, newValue))
{
value = newValue;
UpdateOptions();
}
}
}
}
// 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 Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim
......@@ -49,22 +44,11 @@ public void Unused()
public void OnSourceFileAdded(string filename)
{
var extension = Path.GetExtension(filename);
// The Workflow MSBuild targets and CompileWorkflowTask choose to pass the .xoml files to the language
// service as if they were actual C# files. We should just ignore them.
if (extension.Equals(".xoml", StringComparison.OrdinalIgnoreCase))
{
AddUntrackedFile(filename);
return;
}
// TODO: uncomment when fixing https://github.com/dotnet/roslyn/issues/5325
//var sourceCodeKind = extension.Equals(".csx", StringComparison.OrdinalIgnoreCase)
// ? SourceCodeKind.Script
// : SourceCodeKind.Regular;
var sourceCodeKind = SourceCodeKind.Regular;
AddFile(filename, sourceCodeKind);
AddFile(filename, SourceCodeKind.Regular);
}
public void OnSourceFileRemoved(string filename)
......@@ -91,34 +75,32 @@ public int OnImportAdded(string filename, string project)
public int OnImportAddedEx(string filename, string project, CompilerOptions optionID)
{
filename = FileUtilities.NormalizeAbsolutePath(filename);
if (optionID != CompilerOptions.OPTID_IMPORTS && optionID != CompilerOptions.OPTID_IMPORTSUSINGNOPIA)
{
throw new ArgumentException("optionID was an unexpected value.", nameof(optionID));
}
bool embedInteropTypes = optionID == CompilerOptions.OPTID_IMPORTSUSINGNOPIA;
var properties = new MetadataReferenceProperties(embedInteropTypes: embedInteropTypes);
VisualStudioProject.AddMetadataReference(filename, new MetadataReferenceProperties(embedInteropTypes: embedInteropTypes));
return AddMetadataReferenceAndTryConvertingToProjectReferenceIfPossible(filename, properties);
return VSConstants.S_OK;
}
public void OnImportRemoved(string filename, string project)
{
filename = FileUtilities.NormalizeAbsolutePath(filename);
RemoveMetadataReference(filename);
VisualStudioProject.RemoveMetadataReference(filename, VisualStudioProject.GetPropertiesForMetadataReference(filename).Single());
}
public void OnOutputFileChanged(string filename)
{
// We have nothing to do here (yet)
// We have nothing to do here
}
public void OnActiveConfigurationChanged(string configName)
{
// We have nothing to do here (yet)
// We have nothing to do here
}
public void OnProjectLoadCompletion()
......@@ -153,10 +135,10 @@ public int GetValidStartupClasses(IntPtr[] classNames, ref int count)
{
// If classNames is NULL, then we need to populate the number of valid startup
// classes only
var project = Workspace.CurrentSolution.GetProject(Id);
var project = Workspace.CurrentSolution.GetProject(VisualStudioProject.Id);
var compilation = project.GetCompilationAsync(CancellationToken.None).WaitAndGetResult(CancellationToken.None);
var entryPoints = GetEntryPoints(project, compilation);
var entryPoints = EntryPointFinder.FindEntryPoints(compilation.Assembly.GlobalNamespace);
if (classNames == null)
{
......@@ -165,7 +147,7 @@ public int GetValidStartupClasses(IntPtr[] classNames, ref int count)
}
else
{
// We return s_false if we have more entrypoints than places in the array.
// We return S_FALSE if we have more entrypoints than places in the array.
var entryPointNames = entryPoints.Select(e => e.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted))).ToArray();
if (entryPointNames.Length > classNames.Length)
......@@ -194,14 +176,14 @@ public int GetValidStartupClasses(IntPtr[] classNames, ref int count)
}
}
private IEnumerable<INamedTypeSymbol> GetEntryPoints(Project project, Compilation compilation)
{
return EntryPointFinder.FindEntryPoints(compilation.Assembly.GlobalNamespace);
}
public void OnAliasesChanged(string file, string project, int previousAliasesCount, string[] previousAliases, int currentAliasesCount, string[] currentAliases)
{
UpdateMetadataReferenceAliases(file, ImmutableArray.CreateRange(currentAliases));
using (VisualStudioProject.CreateBatchScope())
{
var existingProperties = VisualStudioProject.GetPropertiesForMetadataReference(file).Single();
VisualStudioProject.RemoveMetadataReference(file, existingProperties);
VisualStudioProject.AddMetadataReference(file, existingProperties.WithAliases(currentAliases));
}
}
}
}
......@@ -19,16 +19,16 @@ public void RemoveReferenceToCodeDirectory(string assemblyFileName, ICSharpProje
{
CSharpProjectShim projectSite = GetProjectSite(project);
if (!this.CurrentProjectReferencesContains(projectSite.Id))
var projectReferencesToRemove = VisualStudioProject.GetProjectReferences().Where(p => p.ProjectId == projectSite.VisualStudioProject.Id).ToList();
if (projectReferencesToRemove.Count == 0)
{
throw new ArgumentException("The finalProject reference is not currently referenced by this finalProject.", "finalProject");
throw new ArgumentException($"The project {nameof(project)} is not currently referenced by this project.");
}
var projectReferences = GetCurrentProjectReferences().Where(r => r.ProjectId == projectSite.Id);
foreach (var projectReference in projectReferences)
foreach (var projectReferenceToRemove in projectReferencesToRemove)
{
RemoveProjectReference(projectReference);
VisualStudioProject.RemoveProjectReference(new ProjectReference(projectSite.VisualStudioProject.Id));
}
}
......@@ -41,14 +41,20 @@ public void OnCodeDirectoryAliasesChanged(ICSharpProjectRoot project, int previo
{
CSharpProjectShim projectSite = GetProjectSite(project);
UpdateProjectReferenceAliases(projectSite, ImmutableArray.Create(currentAliases));
using (VisualStudioProject.CreateBatchScope())
{
var existingProjectReference = VisualStudioProject.GetProjectReferences().Single(p => p.ProjectId == projectSite.VisualStudioProject.Id);
VisualStudioProject.RemoveProjectReference(existingProjectReference);
VisualStudioProject.AddProjectReference(new ProjectReference(existingProjectReference.ProjectId, ImmutableArray.Create(currentAliases), existingProjectReference.EmbedInteropTypes));
}
}
public void AddReferenceToCodeDirectoryEx(string assemblyFileName, ICSharpProjectRoot project, CompilerOptions optionID)
public void AddReferenceToCodeDirectoryEx(string assemblyFileName, ICSharpProjectRoot projectRoot, CompilerOptions optionID)
{
CSharpProjectShim projectSite = GetProjectSite(project);
CSharpProjectShim projectSite = GetProjectSite(projectRoot);
AddProjectReference(new ProjectReference(projectSite.Id));
VisualStudioProject.AddProjectReference(new ProjectReference(projectSite.VisualStudioProject.Id, embedInteropTypes: optionID == CompilerOptions.OPTID_IMPORTSUSINGNOPIA));
}
/// <summary>
......@@ -66,7 +72,7 @@ private static CSharpProjectShim GetProjectSite(ICSharpProjectRoot project)
// the project system.
if (projectSite == null)
{
throw new ArgumentException("finalProject was not properly sited with the languageServices service.", "finalProject");
throw new ArgumentException($"{project} was not properly sited with the language service.", nameof(project));
}
return projectSite;
......
......@@ -10,7 +10,7 @@ internal partial class CSharpProjectShim : Microsoft.VisualStudio.OLE.Interop.IS
{
public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject)
{
var serviceProvider = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)ServiceProvider;
var serviceProvider = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)_serviceProvider;
return serviceProvider.QueryService(ref guidService, ref riid, out ppvObject);
}
}
......
// 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.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Host;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim
{
internal partial class CSharpProjectShim
{
private class OptionsProcessor : VisualStudioProjectOptionsProcessor
{
private readonly VisualStudioProject _visualStudioProject;
private readonly object[] _options = new object[(int)CompilerOptions.LARGEST_OPTION_ID];
private string _mainTypeName;
private OutputKind _outputKind;
public OptionsProcessor(VisualStudioProject visualStudioProject, HostWorkspaceServices workspaceServices)
: base(visualStudioProject, workspaceServices)
{
_visualStudioProject = visualStudioProject;
}
public object this[CompilerOptions compilerOption]
{
get
{
return _options[(int)compilerOption];
}
set
{
if (object.Equals(_options[(int)compilerOption], value))
{
return;
}
_options[(int)compilerOption] = value;
UpdateProjectForNewHostValues();
}
}
protected override CompilationOptions ComputeCompilationOptionsWithHostValues(CompilationOptions compilationOptions, IRuleSetFile ruleSetFileOpt)
{
IDictionary<string, ReportDiagnostic> ruleSetSpecificDiagnosticOptions = null;
// Get options from the ruleset file, if any, first. That way project-specific
// options can override them.
ReportDiagnostic? ruleSetGeneralDiagnosticOption = null;
// TODO: merge this core logic back down to the base of OptionsProcessor, since this should be the same for all languages. The CompilationOptions
// would then already contain the right information, and could be updated accordingly by the language-specific logic.
if (ruleSetFileOpt != null)
{
ruleSetGeneralDiagnosticOption = ruleSetFileOpt.GetGeneralDiagnosticOption();
ruleSetSpecificDiagnosticOptions = new Dictionary<string, ReportDiagnostic>(ruleSetFileOpt.GetSpecificDiagnosticOptions());
}
else
{
ruleSetSpecificDiagnosticOptions = new Dictionary<string, ReportDiagnostic>();
}
ReportDiagnostic generalDiagnosticOption;
var warningsAreErrors = GetNullableBooleanOption(CompilerOptions.OPTID_WARNINGSAREERRORS);
if (warningsAreErrors.HasValue)
{
generalDiagnosticOption = warningsAreErrors.Value ? ReportDiagnostic.Error : ReportDiagnostic.Default;
}
else if (ruleSetGeneralDiagnosticOption.HasValue)
{
generalDiagnosticOption = ruleSetGeneralDiagnosticOption.Value;
}
else
{
generalDiagnosticOption = ReportDiagnostic.Default;
}
// Start with the rule set options
var diagnosticOptions = new Dictionary<string, ReportDiagnostic>(ruleSetSpecificDiagnosticOptions);
// Update the specific options based on the general settings
if (warningsAreErrors.HasValue && warningsAreErrors.Value == true)
{
foreach (var pair in ruleSetSpecificDiagnosticOptions)
{
if (pair.Value == ReportDiagnostic.Warn)
{
diagnosticOptions[pair.Key] = ReportDiagnostic.Error;
}
}
}
// Update the specific options based on the specific settings
foreach (var diagnosticID in ParseWarningCodes(CompilerOptions.OPTID_WARNASERRORLIST))
{
diagnosticOptions[diagnosticID] = ReportDiagnostic.Error;
}
foreach (var diagnosticID in ParseWarningCodes(CompilerOptions.OPTID_WARNNOTASERRORLIST))
{
if (ruleSetSpecificDiagnosticOptions.TryGetValue(diagnosticID, out var ruleSetOption))
{
diagnosticOptions[diagnosticID] = ruleSetOption;
}
else
{
diagnosticOptions[diagnosticID] = ReportDiagnostic.Default;
}
}
foreach (var diagnosticID in ParseWarningCodes(CompilerOptions.OPTID_NOWARNLIST))
{
diagnosticOptions[diagnosticID] = ReportDiagnostic.Suppress;
}
if (!Enum.TryParse(GetStringOption(CompilerOptions.OPTID_PLATFORM, ""), ignoreCase: true, result: out Platform platform))
{
platform = Platform.AnyCpu;
}
if (!int.TryParse(GetStringOption(CompilerOptions.OPTID_WARNINGLEVEL, defaultValue: ""), out var warningLevel))
{
warningLevel = 4;
}
// TODO: appConfigPath: GetFilePathOption(CompilerOptions.OPTID_FUSIONCONFIG), bug #869604
return ((CSharpCompilationOptions)compilationOptions).WithAllowUnsafe(GetBooleanOption(CompilerOptions.OPTID_UNSAFE))
.WithOverflowChecks(GetBooleanOption(CompilerOptions.OPTID_CHECKED))
.WithCryptoKeyContainer(GetStringOption(CompilerOptions.OPTID_KEYNAME, defaultValue: null))
.WithCryptoKeyFile(GetFilePathRelativeOption(CompilerOptions.OPTID_KEYFILE))
.WithDelaySign(GetNullableBooleanOption(CompilerOptions.OPTID_DELAYSIGN))
.WithGeneralDiagnosticOption(generalDiagnosticOption)
.WithMainTypeName(_mainTypeName)
.WithModuleName(GetStringOption(CompilerOptions.OPTID_MODULEASSEMBLY, defaultValue: null))
.WithOptimizationLevel(GetBooleanOption(CompilerOptions.OPTID_OPTIMIZATIONS) ? OptimizationLevel.Release : OptimizationLevel.Debug)
.WithOutputKind(_outputKind)
.WithPlatform(platform)
.WithSpecificDiagnosticOptions(diagnosticOptions)
.WithWarningLevel(warningLevel);
}
private static string GetIdForErrorCode(int errorCode)
{
return "CS" + errorCode.ToString("0000");
}
private IEnumerable<string> ParseWarningCodes(CompilerOptions compilerOptions)
{
Contract.ThrowIfFalse(
compilerOptions == CompilerOptions.OPTID_NOWARNLIST ||
compilerOptions == CompilerOptions.OPTID_WARNASERRORLIST ||
compilerOptions == CompilerOptions.OPTID_WARNNOTASERRORLIST);
foreach (var warning in GetStringOption(compilerOptions, defaultValue: "").Split(new[] { ' ', ',', ';' }, StringSplitOptions.RemoveEmptyEntries))
{
var warningStringID = warning;
if (int.TryParse(warning, out var warningId))
{
warningStringID = GetIdForErrorCode(warningId);
}
yield return warningStringID;
}
}
private bool? GetNullableBooleanOption(CompilerOptions optionID)
{
return (bool?)_options[(int)optionID];
}
private bool GetBooleanOption(CompilerOptions optionID)
{
return GetNullableBooleanOption(optionID).GetValueOrDefault(defaultValue: false);
}
private string GetFilePathRelativeOption(CompilerOptions optionID)
{
var path = GetStringOption(optionID, defaultValue: null);
if (string.IsNullOrEmpty(path))
{
return null;
}
var directory = Path.GetDirectoryName(_visualStudioProject.FilePath);
if (!string.IsNullOrEmpty(directory))
{
return FileUtilities.ResolveRelativePath(path, directory);
}
return null;
}
private string GetStringOption(CompilerOptions optionID, string defaultValue)
{
string value = (string)_options[(int)optionID];
if (string.IsNullOrEmpty(value))
{
return defaultValue;
}
else
{
return value;
}
}
protected override ParseOptions ComputeParseOptionsWithHostValues(ParseOptions parseOptions)
{
var symbols = GetStringOption(CompilerOptions.OPTID_CCSYMBOLS, defaultValue: "").Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
// The base implementation of OptionsProcessor already tried this, but it didn't have the real documentation
// path so we have to do it a second time
DocumentationMode documentationMode = DocumentationMode.Parse;
if (GetStringOption(CompilerOptions.OPTID_XML_DOCFILE, defaultValue: null) != null)
{
documentationMode = DocumentationMode.Diagnose;
}
LanguageVersionFacts.TryParse(GetStringOption(CompilerOptions.OPTID_COMPATIBILITY, defaultValue: ""), out var languageVersion);
return ((CSharpParseOptions)parseOptions).WithKind(SourceCodeKind.Regular)
.WithLanguageVersion(languageVersion)
.WithPreprocessorSymbols(symbols.AsImmutable())
.WithDocumentationMode(documentationMode);
}
public void SetOutputFileType(OutputFileType fileType)
{
OutputKind newOutputKind;
switch (fileType)
{
case OutputFileType.Console:
newOutputKind = OutputKind.ConsoleApplication;
break;
case OutputFileType.Windows:
newOutputKind = OutputKind.WindowsApplication;
break;
case OutputFileType.Library:
newOutputKind = OutputKind.DynamicallyLinkedLibrary;
break;
case OutputFileType.Module:
newOutputKind = OutputKind.NetModule;
break;
case OutputFileType.AppContainer:
newOutputKind = OutputKind.WindowsRuntimeApplication;
break;
case OutputFileType.WinMDObj:
newOutputKind = OutputKind.WindowsRuntimeMetadata;
break;
default:
throw new ArgumentException("fileType was not a valid OutputFileType", nameof(fileType));
}
if (_outputKind != newOutputKind)
{
_outputKind = newOutputKind;
UpdateProjectForNewHostValues();
}
}
public void SetMainTypeName(string mainTypeName)
{
if (_mainTypeName != mainTypeName)
{
_mainTypeName = mainTypeName;
UpdateProjectForNewHostValues();
}
}
}
}
}
// 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.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using EnvDTE;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Legacy;
using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.TextManager.Interop;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim
{
......@@ -30,7 +23,6 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim
/// are in a separate files. Methods that are shared across multiple interfaces (which are
/// effectively methods that just QI from one interface to another), are implemented here.
/// </remarks>
[ExcludeFromCodeCoverage]
internal sealed partial class CSharpProjectShim : AbstractLegacyProject, ICodeModelInstanceFactory
{
/// <summary>
......@@ -41,40 +33,45 @@ internal sealed partial class CSharpProjectShim : AbstractLegacyProject, ICodeMo
private ICSharpProjectRoot _projectRoot;
private OutputKind _outputKind = OutputKind.DynamicallyLinkedLibrary;
private string _mainTypeName;
private object[] _options = new object[(int)CompilerOptions.LARGEST_OPTION_ID];
private readonly IServiceProvider _serviceProvider;
/// <summary>
/// Fetches the options processor for this C# project. Equivalent to the underlying member, but fixed to the derived type.
/// </summary>
private new OptionsProcessor VisualStudioProjectOptionsProcessor
{
get => (OptionsProcessor)base.VisualStudioProjectOptionsProcessor;
set => base.VisualStudioProjectOptionsProcessor = value;
}
public CSharpProjectShim(
ICSharpProjectRoot projectRoot,
VisualStudioProjectTracker projectTracker,
Func<ProjectId, IVsReportExternalErrors> reportExternalErrorCreatorOpt,
string projectSystemName,
IVsHierarchy hierarchy,
IServiceProvider serviceProvider,
VisualStudioWorkspaceImpl visualStudioWorkspaceOpt,
IThreadingContext threadingContext,
HostDiagnosticUpdateSource hostDiagnosticUpdateSourceOpt,
ICommandLineParserService commandLineParserServiceOpt)
: base(projectTracker,
reportExternalErrorCreatorOpt,
projectSystemName,
: base(projectSystemName,
hierarchy,
LanguageNames.CSharp,
serviceProvider,
visualStudioWorkspaceOpt,
hostDiagnosticUpdateSourceOpt,
commandLineParserServiceOpt)
threadingContext,
externalErrorReportingPrefix: "CS",
hostDiagnosticUpdateSourceOpt: hostDiagnosticUpdateSourceOpt,
commandLineParserServiceOpt: commandLineParserServiceOpt)
{
_projectRoot = projectRoot;
_serviceProvider = serviceProvider;
_warningNumberArrayPointer = Marshal.AllocHGlobal(0);
// Ensure the default options are set up
ResetAllOptions();
UpdateOptions();
var componentModel = (IComponentModel)serviceProvider.GetService(typeof(SComponentModel));
projectTracker.AddProject(this);
this.ProjectCodeModel = componentModel.GetService<IProjectCodeModelFactory>().CreateProjectCodeModel(VisualStudioProject.Id, this);
this.VisualStudioProjectOptionsProcessor = new OptionsProcessor(this.VisualStudioProject, Workspace.Services);
ProjectCodeModel = new ProjectCodeModel(projectTracker.ThreadingContext, this.Id, this, visualStudioWorkspaceOpt, ServiceProvider);
// Ensure the default options are set up
ResetAllOptions();
}
public override void Disconnect()
......@@ -84,202 +81,6 @@ public override void Disconnect()
base.Disconnect();
}
private string GetIdForErrorCode(int errorCode)
{
return "CS" + errorCode.ToString("0000");
}
protected override bool CanUseTextBuffer(ITextBuffer textBuffer)
{
// In Web scenarios, the project system tells us about all files in the project, including ".aspx" and ".cshtml" files.
// The impact of this is that we try to add a StandardTextDocument for the file, and parse it on disk, etc, which won't
// end well. We prevent that from happening by not allowing buffers that aren't of our content type to be used for
// StandardTextDocuments. In the web scenarios, we will instead end up creating a ContainedDocument that actually
// knows about the secondary buffer that contains valid code in our content type.
return textBuffer.ContentType.IsOfType(ContentTypeNames.CSharpContentType);
}
protected override CompilationOptions CreateCompilationOptions(CommandLineArguments commandLineArguments, ParseOptions newParseOptions)
{
// Get the base options from command line arguments + common workspace defaults.
var options = (CSharpCompilationOptions)base.CreateCompilationOptions(commandLineArguments, newParseOptions);
// Now override these with the options from our state.
IDictionary<string, ReportDiagnostic> ruleSetSpecificDiagnosticOptions = null;
// Get options from the ruleset file, if any, first. That way project-specific
// options can override them.
ReportDiagnostic? ruleSetGeneralDiagnosticOption = null;
if (this.RuleSetFile != null)
{
ruleSetGeneralDiagnosticOption = this.RuleSetFile.Target.GetGeneralDiagnosticOption();
ruleSetSpecificDiagnosticOptions = new Dictionary<string, ReportDiagnostic>(this.RuleSetFile.Target.GetSpecificDiagnosticOptions());
}
else
{
ruleSetSpecificDiagnosticOptions = new Dictionary<string, ReportDiagnostic>();
}
UpdateRuleSetError(this.RuleSetFile?.Target);
ReportDiagnostic generalDiagnosticOption;
var warningsAreErrors = GetNullableBooleanOption(CompilerOptions.OPTID_WARNINGSAREERRORS);
if (warningsAreErrors.HasValue)
{
generalDiagnosticOption = warningsAreErrors.Value ? ReportDiagnostic.Error : ReportDiagnostic.Default;
}
else if (ruleSetGeneralDiagnosticOption.HasValue)
{
generalDiagnosticOption = ruleSetGeneralDiagnosticOption.Value;
}
else
{
generalDiagnosticOption = ReportDiagnostic.Default;
}
// Start with the rule set options
IDictionary<string, ReportDiagnostic> diagnosticOptions = new Dictionary<string, ReportDiagnostic>(ruleSetSpecificDiagnosticOptions);
// Update the specific options based on the general settings
if (warningsAreErrors.HasValue && warningsAreErrors.Value == true)
{
foreach (var pair in ruleSetSpecificDiagnosticOptions)
{
if (pair.Value == ReportDiagnostic.Warn)
{
diagnosticOptions[pair.Key] = ReportDiagnostic.Error;
}
}
}
// Update the specific options based on the specific settings
foreach (var diagnosticID in ParseWarningCodes(CompilerOptions.OPTID_WARNASERRORLIST))
{
diagnosticOptions[diagnosticID] = ReportDiagnostic.Error;
}
foreach (var diagnosticID in ParseWarningCodes(CompilerOptions.OPTID_WARNNOTASERRORLIST))
{
if (ruleSetSpecificDiagnosticOptions.TryGetValue(diagnosticID, out var ruleSetOption))
{
diagnosticOptions[diagnosticID] = ruleSetOption;
}
else
{
diagnosticOptions[diagnosticID] = ReportDiagnostic.Default;
}
}
foreach (var diagnosticID in ParseWarningCodes(CompilerOptions.OPTID_NOWARNLIST))
{
diagnosticOptions[diagnosticID] = ReportDiagnostic.Suppress;
}
if (!Enum.TryParse(GetStringOption(CompilerOptions.OPTID_PLATFORM, ""), ignoreCase: true, result: out Platform platform))
{
platform = Platform.AnyCpu;
}
if (!int.TryParse(GetStringOption(CompilerOptions.OPTID_WARNINGLEVEL, defaultValue: ""), out var warningLevel))
{
warningLevel = 4;
}
// TODO: appConfigPath: GetFilePathOption(CompilerOptions.OPTID_FUSIONCONFIG), bug #869604
return options.WithAllowUnsafe(GetBooleanOption(CompilerOptions.OPTID_UNSAFE))
.WithOverflowChecks(GetBooleanOption(CompilerOptions.OPTID_CHECKED))
.WithCryptoKeyContainer(GetStringOption(CompilerOptions.OPTID_KEYNAME, defaultValue: null))
.WithCryptoKeyFile(GetFilePathRelativeOption(CompilerOptions.OPTID_KEYFILE))
.WithDelaySign(GetNullableBooleanOption(CompilerOptions.OPTID_DELAYSIGN))
.WithGeneralDiagnosticOption(generalDiagnosticOption)
.WithMainTypeName(_mainTypeName)
.WithModuleName(GetStringOption(CompilerOptions.OPTID_MODULEASSEMBLY, defaultValue: null))
.WithOptimizationLevel(GetBooleanOption(CompilerOptions.OPTID_OPTIMIZATIONS) ? OptimizationLevel.Release : OptimizationLevel.Debug)
.WithOutputKind(_outputKind)
.WithPlatform(platform)
.WithSpecificDiagnosticOptions(diagnosticOptions)
.WithWarningLevel(warningLevel);
}
private IEnumerable<string> ParseWarningCodes(CompilerOptions compilerOptions)
{
Contract.ThrowIfFalse(compilerOptions == CompilerOptions.OPTID_NOWARNLIST || compilerOptions == CompilerOptions.OPTID_WARNASERRORLIST || compilerOptions == CompilerOptions.OPTID_WARNNOTASERRORLIST);
foreach (var warning in GetStringOption(compilerOptions, defaultValue: "").Split(new[] { ' ', ',', ';' }, StringSplitOptions.RemoveEmptyEntries))
{
var warningStringID = warning;
if (int.TryParse(warning, out var warningId))
{
warningStringID = GetIdForErrorCode(warningId);
}
yield return warningStringID;
}
}
private bool? GetNullableBooleanOption(CompilerOptions optionID)
{
return (bool?)_options[(int)optionID];
}
private bool GetBooleanOption(CompilerOptions optionID)
{
return GetNullableBooleanOption(optionID).GetValueOrDefault(defaultValue: false);
}
private string GetFilePathRelativeOption(CompilerOptions optionID)
{
var path = GetStringOption(optionID, defaultValue: null);
if (string.IsNullOrEmpty(path))
{
return null;
}
var directory = this.ContainingDirectoryPathOpt;
if (!string.IsNullOrEmpty(directory))
{
return FileUtilities.ResolveRelativePath(path, directory);
}
return null;
}
private string GetStringOption(CompilerOptions optionID, string defaultValue)
{
string value = (string)_options[(int)optionID];
if (string.IsNullOrEmpty(value))
{
return defaultValue;
}
else
{
return value;
}
}
protected override ParseOptions CreateParseOptions(CommandLineArguments commandLineArguments)
{
// Get the base parse options and override the defaults with the options from state.
var options = (CSharpParseOptions)base.CreateParseOptions(commandLineArguments);
var symbols = GetStringOption(CompilerOptions.OPTID_CCSYMBOLS, defaultValue: "").Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
DocumentationMode documentationMode = DocumentationMode.Parse;
if (GetStringOption(CompilerOptions.OPTID_XML_DOCFILE, defaultValue: null) != null)
{
documentationMode = DocumentationMode.Diagnose;
}
LanguageVersionFacts.TryParse(GetStringOption(CompilerOptions.OPTID_COMPATIBILITY, defaultValue: ""), out var languageVersion);
return options.WithKind(SourceCodeKind.Regular)
.WithLanguageVersion(languageVersion)
.WithPreprocessorSymbols(symbols.AsImmutable())
.WithDocumentationMode(documentationMode);
}
~CSharpProjectShim()
{
// Free the unmanaged memory we allocated in the constructor
......
......@@ -19,14 +19,16 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim
/// as this structure. We can then pick out this broken pattern, and convert
/// it to null instead of true.
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 16)]
internal struct HACK_VariantStructure
{
[FieldOffset(0)]
private short _type;
[FieldOffset(8)]
private short _padding1;
private short _padding2;
private short _padding3;
private short _booleanValue;
private IntPtr _padding4; // this will be aligned to the IntPtr-sized address
public unsafe object ConvertToObject()
{
......
......@@ -9,6 +9,7 @@
using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;
using Microsoft.VisualStudio.LanguageServices.UnitTests;
using static Microsoft.VisualStudio.LanguageServices.UnitTests.CodeModel.CodeModelTestHelpers;
using Microsoft.VisualStudio.LanguageServices.UnitTests.CodeModel.Mocks;
namespace Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.CodeModel
{
......@@ -34,8 +35,14 @@ internal static class FileCodeModelTestHelpers
WrapperPolicy.s_ComWrapperFactory = MockComWrapperFactory.Instance;
var visualStudioWorkspaceMock = new MockVisualStudioWorkspace(workspace);
var state = new CodeModelState(workspace.ExportProvider.GetExportedValue<IThreadingContext>(), serviceProvider, project.LanguageServices, visualStudioWorkspaceMock);
var threadingContext = workspace.ExportProvider.GetExportedValue<IThreadingContext>();
var state = new CodeModelState(
threadingContext,
serviceProvider,
project.LanguageServices,
visualStudioWorkspaceMock,
new ProjectCodeModelFactory(visualStudioWorkspaceMock, serviceProvider, threadingContext));
var codeModel = FileCodeModel.Create(state, null, document, new MockTextManagerAdapter()).Handle;
......
......@@ -9,6 +9,7 @@
<TargetFramework>net46</TargetFramework>
<RuntimeIdentifier>$(RoslynDesktopRuntimeIdentifier)</RuntimeIdentifier>
<RoslynProjectType>UnitTest</RoslynProjectType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\..\..\Compilers\Test\Resources\Core\Microsoft.CodeAnalysis.Compiler.Test.Resources.csproj" />
......
......@@ -36,7 +36,6 @@ public void RuleSet_GeneralOption_CPS()
Assert.Equal(expected: ReportDiagnostic.Default, actual: options.GeneralDiagnosticOption);
project.SetRuleSetFile(ruleSetFile.Path);
project.SetOptions($"/ruleset:{ruleSetFile.Path}");
workspaceProject = environment.Workspace.CurrentSolution.Projects.Single();
......@@ -58,36 +57,19 @@ public void RuleSet_SpecificOptions_CPS()
</Rules>
</RuleSet>
";
string ruleSetSource2 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"">
<IncludeAll Action=""Warning"" />
<Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
<Rule Id=""CA1012"" Action=""Warning"" />
</Rules>
</RuleSet>
";
using (var ruleSetFile = new DisposableFile())
using (var environment = new TestEnvironment())
using (var project = CSharpHelpers.CreateCSharpCPSProject(environment, "Test"))
{
Assert.Null(project.RuleSetFile);
// Verify SetRuleSetFile updates the ruleset.
File.WriteAllText(ruleSetFile.Path, ruleSetSource);
project.SetRuleSetFile(ruleSetFile.Path);
Assert.Equal(ruleSetFile.Path, project.RuleSetFile.Target.FilePath);
project.SetOptions($"/ruleset:{ruleSetFile.Path}");
// We need to explicitly update the command line arguments so the new ruleset is used to update options.
project.SetOptions($"/ruleset:{ruleSetFile.Path}");
var ca1012DiagnosticOption = project.CurrentCompilationOptions.SpecificDiagnosticOptions["CA1012"];
var ca1012DiagnosticOption = environment.Workspace.CurrentSolution.Projects.Single().CompilationOptions.SpecificDiagnosticOptions["CA1012"];
Assert.Equal(expected: ReportDiagnostic.Error, actual: ca1012DiagnosticOption);
// Verify edits to the ruleset file updates options.
var lastOptions = project.CurrentCompilationOptions;
File.WriteAllText(ruleSetFile.Path, ruleSetSource2);
project.OnRuleSetFileUpdateOnDisk(this, EventArgs.Empty);
ca1012DiagnosticOption = project.CurrentCompilationOptions.SpecificDiagnosticOptions["CA1012"];
Assert.Equal(expected: ReportDiagnostic.Warn, actual: ca1012DiagnosticOption);
}
}
}
......
......@@ -2,6 +2,7 @@
using System;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
......@@ -22,7 +23,8 @@ public void DocumentationModeSetToDiagnoseIfProducingDocFile_CPS()
using (var environment = new TestEnvironment())
using (var project = CSharpHelpers.CreateCSharpCPSProject(environment, "Test", commandLineArguments: @"/doc:DocFile.xml"))
{
Assert.Equal(DocumentationMode.Diagnose, project.CurrentParseOptions.DocumentationMode);
var parseOptions = environment.Workspace.CurrentSolution.Projects.Single().ParseOptions;
Assert.Equal(DocumentationMode.Diagnose, parseOptions.DocumentationMode);
}
}
......@@ -33,7 +35,8 @@ public void DocumentationModeSetToParseIfNotProducingDocFile_CPS()
using (var environment = new TestEnvironment())
using (var project = CSharpHelpers.CreateCSharpCPSProject(environment, "Test", commandLineArguments: @"/doc:"))
{
Assert.Equal(DocumentationMode.Parse, project.CurrentParseOptions.DocumentationMode);
var parseOptions = environment.Workspace.CurrentSolution.Projects.Single().ParseOptions;
Assert.Equal(DocumentationMode.Parse, parseOptions.DocumentationMode);
}
}
......@@ -63,57 +66,73 @@ public void ProjectOutputBinPathChange_CPS()
using (var environment = new TestEnvironment())
using (var project = CSharpHelpers.CreateCSharpCPSProject(environment, "Test", commandLineArguments: $"/out:{initialObjPath}"))
{
Assert.Equal(initialObjPath, project.ObjOutputPath);
Assert.Equal(initialObjPath, project.GetIntermediateOutputFilePath());
Assert.Equal(initialBinPath, project.BinOutputPath);
// Change obj output folder from command line arguments - verify that objOutputPath changes, but binOutputPath is the same.
var newObjPath = @"C:\NewFolder\test.dll";
project.SetOptions($"/out:{newObjPath}");
Assert.Equal(newObjPath, project.ObjOutputPath);
Assert.Equal(newObjPath, project.GetIntermediateOutputFilePath());
Assert.Equal(initialBinPath, project.BinOutputPath);
// Change output file name - verify that objOutputPath changes, but binOutputPath is the same.
newObjPath = @"C:\NewFolder\test2.dll";
project.SetOptions($"/out:{newObjPath}");
Assert.Equal(newObjPath, project.ObjOutputPath);
Assert.Equal(newObjPath, project.GetIntermediateOutputFilePath());
Assert.Equal(initialBinPath, project.BinOutputPath);
// Change output file name and folder - verify that objOutputPath changes, but binOutputPath is the same.
newObjPath = @"C:\NewFolder3\test3.dll";
project.SetOptions($"/out:{newObjPath}");
Assert.Equal(newObjPath, project.ObjOutputPath);
Assert.Equal(newObjPath, project.GetIntermediateOutputFilePath());
Assert.Equal(initialBinPath, project.BinOutputPath);
// Change bin output folder - verify that binOutputPath changes, but objOutputPath is the same.
var newBinPath = @"C:\NewFolder4\test.dll";
((IWorkspaceProjectContext)project).BinOutputPath = newBinPath;
Assert.Equal(newObjPath, project.ObjOutputPath);
project.BinOutputPath = newBinPath;
Assert.Equal(newObjPath, project.GetIntermediateOutputFilePath());
Assert.Equal(newBinPath, project.BinOutputPath);
// Change bin output folder to non-normalized path - verify that binOutputPath changes to normalized path, but objOutputPath is the same.
newBinPath = @"test.dll";
var expectedNewBinPath = Path.Combine(Path.GetTempPath(), newBinPath);
((IWorkspaceProjectContext)project).BinOutputPath = newBinPath;
Assert.Equal(newObjPath, project.ObjOutputPath);
project.BinOutputPath = newBinPath;
Assert.Equal(newObjPath, project.GetIntermediateOutputFilePath());
Assert.Equal(expectedNewBinPath, project.BinOutputPath);
}
}
[WpfFact, WorkItem(14520, "https://github.com/dotnet/roslyn/issues/14520")]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void InvalidProjectOutputBinPaths_CPS()
public void InvalidProjectOutputBinPaths_CPS1()
{
using (var environment = new TestEnvironment())
using (var project1 = CSharpHelpers.CreateCSharpCPSProject(environment, "Test", binOutputPath: null)) // Null binOutputPath
using (var project2 = CSharpHelpers.CreateCSharpCPSProject(environment, "Test2", binOutputPath: String.Empty)) // Empty binOutputPath
using (var project3 = CSharpHelpers.CreateCSharpCPSProject(environment, "Test3", binOutputPath: "Test.dll")) // Non-rooted binOutputPath
{
// Null output path is allowed.
Assert.Equal(null, project1.BinOutputPath);
}
}
[WpfFact, WorkItem(14520, "https://github.com/dotnet/roslyn/issues/14520")]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void InvalidProjectOutputBinPaths_CPS2()
{
using (var environment = new TestEnvironment())
using (var project2 = CSharpHelpers.CreateCSharpCPSProject(environment, "Test2", binOutputPath: String.Empty)) // Empty binOutputPath
{
// Empty output path is not allowed, it gets reset to null.
Assert.Equal(null, project2.BinOutputPath);
}
}
[WpfFact, WorkItem(14520, "https://github.com/dotnet/roslyn/issues/14520")]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void InvalidProjectOutputBinPaths_CPS3()
{
using (var environment = new TestEnvironment())
using (var project3 = CSharpHelpers.CreateCSharpCPSProject(environment, "Test3", binOutputPath: "Test.dll")) // Non-rooted binOutputPath
{
// Non-rooted output path is not allowed, it gets reset to a temp rooted path.
Assert.Equal(Path.Combine(Path.GetTempPath(), "Test.dll"), project3.BinOutputPath);
}
......
// 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.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework;
using Roslyn.Test.Utilities;
using Xunit;
namespace Roslyn.VisualStudio.CSharp.UnitTests.ProjectSystemShim.CPS
{
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using static CSharpHelpers;
[UseExportProvider]
......@@ -39,16 +41,29 @@ public void AddRemoveProjectAndMetadataReference_CPS()
var metadaRefFilePath = @"c:\someAssembly.dll";
project3.AddMetadataReference(metadaRefFilePath, new MetadataReferenceProperties(embedInteropTypes: true));
Assert.True(project3.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project1.Id));
Assert.True(project3.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project2.Id));
Assert.True(project3.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project4.Id));
Assert.True(project3.GetCurrentMetadataReferences().Any(mr => mr.FilePath == metadaRefFilePath));
IEnumerable<ProjectReference> GetProject3ProjectReferences()
{
return environment.Workspace
.CurrentSolution.GetProject(project3.Id).ProjectReferences;
}
IEnumerable<PortableExecutableReference> GetProject3MetadataReferences()
{
return environment.Workspace.CurrentSolution.GetProject(project3.Id)
.MetadataReferences
.Cast<PortableExecutableReference>();
}
Assert.True(GetProject3ProjectReferences().Any(pr => pr.ProjectId == project1.Id));
Assert.True(GetProject3ProjectReferences().Any(pr => pr.ProjectId == project2.Id));
Assert.True(GetProject3ProjectReferences().Any(pr => pr.ProjectId == project4.Id));
Assert.True(GetProject3MetadataReferences().Any(mr => mr.FilePath == metadaRefFilePath));
// Change output path for project reference and verify the reference.
((IWorkspaceProjectContext)project4).BinOutputPath = @"C:\project4.dll";
Assert.Equal(@"C:\project4.dll", project4.BinOutputPath);
Assert.True(project3.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project4.Id));
Assert.True(GetProject3ProjectReferences().Any(pr => pr.ProjectId == project4.Id));
// Remove project reference
project3.RemoveProjectReference(project1);
......@@ -59,9 +74,9 @@ public void AddRemoveProjectAndMetadataReference_CPS()
// Remove metadata reference
project3.RemoveMetadataReference(metadaRefFilePath);
Assert.False(project3.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project1.Id));
Assert.False(project3.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project2.Id));
Assert.False(project3.GetCurrentMetadataReferences().Any(mr => mr.FilePath == metadaRefFilePath));
Assert.False(GetProject3ProjectReferences().Any(pr => pr.ProjectId == project1.Id));
Assert.False(GetProject3ProjectReferences().Any(pr => pr.ProjectId == project2.Id));
Assert.False(GetProject3MetadataReferences().Any(mr => mr.FilePath == metadaRefFilePath));
project1.Dispose();
project2.Dispose();
......@@ -79,12 +94,20 @@ public void AddRemoveAnalyzerReference_CPS()
{
// Add analyzer reference
var analyzerAssemblyFullPath = @"c:\someAssembly.dll";
bool AnalyzersContainsAnalyzer()
{
return environment.Workspace.CurrentSolution.Projects.Single()
.AnalyzerReferences.Cast<AnalyzerReference>()
.Any(a => a.FullPath == analyzerAssemblyFullPath);
}
project.AddAnalyzerReference(analyzerAssemblyFullPath);
Assert.True(project.CurrentProjectAnalyzersContains(analyzerAssemblyFullPath));
Assert.True(AnalyzersContainsAnalyzer());
// Remove analyzer reference
project.RemoveAnalyzerReference(analyzerAssemblyFullPath);
Assert.False(project.CurrentProjectAnalyzersContains(analyzerAssemblyFullPath));
Assert.False(AnalyzersContainsAnalyzer());
}
}
}
......
// 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework;
using Roslyn.Test.Utilities;
......@@ -8,6 +9,7 @@
namespace Roslyn.VisualStudio.CSharp.UnitTests.ProjectSystemShim.CPS
{
using System.Collections.Generic;
using static CSharpHelpers;
[UseExportProvider]
......@@ -20,16 +22,18 @@ public void AddRemoveSourceFile_CPS()
using (var environment = new TestEnvironment())
using (var project = CreateCSharpCPSProject(environment, "project1"))
{
Assert.Empty(project.GetCurrentDocuments());
IEnumerable<Document> GetCurrentDocuments() => environment.Workspace.CurrentSolution.Projects.Single().Documents;
Assert.Empty(GetCurrentDocuments());
// Add source file
var sourceFileFullPath = @"c:\source.cs";
project.AddSourceFile(sourceFileFullPath);
Assert.True(project.GetCurrentDocuments().Any(s => s.FilePath == sourceFileFullPath));
Assert.True(GetCurrentDocuments().Any(s => s.FilePath == sourceFileFullPath));
// Remove source file
project.RemoveSourceFile(sourceFileFullPath);
Assert.Empty(project.GetCurrentDocuments());
Assert.Empty(GetCurrentDocuments());
}
}
......@@ -40,16 +44,17 @@ public void AddRemoveAdditionalFile_CPS()
using (var environment = new TestEnvironment())
using (var project = CreateCSharpCPSProject(environment, "project1"))
{
Assert.Empty(project.GetCurrentAdditionalDocuments());
IEnumerable<TextDocument> GetCurrentAdditionalDocuments() => environment.Workspace.CurrentSolution.Projects.Single().AdditionalDocuments;
Assert.Empty(GetCurrentAdditionalDocuments());
// Add additional file
var additionalFileFullPath = @"c:\source.cs";
project.AddAdditionalFile(additionalFileFullPath);
Assert.True(project.GetCurrentAdditionalDocuments().Any(s => s.FilePath == additionalFileFullPath));
Assert.True(GetCurrentAdditionalDocuments().Any(s => s.FilePath == additionalFileFullPath));
// Remove additional file
project.RemoveAdditionalFile(additionalFileFullPath);
Assert.Empty(project.GetCurrentAdditionalDocuments());
Assert.Empty(GetCurrentAdditionalDocuments());
}
}
}
......
......@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
......@@ -12,9 +13,12 @@
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.CPS;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework;
using Microsoft.VisualStudio.Shell.Interop;
using Xunit;
namespace Roslyn.VisualStudio.CSharp.UnitTests.ProjectSystemShim
{
......@@ -27,12 +31,10 @@ public static CSharpProjectShim CreateCSharpProject(TestEnvironment environment,
return new CSharpProjectShim(
new MockCSharpProjectRoot(hierarchy),
environment.ProjectTracker,
reportExternalErrorCreatorOpt: null,
projectSystemName: projectName,
hierarchy: hierarchy,
serviceProvider: environment.ServiceProvider,
visualStudioWorkspaceOpt: null,
threadingContext: environment.ThreadingContext,
hostDiagnosticUpdateSourceOpt: null,
commandLineParserServiceOpt: new CSharpCommandLineParserService());
}
......@@ -56,19 +58,26 @@ public static CPSProject CreateCSharpCPSProject(TestEnvironment environment, str
return CreateCSharpCPSProject(environment, projectName, projectFilePath, binOutputPath, projectGuid: Guid.NewGuid(), commandLineArguments: commandLineArguments);
}
public static unsafe void SetOption(this CSharpProjectShim csharpProject, CompilerOptions optionID, object value)
{
Assert.Equal(8 + 2 * IntPtr.Size, sizeof(HACK_VariantStructure));
Assert.Equal(8, (int)Marshal.OffsetOf<HACK_VariantStructure>("_booleanValue"));
HACK_VariantStructure variant = default;
Marshal.GetNativeVariantForObject(value, (IntPtr)(&variant));
csharpProject.SetOption(optionID, variant);
}
public static CPSProject CreateCSharpCPSProject(TestEnvironment environment, string projectName, string projectFilePath, string binOutputPath, Guid projectGuid, params string[] commandLineArguments)
{
var hierarchy = environment.CreateHierarchy(projectName, projectFilePath, "CSharp");
var cpsProject = CPSProjectFactory.CreateCPSProject(
environment.ProjectTracker,
environment.ServiceProvider,
hierarchy,
var cpsProjectFactory = environment.ExportProvider.GetExportedValue<IWorkspaceProjectContextFactory>();
var cpsProject = (CPSProject)cpsProjectFactory.CreateProjectContext(
LanguageNames.CSharp,
projectName,
projectFilePath,
projectGuid,
LanguageNames.CSharp,
new TestCSharpCommandLineParserService(),
hierarchy,
binOutputPath);
var commandLineForOptions = string.Join(" ", commandLineArguments);
......@@ -80,16 +89,14 @@ public static CPSProject CreateCSharpCPSProject(TestEnvironment environment, str
public static CPSProject CreateNonCompilableProject(TestEnvironment environment, string projectName, string projectFilePath)
{
var hierarchy = environment.CreateHierarchy(projectName, projectFilePath, "");
var cpsProjectFactory = environment.ExportProvider.GetExportedValue<IWorkspaceProjectContextFactory>();
return CPSProjectFactory.CreateCPSProject(
environment.ProjectTracker,
environment.ServiceProvider,
hierarchy,
return (CPSProject)cpsProjectFactory.CreateProjectContext(
NoCompilationConstants.LanguageName,
projectName,
projectFilePath,
Guid.NewGuid(),
NoCompilationConstants.LanguageName,
commandLineParserService: null,
hierarchy,
binOutputPath: null);
}
......
......@@ -7,6 +7,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Interop;
using Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework;
using Roslyn.Test.Utilities;
using Xunit;
......@@ -52,15 +53,13 @@ public void RuleSet_GeneralOption()
var project = CSharpHelpers.CreateCSharpProject(environment, "Test");
var workspaceProject = environment.Workspace.CurrentSolution.Projects.Single();
var options = (CSharpCompilationOptions)workspaceProject.CompilationOptions;
var options = (CSharpCompilationOptions)environment.GetUpdatedCompilationOptionOfSingleProject();
Assert.Equal(expected: ReportDiagnostic.Default, actual: options.GeneralDiagnosticOption);
project.SetRuleSetFile(ruleSetFile.Path);
((IAnalyzerHost)project).SetRuleSetFile(ruleSetFile.Path);
workspaceProject = environment.Workspace.CurrentSolution.Projects.Single();
options = (CSharpCompilationOptions)workspaceProject.CompilationOptions;
options = (CSharpCompilationOptions)environment.GetUpdatedCompilationOptionOfSingleProject();
Assert.Equal(expected: ReportDiagnostic.Error, actual: options.GeneralDiagnosticOption);
}
......@@ -83,14 +82,14 @@ public void RuleSet_ProjectSettingOverridesGeneralOption()
var project = CSharpHelpers.CreateCSharpProject(environment, "Test");
project.SetRuleSetFile(ruleSetFile.Path);
((IAnalyzerHost)project).SetRuleSetFile(ruleSetFile.Path);
var workspaceProject = environment.Workspace.CurrentSolution.Projects.Single();
var options = (CSharpCompilationOptions)workspaceProject.CompilationOptions;
Assert.Equal(expected: ReportDiagnostic.Warn, actual: options.GeneralDiagnosticOption);
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_WARNINGSAREERRORS, true);
project.SetOption(CompilerOptions.OPTID_WARNINGSAREERRORS, true);
workspaceProject = environment.Workspace.CurrentSolution.Projects.Single();
options = (CSharpCompilationOptions)workspaceProject.CompilationOptions;
......@@ -119,10 +118,9 @@ public void RuleSet_SpecificOptions()
var project = CSharpHelpers.CreateCSharpProject(environment, "Test");
project.SetRuleSetFile(ruleSetFile.Path);
((IAnalyzerHost)project).SetRuleSetFile(ruleSetFile.Path);
var workspaceProject = environment.Workspace.CurrentSolution.Projects.Single();
var options = (CSharpCompilationOptions)workspaceProject.CompilationOptions;
var options = (CSharpCompilationOptions)environment.GetUpdatedCompilationOptionOfSingleProject();
var ca1012DiagnosticOption = options.SpecificDiagnosticOptions["CA1012"];
Assert.Equal(expected: ReportDiagnostic.Error, actual: ca1012DiagnosticOption);
......@@ -149,11 +147,10 @@ public void RuleSet_ProjectSettingsOverrideSpecificOptions()
var project = CSharpHelpers.CreateCSharpProject(environment, "Test");
project.SetRuleSetFile(ruleSetFile.Path);
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_WARNASERRORLIST, "1014");
((IAnalyzerHost)project).SetRuleSetFile(ruleSetFile.Path);
project.SetOption(CompilerOptions.OPTID_WARNASERRORLIST, "1014");
var workspaceProject = environment.Workspace.CurrentSolution.Projects.Single();
var options = (CSharpCompilationOptions)workspaceProject.CompilationOptions;
var options = (CSharpCompilationOptions)environment.GetUpdatedCompilationOptionOfSingleProject();
var ca1014DiagnosticOption = options.SpecificDiagnosticOptions["CS1014"];
Assert.Equal(expected: ReportDiagnostic.Error, actual: ca1014DiagnosticOption);
......@@ -170,11 +167,11 @@ public void SetRuleSetFile_RemoveExtraBackslashes()
var project = CSharpHelpers.CreateCSharpProject(environment, "Test");
var pathWithExtraBackslashes = ruleSetFile.Path.Replace(@"\", @"\\");
project.SetRuleSetFile(pathWithExtraBackslashes);
((IAnalyzerHost)project).SetRuleSetFile(pathWithExtraBackslashes);
var projectRuleSetFile = project.RuleSetFile;
var projectRuleSetFile = project.VisualStudioProjectOptionsProcessor.ExplicitRuleSetFilePath;
Assert.Equal(expected: ruleSetFile.Path, actual: projectRuleSetFile.Target.FilePath);
Assert.Equal(expected: ruleSetFile.Path, actual: projectRuleSetFile);
}
}
......@@ -202,21 +199,21 @@ public void RuleSet_ProjectSettingsOverrideSpecificOptionsAndRestore()
var project = CSharpHelpers.CreateCSharpProject(environment, "Test");
project.SetRuleSetFile(ruleSetFile.Path);
((IAnalyzerHost)project).SetRuleSetFile(ruleSetFile.Path);
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_WARNASERRORLIST, "1014");
project.SetOption(CompilerOptions.OPTID_WARNASERRORLIST, "1014");
var options = environment.GetUpdatedCompilationOptionOfSingleProject();
Assert.Equal(expected: ReportDiagnostic.Error, actual: options.SpecificDiagnosticOptions["CS1014"]);
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_WARNNOTASERRORLIST, "1014");
project.SetOption(CompilerOptions.OPTID_WARNNOTASERRORLIST, "1014");
options = environment.GetUpdatedCompilationOptionOfSingleProject();
Assert.Equal(expected: ReportDiagnostic.Suppress, actual: options.SpecificDiagnosticOptions["CS1014"]);
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_WARNNOTASERRORLIST, null);
project.SetOption(CompilerOptions.OPTID_WARNNOTASERRORLIST, null);
options = environment.GetUpdatedCompilationOptionOfSingleProject();
Assert.Equal(expected: ReportDiagnostic.Error, actual: options.SpecificDiagnosticOptions["CS1014"]);
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_WARNASERRORLIST, null);
project.SetOption(CompilerOptions.OPTID_WARNASERRORLIST, null);
options = environment.GetUpdatedCompilationOptionOfSingleProject();
Assert.Equal(expected: ReportDiagnostic.Suppress, actual: options.SpecificDiagnosticOptions["CS1014"]);
}
......@@ -243,9 +240,9 @@ public void RuleSet_ProjectNoWarnOverridesOtherSettings()
var project = CSharpHelpers.CreateCSharpProject(environment, "Test");
project.SetRuleSetFile(ruleSetFile.Path);
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_NOWARNLIST, "1014");
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_WARNASERRORLIST, "1014");
((IAnalyzerHost)project).SetRuleSetFile(ruleSetFile.Path);
project.SetOption(CompilerOptions.OPTID_NOWARNLIST, "1014");
project.SetOption(CompilerOptions.OPTID_WARNASERRORLIST, "1014");
var workspaceProject = environment.Workspace.CurrentSolution.Projects.Single();
var options = (CSharpCompilationOptions)workspaceProject.CompilationOptions;
......
......@@ -23,7 +23,7 @@ public void DocumentationModeSetToDiagnoseIfProducingDocFile()
{
var project = CSharpHelpers.CreateCSharpProject(environment, "Test");
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_XML_DOCFILE, "DocFile.xml");
project.SetOption(CompilerOptions.OPTID_XML_DOCFILE, "DocFile.xml");
var workspaceProject = environment.Workspace.CurrentSolution.Projects.Single();
var options = (CSharpParseOptions)workspaceProject.ParseOptions;
......@@ -41,7 +41,7 @@ public void DocumentationModeSetToParseIfNotProducingDocFile()
{
var project = CSharpHelpers.CreateCSharpProject(environment, "Test");
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_XML_DOCFILE, "");
project.SetOption(CompilerOptions.OPTID_XML_DOCFILE, "");
var workspaceProject = environment.Workspace.CurrentSolution.Projects.Single();
var options = (CSharpParseOptions)workspaceProject.ParseOptions;
......@@ -58,7 +58,7 @@ public void UseOPTID_COMPATIBILITY()
{
var project = CSharpHelpers.CreateCSharpProject(environment, "Test");
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_COMPATIBILITY, "6");
project.SetOption(CompilerOptions.OPTID_COMPATIBILITY, "6");
var workspaceProject = environment.Workspace.CurrentSolution.Projects.Single();
var options = (CSharpParseOptions)workspaceProject.ParseOptions;
......@@ -95,11 +95,11 @@ public void ProjectSettingsOptionAddAndRemove()
{
var project = CSharpHelpers.CreateCSharpProject(environment, "Test");
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_WARNASERRORLIST, "1111");
project.SetOption(CompilerOptions.OPTID_WARNASERRORLIST, "1111");
var options = environment.GetUpdatedCompilationOptionOfSingleProject();
Assert.Equal(expected: ReportDiagnostic.Error, actual: options.SpecificDiagnosticOptions["CS1111"]);
project.SetOptionWithMarshaledValue(CompilerOptions.OPTID_WARNASERRORLIST, null);
project.SetOption(CompilerOptions.OPTID_WARNASERRORLIST, null);
options = environment.GetUpdatedCompilationOptionOfSingleProject();
Assert.False(options.SpecificDiagnosticOptions.ContainsKey("CS1111"));
}
......
// 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework;
using Roslyn.Test.Utilities;
using Xunit;
namespace Roslyn.VisualStudio.CSharp.UnitTests.ProjectSystemShim.LegacyProject
{
using static CSharpHelpers;
[UseExportProvider]
public class CSharpReferenceTests
{
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void AddingReferenceToProjectMetadataPromotesToProjectReference()
{
using (var environment = new TestEnvironment())
{
var project1 = CreateCSharpProject(environment, "project1");
project1.SetBinOutputPathAndRelatedData(@"c:\project1.dll");
var project2 = CreateCSharpProject(environment, "project2");
project2.SetBinOutputPathAndRelatedData(@"c:\project2.dll");
// since this is known to be the output path of project1, the metadata reference is converted to a project reference
project2.OnImportAdded(@"c:\project1.dll", "project1");
Assert.Equal(true, project2.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project1.Id));
project2.Disconnect();
project1.Disconnect();
}
}
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void AddCyclicProjectMetadataReferences()
{
using (var environment = new TestEnvironment())
{
var project1 = CreateCSharpProject(environment, "project1");
project1.SetBinOutputPathAndRelatedData(@"c:\project1.dll");
var project2 = CreateCSharpProject(environment, "project2");
project2.SetBinOutputPathAndRelatedData(@"c:\project2.dll");
project1.AddProjectReference(new ProjectReference(project2.Id));
// normally this metadata reference would be elevated to a project reference, but fails because of cyclicness
project2.OnImportAdded(@"c:\project1.dll", "project1");
Assert.Equal(true, project1.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project2.Id));
Assert.Equal(false, project2.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project1.Id));
project2.Disconnect();
project1.Disconnect();
}
}
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void AddCyclicProjectReferences()
{
using (var environment = new TestEnvironment())
{
var project1 = CreateCSharpProject(environment, "project1");
var project2 = CreateCSharpProject(environment, "project2");
project1.AddProjectReference(new ProjectReference(project2.Id));
project2.AddProjectReference(new ProjectReference(project1.Id));
Assert.Equal(true, project1.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project2.Id));
Assert.Equal(false, project2.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project1.Id));
project2.Disconnect();
project1.Disconnect();
}
}
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void AddCyclicProjectReferencesDeep()
{
using (var environment = new TestEnvironment())
{
var project1 = CreateCSharpProject(environment, "project1");
var project2 = CreateCSharpProject(environment, "project2");
var project3 = CreateCSharpProject(environment, "project3");
var project4 = CreateCSharpProject(environment, "project4");
project1.AddProjectReference(new ProjectReference(project2.Id));
project2.AddProjectReference(new ProjectReference(project3.Id));
project3.AddProjectReference(new ProjectReference(project4.Id));
project4.AddProjectReference(new ProjectReference(project1.Id));
Assert.Equal(true, project1.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project2.Id));
Assert.Equal(true, project2.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project3.Id));
Assert.Equal(true, project3.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project4.Id));
Assert.Equal(false, project4.GetCurrentProjectReferences().Any(pr => pr.ProjectId == project1.Id));
project4.Disconnect();
project3.Disconnect();
project2.Disconnect();
project1.Disconnect();
}
}
[WorkItem(12707, "https://github.com/dotnet/roslyn/issues/12707")]
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void AddingProjectReferenceAndUpdateReferenceBinPath()
{
using (var environment = new TestEnvironment())
{
var project1 = CreateCSharpProject(environment, "project1");
project1.SetBinOutputPathAndRelatedData(@"c:\project1.dll");
var project2 = CreateCSharpProject(environment, "project2");
project2.SetBinOutputPathAndRelatedData(@"c:\project2.dll");
// since this is known to be the output path of project1, the metadata reference is converted to a project reference
project2.OnImportAdded(@"c:\project1.dll", "project1");
Assert.Single(project2.GetCurrentProjectReferences().Where(pr => pr.ProjectId == project1.Id));
// update bin bath for project1.
project1.SetBinOutputPathAndRelatedData(@"c:\new_project1.dll");
// Verify project reference updated after bin path change.
Assert.Empty(project2.GetCurrentProjectReferences());
// This is a metadata reference to the original path
var metadataReference = Assert.Single(project2.GetCurrentMetadataReferences());
Assert.Equal(@"c:\project1.dll", metadataReference.FilePath);
project2.Disconnect();
project1.Disconnect();
}
}
[WorkItem(12707, "https://github.com/dotnet/roslyn/issues/12707")]
[WpfFact]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void DisconnectingProjectShouldConvertConvertedReferencesBack()
{
using (var environment = new TestEnvironment())
{
var project1 = CreateCSharpProject(environment, "project1");
project1.SetBinOutputPathAndRelatedData(@"c:\project1.dll");
var project2 = CreateCSharpProject(environment, "project2");
project2.SetBinOutputPathAndRelatedData(@"c:\project2.dll");
// since this is known to be the output path of project1, the metadata reference is converted to a project reference
project2.OnImportAdded(@"c:\project1.dll", "project1");
Assert.Single(project2.GetCurrentProjectReferences().Where(pr => pr.ProjectId == project1.Id));
project1.Disconnect();
// Verify project reference updated after bin path change.
Assert.Empty(project2.GetCurrentProjectReferences());
Assert.Single(project2.GetCurrentMetadataReferences().Where(r => r.FilePath == @"c:\project1.dll"));
project2.Disconnect();
}
}
[WorkItem(461967, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/461967")]
[WpfFact()]
[Trait(Traits.Feature, Traits.Features.ProjectSystemShims)]
public void AddingMetadataReferenceToProjectThatCannotCompileInTheIdeKeepsMetadataReference()
{
using (var environment = new TestEnvironment())
{
var project1 = CreateCSharpProject(environment, "project1");
project1.SetBinOutputPathAndRelatedData(@"c:\project1.dll");
var project2 = CreateNonCompilableProject(environment, "project2", @"C:\project2.fsproj");
project2.SetBinOutputPathAndRelatedData(@"c:\project2.dll");
project1.OnImportAdded(@"c:\project2.dll", "project2");
// We shoudl not have converted that to a project reference, because we would have no way to produce the compilation
Assert.Empty(project1.GetCurrentProjectReferences());
project2.Disconnect();
project1.Disconnect();
}
}
}
}
......@@ -9,6 +9,7 @@
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.LanguageServices.Implementation.EditAndContinue;
using Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.Text;
......@@ -140,10 +141,11 @@ int IVsReadOnlyViewNotification.OnDisabledEditingCommand(ref Guid pguidCmdGuid,
foreach (var documentId in vsWorkspace.GetRelatedDocumentIds(container))
{
var hostProject = vsWorkspace.GetHostProject(documentId.ProjectId) as AbstractProject;
if (hostProject?.EditAndContinueImplOpt != null)
var project = VsENCRebuildableProjectImpl.TryGetRebuildableProject(documentId.ProjectId);
if (project != null)
{
if (hostProject.EditAndContinueImplOpt.OnEdit(documentId))
if (project.OnEdit(documentId))
{
break;
}
......
......@@ -152,11 +152,15 @@ private void Tracker_UpdatedOnDisk(object sender, EventArgs e)
// Traverse the chain of requesting assemblies to get back to the original analyzer
// assembly.
var projectsWithAnalyzer = _workspace.DeferredState.ProjectTracker.ImmutableProjects.Where(p => p.CurrentProjectAnalyzersContains(filePath)).ToArray();
foreach (var project in projectsWithAnalyzer)
foreach (var project in _workspace.CurrentSolution.Projects)
{
var analyzerFileReferences = project.AnalyzerReferences.OfType<AnalyzerFileReference>();
if (analyzerFileReferences.Any(a => a.FullPath.Equals(filePath, StringComparison.OrdinalIgnoreCase)))
{
RaiseAnalyzerChangedWarning(project.Id, filePath);
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.CodeAnalysis;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel
{
internal interface IProjectCodeModelFactory
{
IProjectCodeModel CreateProjectCodeModel(ProjectId id, ICodeModelInstanceFactory codeModelInstanceFactory);
}
}
......@@ -31,9 +31,10 @@ public bool TryOnAfterGlobalSymbolRenamed(Workspace workspace, IEnumerable<Docum
{
foreach (var documentId in changedDocumentIDs)
{
if (visualStudioWorkspace.GetHostDocument(documentId) is ContainedDocument containedDocument)
var containedDocument = visualStudioWorkspace.TryGetContainedDocument(documentId);
if (containedDocument != null)
{
var containedLanguageHost = containedDocument.ContainedLanguage.ContainedLanguageHost;
var containedLanguageHost = containedDocument.ContainedLanguageHost;
if (containedLanguageHost != null)
{
var hresult = containedLanguageHost.OnRenamed(
......
......@@ -201,20 +201,20 @@ private void RegisterDesignerAttribute(Document document, string designerAttribu
var documentId = document.Id;
_notificationService.RegisterNotification(() =>
{
var vsDocument = workspace.GetHostDocument(documentId);
if (vsDocument == null)
var hierarchy = workspace.GetHierarchy(documentId.ProjectId);
if (hierarchy == null)
{
return;
}
uint itemId = vsDocument.GetItemId();
if (itemId == (uint)VSConstants.VSITEMID.Nil)
uint itemId = hierarchy.TryGetItemId(document.FilePath);
if (itemId == VSConstants.VSITEMID_NIL)
{
// it is no longer part of the solution
return;
}
if (ErrorHandler.Succeeded(vsDocument.Project.Hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_ItemSubType, out var currentValue)))
if (ErrorHandler.Succeeded(hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_ItemSubType, out var currentValue)))
{
var currentStringValue = string.IsNullOrEmpty(currentValue as string) ? null : (string)currentValue;
if (string.Equals(currentStringValue, designerAttributeArgument, StringComparison.OrdinalIgnoreCase))
......@@ -229,7 +229,7 @@ private void RegisterDesignerAttribute(Document document, string designerAttribu
var designer = GetDesignerFromForegroundThread();
if (designer != null)
{
designer.RegisterDesignViewAttribute(vsDocument.Project.Hierarchy, (int)itemId, dwClass: 0, pwszAttributeValue: designerAttributeArgument);
designer.RegisterDesignViewAttribute(hierarchy, (int)itemId, dwClass: 0, pwszAttributeValue: designerAttributeArgument);
}
}
catch
......
......@@ -15,11 +15,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics
[Export(typeof(IVisualStudioDiagnosticAnalyzerService))]
internal partial class VisualStudioDiagnosticAnalyzerService : IVisualStudioDiagnosticAnalyzerService
{
private readonly VisualStudioWorkspaceImpl _workspace;
private readonly VisualStudioWorkspace _workspace;
private readonly IDiagnosticAnalyzerService _diagnosticService;
[ImportingConstructor]
public VisualStudioDiagnosticAnalyzerService(VisualStudioWorkspaceImpl workspace, IDiagnosticAnalyzerService diagnosticService)
public VisualStudioDiagnosticAnalyzerService(VisualStudioWorkspace workspace, IDiagnosticAnalyzerService diagnosticService)
{
_workspace = workspace;
_diagnosticService = diagnosticService;
......@@ -35,10 +35,9 @@ public VisualStudioDiagnosticAnalyzerService(VisualStudioWorkspaceImpl workspace
}
// Analyzers are only supported for C# and VB currently.
var projectsWithHierarchy = (_workspace.DeferredState?.ProjectTracker.ImmutableProjects ?? ImmutableArray<AbstractProject>.Empty)
var projectsWithHierarchy = _workspace.CurrentSolution.Projects
.Where(p => p.Language == LanguageNames.CSharp || p.Language == LanguageNames.VisualBasic)
.Where(p => p.Hierarchy == hierarchyOpt)
.Select(p => _workspace.CurrentSolution.GetProject(p.Id));
.Where(p => _workspace.GetHierarchy(p.Id) == hierarchyOpt);
if (projectsWithHierarchy.Count() <= 1)
{
......
......@@ -159,7 +159,7 @@ private static bool TryAdjustSpanIfNeededForVenus(VisualStudioWorkspaceImpl work
return false;
}
var containedDocument = workspace.GetHostDocument(documentId) as ContainedDocument;
var containedDocument = workspace.TryGetContainedDocument(documentId);
if (containedDocument == null)
{
return false;
......@@ -173,9 +173,8 @@ private static bool TryAdjustSpanIfNeededForVenus(VisualStudioWorkspaceImpl work
iEndIndex = originalColumn
};
var containedLanguage = containedDocument.ContainedLanguage;
var bufferCoordinator = containedLanguage.BufferCoordinator;
var containedLanguageHost = containedLanguage.ContainedLanguageHost;
var bufferCoordinator = containedDocument.BufferCoordinator;
var containedLanguageHost = containedDocument.ContainedLanguageHost;
var spansOnPrimaryBuffer = new TextManager.Interop.TextSpan[1];
if (VSConstants.S_OK == bufferCoordinator.MapSecondaryToPrimarySpan(originalSpanOnSecondaryBuffer, spansOnPrimaryBuffer))
......
// 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.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
......@@ -9,6 +10,7 @@
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
......@@ -39,7 +41,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.EditAndContinue
{
internal sealed class VsENCRebuildableProjectImpl
{
private readonly AbstractProject _vsProject;
private readonly VisualStudioWorkspace _workspace;
private readonly VisualStudioProject _project;
// number of projects that are in the debug state:
private static int s_debugStateProjectCount;
......@@ -89,27 +92,33 @@ internal sealed class VsENCRebuildableProjectImpl
#endregion
private readonly static ConditionalWeakTable<ProjectId, VsENCRebuildableProjectImpl> _rebuildableProjectImpls = new ConditionalWeakTable<ProjectId, VsENCRebuildableProjectImpl>();
private bool IsDebuggable => _mvid != Guid.Empty;
internal VsENCRebuildableProjectImpl(AbstractProject project)
internal VsENCRebuildableProjectImpl(VisualStudioWorkspace workspace, VisualStudioProject project, IServiceProvider serviceProvider)
{
Debug.Assert(workspace != null);
Debug.Assert(project != null);
_vsProject = project;
_workspace = workspace;
_project = project;
_debuggingService = _vsProject.Workspace.Services.GetService<IDebuggingWorkspaceService>();
_trackingService = _vsProject.Workspace.Services.GetService<IActiveStatementTrackingService>();
_notifications = _vsProject.Workspace.Services.GetService<INotificationService>();
_debuggingService = _workspace.Services.GetRequiredService<IDebuggingWorkspaceService>();
_trackingService = _workspace.Services.GetRequiredService<IActiveStatementTrackingService>();
_notifications = _workspace.Services.GetService<INotificationService>();
_debugEncNotify = (IDebugEncNotify)project.ServiceProvider.GetService(typeof(SVsShellDebugger));
_debugEncNotify = (IDebugEncNotify)serviceProvider.GetService(typeof(SVsShellDebugger));
var componentModel = (IComponentModel)project.ServiceProvider.GetService(typeof(SComponentModel));
var componentModel = (IComponentModel)serviceProvider.GetService(typeof(SComponentModel));
_threadingContext = componentModel.GetService<IThreadingContext>();
_diagnosticProvider = componentModel.GetService<EditAndContinueDiagnosticUpdateSource>();
_editorAdaptersFactoryService = componentModel.GetService<IVsEditorAdaptersFactoryService>();
_moduleMetadataProvider = componentModel.GetService<IDebuggeeModuleMetadataProvider>();
_encService = _debuggingService.EditAndContinueServiceOpt;
_rebuildableProjectImpls.Add(project.Id, this);
Debug.Assert(_debugEncNotify != null);
Debug.Assert(_encService != null);
Debug.Assert(_trackingService != null);
......@@ -118,6 +127,12 @@ internal VsENCRebuildableProjectImpl(AbstractProject project)
Debug.Assert(_moduleMetadataProvider != null);
}
internal static VsENCRebuildableProjectImpl TryGetRebuildableProject(ProjectId projectId)
{
_rebuildableProjectImpls.TryGetValue(projectId, out var rebuildableProject);
return rebuildableProject;
}
// called from an edit filter if an edit of a read-only buffer is attempted:
internal bool OnEdit(DocumentId documentId)
{
......@@ -141,11 +156,9 @@ internal bool OnEdit(DocumentId documentId)
return;
}
var visualStudioWorkspace = _vsProject.Workspace as VisualStudioWorkspaceImpl;
var hostProject = visualStudioWorkspace?.GetHostProject(documentId.ProjectId) as AbstractProject;
if (hostProject?.EditAndContinueImplOpt?._mvid != Guid.Empty)
if (documentId.ProjectId == _project.Id && _mvid != Guid.Empty)
{
_debugEncNotify.NotifyEncEditDisallowedByProject(hostProject.Hierarchy);
_debugEncNotify.NotifyEncEditDisallowedByProject(_workspace.GetHierarchy(documentId.ProjectId));
return;
}
......@@ -204,7 +217,7 @@ public int StartDebuggingPE()
{
try
{
log.Write("Enter Debug Mode: project '{0}'", _vsProject.DisplayName);
log.Write("Enter Debug Mode: project '{0}'", _project.Id.ToString());
// EnC service is global (per solution), but the debugger calls this for each project.
// Avoid starting the debug session if it has already been started.
......@@ -217,13 +230,13 @@ public int StartDebuggingPE()
_debuggingService.OnBeforeDebuggingStateChanged(DebuggingState.Design, DebuggingState.Run);
_encService.StartDebuggingSession(_vsProject.Workspace.CurrentSolution);
_encService.StartDebuggingSession(_workspace.CurrentSolution);
s_encDebuggingSessionInfo = new EncDebuggingSessionInfo();
s_readOnlyDocumentTracker = new VsReadOnlyDocumentTracker(_threadingContext, _encService, _editorAdaptersFactoryService);
}
string outputPath = _vsProject.ObjOutputPath;
string outputPath = _project.IntermediateOutputFilePath;
// The project doesn't produce a debuggable binary or we can't read it.
// Continue on since the debugger ignores HResults and we need to handle subsequent calls.
......@@ -231,26 +244,25 @@ public int StartDebuggingPE()
{
try
{
InjectFault_MvidRead();
_mvid = ReadMvid(outputPath);
}
catch (Exception e) when (e is FileNotFoundException || e is DirectoryNotFoundException)
{
// If the project isn't referenced by the project being debugged it might not be built.
// In that case EnC is never allowed for the project, and thus we can assume the project hasn't entered debug state.
log.Write("StartDebuggingPE: '{0}' metadata file not found: '{1}'", _vsProject.DisplayName, outputPath);
log.Write("StartDebuggingPE: '{0}' metadata file not found: '{1}'", _project.Id.ToString(), outputPath);
_mvid = Guid.Empty;
}
catch (Exception e)
{
log.Write("StartDebuggingPE: error reading MVID of '{0}' ('{1}'): {2}", _vsProject.DisplayName, outputPath, e.Message);
log.Write("StartDebuggingPE: error reading MVID of '{0}' ('{1}'): {2}", _project.Id.ToString(), outputPath, e.Message);
_mvid = Guid.Empty;
ReportInternalError(InternalErrorCode.ErrorReadingFile, new[] { outputPath, e.Message });
}
}
else
{
log.Write("StartDebuggingPE: project has no output path '{0}'", _vsProject.DisplayName);
log.Write("StartDebuggingPE: project has no output path '{0}'", _project.Id.ToString());
_mvid = Guid.Empty;
}
......@@ -296,7 +308,7 @@ public int StopDebuggingPE()
{
try
{
log.Write("Exit Debug Mode: project '{0}'", _vsProject.DisplayName);
log.Write("Exit Debug Mode: project '{0}'", _project.Id.ToString());
Debug.Assert(s_breakStateEnteredProjects.Count == 0);
Debug.Assert(s_pendingNonRemappableRegions.Count == 0);
......@@ -329,7 +341,7 @@ public int StopDebuggingPE()
{
// an error might have been reported:
var errorId = new EncErrorId(_encService.DebuggingSession, EditAndContinueDiagnosticUpdateSource.InternalErrorId);
_diagnosticProvider.ClearDiagnostics(errorId, _vsProject.Workspace.CurrentSolution, _vsProject.Id, documentIdOpt: null);
_diagnosticProvider.ClearDiagnostics(errorId, _workspace.CurrentSolution, _project.Id, documentIdOpt: null);
}
_committedBaseline = null;
......@@ -400,7 +412,7 @@ public int GetPEidentity(Guid[] pMVID, string[] pbstrPEName)
if (pbstrPEName != null && pbstrPEName.Length != 0)
{
var outputPath = _vsProject.ObjOutputPath;
var outputPath = _project.IntermediateOutputFilePath;
Debug.Assert(outputPath != null);
pbstrPEName[0] = Path.GetFileName(outputPath);
......@@ -421,7 +433,7 @@ public int EnterBreakStateOnPE(ENC_BREAKSTATE_REASON encBreakReason, ENC_ACTIVE_
{
using (NonReentrantContext)
{
log.Write("Enter {2}Break Mode: project '{0}', AS#: {1}", _vsProject.DisplayName, pActiveStatements != null ? pActiveStatements.Length : -1, encBreakReason == ENC_BREAKSTATE_REASON.ENC_BREAK_EXCEPTION ? "Exception " : "");
log.Write("Enter {2}Break Mode: project '{0}', AS#: {1}", _project.Id.ToString(), pActiveStatements != null ? pActiveStatements.Length : -1, encBreakReason == ENC_BREAKSTATE_REASON.ENC_BREAK_EXCEPTION ? "Exception " : "");
Debug.Assert(cActiveStatements == (pActiveStatements != null ? pActiveStatements.Length : 0));
Debug.Assert(s_breakStateProjectCount < s_debugStateProjectCount);
......@@ -433,7 +445,7 @@ public int EnterBreakStateOnPE(ENC_BREAKSTATE_REASON encBreakReason, ENC_ACTIVE_
{
_debuggingService.OnBeforeDebuggingStateChanged(DebuggingState.Run, DebuggingState.Break);
s_breakStateEntrySolution = _vsProject.Workspace.CurrentSolution;
s_breakStateEntrySolution = _workspace.CurrentSolution;
// TODO: This is a workaround for a debugger bug in which not all projects exit the break state.
// Reset the project count.
......@@ -453,7 +465,7 @@ public int EnterBreakStateOnPE(ENC_BREAKSTATE_REASON encBreakReason, ENC_ACTIVE_
// If pActiveStatements is null the EnC Manager failed to retrieve the module corresponding
// to the project in the debuggee. We won't include such projects in the edit session.
s_breakStateEnteredProjects.Add(KeyValuePairUtil.Create(_vsProject.Id, state));
s_breakStateEnteredProjects.Add(KeyValuePairUtil.Create(_project.Id, state));
s_breakStateProjectCount++;
// EnC service is global, but the debugger calls this for each project.
......@@ -564,15 +576,17 @@ public int GetENCBuildState(ENC_BUILD_STATE[] pENCBuildState)
{
// Fetch the latest snapshot of the project and get an analysis summary for any changes
// made since the break mode was entered.
var currentProject = _vsProject.Workspace.CurrentSolution.GetProject(_vsProject.Id);
var currentProject = _workspace.CurrentSolution.GetProject(_project.Id);
if (currentProject == null)
{
// If the project has yet to be loaded into the solution (which may be the case,
// since they are loaded on-demand), then it stands to reason that it has not yet
// been modified.
// TODO (https://github.com/dotnet/roslyn/issues/1204): this check should be unnecessary.
// TODO (https://github.com/dotnet/roslyn/issues/1204): this check should be unnecessary,
// especially because projects themselves are always added to the workspace, even if their contents
// are not.
_lastEditSessionSummary = ProjectAnalysisSummary.NoChanges;
log.Write("Project '{0}' has not yet been loaded into the solution", _vsProject.DisplayName);
log.Write("Project '{0}' has not yet been loaded into the solution", _project.Id.ToString());
}
else
{
......@@ -608,7 +622,7 @@ public int GetENCBuildState(ENC_BUILD_STATE[] pENCBuildState)
}
log.Write("EnC state of '{0}' queried: {1}{2}",
_vsProject.DisplayName,
_project.Id.ToString(),
EncStateToString(pENCBuildState[0]),
_encService.EditSession != null ? "" : " (no session)");
......@@ -656,7 +670,7 @@ public int ExitBreakStateOnPE()
return VSConstants.S_OK;
}
log.Write("Exit Break Mode: project '{0}'", _vsProject.DisplayName);
log.Write("Exit Break Mode: project '{0}'", _project.Id.ToString());
// EnC service is global, but the debugger calls this for each project.
// Avoid ending the edit session if it has already been ended.
......@@ -693,8 +707,8 @@ public int ExitBreakStateOnPE()
_diagnosticProvider.ClearDiagnostics(
new EncErrorId(_encService.DebuggingSession, EditAndContinueDiagnosticUpdateSource.EmitErrorId),
_vsProject.Workspace.CurrentSolution,
_vsProject.Id,
_workspace.CurrentSolution,
_project.Id,
_documentsWithEmitError);
_documentsWithEmitError = ImmutableArray<DocumentId>.Empty;
......@@ -725,7 +739,7 @@ public unsafe int BuildForEnc(object pUpdatePE)
{
try
{
log.Write("Applying changes to {0}", _vsProject.DisplayName);
log.Write("Applying changes to {0}", _project.Id.ToString());
Debug.Assert(_encService.EditSession != null);
Debug.Assert(!_encService.EditSession.StoppedAtException);
......@@ -735,7 +749,7 @@ public unsafe int BuildForEnc(object pUpdatePE)
if (_changesApplied)
{
log.Write("Changes already applied to {0}, can't apply again", _vsProject.DisplayName);
log.Write("Changes already applied to {0}, can't apply again", _project.Id.ToString());
throw ExceptionUtilities.Unreachable;
}
......@@ -768,12 +782,12 @@ public unsafe int BuildForEnc(object pUpdatePE)
var errorId = new EncErrorId(_encService.DebuggingSession, EditAndContinueDiagnosticUpdateSource.EmitErrorId);
// Clear diagnostics, in case the project was built before and failed due to errors.
_diagnosticProvider.ClearDiagnostics(errorId, _projectBeingEmitted.Solution, _vsProject.Id, _documentsWithEmitError);
_diagnosticProvider.ClearDiagnostics(errorId, _projectBeingEmitted.Solution, _project.Id, _documentsWithEmitError);
if (!delta.EmitResult.Success)
{
var errors = delta.EmitResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error);
_documentsWithEmitError = _diagnosticProvider.ReportDiagnostics(errorId, _projectBeingEmitted.Solution, _vsProject.Id, errors);
_documentsWithEmitError = _diagnosticProvider.ReportDiagnostics(errorId, _projectBeingEmitted.Solution, _project.Id, errors);
_encService.EditSession.LogEmitProjectDeltaErrors(errors.Select(e => e.Id));
return VSConstants.E_FAIL;
......@@ -898,7 +912,7 @@ internal static ENCPROG_EXCEPTION_RANGE[] GetExceptionRanges(ImmutableArray<(Act
{
var (documentId, deltas) = edits[f];
fileUpdates[f].FileName = _vsProject.GetDocumentOrAdditionalDocument(documentId).FilePath;
fileUpdates[f].FileName = _workspace.CurrentSolution.GetProject(_project.Id).GetDocument(documentId).FilePath;
fileUpdates[f].LineUpdateCount = (uint)deltas.Length;
fileUpdates[f].LineUpdates = (IntPtr)(lineUpdatesPtr + index);
......@@ -936,11 +950,11 @@ private Deltas EmitProjectDelta()
if (baseline == null || baseline.OriginalMetadata.IsDisposed)
{
var moduleName = PathUtilities.GetFileName(_vsProject.ObjOutputPath);
var moduleName = PathUtilities.GetFileName(_project.IntermediateOutputFilePath);
// The metadata blob is guaranteed to not be disposed while BuildForEnc is being executed.
// If it is disposed it means it had been disposed when entering BuildForEnc.
log.Write("Module has been unloaded: module '{0}', project '{1}', MVID: {2}", moduleName, _vsProject.DisplayName, _mvid.ToString());
log.Write("Module has been unloaded: module '{0}', project '{1}', MVID: {2}", moduleName, _project.Id.ToString(), _mvid.ToString());
ReportInternalError(InternalErrorCode.CantApplyChangesModuleHasBeenUnloaded, new[] { moduleName });
return null;
......@@ -1075,7 +1089,7 @@ public int EncApplySucceeded(int hrApplyResult)
{
try
{
log.Write("Change applied to {0}", _vsProject.DisplayName);
log.Write("Change applied to {0}", _project.Id.ToString());
Debug.Assert(IsDebuggable);
Debug.Assert(_encService.EditSession != null);
Debug.Assert(!_encService.EditSession.StoppedAtException);
......@@ -1146,7 +1160,7 @@ private void ReportInternalError(InternalErrorCode errorId, object[] args)
_diagnosticProvider.ReportDiagnostics(
new EncErrorId(_encService.DebuggingSession, EditAndContinueDiagnosticUpdateSource.InternalErrorId),
_encService.DebuggingSession.InitialSolution,
_vsProject.Id,
_project.Id,
new[] { Diagnostic.Create(descriptor, Location.None, args) });
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e))
......@@ -1154,27 +1168,5 @@ private void ReportInternalError(InternalErrorCode errorId, object[] args)
// nop
}
}
#region Testing
#if DEBUG
// Fault injection:
// If set we'll fail to read MVID of specified projects to test error reporting.
internal static ImmutableArray<string> InjectMvidReadingFailure;
private void InjectFault_MvidRead()
{
if (!InjectMvidReadingFailure.IsDefault && InjectMvidReadingFailure.Contains(_vsProject.DisplayName))
{
throw new IOException("Fault injection");
}
}
#else
[Conditional("DEBUG")]
private void InjectFault_MvidRead()
{
}
#endif
#endregion
}
}
......@@ -6,6 +6,8 @@
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.LanguageServices;
using Microsoft.VisualStudio.LanguageServices.Implementation.EditAndContinue;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.Implementation.Venus;
using Microsoft.VisualStudio.Text;
......@@ -78,10 +80,9 @@ public void Dispose()
private void SetReadOnly(Document document)
{
// Only set documents read-only if they're part of a project that supports Enc.
var workspace = document.Project.Solution.Workspace as VisualStudioWorkspaceImpl;
var project = workspace?.DeferredState?.ProjectTracker?.GetProject(document.Project.Id);
var workspace = document.Project.Solution.Workspace as VisualStudioWorkspace;
if (project?.EditAndContinueImplOpt != null)
if (workspace != null && VsENCRebuildableProjectImpl.TryGetRebuildableProject(document.Project.Id) != null)
{
SetReadOnly(document.Id, _encService.IsProjectReadOnly(document.Project.Id, out var sessionReason, out var projectReason) && AllowsReadOnly(document.Id));
}
......@@ -93,9 +94,8 @@ private bool AllowsReadOnly(DocumentId documentId)
// However, ASP.NET doesn’t want its views (aspx, cshtml, or vbhtml) to be read-only, so they can be editable
// while the code is running and get refreshed next time the web page is hit.
// Note that Razor-like views are modelled as a ContainedDocument but normal code including code-behind are modelled as a StandardTextDocument.
var visualStudioWorkspace = _workspace as VisualStudioWorkspaceImpl;
var containedDocument = visualStudioWorkspace?.GetHostDocument(documentId) as ContainedDocument;
// Note that Razor-like views are modelled as a ContainedDocument
var containedDocument = ContainedDocument.TryGetContainedDocument(documentId);
return containedDocument == null;
}
......
......@@ -75,7 +75,7 @@ public static bool TryGetImageListAndIndex(this VisualStudioWorkspaceImpl worksp
public static bool TryGetImageListAndIndex(this VisualStudioWorkspaceImpl workspace, IVsImageService2 imageService, ProjectId id, out IntPtr imageList, out ushort index)
{
var hierarchy = workspace.GetHostProject(id)?.Hierarchy;
var hierarchy = workspace.GetHierarchy(id);
if (hierarchy != null)
{
return TryGetImageListAndIndex(hierarchy, imageService, VSConstants.VSITEMID_ROOT, out imageList, out index);
......@@ -88,11 +88,11 @@ public static bool TryGetImageListAndIndex(this VisualStudioWorkspaceImpl worksp
public static bool TryGetImageListAndIndex(this VisualStudioWorkspaceImpl workspace, IVsImageService2 imageService, DocumentId id, out IntPtr imageList, out ushort index)
{
var hostDocument = workspace.GetHostDocument(id);
if (hostDocument != null)
var hierarchy = workspace.GetHierarchy(id.ProjectId);
var document = workspace.CurrentSolution.GetDocument(id);
if (hierarchy != null)
{
var hierarchy = hostDocument.Project.Hierarchy;
var itemId = hostDocument.GetItemId();
var itemId = hierarchy.TryGetItemId(document.FilePath);
return TryGetImageListAndIndex(hierarchy, imageService, itemId, out imageList, out index);
}
......
......@@ -19,13 +19,13 @@ public static bool TryMapSpanFromSecondaryBufferToPrimaryBuffer(this VsTextSpan
return false;
}
var containedDocument = visualStudioWorkspace.GetHostDocument(documentId) as ContainedDocument;
var containedDocument = visualStudioWorkspace.TryGetContainedDocument(documentId);
if (containedDocument == null)
{
return false;
}
var bufferCoordinator = containedDocument.ContainedLanguage.BufferCoordinator;
var bufferCoordinator = containedDocument.BufferCoordinator;
var primary = new VsTextSpan[1];
var hresult = bufferCoordinator.MapSecondaryToPrimarySpan(spanInSecondaryBuffer, primary);
......
......@@ -254,11 +254,10 @@ protected async Task<(Guid, string projectName, SourceText)> GetGuidAndProjectNa
// in cases like Any-Code (which does not use a VSWorkspace). So we are tolerant
// when we have another type of workspace. This means we will show results, but
// certain features (like filtering) may not work in that context.
var workspace = document.Project.Solution.Workspace as VisualStudioWorkspaceImpl;
var hostProject = workspace?.GetHostProject(document.Project.Id);
var workspace = document.Project.Solution.Workspace as VisualStudioWorkspace;
var projectName = hostProject?.DisplayName ?? document.Project.Name;
var guid = hostProject?.Guid ?? Guid.Empty;
var projectName = document.Project.Name;
var guid = workspace.GetProjectGuid(document.Project.Id);
var sourceText = await document.GetTextAsync(CancellationToken).ConfigureAwait(false);
return (guid, projectName, sourceText);
......
......@@ -64,7 +64,7 @@ public VisualStudioDefinitionsAndReferencesFactory(SVsServiceProvider servicePro
private string GetSourceLine(string filePath, int lineNumber)
{
using (var invisibleEditor = new InvisibleEditor(
_serviceProvider, filePath, projectOpt: null, needsSave: false, needsUndoDisabled: false))
_serviceProvider, filePath, hierarchyOpt: null, needsSave: false, needsUndoDisabled: false))
{
var vsTextLines = invisibleEditor.VsTextLines;
if (vsTextLines != null &&
......
......@@ -671,9 +671,12 @@ public bool AreFoldersValidIdentifiers
{
if (_areFoldersValidIdentifiers)
{
/*
var workspace = this.SelectedProject.Solution.Workspace as VisualStudioWorkspaceImpl;
var project = workspace?.GetHostProject(this.SelectedProject.Id) as AbstractProject;
return !(project?.IsWebSite == true);
*/
return false;
}
return false;
......
......@@ -13,22 +13,16 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation
[ExportWorkspaceService(typeof(IHierarchyItemToProjectIdMap), ServiceLayer.Host), Shared]
internal class HierarchyItemToProjectIdMap : IHierarchyItemToProjectIdMap
{
private readonly VisualStudioWorkspaceImpl _workspace;
private readonly VisualStudioWorkspace _workspace;
[ImportingConstructor]
public HierarchyItemToProjectIdMap(VisualStudioWorkspaceImpl workspace)
public HierarchyItemToProjectIdMap(VisualStudioWorkspace workspace)
{
_workspace = workspace;
}
public bool TryGetProjectId(IVsHierarchyItem hierarchyItem, string targetFrameworkMoniker, out ProjectId projectId)
{
if (_workspace.DeferredState == null)
{
projectId = default(ProjectId);
return false;
}
// A project node is represented in two different hierarchies: the solution's IVsHierarchy (where it is a leaf node)
// and the project's own IVsHierarchy (where it is the root node). The IVsHierarchyItem joins them together for the
// purpose of creating the tree displayed in Solution Explorer. The project's hierarchy is what is passed from the
......@@ -46,7 +40,7 @@ public bool TryGetProjectId(IVsHierarchyItem hierarchyItem, string targetFramewo
// First filter the projects by matching up properties on the input hierarchy against properties on each
// project's hierarchy.
var candidateProjects = _workspace.DeferredState.ProjectTracker.ImmutableProjects
var candidateProjects = _workspace.CurrentSolution.Projects
.Where(p =>
{
// We're about to access various properties of the IVsHierarchy associated with the project.
......@@ -67,9 +61,11 @@ public bool TryGetProjectId(IVsHierarchyItem hierarchyItem, string targetFramewo
// if the two projects are in the same folder.
// Note that if a project has been loaded with Lightweight Solution Load it won't even have a
// hierarchy, so we need to check for null first.
if (p.Hierarchy != null
&& p.Hierarchy.TryGetCanonicalName((uint)VSConstants.VSITEMID.Root, out string projectCanonicalName)
&& p.Hierarchy.TryGetItemName((uint)VSConstants.VSITEMID.Root, out string projectName)
var hierarchy = _workspace.GetHierarchy(p.Id);
if (hierarchy != null
&& hierarchy.TryGetCanonicalName((uint)VSConstants.VSITEMID.Root, out string projectCanonicalName)
&& hierarchy.TryGetItemName((uint)VSConstants.VSITEMID.Root, out string projectName)
&& projectCanonicalName.Equals(nestedCanonicalName, System.StringComparison.OrdinalIgnoreCase)
&& projectName.Equals(nestedName))
{
......@@ -78,7 +74,7 @@ public bool TryGetProjectId(IVsHierarchyItem hierarchyItem, string targetFramewo
return true;
}
return p.Hierarchy.TryGetTargetFrameworkMoniker((uint)VSConstants.VSITEMID.Root, out string projectTargetFrameworkMoniker)
return hierarchy.TryGetTargetFrameworkMoniker((uint)VSConstants.VSITEMID.Root, out string projectTargetFrameworkMoniker)
&& projectTargetFrameworkMoniker.Equals(targetFrameworkMoniker);
}
......@@ -99,7 +95,7 @@ public bool TryGetProjectId(IVsHierarchyItem hierarchyItem, string targetFramewo
// without a ContainedDocument.
foreach (var candidateProject in candidateProjects)
{
if (!candidateProject.GetCurrentDocuments().Any(doc => doc is ContainedDocument))
if (!candidateProject.DocumentIds.Any(id => ContainedDocument.TryGetContainedDocument(id) != null))
{
projectId = candidateProject.Id;
return true;
......
......@@ -12,7 +12,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService
{
internal abstract partial class AbstractLanguageService<TPackage, TLanguageService> : IVsContainedLanguageFactory
{
private AbstractProject FindMatchingProject(IVsHierarchy hierarchy, uint itemid)
private VisualStudioProject FindMatchingProject(IVsHierarchy hierarchy, uint itemid)
{
// Here we must determine the project that this file's document is to be a part of.
// Venus creates a separate Project for a .aspx or .ascx file, and so we must associate
......@@ -60,10 +60,7 @@ private AbstractProject FindMatchingProject(IVsHierarchy hierarchy, uint itemid)
return null;
}
return this.Workspace.DeferredState.ProjectTracker.ImmutableProjects
.Where(p => p.Hierarchy == hierarchy)
.Where(p => p.ProjectSystemName == projectName)
.SingleOrDefault();
return this.Workspace.GetProjectForUniqueName(projectName);
}
public int GetLanguage(IVsHierarchy hierarchy, uint itemid, IVsTextBufferCoordinator bufferCoordinator, out IVsContainedLanguage language)
......
......@@ -241,10 +241,15 @@ protected virtual void SetupNewTextView(IVsTextView textView)
new StandaloneCommandFilter<TPackage, TLanguageService>(
(TLanguageService)this, v, commandHandlerFactory, EditorAdaptersFactoryService).AttachToVsTextView());
// Ensure we start sending save events
var saveEventsService = Package.ComponentModel.GetService<SaveEventsService>();
saveEventsService.StartSendingSaveEvents();
var openDocument = wpfTextView.TextBuffer.AsTextContainer().GetRelatedDocuments().FirstOrDefault();
var isOpenMetadataAsSource = openDocument != null && openDocument.Project.Solution.Workspace.Kind == WorkspaceKind.MetadataAsSource;
ConditionallyCollapseOutliningRegions(textView, wpfTextView, workspace, isOpenMetadataAsSource);
// If this is a metadata-to-source view, we want to consider the file read-only
if (isOpenMetadataAsSource && ErrorHandler.Succeeded(textView.GetBuffer(out var vsTextLines)))
{
......@@ -388,12 +393,12 @@ private void UninitializeDebugMode()
}
protected virtual IVsContainedLanguage CreateContainedLanguage(
IVsTextBufferCoordinator bufferCoordinator, AbstractProject project,
IVsTextBufferCoordinator bufferCoordinator, VisualStudioProject project,
IVsHierarchy hierarchy, uint itemid)
{
return new ContainedLanguage<TPackage, TLanguageService>(
bufferCoordinator, this.Package.ComponentModel, project, hierarchy, itemid,
(TLanguageService)this, SourceCodeKind.Regular);
(TLanguageService)this);
}
}
}
......@@ -92,8 +92,6 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke
// start remote host
EnableRemoteHostClientService();
Workspace.AdviseSolutionEvents(solution);
}
LoadComponentsInUIContextOnceSolutionFullyLoadedAsync(cancellationToken).Forget();
......
// 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.Text;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.Implementation.Venus;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser
......@@ -52,7 +51,20 @@ public static string GetProjectDisplayName(this Project project)
{
if (project.Solution.Workspace is VisualStudioWorkspaceImpl workspace)
{
return workspace.GetProjectDisplayName(project);
var hierarchy = workspace.GetHierarchy(project.Id);
if (hierarchy != null)
{
var solution = (IVsSolution3)ServiceProvider.GlobalProvider.GetService(typeof(SVsSolution));
if (solution != null)
{
if (ErrorHandler.Succeeded(solution.GetUniqueUINameOfProject(hierarchy, out string name)) && name != null)
{
return name;
}
}
}
return project.Name;
}
return project.Name;
......@@ -68,7 +80,7 @@ public static bool IsVenus(this Project project)
foreach (var documentId in project.DocumentIds)
{
if (workspace.GetHostDocument(documentId) is ContainedDocument)
if (workspace.TryGetContainedDocument(documentId) != null)
{
return true;
}
......
......@@ -30,7 +30,7 @@ public bool CanPreview(Document document)
return false;
}
return !(visualStudioWorkspace.GetHostDocument(document.Id) is ContainedDocument);
return visualStudioWorkspace.TryGetContainedDocument(document.Id) == null;
}
public void PreviewItem(INavigateToItemDisplay itemDisplay)
......
......@@ -400,17 +400,12 @@ private static async Task<Uri> GetAssemblyFullPathAsync(IAssemblySymbol containi
Project foundProject = solution.GetProject(containingAssembly, cancellationToken);
if (foundProject != null)
{
if (solution.Workspace is VisualStudioWorkspaceImpl workspace)
if (solution.Workspace is VisualStudioWorkspace workspace)
{
// We have found a project in the solution, so clearly the deferred state has been loaded
var vsProject = workspace.DeferredState.ProjectTracker.GetProject(foundProject.Id);
if (vsProject != null)
// TODO: audit the OutputFilePath and whether this is bin or obj
if (!string.IsNullOrWhiteSpace(foundProject.OutputFilePath))
{
var output = vsProject.BinOutputPath;
if (!string.IsNullOrWhiteSpace(output))
{
return new Uri(output, UriKind.RelativeOrAbsolute);
}
return new Uri(foundProject.OutputFilePath, UriKind.RelativeOrAbsolute);
}
return null;
......
......@@ -16,10 +16,7 @@ public bool GeneratedTypesMustBePublic(Project project)
return false;
}
if (workspace.GetHostProject(project.Id) is AbstractProject hostProject)
{
return hostProject.IsWebSite;
}
// TODO: reimplement
return false;
}
......
// 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.IO;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal partial class AbstractProject
{
private AnalyzerFileWatcherService _analyzerFileWatcherService = null;
private AnalyzerDependencyCheckingService _dependencyCheckingService = null;
public void AddAnalyzerReference(string analyzerAssemblyFullPath)
{
AssertIsForeground();
if (CurrentProjectAnalyzersContains(analyzerAssemblyFullPath))
{
return;
}
var fileChangeService = (IVsFileChangeEx)this.ServiceProvider.GetService(typeof(SVsFileChangeEx));
if (Workspace == null)
{
// This can happen only in tests.
var testAnalyzer = new VisualStudioAnalyzer(analyzerAssemblyFullPath, fileChangeService, this.HostDiagnosticUpdateSource, this.Id, this.Workspace, loader: null, language: this.Language);
this.AddOrUpdateAnalyzer(analyzerAssemblyFullPath, testAnalyzer);
return;
}
var analyzerLoader = Workspace.Services.GetRequiredService<IAnalyzerService>().GetLoader();
analyzerLoader.AddDependencyLocation(analyzerAssemblyFullPath);
var analyzer = new VisualStudioAnalyzer(analyzerAssemblyFullPath, fileChangeService, this.HostDiagnosticUpdateSource, this.Id, this.Workspace, analyzerLoader, this.Language);
this.AddOrUpdateAnalyzer(analyzerAssemblyFullPath, analyzer);
if (PushingChangesToWorkspace)
{
var analyzerReference = analyzer.GetReference();
this.ProjectTracker.NotifyWorkspace(workspace => workspace.OnAnalyzerReferenceAdded(Id, analyzerReference));
List<VisualStudioAnalyzer> existingReferencesWithLoadErrors = GetCurrentAnalyzers().Where(a => a.HasLoadErrors).ToList();
foreach (var existingReference in existingReferencesWithLoadErrors)
{
this.ProjectTracker.NotifyWorkspace(workspace => workspace.OnAnalyzerReferenceRemoved(Id, existingReference.GetReference()));
existingReference.Reset();
this.ProjectTracker.NotifyWorkspace(workspace => workspace.OnAnalyzerReferenceAdded(Id, existingReference.GetReference()));
}
GetAnalyzerDependencyCheckingService().ReanalyzeSolutionForConflicts();
}
if (File.Exists(analyzerAssemblyFullPath))
{
GetAnalyzerFileWatcherService().TrackFilePathAndReportErrorIfChanged(analyzerAssemblyFullPath, projectId: Id);
}
else
{
analyzer.UpdatedOnDisk += OnAnalyzerChanged;
}
}
public void RemoveAnalyzerReference(string analyzerAssemblyFullPath)
{
AssertIsForeground();
if (!TryGetAnalyzer(analyzerAssemblyFullPath, out var analyzer))
{
return;
}
if (Workspace == null)
{
// This can happen only in tests.
RemoveAnalyzer(analyzerAssemblyFullPath);
analyzer.Dispose();
return;
}
GetAnalyzerFileWatcherService().RemoveAnalyzerAlreadyLoadedDiagnostics(Id, analyzerAssemblyFullPath);
RemoveAnalyzer(analyzerAssemblyFullPath);
if (PushingChangesToWorkspace)
{
var analyzerReference = analyzer.GetReference();
this.ProjectTracker.NotifyWorkspace(workspace => workspace.OnAnalyzerReferenceRemoved(Id, analyzerReference));
GetAnalyzerDependencyCheckingService().ReanalyzeSolutionForConflicts();
}
analyzer.Dispose();
}
public void SetRuleSetFile(string ruleSetFileFullPath)
{
AssertIsForeground();
if (ruleSetFileFullPath == null)
{
ruleSetFileFullPath = string.Empty;
}
if (ruleSetFileFullPath.Length > 0)
{
// This is already a full path, but run it through GetFullPath to clean it (e.g., remove
// extra backslashes).
ruleSetFileFullPath = Path.GetFullPath(ruleSetFileFullPath);
}
if (this.RuleSetFile != null &&
this.RuleSetFile.Target.FilePath.Equals(ruleSetFileFullPath, StringComparison.OrdinalIgnoreCase))
{
return;
}
ResetAnalyzerRuleSet(ruleSetFileFullPath);
}
public void AddAdditionalFile(string additionalFilePath, Func<IVisualStudioHostDocument, bool> getIsInCurrentContext)
{
AssertIsForeground();
var document = this.DocumentProvider.TryGetDocumentForFile(
this,
filePath: additionalFilePath,
sourceCodeKind: SourceCodeKind.Regular,
folderNames: ImmutableArray<string>.Empty,
canUseTextBuffer: _ => true,
updatedOnDiskHandler: s_additionalDocumentUpdatedOnDiskEventHandler,
openedHandler: s_additionalDocumentOpenedEventHandler,
closingHandler: s_additionalDocumentClosingEventHandler);
if (document == null)
{
return;
}
AddAdditionalDocument(document, isCurrentContext: getIsInCurrentContext(document));
}
public void RemoveAdditionalFile(string additionalFilePath)
{
IVisualStudioHostDocument document = this.GetCurrentDocumentFromPath(additionalFilePath);
if (document == null)
{
throw new InvalidOperationException("The document is not a part of the finalProject.");
}
RemoveAdditionalDocument(document);
}
private void ResetAnalyzerRuleSet(string ruleSetFileFullPath)
{
ClearAnalyzerRuleSet();
SetAnalyzerRuleSet(ruleSetFileFullPath);
ResetArgumentsAndUpdateOptions();
}
private void SetAnalyzerRuleSet(string ruleSetFileFullPath)
{
if (ruleSetFileFullPath.Length != 0)
{
this.RuleSetFile = this.ProjectTracker.RuleSetFileManager.GetOrCreateRuleSet(ruleSetFileFullPath);
this.RuleSetFile.Target.UpdatedOnDisk += OnRuleSetFileUpdateOnDisk;
}
}
private void ClearAnalyzerRuleSet()
{
if (this.RuleSetFile != null)
{
this.RuleSetFile.Target.UpdatedOnDisk -= OnRuleSetFileUpdateOnDisk;
this.RuleSetFile.Dispose();
this.RuleSetFile = null;
}
}
// internal for testing purpose.
internal void OnRuleSetFileUpdateOnDisk(object sender, EventArgs e)
{
AssertIsForeground();
var filePath = this.RuleSetFile.Target.FilePath;
ResetAnalyzerRuleSet(filePath);
}
private AnalyzerFileWatcherService GetAnalyzerFileWatcherService()
{
if (_analyzerFileWatcherService == null)
{
var componentModel = (IComponentModel)this.ServiceProvider.GetService(typeof(SComponentModel));
Interlocked.CompareExchange(ref _analyzerFileWatcherService, componentModel.GetService<AnalyzerFileWatcherService>(), null);
}
return _analyzerFileWatcherService;
}
private AnalyzerDependencyCheckingService GetAnalyzerDependencyCheckingService()
{
if (_dependencyCheckingService == null)
{
var componentModel = (IComponentModel)this.ServiceProvider.GetService(typeof(SComponentModel));
Interlocked.CompareExchange(ref _dependencyCheckingService, componentModel.GetService<AnalyzerDependencyCheckingService>(), null);
}
return _dependencyCheckingService;
}
}
}
// 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.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal abstract partial class AbstractProject
{
#region Options
private string _lastParsedCompilerOptions;
/// Can be null if there is no <see cref="CommandLineParserService" /> available.
private CommandLineArguments _lastParsedCommandLineArguments;
private CompilationOptions _currentCompilationOptions;
private ParseOptions _currentParseOptions;
// internal for testing purposes.
internal CompilationOptions CurrentCompilationOptions => _currentCompilationOptions;
internal ParseOptions CurrentParseOptions => _currentParseOptions;
private void SetOptionsCore(CompilationOptions newCompilationOptions)
{
lock (_gate)
{
_currentCompilationOptions = newCompilationOptions;
}
}
private void SetOptionsCore(CompilationOptions newCompilationOptions, ParseOptions newParseOptions)
{
lock (_gate)
{
_currentCompilationOptions = newCompilationOptions;
_currentParseOptions = newParseOptions;
}
}
private void SetArgumentsCore(string commandLine, CommandLineArguments commandLineArguments)
{
lock (_gate)
{
_lastParsedCompilerOptions = commandLine;
_lastParsedCommandLineArguments = commandLineArguments;
}
}
#endregion
/// <summary>
/// Creates and sets new options using the last parsed command line arguments. In the case that the last
/// parsed options are <see langword="null"/> then this call is a NOP.
/// </summary>
protected void UpdateOptions()
{
AssertIsForeground();
CommandLineArguments lastParsedCommandLineArguments = _lastParsedCommandLineArguments;
if (lastParsedCommandLineArguments != null)
{
// do nothing if the last parsed arguments aren't present, which is the case for languages that don't
// export an ICommandLineParserService like F#
var newParseOptions = CreateParseOptions(lastParsedCommandLineArguments);
var newCompilationOptions = CreateCompilationOptions(lastParsedCommandLineArguments, newParseOptions);
if (newCompilationOptions == CurrentCompilationOptions && newParseOptions == CurrentParseOptions)
{
return;
}
SetOptions(newCompilationOptions, newParseOptions);
}
}
/// <summary>
/// Parses the given command line and sets new command line arguments.
/// Subsequently, creates and sets new options using the last parsed command line arguments.
/// </summary>
protected CommandLineArguments SetArgumentsAndUpdateOptions(string commandLine)
{
AssertIsForeground();
var commandLineArguments = SetArguments(commandLine);
UpdateOptions();
return commandLineArguments;
}
/// <summary>
/// Resets the last parsed command line and updates options with the same command line.
/// </summary>
/// <remarks>
/// Use this method when options can go stale due to side effects, even though the command line is identical.
/// For example, changes to contents of a ruleset file needs to force update the options for the same command line.
/// </remarks>
protected CommandLineArguments ResetArgumentsAndUpdateOptions()
{
// Clear last parsed command line.
string savedLastParsedCompilerOptions = _lastParsedCompilerOptions;
SetArgumentsCore(commandLine: null, commandLineArguments: null);
// Now set arguments and update options with the saved command line.
return SetArgumentsAndUpdateOptions(savedLastParsedCompilerOptions);
}
/// <summary>
/// Parses the given command line and sets new command line arguments.
/// </summary>
protected CommandLineArguments SetArguments(string commandLine)
{
// Command line options have changed, so update options with new parsed CommandLineArguments.
var splitArguments = CommandLineParser.SplitCommandLineIntoArguments(commandLine, removeHashComments: false);
var parsedCommandLineArguments = CommandLineParserService?.Parse(splitArguments, this.ContainingDirectoryPathOpt, isInteractive: false, sdkDirectory: null);
SetArgumentsCore(commandLine, parsedCommandLineArguments);
return parsedCommandLineArguments;
}
/// <summary>
/// Sets the given compilation and parse options.
/// </summary>
protected void SetOptions(CompilationOptions newCompilationOptions, ParseOptions newParseOptions)
{
AssertIsForeground();
this.UpdateRuleSetError(this.RuleSetFile?.Target);
// Set options.
this.SetOptionsCore(newCompilationOptions, newParseOptions);
if (PushingChangesToWorkspace)
{
this.ProjectTracker.NotifyWorkspace(workspace => workspace.OnCompilationOptionsChanged(Id, newCompilationOptions));
this.ProjectTracker.NotifyWorkspace(workspace => workspace.OnParseOptionsChanged(Id, newParseOptions));
}
}
/// <summary>
/// Creates new compilation options from parsed command line arguments, with additional workspace specific options appended.
/// It is expected that derived types which need to add more specific options will fetch the base options and override those options.
/// </summary>
protected virtual CompilationOptions CreateCompilationOptions(CommandLineArguments commandLineArguments, ParseOptions newParseOptions)
{
Contract.ThrowIfNull(commandLineArguments);
// Get options from command line arguments.
var options = commandLineArguments.CompilationOptions;
// Now set the default workspace options (these are not set by the command line parser).
string projectDirectory = this.ContainingDirectoryPathOpt;
// TODO: #r support, should it include bin path?
var referenceSearchPaths = ImmutableArray<string>.Empty;
// TODO: #load support
var sourceSearchPaths = ImmutableArray<string>.Empty;
MetadataReferenceResolver referenceResolver;
if (Workspace != null)
{
referenceResolver = new WorkspaceMetadataFileReferenceResolver(
Workspace.CurrentSolution.Services.MetadataService,
new RelativePathResolver(referenceSearchPaths, projectDirectory));
}
else
{
// can only happen in tests
referenceResolver = null;
}
// Explicitly disable concurrent build.
options = options.WithConcurrentBuild(concurrent: false);
// Set default resolvers.
options = options.WithMetadataReferenceResolver(referenceResolver)
.WithXmlReferenceResolver(new XmlFileResolver(projectDirectory))
.WithSourceReferenceResolver(new SourceFileResolver(sourceSearchPaths, projectDirectory))
.WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default)
.WithStrongNameProvider(new DesktopStrongNameProvider(GetStrongNameKeyPaths()));
return options;
}
/// <summary>
/// Creates new parse options from parsed command line arguments (with overridden default DocumentationMode).
/// It is expected that derived types which need to add more specific options will fetch the base options and override those options.
/// </summary>
protected virtual ParseOptions CreateParseOptions(CommandLineArguments commandLineArguments)
{
Contract.ThrowIfNull(commandLineArguments);
// Override the default documentation mode.
var documentationMode = commandLineArguments.DocumentationPath != null ? DocumentationMode.Diagnose : DocumentationMode.Parse;
return commandLineArguments.ParseOptions.WithDocumentationMode(documentationMode);
}
}
}
......@@ -2,6 +2,8 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem
......@@ -18,6 +20,8 @@ internal interface IWorkspaceProjectContext : IDisposable
bool LastDesignTimeBuildSucceeded { get; set; }
string BinOutputPath { get; set; }
ProjectId Id { get; }
// Options.
void SetOptions(string commandLineForOptions);
......@@ -34,6 +38,11 @@ internal interface IWorkspaceProjectContext : IDisposable
void RemoveSourceFile(string filePath);
void AddAdditionalFile(string filePath, bool isInCurrentContext = true);
void RemoveAdditionalFile(string filePath);
void AddDynamicFile(string filePath, IEnumerable<string> folderNames = null);
void RemoveDynamicFile(string fullPath);
void SetRuleSetFile(string filePath);
void StartBatch();
void EndBatch();
}
}
......@@ -14,29 +14,25 @@ internal interface IWorkspaceProjectContextFactory
{
/// <summary>
/// Creates and initializes a new Workspace project and returns a <see cref="IWorkspaceProjectContext"/> to lazily initialize the properties and items for the project.
/// This method can be invoked on a background thread and doesn't access any members of the given UI <paramref name="hierarchy"/>,
/// allowing the UI hierarchy to be published lazily, as long as <see cref="VisualStudioWorkspaceImpl.GetProjectTrackerAndInitializeIfNecessary"/> has been called once.
/// </summary>
/// <param name="languageName">Project language.</param>
/// <param name="projectDisplayName">Display name for the project.</param>
/// <param name="projectUniqueName">Unique name 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="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>
IWorkspaceProjectContext CreateProjectContext(string languageName, string projectDisplayName, string projectFilePath, Guid projectGuid, object hierarchy, string binOutputPath);
IWorkspaceProjectContext CreateProjectContext(string languageName, string projectUniqueName, string projectFilePath, Guid projectGuid, object hierarchy, string binOutputPath);
/// <summary>
/// Creates and initializes a new Workspace project and returns a <see cref="IWorkspaceProjectContext"/> to lazily initialize the properties and items for the project.
/// This method can be invoked on a background thread and doesn't access any members of the given UI <paramref name="hierarchy"/>,
/// allowing the UI hierarchy to be published lazily, as long as <see cref="VisualStudioWorkspaceImpl.GetProjectTrackerAndInitializeIfNecessary"/> has been called once.
/// </summary>
/// <param name="languageName">Project language.</param>
/// <param name="projectDisplayName">Display name for the project.</param>
/// <param name="projectUniqueName">Display name 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="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="errorReporter">Error reporter object.</param>
IWorkspaceProjectContext CreateProjectContext(string languageName, string projectDisplayName, string projectFilePath, Guid projectGuid, object hierarchy, string binOutputPath, ProjectExternalErrorReporter errorReporter);
IWorkspaceProjectContext CreateProjectContext(string languageName, string projectUniqueName, string projectFilePath, Guid projectGuid, object hierarchy, string binOutputPath, ProjectExternalErrorReporter errorReporter);
}
}
// 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
/// <summary>
/// Represents the information which uniquely defines a document -- the project which contains
/// it and the moniker.
///
/// Immutable, since this object is used as a key into some dictionaries.
/// </summary>
internal class DocumentKey : IEquatable<DocumentKey>
{
private readonly AbstractProject _hostProject;
private readonly string _moniker;
public AbstractProject HostProject { get { return _hostProject; } }
public string Moniker { get { return _moniker; } }
public DocumentKey(AbstractProject hostProject, string moniker)
{
Contract.ThrowIfNull(hostProject);
Contract.ThrowIfNull(moniker);
_hostProject = hostProject;
_moniker = moniker;
}
public bool Equals(DocumentKey other)
{
return other != null &&
HostProject == other.HostProject &&
Moniker.Equals(other.Moniker, StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode()
{
return Hash.Combine(HostProject.GetHashCode(), StringComparer.OrdinalIgnoreCase.GetHashCode(Moniker));
}
public override bool Equals(object obj)
{
return this.Equals(obj as DocumentKey);
}
}
}
// 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 Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal partial class DocumentProvider
{
private class RunningDocTableEventsSink : IVsRunningDocTableEvents3
{
private readonly DocumentProvider _documentProvider;
public RunningDocTableEventsSink(DocumentProvider documentProvider)
{
_documentProvider = documentProvider;
}
public int OnAfterAttributeChange(uint docCookie, uint grfAttribs)
{
return VSConstants.S_OK;
}
public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew)
{
_documentProvider.OnAfterAttributeChangeEx(docCookie, grfAttribs, pHierOld, itemidOld, pszMkDocumentOld, pHierNew, itemidNew, pszMkDocumentNew);
return VSConstants.S_OK;
}
public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame)
{
return VSConstants.S_OK;
}
public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
{
return VSConstants.S_OK;
}
public int OnAfterSave(uint docCookie)
{
return VSConstants.S_OK;
}
public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame)
{
_documentProvider.OnBeforeDocumentWindowShow(pFrame, docCookie, fFirstShow != 0);
return VSConstants.S_OK;
}
public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
{
// If we have no remaining locks, then we're done
if (dwReadLocksRemaining + dwEditLocksRemaining == 0)
{
_documentProvider.CloseDocuments(docCookie, monikerToKeep: null);
}
return VSConstants.S_OK;
}
public int OnBeforeSave(uint docCookie)
{
return VSConstants.S_OK;
}
}
}
}
// 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 Microsoft.VisualStudio.TextManager.Interop;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal partial class DocumentProvider
{
private class TextBufferDataEventsSink : IVsTextBufferDataEvents
{
private readonly Action _onDocumentLoadCompleted;
private ComEventSink _sink;
/// <summary>
/// Helper method for creating and hooking up a <c>TextBufferDataEventsSink</c>.
/// </summary>
public static void HookupHandler(IVsTextBuffer textBuffer, Action onDocumentLoadCompleted)
{
var eventHandler = new TextBufferDataEventsSink(onDocumentLoadCompleted);
eventHandler._sink = ComEventSink.Advise<IVsTextBufferDataEvents>(textBuffer, eventHandler);
}
private TextBufferDataEventsSink(Action onDocumentLoadCompleted)
{
_onDocumentLoadCompleted = onDocumentLoadCompleted;
}
public void OnFileChanged(uint grfChange, uint dwFileAttrs)
{
}
public int OnLoadCompleted(int fReload)
{
_sink.Unadvise();
_onDocumentLoadCompleted();
return VSConstants.S_OK;
}
}
}
}
......@@ -2,6 +2,7 @@
using System;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
......@@ -93,5 +94,11 @@ public static uint TryGetItemId(this IVsHierarchy hierarchy, string moniker)
return VSConstants.VSITEMID_NIL;
}
public static string GetProjectFilePath(this IVsHierarchy hierarchy)
{
Marshal.ThrowExceptionForHR(((IVsProject3)hierarchy).GetMkDocument((uint)VSConstants.VSITEMID.Root, out var projectFilePath));
return projectFilePath;
}
}
}
......@@ -7,24 +7,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal static class IVsRunningDocumentTableExtensions
{
public static bool TryGetCookieForInitializedDocument(this IVsRunningDocumentTable4 runningDocTable, string moniker, out uint docCookie)
{
docCookie = VSConstants.VSCOOKIE_NIL;
if (runningDocTable != null && runningDocTable.IsMonikerValid(moniker))
{
var foundDocCookie = runningDocTable.GetDocumentCookie(moniker);
if (runningDocTable.IsDocumentInitialized(foundDocCookie))
{
docCookie = foundDocCookie;
return true;
}
}
return false;
}
public static bool IsDocumentInitialized(this IVsRunningDocumentTable4 runningDocTable, uint docCookie)
{
var flags = runningDocTable.GetDocumentFlags(docCookie);
......
using System;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
[Export(typeof(FileChangeWatcherProvider))]
internal sealed class FileChangeWatcherProvider
{
private readonly TaskCompletionSource<IVsFileChangeEx> _fileChangeService = new TaskCompletionSource<IVsFileChangeEx>(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly Lazy<FileChangeWatcher> _fileChangeWatcher;
public FileChangeWatcherProvider()
{
_fileChangeWatcher = new Lazy<FileChangeWatcher>(() => new FileChangeWatcher(_fileChangeService.Task));
}
public FileChangeWatcher Watcher => _fileChangeWatcher.Value;
internal void SetFileChangeService(IVsFileChangeEx fileChangeService)
{
_fileChangeService.TrySetResult(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.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal sealed class HierarchyEventsSink : IVsHierarchyEvents
{
private readonly DocumentId _documentId;
private readonly IVsHierarchy _sharedHierarchy;
private readonly VisualStudioWorkspaceImpl _workspace;
public HierarchyEventsSink(VisualStudioWorkspaceImpl visualStudioWorkspace, IVsHierarchy sharedHierarchy, DocumentId documentId)
{
_workspace = visualStudioWorkspace;
_sharedHierarchy = sharedHierarchy;
_documentId = documentId;
}
public int OnPropertyChanged(uint itemid, int propid, uint flags)
{
if (propid == (int)__VSHPROPID7.VSHPROPID_SharedItemContextHierarchy ||
propid == (int)__VSHPROPID8.VSHPROPID_ActiveIntellisenseProjectContext)
{
_workspace.UpdateDocumentContextIfContainsDocument(_sharedHierarchy, _documentId);
return VSConstants.S_OK;
}
return VSConstants.DISP_E_MEMBERNOTFOUND;
}
public int OnInvalidateIcon(IntPtr hicon)
{
return VSConstants.E_NOTIMPL;
}
public int OnInvalidateItems([ComAliasName("Microsoft.VisualStudio.Shell.Interop.VSITEMID")]uint itemidParent)
{
return VSConstants.E_NOTIMPL;
}
public int OnItemAdded([ComAliasName("Microsoft.VisualStudio.Shell.Interop.VSITEMID")]uint itemidParent, [ComAliasName("Microsoft.VisualStudio.Shell.Interop.VSITEMID")]uint itemidSiblingPrev, [ComAliasName("Microsoft.VisualStudio.Shell.Interop.VSITEMID")]uint itemidAdded)
{
return VSConstants.E_NOTIMPL;
}
public int OnItemDeleted([ComAliasName("Microsoft.VisualStudio.Shell.Interop.VSITEMID")]uint itemid)
{
return VSConstants.E_NOTIMPL;
}
public int OnItemsAppended([ComAliasName("Microsoft.VisualStudio.Shell.Interop.VSITEMID")]uint itemidParent)
{
return VSConstants.E_NOTIMPL;
}
}
}
......@@ -5,10 +5,7 @@
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
/// <summary>
/// This interface only exists to maintain an overload of <see cref="DocumentProvider.TryGetDocumentForFile(AbstractProject, string, SourceCodeKind, Func{Text.ITextBuffer, bool}, Func{uint, System.Collections.Generic.IReadOnlyList{string}}, EventHandler, EventHandler{bool}, EventHandler{bool})"/>.
/// </summary>
[Obsolete("This overload is a compatibility shim for TypeScript; please do not use it.")]
[Obsolete("This is a compatibility shim for TypeScript; please do not use it.")]
internal interface IVisualStudioHostProject
{
ProjectId Id { get; }
......
......@@ -35,14 +35,14 @@ internal partial class InvisibleEditor : IInvisibleEditor
/// <see cref="IVsUIShellOpenDocument4.IsDocumentInAProject2"/>, which performs a much slower query of all
/// projects in the solution.</para>
/// </remarks>
public InvisibleEditor(IServiceProvider serviceProvider, string filePath, AbstractProject projectOpt, bool needsSave, bool needsUndoDisabled)
public InvisibleEditor(IServiceProvider serviceProvider, string filePath, IVsHierarchy hierarchyOpt, bool needsSave, bool needsUndoDisabled)
{
_serviceProvider = serviceProvider;
_filePath = filePath;
_needsSave = needsSave;
var invisibleEditorManager = (IIntPtrReturningVsInvisibleEditorManager)serviceProvider.GetService(typeof(SVsInvisibleEditorManager));
var vsProject = TryGetProjectOfHierarchy(projectOpt?.Hierarchy);
var vsProject = TryGetProjectOfHierarchy(hierarchyOpt);
var invisibleEditorPtr = IntPtr.Zero;
Marshal.ThrowExceptionForHR(invisibleEditorManager.RegisterInvisibleEditor(filePath, vsProject, 0, null, out invisibleEditorPtr));
......
......@@ -8,7 +8,7 @@ internal partial class AbstractLegacyProject : ICompilerOptionsHostObject
{
int ICompilerOptionsHostObject.SetCompilerOptions(string compilerOptions, out bool supported)
{
SetArgumentsAndUpdateOptions(compilerOptions);
VisualStudioProjectOptionsProcessor.CommandLine = compilerOptions;
supported = true;
return VSConstants.S_OK;
}
......
......@@ -2,6 +2,7 @@
using System;
using System.Diagnostics;
using System.IO;
using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Legacy
......@@ -66,17 +67,17 @@ int IVsHierarchyEvents.OnPropertyChanged(uint itemid, int propid, uint flags)
propid == (int)__VSHPROPID.VSHPROPID_Name) &&
itemid == (uint)VSConstants.VSITEMID.Root)
{
string newDisplayName = GetProjectDisplayName(Hierarchy);
string newPath = GetProjectFilePath(Hierarchy);
var filePath = Hierarchy.GetProjectFilePath();
UpdateProjectDisplayNameAndFilePath(newDisplayName, newPath);
if (File.Exists(filePath))
{
VisualStudioProject.FilePath = filePath;
}
if ((propid == (int)__VSHPROPID.VSHPROPID_ProjectIDGuid) &&
itemid == (uint)VSConstants.VSITEMID.Root)
if (Hierarchy.TryGetName(out var name))
{
// this should happen while project loading if it ever happens
Guid = GetProjectIDGuid(Hierarchy);
VisualStudioProject.DisplayName = name;
}
}
return VSConstants.S_OK;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册