Skip to content

Commit 00c78a6

Browse files
committed
Merge pull request #550 from dsyme/fix-tss
add time stamp cache
2 parents 438d7c3 + 265028b commit 00c78a6

File tree

11 files changed

+304
-153
lines changed

11 files changed

+304
-153
lines changed

docs/content/react.fsx

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
(*** hide ***)
2+
#I "../../bin/v4.5/"
3+
(**
4+
Compiler Services: Reacting to Changes
5+
============================================
6+
7+
This tutorial discusses some technical aspects of how to make sure the F# compiler service is
8+
providing up-to-date results especially when hosted in an IDE. See also [project wide analysis](project.html)
9+
for information on project analysis.
10+
11+
> **NOTE:** The FSharp.Compiler.Service API is subject to change when later versions of the nuget package are published.
12+
13+
The logical results of all "Check" routines (``ParseAndCheckFileInProject``, ``GetBackgroundCheckResultsForFileInProject``,
14+
``TryGetRecentTypeCheckResultsForFile``, ``ParseAndCheckProject``) depend on results reported by the file system,
15+
especially the ``IFileSystem`` implementation described in the tutorial on [project wide analysis](project.html).
16+
Logically speaking, these results would be different if file system changes occur. For example,
17+
referenced DLLs may change on disk, or referenced files may change.
18+
19+
The ``FSharpChecker`` component from FSharp.Compiler.Service does _not_ actively "listen"
20+
to changes in the file system. However ``FSharpChecker`` _does_ repeatedly ask for
21+
time stamps from the file system which it uses to decide if recomputation is needed.
22+
FCS doesn’t listen for changes directly - for example, it creates no ``FileWatcher`` object (and the
23+
``IFileSystem`` API has no ability to create such objects). This is partly for legacy reasons,
24+
and partly because some hosts forbid the creation of FileWatcher objects.
25+
26+
In most cases the repeated timestamp requests are sufficient. If you don't actively
27+
listen for changes, then ``FSharpChecker`` will still do _approximately_
28+
the right thing, because it is asking for time stamps repeatedly. However, some updates on the file system
29+
(such as a DLL appearing after a build, or the user randomly pasting a file into a folder)
30+
may not actively be noticed by ``FSharpChecker`` until some operation happens to ask for a timestamp.
31+
By issuing fresh requests, you can ensure that FCS actively reassesses the state of play when
32+
stays up-to-date when changes are observed.
33+
34+
If you want to more actively listen for changes, then you should add watchers for the
35+
files specified in the ``DependencyFiles`` property of ``FSharpCheckFileResults`` and ``FSharpCheckProjectResults``.
36+
Here’s what you need to do:
37+
38+
* When your client notices an CHANGE event on a DependencyFile, it should schedule a refresh call to perform the ParseAndCheckFileInProject (or other operation) again.
39+
This will result in fresh FileSystem calls to compute time stamps.
40+
41+
* When your client notices an ADD event on a DependencyFile, it should call ``checker.InvalidateConfiguration``
42+
for all active projects in which the file occurs. This will result in fresh FileSystem calls to compute time
43+
stamps, and fresh calls to compute whether files exist.
44+
45+
* Generally clients don’t listen for DELETE events on files. Although it would be logically more consistent
46+
to do so, in practice it’s very irritating for a "project clean" to invalidate all intellisense and
47+
cause lots of red squiggles. Some source control tools also make a change by removing and adding files, which
48+
is best noticed as a single change event.
49+
50+
51+
52+
If your host happens to be Visual Studio, then this is one technique you can use:
53+
* Listeners should be associated with a visual source file buffer
54+
* Use fragments like this to watch the DependencyFiles:
55+
56+
// Get the service
57+
let vsFileWatch = fls.GetService(typeof<SVsFileChangeEx >) :?> IVsFileChangeEx
58+
59+
// Watch the Add and Change events
60+
let fileChangeFlags =
61+
uint32 (_VSFILECHANGEFLAGS.VSFILECHG_Add |||
62+
// _VSFILECHANGEFLAGS.VSFILECHG_Del ||| // don't listen for deletes - if a file (such as a 'Clean'ed project reference) is deleted, just keep using stale info
63+
_VSFILECHANGEFLAGS.VSFILECHG_Time)
64+
65+
// Advise on file changes...
66+
let cookie = Com.ThrowOnFailure1(vsFileWatch.AdviseFileChange(file, fileChangeFlags, changeEvents))
67+
68+
...
69+
70+
// Unadvise file changes...
71+
Com.ThrowOnFailure0(vsFileWatch.UnadviseFileChange(cookie))
72+
73+
74+
*)

src/absil/illib.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,6 @@ module Shim =
968968
abstract AssemblyLoadFrom: fileName:string -> System.Reflection.Assembly
969969
abstract AssemblyLoad: assemblyName:System.Reflection.AssemblyName -> System.Reflection.Assembly
970970

971-
972971
type DefaultFileSystem() =
973972
interface IFileSystem with
974973
member __.AssemblyLoadFrom(fileName:string) =

src/fsharp/CompileOps.fs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1749,13 +1749,21 @@ type IRawFSharpAssemblyData =
17491749
abstract HasAnyFSharpSignatureDataAttribute : ILGlobals -> bool
17501750
abstract HasMatchingFSharpSignatureDataAttribute : ILGlobals -> bool
17511751

1752-
type IProjectReference =
1752+
type TimeStampCache() =
1753+
let cacheCreation = DateTime.Now
1754+
let files = Dictionary<string,DateTime>()
1755+
let projects = Dictionary<IProjectReference,DateTime>(HashIdentity.Reference)
1756+
member x.CacheCreation = cacheCreation
1757+
member x.Files = files
1758+
member x.Projects = projects
1759+
1760+
and IProjectReference =
17531761
/// The name of the assembly file generated by the project
17541762
abstract FileName : string
17551763
/// Evaluate raw contents of the assembly file generated by the project
17561764
abstract EvaluateRawContents : unit -> IRawFSharpAssemblyData option
17571765
/// Get the logical timestamp that would be the timestamp of the assembly file generated by the project
1758-
abstract GetLogicalTimeStamp : unit -> System.DateTime option
1766+
abstract GetLogicalTimeStamp : TimeStampCache -> DateTime option
17591767

17601768
type AssemblyReference =
17611769
| AssemblyReference of range * string * IProjectReference option

src/fsharp/CompileOps.fsi

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
/// Coordinating compiler operations - configuration, loading initial context, reporting errors etc.
44
module internal Microsoft.FSharp.Compiler.CompileOps
55

6+
open System
67
open System.Text
8+
open System.Collections.Generic
79
open Internal.Utilities
810
open Microsoft.FSharp.Compiler.AbstractIL
911
open Microsoft.FSharp.Compiler.AbstractIL.IL
@@ -164,13 +166,20 @@ type IRawFSharpAssemblyData =
164166
abstract ILAssemblyRefs : ILAssemblyRef list
165167
abstract ShortAssemblyName : string
166168

167-
type IProjectReference =
169+
170+
type TimeStampCache =
171+
new : unit -> TimeStampCache
172+
member CacheCreation: DateTime
173+
member Files: Dictionary<string,DateTime>
174+
member Projects:Dictionary<IProjectReference,DateTime>
175+
176+
and IProjectReference =
168177
/// The name of the assembly file generated by the project
169178
abstract FileName : string
170179
/// Evaluate raw contents of the assembly file generated by the project
171180
abstract EvaluateRawContents : unit -> IRawFSharpAssemblyData option
172181
/// Get the logical timestamp that would be the timestamp of the assembly file generated by the project
173-
abstract GetLogicalTimeStamp : unit -> System.DateTime option
182+
abstract GetLogicalTimeStamp : TimeStampCache -> DateTime option
174183

175184
type AssemblyReference =
176185
| AssemblyReference of range * string * IProjectReference option

0 commit comments

Comments
 (0)