Skip to content

Commit 0437472

Browse files
authored
Merge pull request #3858 from KevinRansom/issue3829branch15.5
Enable - Referencing a C# or VB project from a NetSdk F# project following successfull build
2 parents ebd20de + 87c4f5e commit 0437472

File tree

4 files changed

+138
-120
lines changed

4 files changed

+138
-120
lines changed

vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs

+60-57
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,16 @@ open System.Linq
1515
open System.Runtime.CompilerServices
1616
open System.Runtime.InteropServices
1717
open System.Threading
18-
19-
open Microsoft.FSharp.Compiler.CompileOps
20-
open Microsoft.FSharp.Compiler.SourceCodeServices
21-
open Microsoft.VisualStudio.FSharp.LanguageService.SiteProvider
22-
2318
open Microsoft.CodeAnalysis
2419
open Microsoft.CodeAnalysis.Diagnostics
2520
open Microsoft.CodeAnalysis.Completion
2621
open Microsoft.CodeAnalysis.Options
22+
open Microsoft.FSharp.Compiler.CompileOps
23+
open Microsoft.FSharp.Compiler.SourceCodeServices
2724
open Microsoft.VisualStudio
2825
open Microsoft.VisualStudio.Editor
26+
open Microsoft.VisualStudio.FSharp.LanguageService
27+
open Microsoft.VisualStudio.FSharp.LanguageService.SiteProvider
2928
open Microsoft.VisualStudio.TextManager.Interop
3029
open Microsoft.VisualStudio.LanguageServices
3130
open Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService
@@ -34,10 +33,9 @@ open Microsoft.VisualStudio.LanguageServices.Implementation.TaskList
3433
open Microsoft.VisualStudio.LanguageServices.ProjectSystem
3534
open Microsoft.VisualStudio.Shell
3635
open Microsoft.VisualStudio.Shell.Interop
37-
open Microsoft.VisualStudio.FSharp.LanguageService
3836
open Microsoft.VisualStudio.ComponentModelHost
3937

40-
// Exposes FSharpChecker as MEF export
38+
/// Exposes FSharpChecker as MEF export
4139
[<Export(typeof<FSharpCheckerProvider>); Composition.Shared>]
4240
type internal FSharpCheckerProvider
4341
[<ImportingConstructor>]
@@ -84,12 +82,12 @@ type internal FSharpCheckerProvider
8482

8583

8684
/// Exposes FCS FSharpProjectOptions information management as MEF component.
87-
//
88-
// This service allows analyzers to get an appropriate FSharpProjectOptions value for a project or single file.
89-
// It also allows a 'cheaper' route to get the project options relevant to parsing (e.g. the #define values).
90-
// The main entrypoints are TryGetOptionsForDocumentOrProject and TryGetOptionsForEditingDocumentOrProject.
85+
///
86+
/// This service allows analyzers to get an appropriate FSharpProjectOptions value for a project or single file.
87+
/// It also allows a 'cheaper' route to get the project options relevant to parsing (e.g. the #define values).
88+
/// The main entrypoints are TryGetOptionsForDocumentOrProject and TryGetOptionsForEditingDocumentOrProject.
9189
[<Export(typeof<FSharpProjectOptionsManager>); Composition.Shared>]
92-
type internal FSharpProjectOptionsManager
90+
type internal FSharpProjectOptionsManager
9391
[<ImportingConstructor>]
9492
(
9593
checkerProvider: FSharpCheckerProvider,
@@ -114,33 +112,36 @@ type internal FSharpProjectOptionsManager
114112
/// Clear a project from the project table
115113
member this.ClearInfoForProject(projectId:ProjectId) = projectOptionsTable.ClearInfoForProject(projectId)
116114

115+
/// Clear a project from the single file project table
117116
member this.ClearInfoForSingleFileProject(projectId) =
118117
singleFileProjectTable.TryRemove(projectId) |> ignore
119118

119+
/// Update a project in the single file project table
120120
member this.AddOrUpdateSingleFileProject(projectId, data) = singleFileProjectTable.[projectId] <- data
121121

122122
/// Get the exact options for a single-file script
123-
member this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, fileContents, workspace: Workspace) = async {
124-
let extraProjectInfo = Some(box workspace)
125-
let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject |> Option.map(fun (_, _, projectOptions) -> projectOptions)
126-
if SourceFile.MustBeSingleFileProject(fileName) then
127-
// NOTE: we don't use a unique stamp for single files, instead comparing options structurally.
128-
// This is because we repeatedly recompute the options.
129-
let optionsStamp = None
130-
let! options, _diagnostics = checkerProvider.Checker.GetProjectOptionsFromScript(fileName, fileContents, loadTime, [| |], ?extraProjectInfo=extraProjectInfo, ?optionsStamp=optionsStamp)
131-
// NOTE: we don't use FCS cross-project references from scripts to projects. THe projects must have been
132-
// compiled and #r will refer to files on disk
133-
let referencedProjectFileNames = [| |]
134-
let site = ProjectSitesAndFiles.CreateProjectSiteForScript(fileName, referencedProjectFileNames, options)
135-
let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, serviceProvider, (tryGetOrCreateProjectId fileName), fileName, options.ExtraProjectInfo, Some projectOptionsTable, true)
136-
let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions)
137-
return (deps, parsingOptions, projectOptions)
138-
else
139-
let site = ProjectSitesAndFiles.ProjectSiteOfSingleFile(fileName)
140-
let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, serviceProvider, (tryGetOrCreateProjectId fileName), fileName, extraProjectInfo, Some projectOptionsTable, true)
141-
let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions)
142-
return (deps, parsingOptions, projectOptions)
143-
}
123+
member this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, fileContents) =
124+
async {
125+
let extraProjectInfo = Some(box workspace)
126+
let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject |> Option.map(fun (_, _, projectOptions) -> projectOptions)
127+
if SourceFile.MustBeSingleFileProject(fileName) then
128+
// NOTE: we don't use a unique stamp for single files, instead comparing options structurally.
129+
// This is because we repeatedly recompute the options.
130+
let optionsStamp = None
131+
let! options, _diagnostics = checkerProvider.Checker.GetProjectOptionsFromScript(fileName, fileContents, loadTime, [| |], ?extraProjectInfo=extraProjectInfo, ?optionsStamp=optionsStamp)
132+
// NOTE: we don't use FCS cross-project references from scripts to projects. THe projects must have been
133+
// compiled and #r will refer to files on disk
134+
let referencedProjectFileNames = [| |]
135+
let site = ProjectSitesAndFiles.CreateProjectSiteForScript(fileName, referencedProjectFileNames, options)
136+
let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, serviceProvider, (tryGetOrCreateProjectId fileName), fileName, options.ExtraProjectInfo, Some projectOptionsTable, true)
137+
let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions)
138+
return (deps, parsingOptions, projectOptions)
139+
else
140+
let site = ProjectSitesAndFiles.ProjectSiteOfSingleFile(fileName)
141+
let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, serviceProvider, (tryGetOrCreateProjectId fileName), fileName, extraProjectInfo, Some projectOptionsTable, true)
142+
let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions)
143+
return (deps, parsingOptions, projectOptions)
144+
}
144145

145146
/// Update the info for a project in the project table
146147
member this.UpdateProjectInfo(tryGetOrCreateProjectId, projectId, site, userOpName) =
@@ -164,32 +165,34 @@ type internal FSharpProjectOptionsManager
164165
| _ -> FSharpParsingOptions.Default
165166
CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions)
166167

168+
/// Try and get the Options for a project
167169
member this.TryGetOptionsForProject(projectId:ProjectId) = projectOptionsTable.TryGetOptionsForProject(projectId)
168170

169171
/// Get the exact options for a document or project
170-
member this.TryGetOptionsForDocumentOrProject(document: Document) = async {
171-
let projectId = document.Project.Id
172-
173-
// The options for a single-file script project are re-requested each time the file is analyzed. This is because the
174-
// single-file project may contain #load and #r references which are changing as the user edits, and we may need to re-analyze
175-
// to determine the latest settings. FCS keeps a cache to help ensure these are up-to-date.
176-
match singleFileProjectTable.TryGetValue(projectId) with
177-
| true, (loadTime, _, _) ->
178-
try
179-
let fileName = document.FilePath
180-
let! cancellationToken = Async.CancellationToken
181-
let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask
182-
// NOTE: we don't use FCS cross-project references from scripts to projects. The projects must have been
183-
// compiled and #r will refer to files on disk.
184-
let tryGetOrCreateProjectId _ = None
185-
let! _referencedProjectFileNames, parsingOptions, projectOptions = this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, sourceText.ToString(), document.Project.Solution.Workspace)
186-
this.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions))
187-
return Some (parsingOptions, None, projectOptions)
188-
with ex ->
189-
Assert.Exception(ex)
190-
return None
191-
| _ -> return this.TryGetOptionsForProject(projectId)
192-
}
172+
member this.TryGetOptionsForDocumentOrProject(document: Document) =
173+
async {
174+
let projectId = document.Project.Id
175+
176+
// The options for a single-file script project are re-requested each time the file is analyzed. This is because the
177+
// single-file project may contain #load and #r references which are changing as the user edits, and we may need to re-analyze
178+
// to determine the latest settings. FCS keeps a cache to help ensure these are up-to-date.
179+
match singleFileProjectTable.TryGetValue(projectId) with
180+
| true, (loadTime, _, _) ->
181+
try
182+
let fileName = document.FilePath
183+
let! cancellationToken = Async.CancellationToken
184+
let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask
185+
// NOTE: we don't use FCS cross-project references from scripts to projects. The projects must have been
186+
// compiled and #r will refer to files on disk.
187+
let tryGetOrCreateProjectId _ = None
188+
let! _referencedProjectFileNames, parsingOptions, projectOptions = this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, sourceText.ToString())
189+
this.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions))
190+
return Some (parsingOptions, None, projectOptions)
191+
with ex ->
192+
Assert.Exception(ex)
193+
return None
194+
| _ -> return this.TryGetOptionsForProject(projectId)
195+
}
193196

194197
/// Get the options for a document or project relevant for syntax processing.
195198
/// Quicker then TryGetOptionsForDocumentOrProject as it doesn't need to recompute the exact project options for a script.
@@ -540,7 +543,7 @@ type
540543
let projectDisplayName = projectDisplayNameOf projectFileName
541544

542545
let projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName)
543-
let _referencedProjectFileNames, parsingOptions, projectOptions = projectInfoManager.ComputeSingleFileOptions (tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents, workspace) |> Async.RunSynchronously
546+
let _referencedProjectFileNames, parsingOptions, projectOptions = projectInfoManager.ComputeSingleFileOptions (tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents) |> Async.RunSynchronously
544547
projectInfoManager.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions))
545548

546549
if isNull (workspace.ProjectTracker.GetProject projectId) then

vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
<GeneratedModuleName>Microsoft.VisualStudio.FSharp.LanguageService.Strings</GeneratedModuleName>
6868
</EmbeddedResource>
6969
<Compile Include="AssemblyInfo.fs" />
70+
<Compile Include="LanguageServiceConstants.fs" />
7071
<Compile Include="Error.fs" />
7172
<Compile Include="Vs.fs" />
7273
<Compile Include="Colorize.fs" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
2+
3+
namespace Microsoft.VisualStudio.FSharp.LanguageService
4+
5+
[<RequireQualifiedAccess>]
6+
module internal LanguageServiceConstants =
7+
8+
/// "F#"
9+
[<Literal>]
10+
let FSharpLanguageName = "F#"

0 commit comments

Comments
 (0)