diff --git a/src/Tools/ExternalAccess/FSharp/Microsoft.CodeAnalysis.ExternalAccess.FSharp.csproj b/src/Tools/ExternalAccess/FSharp/Microsoft.CodeAnalysis.ExternalAccess.FSharp.csproj
index d0964c328354a..e0e3171327ed3 100644
--- a/src/Tools/ExternalAccess/FSharp/Microsoft.CodeAnalysis.ExternalAccess.FSharp.csproj
+++ b/src/Tools/ExternalAccess/FSharp/Microsoft.CodeAnalysis.ExternalAccess.FSharp.csproj
@@ -26,6 +26,7 @@
+
diff --git a/src/Tools/ExternalAccess/FSharp/VS/IFSharpWorkspaceProjectContextFactory.cs b/src/Tools/ExternalAccess/FSharp/VS/IFSharpWorkspaceProjectContextFactory.cs
new file mode 100644
index 0000000000000..f5e56843ca037
--- /dev/null
+++ b/src/Tools/ExternalAccess/FSharp/VS/IFSharpWorkspaceProjectContextFactory.cs
@@ -0,0 +1,178 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Composition;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.VisualStudio.LanguageServer.Protocol;
+using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
+using Microsoft.VisualStudio.Shell.Interop;
+
+namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp
+{
+ internal interface IFSharpWorkspaceProjectContextFactory
+ {
+ IFSharpWorkspaceProjectContext CreateProjectContext(string filePath, string uniqueName);
+ }
+
+ internal interface IFSharpWorkspaceProjectContext : IDisposable
+ {
+ string DisplayName { get; set; }
+ ProjectId Id { get; }
+ string FilePath { get; }
+ int ProjectReferenceCount { get; }
+ bool HasProjectReference(string filePath);
+ int MetadataReferenceCount { get; }
+ bool HasMetadataReference(string referencePath);
+ void SetProjectReferences(IEnumerable projRefs);
+ void SetMetadataReferences(IEnumerable referencePaths);
+ void AddMetadataReference(string referencePath);
+ void AddSourceFile(string path, SourceCodeKind kind);
+ }
+
+ [Shared]
+ [Export(typeof(FSharpWorkspaceProjectContextFactory))]
+ [Export(typeof(IFSharpWorkspaceProjectContextFactory))]
+ internal sealed class FSharpWorkspaceProjectContextFactory : IFSharpWorkspaceProjectContextFactory
+ {
+ private readonly IWorkspaceProjectContextFactory _factory;
+ private readonly IThreadingContext _threadingContext;
+
+ [ImportingConstructor]
+ [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
+ public FSharpWorkspaceProjectContextFactory(IWorkspaceProjectContextFactory factory, IThreadingContext threadingContext)
+ {
+ _factory = factory;
+ _threadingContext = threadingContext;
+ }
+
+ IFSharpWorkspaceProjectContext IFSharpWorkspaceProjectContextFactory.CreateProjectContext(string filePath, string uniqueName)
+ => CreateProjectContext(filePath, uniqueName);
+
+ public FSharpWorkspaceProjectContext CreateProjectContext(string filePath, string uniqueName)
+ => CreateProjectContext(
+ projectUniqueName: uniqueName,
+ projectFilePath: filePath,
+ projectGuid: Guid.NewGuid(),
+ hierarchy: null,
+ binOutputPath: null);
+
+ public FSharpWorkspaceProjectContext CreateProjectContext(string projectUniqueName, string projectFilePath, Guid projectGuid, object? hierarchy, string? binOutputPath)
+ => new(_threadingContext.JoinableTaskFactory.Run(() => _factory.CreateProjectContextAsync(
+ languageName: LanguageNames.FSharp,
+ projectUniqueName: projectUniqueName,
+ projectFilePath: projectFilePath,
+ projectGuid: projectGuid,
+ hierarchy,
+ binOutputPath,
+ assemblyName: null,
+ CancellationToken.None)));
+ }
+
+ internal sealed class FSharpWorkspaceProjectContext : IFSharpWorkspaceProjectContext
+ {
+ private readonly IWorkspaceProjectContext _vsProjectContext;
+
+ private ImmutableDictionary _projectReferences;
+ private ImmutableHashSet _metadataReferences;
+
+ public FSharpWorkspaceProjectContext(IWorkspaceProjectContext vsProjectContext)
+ {
+ _vsProjectContext = vsProjectContext;
+ _projectReferences = ImmutableDictionary.Create(StringComparer.OrdinalIgnoreCase);
+ _metadataReferences = ImmutableHashSet.Create(StringComparer.OrdinalIgnoreCase);
+ }
+
+ public void Dispose()
+ => _vsProjectContext.Dispose();
+
+ public IVsLanguageServiceBuildErrorReporter2? BuildErrorReporter
+ => _vsProjectContext as IVsLanguageServiceBuildErrorReporter2;
+
+ public string DisplayName
+ {
+ get => _vsProjectContext.DisplayName;
+ set => _vsProjectContext.DisplayName = value;
+ }
+
+ public string BinOutputPath
+ {
+ get => _vsProjectContext.BinOutputPath;
+ set => _vsProjectContext.BinOutputPath = value;
+ }
+
+ public ProjectId Id
+ => _vsProjectContext.Id;
+
+ public string FilePath
+ => _vsProjectContext.ProjectFilePath;
+
+ public int ProjectReferenceCount
+ => _projectReferences.Count;
+
+ public bool HasProjectReference(string filePath)
+ => _projectReferences.ContainsKey(filePath);
+
+ public int MetadataReferenceCount
+ => _metadataReferences.Count;
+
+ public bool HasMetadataReference(string referencePath)
+ => _metadataReferences.Contains(referencePath);
+
+ public void SetProjectReferences(IEnumerable projRefs)
+ {
+ var builder = ImmutableDictionary.CreateBuilder();
+
+ foreach (var reference in _projectReferences.Values.Cast())
+ {
+ _vsProjectContext.RemoveProjectReference(reference._vsProjectContext);
+ }
+
+ foreach (var reference in projRefs.Cast())
+ {
+ _vsProjectContext.AddProjectReference(reference._vsProjectContext, MetadataReferenceProperties.Assembly);
+ builder.Add(reference.FilePath, reference);
+ }
+
+ _projectReferences = builder.ToImmutable();
+ }
+
+ public void SetMetadataReferences(IEnumerable referencePaths)
+ {
+ var builder = ImmutableHashSet.CreateBuilder();
+
+ foreach (var referencePath in _metadataReferences)
+ {
+ RemoveMetadataReference(referencePath);
+ }
+
+ foreach (var referencePath in referencePaths)
+ {
+ AddMetadataReference(referencePath);
+ builder.Add(referencePath);
+ }
+
+ _metadataReferences = builder.ToImmutable();
+ }
+
+ public void RemoveMetadataReference(string referencePath)
+ => _vsProjectContext.RemoveMetadataReference(referencePath);
+
+ public void AddMetadataReference(string referencePath)
+ => _vsProjectContext.AddMetadataReference(referencePath, MetadataReferenceProperties.Assembly);
+
+ public void AddSourceFile(string path, SourceCodeKind kind)
+ => _vsProjectContext.AddSourceFile(path, sourceCodeKind: kind);
+
+ public void RemoveSourceFile(string path)
+ => _vsProjectContext.RemoveSourceFile(path);
+ }
+}