Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 15, 2025

Summary

This PR implements a shared "canonical miscellaneous files project" for genuine miscellaneous files in the VSCode language server, avoiding the overhead of running individual design-time builds for each misc file.

Problem

Previously, the FileBasedProgramsProjectSystem treated both genuine miscellaneous files and file-based programs similarly - for each file it would:

  1. Run a design-time build
  2. Run restore
  3. Create a new project

This approach caused several issues for genuine miscellaneous files:

  • Unnecessary design-time builds for each file
  • Nuisance restore popups
  • Build artifacts (obj/ folders) created in random user directories
  • No support for virtual miscellaneous files

Solution

This PR introduces a canonical miscellaneous files project that is shared across all genuine misc files (non-file-based programs):

Key Changes

  1. New CanonicalMiscFilesProject class - Manages a single shared project:

    • Creates a temp directory (roslyn-lsp/canonical-misc-files) to hold the project
    • Performs a design-time build once during initialization with an empty file
    • Uses workspace APIs to add/remove documents dynamically without additional builds
    • Thread-safe with proper gate management
  2. Updated FileBasedProgramsProjectSystem - Intelligently routes files:

    • Detects file-based programs using VirtualProjectXmlProvider.IsFileBasedProgram() (checks for global statements or #! directive)
    • Genuine misc files → canonical project (new behavior)
    • File-based programs → individual virtual projects (existing behavior)
    • Graceful fallback to per-file approach if canonical project initialization fails
  3. Updated LoadedProject - Exposes ProjectSystemProject property to enable the canonical project to manage source files

Benefits

✅ Only one design-time build for all misc files instead of per-file builds
No restore popups for misc files - restore happens once
Clean artifact location - all build outputs in a single temp directory
Supports virtual misc files - no physical file needed for design-time build
Backward compatible - file-based programs continue to work as before

Example

Before:

Open file1.cs → Create project, run build, run restore, create obj/ folder
Open file2.cs → Create project, run build, run restore, create obj/ folder
Open file3.cs → Create project, run build, run restore, create obj/ folder

After:

Open file1.cs → Initialize canonical project (one-time build), add file1.cs
Open file2.cs → Add to canonical project (no build)
Open file3.cs → Add to canonical project (no build)

Testing

  • Built successfully with no compilation errors or warnings
  • Follows Roslyn coding patterns and conventions
  • Proper error handling with fallback to existing behavior
  • Thread-safe implementation

Fixes #78560

Original prompt

This section details on the original issue you should resolve

<issue_title>Use a canonical misc files project for misc files in VSCode</issue_title>
<issue_description>Currently the FileBasedProgramsProjectSystem loads both genuine miscellaneous files and file based programs in mostly the same way - for each file it runs a design time build, runs restore and creates a new project for each file.

This is not ideal for genuine miscellaneous files. We should create a separate path, which:

  1. sets-up a temp location on disk, to hold a "canonical misc files project" created from an empty file
  2. does a design time build on it once, the first time we are asked to make a "rich misc files project" essentially,
  3. then uses the workspace APIs to add or remove misc documents to the "canonical project's" (also removing whatever blank document we originally used for the design time build)

This would support virtual misc files as well, as we don't need a specific file to run a design time build, and avoid nuisance things like restore popups and weird user configurations resulting in obj/ folders in random places.

</issue_description>

<agent_instructions>Please resolve this issue as described. Focus on the implementation only and ignore tests for the moment.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

Fixes #80743


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

}

// Add the source file to the project
_loadedProject.ProjectSystemProject.AddSourceFile(filePath);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like the wrong thing. We want to obtain the "Workspaces-layer" Project instance, which is produced as a result/output of the LoadedProject, and create a new derived Project instance containing the user's document, without making any changes to _loadedProject. Any "blank/meaningless" C# documents which we included in the canonical project, should also not be present in the derived Project instance.

var artifactsPath = GetCanonicalProjectDirectory();
var targetFramework = Environment.GetEnvironmentVariable("DOTNET_RUN_FILE_TFM") ?? "net$(BundledNETCoreAppTargetFrameworkVersion)";

var virtualProjectXml = $"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that since the canonical project and all its on-disk contents are in a temp directory, there shouldn't be any need to do most/all the virtual project stuff that is done for file-based apps. i.e. the project file used for dotnet new classlib should work here.

Comment on lines +31 to +32
private CanonicalMiscFilesProject? _canonicalMiscFilesProject;
private readonly SemaphoreSlim _canonicalProjectGate = new(initialCount: 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just create the CanonicalMiscFilesProject instance when constructing this project system. Then have its internal operations use its internal gate. No need for a gate out here.

{
if (_canonicalMiscFilesProject != null)
{
var removedFromCanonical = await _canonicalMiscFilesProject.RemoveDocumentAsync(documentPath, CancellationToken.None);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will also be unnecessary when the change is made to use derived Workspaces Project instances, instead of adding documents to the LoadedProject.

Copilot AI changed the title [WIP] Implement canonical misc files project in VSCode Use a canonical misc files project for misc files in VSCode Oct 15, 2025
Copilot finished work on behalf of dibarbet October 15, 2025 23:23
Copilot AI requested a review from dibarbet October 15, 2025 23:23
@dibarbet
Copy link
Member

closing in favor of attempt 2 #80748

@dibarbet dibarbet closed this Oct 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

3 participants