ionide / ionide-vscode-fsharp

VS Code plugin for F# development
http://ionide.io
MIT License
856 stars 277 forks source link

Syntax highlighting and suggestions of import references do not sync upon file modification in multi-project solutions. #1652

Open zacharylott94 opened 2 years ago

zacharylott94 commented 2 years ago

Describe the bug

When working with solutions with multiple projects, updating a file in one project will not update syntax highlighting and suggestions for imported modules in another project. As an example, one can add a module to a file in project 1, go to project 2, and that new module won't show up in suggestions and will show an error bar below if the module is typed in.

Steps to reproduce (on Linux with the dotnet CLI)

Set up the environment

  1. Start by making an entirely new solution folder with dotnet new sln -o bugReport
  2. cd to the new folder cd bugReport
  3. In the new bugReport folder make a new project with dotnet new library -lang "F#" -o bugLib
  4. Add the new project to the solution dotnet sln add bugLib/bugLib.fsproj
  5. Make a second new project dotnet new console -lang "F#" -o bugHello
  6. Add this second project to the solution dotnet sln add bugHello/bugHello.fsproj
  7. Add a reference to the library project to the console project dotnet add bugHello/bugHello.fsproj reference bugLib/bugLib.fsproj

Our testing environment is set up now

Reproduce the bug

  1. Open our new solution folder in code code .
  2. Reference our library in our bugHello program.fs file image
  3. Remove unnecessary boilerplate and call our pre-existing method in our bugLib.Say module. This module and the method Say existed before we opened VS code, so syntax highlighting and suggestions should work for this module and method. image
  4. If we run the project now with dotnet run --project bugHello, our console should give us the iconic "Hello World!" message. This is all expected behavior.
  5. Now, if we add another method to our library and save, suggestions and highlighting for that new method will not work in our program.fs file image image
  6. We can prove that this code still compiles and works by just running dotnet run --project bugHello again. We should see two messages, confirming that our goodbye method is working.
  7. If we close and re-open VS Code or reload the window with Ctrl - Shift - P >> Developer: Reload Window, the highlighting and suggestions should be fixed.
  8. Finally, if we remove these two methods (hello and goodbye) from our Say module, the highlighting will not represent that these methods no longer exist when we look at our program.fs image

Link to sample reproduction

Here's the trivial solution we built above in repo form. Play around with it by adding and removing methods from bugLib.Say and verify that syntax highlighting and suggestions don't properly update when viewing program.fs.

https://github.com/zacharylott94/fsharp-ionide-bug

Expected behaviour

I would expect editing a file in a different project in the solution would update the syntax highlighting and suggestions in another project in the same opened solution.

Machine info

Additional context

A user on the F# Discord suggest that this is a bug in FsAutoComplete. Here's a screenshot. image

baronfel commented 2 years ago

I did some work in FSAC and found that we have three distinct scenarios that we need to handle:

The first two are fairly straightforward to solve: after checking a file, trigger checking of dependent files. For scripts, this means finding other scripts where the OriginalLoadReferences include your newly-parsed file. For source files, this means taking the list of SourceFiles for the project and finding all other source files that are after this file in the array. Then, we call our Parse helper function which ensures notifications are up to date, which ends up with the user experience you want:

A: fsx-syncronization

B: same-lib

The third case is substantially harder. Here's my hypothesis: we can trigger re-checking of the files in other projects, but as part of checking the metadata of the earlier project is read from the dll on the disk, which in an editor scenario isn't always up to date (because this requires actually writing the bytes). We would need to implement our own version of the tryGetMetadataSnapshot lambda that can be passed into the Checker in order to have this kind of functionality, and that's a lot of work. The VS integration does just this, but it does so by leaning on a lot of VS services that we don't have in VS code, so we'd need to recreate a lot of infrastructure.

baronfel commented 2 years ago

Ultimately, I want to land the changes for parts 1 and 2 in the very near term, and look into 3 as a longer term effort. There's too many unknowns for me personally to put a reasonable guess at the work there. Like maybe we'd get around that by triggering compilation to a temp location and then creating metadata readers for those locations? Who knows 🤷

tymokvo commented 2 years ago

Hello :smile_cat:

I found this issue while looking for existing issues for a scenario that I think is related. We have a project that uses a lot of #r farts_lib.dll type references in .fsx files. Where the referenced DLL is being built from an .fsproj in the same repo.

I'm not aware of the details of how all of this works, but this seems simpler than the hard 3rd scenario that @baronfel described above in that the language server doesn't need to do any compiling and caching on its own. Rather, the existing DLL just needs to be watched and re-scanned when it is over-written.

I also asked on slack here if there's a way to do this manually. This seems like a valid intermediate solution if there's a way to add a command to the VSCode command palette to "Regenerate Completions" or similar.

Currently, the only way that we have found to get the completions to be correct is to close and restart VSCode. Which is pretty annoying.

baronfel commented 2 years ago

@tymokvo this kind of last-modified-based invalidation should be handled already by the compiler APIs we're using - part of my puzzlement here is that it doesn't seem to be taken into account.