|
| 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 | +*) |
0 commit comments