@@ -15,17 +15,14 @@ open Microsoft.CodeAnalysis
1515open Microsoft.CodeAnalysis .MSBuild
1616open Microsoft.CodeAnalysis .Text
1717open Microsoft.Extensions .Logging
18+ open NuGet.Frameworks
1819
19- open CSharpLanguageServer
2020open CSharpLanguageServer.Lsp
2121open CSharpLanguageServer.Logging
22- open CSharpLanguageServer.Util
2322open CSharpLanguageServer.Roslyn .WorkspaceServices
2423
25-
2624let private logger = Logging.getLoggerByName " Roslyn.Solution"
2725
28-
2926let initializeMSBuild () : unit =
3027 let vsInstanceQueryOpt = VisualStudioInstanceQueryOptions.Default
3128 let vsInstanceList = MSBuildLocator.QueryVisualStudioInstances( vsInstanceQueryOpt)
@@ -61,7 +58,6 @@ let initializeMSBuild () : unit =
6158
6259 MSBuildLocator.RegisterInstance( vsInstance)
6360
64-
6561let solutionLoadProjectFilenames ( solutionPath : string ) =
6662 assert Path.IsPathRooted solutionPath
6763 let projectFilenames = new List< string>()
@@ -75,49 +71,6 @@ let solutionLoadProjectFilenames (solutionPath: string) =
7571 projectFilenames |> Set.ofSeq
7672
7773
78- type TfmCategory =
79- | NetFramework of Version
80- | NetStandard of Version
81- | NetCoreApp of Version
82- | Net of Version
83- | Unknown
84-
85-
86- let selectLatestTfm ( tfms : string seq ) : string option =
87- let parseTfm ( tfm : string ) : TfmCategory =
88- let patterns =
89- [ @" ^net(?<major>\d)(?<minor>\d)?(?<build>\d)?$" , NetFramework
90- @" ^netstandard(?<major>\d+)\.(?<minor>\d+)$" , NetStandard
91- @" ^netcoreapp(?<major>\d+)\.(?<minor>\d+)$" , NetCoreApp
92- @" ^net(?<major>\d+)\.(?<minor>\d+)$" , Net ]
93-
94- let matchingTfmCategory ( pat , categoryCtor ) =
95- let m = Regex.Match( tfm.ToLowerInvariant(), pat)
96-
97- if m.Success then
98- let readVersionNum ( groupName : string ) =
99- let group = m.Groups.[ groupName]
100- if group.Success then int group.Value else 0
101-
102- Version( readVersionNum " major" , readVersionNum " minor" , readVersionNum " build" )
103- |> categoryCtor
104- |> Some
105- else
106- None
107-
108- patterns |> List.tryPick matchingTfmCategory |> Option.defaultValue Unknown
109-
110- let rankTfm =
111- function
112- | Net v -> 3000 + v.Major * 10 + v.Minor
113- | NetCoreApp v -> 2000 + v.Major * 10 + v.Minor
114- | NetStandard v -> 1000 + v.Major * 10 + v.Minor
115- | NetFramework v -> 0 + v.Major * 10 + v.Minor
116- | Unknown -> - 1
117-
118- tfms |> Seq.sortByDescending ( parseTfm >> rankTfm) |> Seq.tryHead
119-
120-
12174let loadProjectTfms ( projs : string seq ) : Map < string , list < string >> =
12275 let mutable projectTfms = Map.empty
12376
@@ -156,6 +109,58 @@ let loadProjectTfms (projs: string seq) : Map<string, list<string>> =
156109
157110 projectTfms
158111
112+ let compatibleFx ( a : NuGetFramework ) ( b : NuGetFramework ) =
113+ let fxCompatibilityProvider = DefaultCompatibilityProvider.Instance
114+
115+ if fxCompatibilityProvider.IsCompatible( a, b) then
116+ Some a
117+ else if fxCompatibilityProvider.IsCompatible( b, a) then
118+ Some b
119+ else
120+ None
121+
122+ let compatibleTfmsOfTwoSets afxs bfxs = seq {
123+ for a in afxs |> Seq.map NuGetFramework.Parse do
124+ for b in bfxs |> Seq.map NuGetFramework.Parse do
125+ match compatibleFx a b with
126+ | Some fx -> fx.GetShortFolderName()
127+ | None -> ()
128+ }
129+
130+ let compatibleTfmSet ( fxSets : list < Set < string >>) : Set < string > =
131+ match fxSets.Length with
132+ | 0 -> Set.empty
133+ | 1 -> fxSets |> List.head
134+ | _ ->
135+ let firstSet = ( fxSets |> List.head)
136+
137+ ( fxSets |> List.skip 1 )
138+ |> Seq.fold compatibleTfmsOfTwoSets firstSet
139+ |> Set.ofSeq
140+
141+ let bestTfm ( frameworks : Set < String >) : string option =
142+ let frameworks = frameworks |> Seq.map NuGetFramework.Parse
143+
144+ let provider = DefaultCompatibilityProvider.Instance
145+
146+ let mutable bestCompatibleFx : NuGetFramework option = None
147+
148+ for candidate in frameworks do
149+ if frameworks |> Seq.forall ( fun f -> provider.IsCompatible( candidate, f)) then
150+ bestCompatibleFx <- Some candidate
151+
152+ match bestCompatibleFx with
153+ | Some best -> best.GetShortFolderName() |> Some
154+ | _ ->
155+ // select platform-specific fx first
156+ if frameworks |> Seq.exists ( fun f -> f.HasPlatform) then
157+ frameworks
158+ |> Seq.filter ( fun f -> f.HasPlatform)
159+ |> Seq.maxBy (_. Version)
160+ |> _. GetShortFolderName()
161+ |> Some
162+ else
163+ frameworks |> Seq.maxBy (_. Version) |> _. GetShortFolderName() |> Some
159164
160165let applyWorkspaceTargetFrameworkProp ( tfmsPerProject : Map < string , list < string >>) props : Map < string , string > =
161166 let selectedTfm =
@@ -164,14 +169,14 @@ let applyWorkspaceTargetFrameworkProp (tfmsPerProject: Map<string, list<string>>
164169 | _ ->
165170 tfmsPerProject.Values
166171 |> Seq.map Set.ofSeq
167- |> Set.intersectMany
168- |> selectLatestTfm
172+ |> List.ofSeq
173+ |> compatibleTfmSet
174+ |> bestTfm
169175
170176 match selectedTfm with
171177 | Some tfm -> props |> Map.add " TargetFramework" tfm
172178 | None -> props
173179
174-
175180let resolveDefaultWorkspaceProps projs : Map < string , string > =
176181 let tfmsPerProject = loadProjectTfms projs
177182
@@ -188,7 +193,6 @@ let solutionGetProjectForPath (solution: Solution) (filePath: string) : Project
188193
189194 solution.Projects |> Seq.filter fileOnProjectDir |> Seq.tryHead
190195
191-
192196let solutionTryAddDocument ( docFilePath : string ) ( text : string ) ( solution : Solution ) : Async < Document option > = async {
193197 let projectOnPath = solutionGetProjectForPath solution docFilePath
194198
@@ -211,7 +215,6 @@ let solutionTryAddDocument (docFilePath: string) (text: string) (solution: Solut
211215 return newDocumentMaybe
212216}
213217
214-
215218let selectPreferredSolution ( slnFiles : string list ) : option < string > =
216219 let getProjectCount ( slnPath : string ) =
217220 try
@@ -229,7 +232,6 @@ let selectPreferredSolution (slnFiles: string list) : option<string> =
229232 |> Seq.map snd
230233 |> Seq.tryHead
231234
232-
233235let solutionTryLoadOnPath ( lspClient : ILspClient ) ( solutionPath : string ) =
234236 assert Path.IsPathRooted solutionPath
235237 let progress = ProgressReporter lspClient
@@ -282,7 +284,6 @@ let solutionTryLoadOnPath (lspClient: ILspClient) (solutionPath: string) =
282284 return None
283285 }
284286
285-
286287let solutionTryLoadFromProjectFiles ( lspClient : ILspClient ) ( logMessage : string -> Async < unit >) ( projs : string list ) =
287288 let progress = ProgressReporter lspClient
288289
@@ -324,7 +325,6 @@ let solutionTryLoadFromProjectFiles (lspClient: ILspClient) (logMessage: string
324325 return Some msbuildWorkspace.CurrentSolution
325326 }
326327
327-
328328let solutionFindAndLoadOnDir ( lspClient : ILspClient ) dir = async {
329329 let fileNotOnNodeModules ( filename : string ) =
330330 filename.Split Path.DirectorySeparatorChar |> Seq.contains " node_modules" |> not
@@ -374,7 +374,6 @@ let solutionFindAndLoadOnDir (lspClient: ILspClient) dir = async {
374374 | Some solutionPath -> return ! solutionTryLoadOnPath lspClient solutionPath
375375}
376376
377-
378377let solutionLoadSolutionWithPathOrOnCwd ( lspClient : ILspClient ) ( solutionPathMaybe : string option ) ( cwd : string ) =
379378 match solutionPathMaybe with
380379 | Some solutionPath -> async {
0 commit comments