Skip to content

Commit 7e02460

Browse files
committed
Did you know that file order matters in F#?
1 parent fd321d8 commit 7e02460

File tree

3 files changed

+102
-49
lines changed

3 files changed

+102
-49
lines changed

ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs

Lines changed: 53 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -203,43 +203,59 @@ type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFShar
203203

204204
let psiModule = psiModules.GetPrimaryPsiModule(project, targetFrameworkId)
205205

206-
let sourceFiles =
206+
let projectItems: (VirtualFileSystemPath * BuildAction) array = x.GetProjectItemsPaths(project, targetFrameworkId)
207+
208+
let tryFindPsiSourceFile name =
207209
psiModule.SourceFiles
208-
|> Seq.filter(fun psiSourceFile ->
209-
isNotNull psiSourceFile.PrimaryPsiLanguage
210-
&& psiSourceFile.PrimaryPsiLanguage.Name = "F#"
211-
)
212-
|> Seq.map (fun psiSourceFile ->
213-
let name = psiSourceFile.GetLocation().FullPath
214-
(*
215-
216-
In order to create the snapshot, we need to ensure that Resharper read lock rules are respected when getting the source.
217-
Today, this happens in DelegatingFileSystemShim.cs.
218-
So we can rely on the file system (that is shimmed) and use FSharpFileSnapshot.CreateFromFileSystem.
219-
220-
Alternatively we can construct the snapshot via getSource:
221-
```fsharp
222-
let version = string psiSourceFile.Document.LastModificationStamp.Value
223-
224-
let getSource () =
225-
task {
226-
let mutable text = ""
227-
FSharpAsyncUtil.UsingReadLockInsideFcs(locks, fun () ->
228-
text <- psiSourceFile.Document.GetText()
229-
)
230-
return FSharp.Compiler.Text.SourceTextNew.ofString text
231-
}
232-
233-
ProjectSnapshot.FSharpFileSnapshot.Create(name, version, getSource)
234-
```
235-
236-
This also worked but for now going with the FileSystemShim seems better?
237-
I favour the getSource option (or a better version of it) over the FileSystemShim
238-
as it makes it more explicit where the source is really coming from.
239-
However, I don't have enough understanding about the plugin to really make this judgement call.
240-
241-
*)
242-
ProjectSnapshot.FSharpFileSnapshot.CreateFromFileSystem(name)
210+
|> Seq.tryFind (fun sf -> sf.GetLocation().FullPath = name)
211+
212+
let sourceFiles =
213+
projectItems
214+
|> Seq.choose (fun (virtualFileSystemPath, buildAction) ->
215+
match buildAction, tryFindPsiSourceFile virtualFileSystemPath.FullPath with
216+
| SourceFile, Some psiSourceFile ->
217+
let name = virtualFileSystemPath.FullPath
218+
(*
219+
220+
In order to create the snapshot, we need to ensure that Resharper read lock rules are respected when getting the source.
221+
Today, this happens in DelegatingFileSystemShim.cs.
222+
So we can rely on the file system (that is shimmed) and use FSharpFileSnapshot.CreateFromFileSystem.
223+
224+
Alternatively we can construct the snapshot via getSource:
225+
```fsharp
226+
let version = string psiSourceFile.Document.LastModificationStamp.Value
227+
228+
let getSource () =
229+
task {
230+
let mutable text = ""
231+
FSharpAsyncUtil.UsingReadLockInsideFcs(locks, fun () ->
232+
text <- psiSourceFile.Document.GetText()
233+
)
234+
return FSharp.Compiler.Text.SourceTextNew.ofString text
235+
}
236+
237+
ProjectSnapshot.FSharpFileSnapshot.Create(name, version, getSource)
238+
```
239+
240+
This also worked but for now going with the FileSystemShim seems better?
241+
I favour the getSource option (or a better version of it) over the FileSystemShim
242+
as it makes it more explicit where the source is really coming from.
243+
However, I don't have enough understanding about the plugin to really make this judgement call.
244+
245+
*)
246+
let getSource () =
247+
task {
248+
let mutable text = ""
249+
FSharpAsyncUtil.UsingReadLockInsideFcs(locks, fun () ->
250+
text <- psiSourceFile.Document.GetText()
251+
)
252+
return FSharp.Compiler.Text.SourceTextNew.ofString text
253+
}
254+
255+
let hash = string (psiSourceFile.Document.GetText().GetHashCode())
256+
// let sf = ProjectSnapshot.FSharpFileSnapshot.CreateFromFileSystem(name)
257+
Some(ProjectSnapshot.FSharpFileSnapshot(name, hash, getSource))
258+
| _ -> None
243259
)
244260
|> Seq.toList
245261

@@ -277,7 +293,7 @@ type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFShar
277293
originalLoadReferences = List.empty,
278294
stamp = None
279295
)
280-
296+
281297
let parsingOptions, errors =
282298
checkerService.Checker.GetParsingOptionsFromCommandLineArgs(otherOptions)
283299

ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ open JetBrains.Application.Threading
1515
open JetBrains.Application.changes
1616
open JetBrains.DataFlow
1717
open JetBrains.Diagnostics
18+
open JetBrains.DocumentManagers
19+
open JetBrains.DocumentManagers.impl
20+
open JetBrains.DocumentModel
1821
open JetBrains.Lifetimes
1922
open JetBrains.ProjectModel
2023
open JetBrains.ProjectModel.Build
@@ -72,7 +75,8 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager:
7275
scriptFcsProjectProvider: IScriptFcsProjectProvider,
7376
fsFileService: IFSharpFileService, fsItemsContainer: IFSharpItemsContainer,
7477
locks: IShellLocks, logger: ILogger, fcsAssemblyReaderShim: IFcsAssemblyReaderShim, psiModules: IPsiModules,
75-
moduleReferencesResolveStore: IModuleReferencesResolveStore) as this =
78+
moduleReferencesResolveStore: IModuleReferencesResolveStore,
79+
solutionDocumentChangeProvider: SolutionDocumentChangeProvider) as this =
7680
inherit RecursiveProjectModelChangeDeltaVisitor()
7781

7882
/// The main cache for FCS project model and related things.
@@ -464,6 +468,36 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager:
464468
if isNotNull change && not change.IsClosingSolution then
465469
x.VisitDelta(change)
466470

471+
let documentChange = obj.ChangeMap.GetChange<DocumentChange>(solutionDocumentChangeProvider)
472+
if isNotNull documentChange then
473+
let projectFile =
474+
match documentChange with
475+
| :? ProjectFileDocumentChange as change -> change.ProjectFile
476+
| :? ProjectFileDocumentCopyChange as change -> change.ProjectFile
477+
| _ -> null
478+
479+
if isNotNull projectFile then
480+
// call .Replace() ?
481+
let impactedProjects =
482+
fcsProjects
483+
|> Seq.filter(fun (KeyValue(_, fcsProject)) ->
484+
fcsProject.ProjectSnapshot.SourceFiles
485+
|> List.exists (fun sf -> sf.FileName = projectFile.Location.FullPath))
486+
487+
for KeyValue(fcsProjectKey, fcsProject) in impactedProjects do
488+
let updatedFiles =
489+
fcsProject.ProjectSnapshot.SourceFiles
490+
|> List.map (fun sf ->
491+
if sf.FileName <> projectFile.Location.FullPath then
492+
sf
493+
else
494+
FSharpFileSnapshot.CreateFromFileSystem(projectFile.Location.FullPath)
495+
)
496+
497+
let updatedProject =
498+
{ fcsProject with ProjectSnapshot = fcsProject.ProjectSnapshot.Replace(updatedFiles) }
499+
fcsProjects.Add(fcsProjectKey, updatedProject)
500+
467501
if dirtyProjects.Count = 0 then () else
468502

469503
use cookie = WriteLockCookie.Create()

ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ type TestFcsProjectProvider(lifetime: Lifetime, checkerService: FcsCheckerServic
228228
do
229229
checkerService.FcsProjectProvider <- this
230230
lifetime.OnTermination(fun _ -> checkerService.FcsProjectProvider <- Unchecked.defaultof<_>) |> ignore
231+
231232

232233
let mutable currentFcsProject = None
233234

@@ -238,19 +239,21 @@ type TestFcsProjectProvider(lifetime: Lifetime, checkerService: FcsCheckerServic
238239
// todo: referenced projects
239240
// todo: unify with FcsProjectProvider check
240241
let areSameForChecking (newProject: FcsProject) (oldProject: FcsProject) =
241-
let getReferencedProjectOutputs (options: FSharpProjectSnapshot) =
242-
options.ReferencedProjects |> List.map (fun project -> project.OutputFile)
243-
244-
let newOptions = newProject.ProjectSnapshot
245-
let oldOptions = oldProject.ProjectSnapshot
246-
247-
newOptions.ProjectFileName = oldOptions.ProjectFileName &&
248-
newOptions.SourceFiles = oldOptions.SourceFiles &&
249-
newOptions.OtherOptions = oldOptions.OtherOptions &&
250-
newOptions.ReferencesOnDisk = oldOptions.ReferencesOnDisk &&
251-
getReferencedProjectOutputs newOptions = getReferencedProjectOutputs oldOptions
242+
false
243+
// let getReferencedProjectOutputs (options: FSharpProjectSnapshot) =
244+
// options.ReferencedProjects |> List.map (fun project -> project.OutputFile)
245+
//
246+
// let newOptions = newProject.ProjectSnapshot
247+
// let oldOptions = oldProject.ProjectSnapshot
248+
//
249+
// newOptions.ProjectFileName = oldOptions.ProjectFileName &&
250+
// newOptions.SourceFiles = oldOptions.SourceFiles &&
251+
// newOptions.OtherOptions = oldOptions.OtherOptions &&
252+
// newOptions.ReferencesOnDisk = oldOptions.ReferencesOnDisk &&
253+
// getReferencedProjectOutputs newOptions = getReferencedProjectOutputs oldOptions
252254

253255
let getFcsProject (psiModule: IPsiModule) =
256+
254257
lock this (fun _ ->
255258
let newFcsProject = getNewFcsProject psiModule
256259
match currentFcsProject with

0 commit comments

Comments
 (0)