Skip to content

Commit

Permalink
Pass through folders for additional files
Browse files Browse the repository at this point in the history
Our workspace model always allowed this at the document level, we just
never had a request for it until recently for some XAML support. This
adds support for legacy project systems in VS and the base C# extension
project system. Changes will need to be made in other repos for the
CPS-based project systems to consume the APIs being added here.

Partially fixes dotnet#65589
  • Loading branch information
jasonmalinowski committed Dec 2, 2023
1 parent b79f0f9 commit ad6a832
Show file tree
Hide file tree
Showing 10 changed files with 51 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

using Microsoft.CodeAnalysis.LanguageServer.BrokeredServices;
using Microsoft.CodeAnalysis.LanguageServer.HostWorkspace;
using Microsoft.CodeAnalysis.LanguageServer.Logging;
using Microsoft.CodeAnalysis.Remote.ProjectSystem;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.Shell.ServiceBroker;
Expand Down Expand Up @@ -37,14 +36,20 @@ public async Task CreateProjectAndBatch()
var sourceFilePath = MakeAbsolutePath("SourceFile.cs");
var additionalFilePath = MakeAbsolutePath("AdditionalFile.txt");

await workspaceProject.AddSourceFilesAsync(new[] { new SourceFileInfo(sourceFilePath, Array.Empty<string>()) }, CancellationToken.None);
await workspaceProject.AddAdditionalFilesAsync(new[] { additionalFilePath }, CancellationToken.None);
await workspaceProject.AddSourceFilesAsync([new SourceFileInfo(sourceFilePath, ["Folder"])], CancellationToken.None);
await workspaceProject.AddAdditionalFilesAsync([new SourceFileInfo(additionalFilePath, FolderNames: ["Folder"])], CancellationToken.None);
await batch.ApplyAsync(CancellationToken.None);

// Verify it actually did something; we won't exclusively test each method since those are tested at lower layers
var project = workspaceFactory.Workspace.CurrentSolution.Projects.Single();
Assert.Equal(sourceFilePath, project.Documents.Single().FilePath);
Assert.Equal(additionalFilePath, project.AdditionalDocuments.Single().FilePath);

var document = Assert.Single(project.Documents);
Assert.Equal(sourceFilePath, document.FilePath);
Assert.Equal("Folder", Assert.Single(document.Folders));

var additionalDocument = Assert.Single(project.AdditionalDocuments);
Assert.Equal(additionalFilePath, additionalDocument.FilePath);
Assert.Equal("Folder", Assert.Single(additionalDocument.Folders));
}

private static string MakeAbsolutePath(string relativePath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageServer.Handler.DebugConfiguration;
using Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.ProjectTelemetry;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.ProjectSystem;
using Microsoft.CodeAnalysis.Workspaces.ProjectSystem;
Expand Down Expand Up @@ -163,7 +161,7 @@ public void Dispose()
newProjectInfo.AdditionalDocuments,
_mostRecentFileInfo?.AdditionalDocuments,
DocumentFileInfoComparer.Instance,
document => _projectSystemProject.AddAdditionalFile(document.FilePath),
document => _projectSystemProject.AddAdditionalFile(document.FilePath, folders: document.Folders),
document => _projectSystemProject.RemoveAdditionalFile(document.FilePath),
"Project {0} now has {1} additional file(s).");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public WorkspaceProject(ProjectSystemProject project, SolutionServices solutionS
_targetFrameworkManager = targetFrameworkManager;
}

[Obsolete($"Call the {nameof(AddAdditionalFilesAsync)} overload that takes {nameof(SourceFileInfo)}.")]
public async Task AddAdditionalFilesAsync(IReadOnlyList<string> additionalFilePaths, CancellationToken _)
{
await using var batchScope = _project.CreateBatchScope();
Expand All @@ -32,6 +33,14 @@ public async Task AddAdditionalFilesAsync(IReadOnlyList<string> additionalFilePa
_project.AddAdditionalFile(additionalFilePath);
}

public async Task AddAdditionalFilesAsync(IReadOnlyList<SourceFileInfo> additionalFiles, CancellationToken cancellationToken)
{
await using var batchScope = _project.CreateBatchScope();

foreach (var additionalFile in additionalFiles)
_project.AddAdditionalFile(additionalFile.FilePath, folders: additionalFile.FolderNames.ToImmutableArray());
}

public async Task AddAnalyzerConfigFilesAsync(IReadOnlyList<string> analyzerConfigPaths, CancellationToken _)
{
await using var batchScope = _project.CreateBatchScope();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public void Dispose()
_project.Dispose();
}

[Obsolete($"Call the {nameof(AddAdditionalFilesAsync)} overload that takes {nameof(SourceFileInfo)}.")]
public async Task AddAdditionalFilesAsync(IReadOnlyList<string> additionalFilePaths, CancellationToken cancellationToken)
{
await using var batch = _project.CreateBatchScope().ConfigureAwait(false);
Expand All @@ -37,6 +38,14 @@ public async Task AddAdditionalFilesAsync(IReadOnlyList<string> additionalFilePa
_project.AddAdditionalFile(additionalFilePath);
}

public async Task AddAdditionalFilesAsync(IReadOnlyList<SourceFileInfo> additionalFiles, CancellationToken cancellationToken)
{
await using var batchScope = _project.CreateBatchScope().ConfigureAwait(false);

foreach (var additionalFile in additionalFiles)
_project.AddAdditionalFile(additionalFile.FilePath, folderNames: additionalFile.FolderNames.ToImmutableArray());
}

public async Task RemoveAdditionalFilesAsync(IReadOnlyList<string> additionalFilePaths, CancellationToken cancellationToken)
{
await using var batch = _project.CreateBatchScope().ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ internal interface IWorkspaceProjectContext : IDisposable
// Files.
void AddSourceFile(string filePath, bool isInCurrentContext = true, IEnumerable<string>? folderNames = null, SourceCodeKind sourceCodeKind = SourceCodeKind.Regular);
void RemoveSourceFile(string filePath);
[Obsolete($"Call the {nameof(AddAdditionalFile)} method that takes folder names.")]
void AddAdditionalFile(string filePath, bool isInCurrentContext = true);
void AddAdditionalFile(string filePath, IEnumerable<string> folderNames, bool isInCurrentContext = true);
void RemoveAdditionalFile(string filePath);
void AddDynamicFile(string filePath, IEnumerable<string>? folderNames = null);
void RemoveDynamicFile(string filePath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,7 @@ protected void AddFile(
return;
}

ImmutableArray<string> folders = default;

var itemid = Hierarchy.TryGetItemId(filename);
if (itemid != VSConstants.VSITEMID_NIL)
{
folders = GetFolderNamesForDocument(itemid);
}
var folders = GetFolderNamesForDocument(filename);

ProjectSystemProject.AddSourceFile(filename, sourceCodeKind, folders);
}
Expand Down Expand Up @@ -309,6 +303,17 @@ private static Guid GetProjectIDGuid(IVsHierarchy hierarchy)
/// IVsHierarchy providers, but this code (which is fairly old) still makes the assumptions anyways.</remarks>
private readonly Dictionary<uint, ImmutableArray<string>> _folderNameMap = new();

private ImmutableArray<string> GetFolderNamesForDocument(string filename)
{
var itemid = Hierarchy.TryGetItemId(filename);
if (itemid != VSConstants.VSITEMID_NIL)
{
return GetFolderNamesForDocument(itemid);
}

return default;
}

private ImmutableArray<string> GetFolderNamesForDocument(uint documentItemID)
{
AssertIsForeground();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ void IAnalyzerHost.SetRuleSetFile(string ruleSetFileFullPath)
}

void IAnalyzerHost.AddAdditionalFile(string additionalFilePath)
=> ProjectSystemProject.AddAdditionalFile(additionalFilePath);
=> ProjectSystemProject.AddAdditionalFile(additionalFilePath, folders: GetFolderNamesForDocument(additionalFilePath));

void IAnalyzerHost.RemoveAdditionalFile(string additionalFilePath)
=> ProjectSystemProject.RemoveAdditionalFile(additionalFilePath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ public void RemoveSourceFile(string filePath)
public void AddAdditionalFile(string filePath, bool isInCurrentContext = true)
=> _projectSystemProject.AddAdditionalFile(filePath);

public void AddAdditionalFile(string filePath, IEnumerable<string> folderNames, bool isInCurrentContext = true)
=> _projectSystemProject.AddAdditionalFile(filePath, folders: folderNames.ToImmutableArray());

public void Dispose()
{
_projectCodeModel?.OnProjectClosed();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -719,8 +719,8 @@ public void RemoveSourceTextContainer(SourceTextContainer textContainer)
#region Additional File Addition/Removal

// TODO: should AdditionalFiles have source code kinds?
public void AddAdditionalFile(string fullPath, SourceCodeKind sourceCodeKind = SourceCodeKind.Regular)
=> _additionalFiles.AddFile(fullPath, sourceCodeKind, folders: default);
public void AddAdditionalFile(string fullPath, SourceCodeKind sourceCodeKind = SourceCodeKind.Regular, ImmutableArray<string> folders = default)
=> _additionalFiles.AddFile(fullPath, sourceCodeKind, folders);

public bool ContainsAdditionalFile(string fullPath)
=> _additionalFiles.ContainsFile(fullPath);
Expand Down
2 changes: 2 additions & 0 deletions src/Workspaces/Remote/Core/ProjectSystem/IWorkspaceProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ internal interface IWorkspaceProject : IDisposable
Task AddMetadataReferencesAsync(IReadOnlyList<MetadataReferenceInfo> metadataReferences, CancellationToken cancellationToken);
Task RemoveMetadataReferencesAsync(IReadOnlyList<MetadataReferenceInfo> metadataReferences, CancellationToken cancellationToken);

[Obsolete($"Call the {nameof(AddAdditionalFilesAsync)} overload that takes {nameof(SourceFileInfo)}.")]
Task AddAdditionalFilesAsync(IReadOnlyList<string> additionalFilePaths, CancellationToken cancellationToken);
Task AddAdditionalFilesAsync(IReadOnlyList<SourceFileInfo> additionalFiles, CancellationToken cancellationToken);
Task RemoveAdditionalFilesAsync(IReadOnlyList<string> additionalFilePaths, CancellationToken cancellationToken);

Task AddAnalyzerReferencesAsync(IReadOnlyList<string> analyzerPaths, CancellationToken cancellationToken);
Expand Down

0 comments on commit ad6a832

Please sign in to comment.