Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements for Find All References & Rename #1037

Merged
merged 26 commits into from
Mar 3, 2023

Conversation

Booksbaum
Copy link
Contributor

Find all References: (textDocument/references)

  • Enhancement: Find references for external symbols (like List or List.map)
  • Enhancement: Better Range fixup for special cases
    (FCS returns range with Namespace, Module, ... -> we want just actual identifier)
    • (I think all for Active Patterns ... those can contain really nasty things (like linebreaks...))
  • Enhancement: Always return all locations found by FCS instead of silently throwing away Ranges that cannot be correctly processed (like failure to fixup range)
    • Mostly for Adaptive Server: currently doesn't load unopened files -> cannot fix ranges because no source inside state yet
  • Enhancement: respect includeDeclaration and return declarations only when set

Rename: (textDocument/rename)

  • Add textDocument/prepareRename:

    • Validate location:
      • prev: Rename (F2) was available everywhere -- but then just didn't rename anything at invalid locations (like space, keywords, strings, external symbols)
      • now: Rename is only available at valid locations. When not valid (in VSCode): no text box any more but instead error message
    • Return identifier range (-> and thus default name inside text box for renaming)
      • -> Fix: Incorrect default name (in text box) when renaming identifier with special chars (rename operation itself already worked correctly: renamed full identifier)
        • Usually the case for operators (like -.- -> empty default name) or names with spaces (like ``foo bar baz`` -> cursor on bar -> default name just bar)
        • Note: I decided not to include backticks in default name (identifier ``my value`` -> default name is my value): Focus on actual name and not if there should be backticks or not (-> gets auto-added by rename operation if necessary)
  • Enhancement/Fix: Rename doesn't remove existing backticks

    • Issue when existing unnecessary backticks at usage and rename to something with backticks:
      ``value`` -> Rename to ``my value`` -> ````my value```` -> invalid
    • Otherwise just nicer because unnecessary backticks get removed
  • Fix: Identifier doesn't get renamed when declaration with backticks, but usage without:

    let ``value`` = 42
    let _ = value + 42
    let _ = ``value`` + 42

    -> Rename to foo (doesn't matter at declaration or usage):

    let foo = 42
    let _ = value + 42
    let _ = foo + 42
  • Fix: Renaming operator -> new name gets put into backticks -> invalid

  • Enhancement: very basic new name validation when operator

    • Note: Currently just for operators. In all other cases the name is simply put inside backticks when necessary.
      • But might still result in invalid name (like something with @)
  • Issue: For Active Pattern & their cases: doesn't rename all or too much locations
    -> Regression/Fix/Workaround/all 3?...: Disable rename for Active Patterns and Active Pattern Cases

    let (|Even|Odd|) v =
      if v % 2 = 0 then Even else Odd
    let _ = (|Even|Odd|) 42
    match 42 with
    | Even -> ()
    | Odd -> ()
    • Usage locations for pos inside Active Pattern (|Even|Odd|) are only complete Active Pattern occurrences (|Even|Odd|), but not individual cases (Even, Odd) -> Rename would only rename complete Active Pattern locations and leave single cases alone
    • Usage locations for pos inside individual Active Pattern case (Even, Odd) sometimes include the other cases too (usage for Even -> finds Odd usages too).
      That can be handled (it shouldn't be too difficult to exclude all locations not matching source case) -- but I haven't implemented that special case.
  • Unchanged Issue with Adaptive Server: Rename fails when declaration or usage in not-loaded files

    • Reason: no textDocument/didOpen yet -> file not inside state -> no range adjustment of occurrences possible -> no exact rename possible because might be incorrect ranges
    • This behaviour leads to strange behavior (from user perspective):
      • Rename of non-local variable fails (because cannot get source of other files)
      • "Find References" on same variable works (because with this PR always returns results from FCS -- just without range adjustments because no source for other files)
      • VSCode opens all files for "Find References" results (-> sends textDocument/didOpen)
      • Rename on same non-local variable now works (because of prev didOpen -> source now inside state)
    • Unpleasant introduced side effect: error message when this happens: Same as when trying to rename external symbol ("Must be declared inside current workspace, but is external"). Previously: no error message at all -- just silent failure
      Don't know if that's better or worse...
      • I left it this way because Adaptive Server still in development -> don't know if all files will still be loaded
        • But it's possible to either adjust error message or open necessary files when renaming.
          But I took the lazy route and do neither :P

Comment on lines 654 to +741
let symbolUseWorkspace
getDeclarationLocation
(findReferencesForSymbolInFile: (string * FSharpProjectOptions * FSharpSymbol) -> Async<Range seq>)
(getDeclarationLocation: FSharpSymbolUse * NamedText -> SymbolDeclarationLocation option)
(findReferencesForSymbolInFile: (string<LocalPath> * FSharpProjectOptions * FSharpSymbol) -> Async<Range seq>)
(tryGetFileSource: string<LocalPath> -> ResultOrString<NamedText>)
getProjectOptionsForFsproj
(tryGetProjectOptionsForFsproj: string<LocalPath> -> FSharpProjectOptions option)
(getAllProjectOptions: unit -> FSharpProjectOptions seq)
(includeDeclarations: bool)
(includeBackticks: bool)
(errorOnFailureToFixRange: bool)
pos
lineStr
(text: NamedText)
(tyRes: ParseAndCheckResults)
=
: Async<Result<(FSharpSymbol * IDictionary<string<LocalPath>, Range[]>), string>> =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prev this function returned declarations & usages separately (one Dictionary for decls, one for usages).
I changed this to return all together in a single Dictionary: Everywhere the results of symbolUseWorkspace are used, these two are immediately thrown together again. This way symbolUseWorkspace as well as using its results is easier.

If you don't need decls there's a parameter for that.

And in case you want the results partitioned: Use Symbol.partitionIntoDeclarationsAndUsages

@Booksbaum
Copy link
Contributor Author

Booksbaum commented Nov 2, 2022

Why does this need WebClient?:

module MyModule =
  let (|Even|Odd|) v = if v % 2 = 0 then Even else Odd
  let _ = (|Even|Odd|) 42

  // backticks
  let _ = (|``Even``|Odd|) 42
  let _ = (|``Even``|``Odd``|) 42
  let _ = (``|Even|Odd|``) 42

  // spaces
  let _ = (| Even | Odd |) 42
  let _ = (| Even|Odd |) 42
  let _ = (|Even | Odd|) 42

  // linebreaks
  let _ = (|Even|
            Odd|) 42
  let _ = (
            |Even|Odd|) 42
  let _ = (
          |Even|
            Odd|
          ) 42

let _ = MyModule.(|Even|Odd|) 42

// backticks
let _ = MyModule.(|``Even``|Odd|) 42
let _ = MyModule.(|``Even``|``Odd``|) 42
// Invalid:
// let _ = MyModule.(``|Even|Odd|``) 42

// spaces
let _ = MyModule.(| Even | Odd |) 42
let _ = MyModule.(| Even|Odd |) 42
let _ = MyModule.(|Even | Odd|) 42

// linebreaks
let _ = MyModule.(|Even|
          Odd|) 42
let _ = MyModule.(
          |Even|Odd|) 42
let _ = MyModule.(
        |Even|
          Odd|
        ) 42
let _ = MyModule.(

        |Even|
          Odd|

        ) 42

The type 'WebClient' is required here and is unavailable. You must add a reference to assembly 'System.Net.WebClient, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.

And why doesn't it need WebClient on Windows, just on Mac & Linux?



And on other tests it cannot find `Some`:

typecheck error The value or constructor 'Some' is not defined.

....


Edit:

GetProjectOptionsFromScript defaults to assumeDotNetFramework=true...which obviously does only work on windows...

The remaining errors seem to be the usual random "no parse/type check results"...

@Krzysztof-Cieslak
Copy link
Member

This needs to be rebased after all the latest changes related to Adaptive work.

@Booksbaum
Copy link
Contributor Author

This needs to be rebased after all the latest changes related to Adaptive work.

done


But I'm not 100% sure everything AdaptiveFSharpLspServer related got correctly merged while rebasing. And I cannot test it because every LSP request with AdaptiveLspServer enabled throws an exception:

System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
File name: 'System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

(Wasn't introduced with this PR: even current main doesn't work. I think because of dotnet 7 and #1043 not yet here? But FSAC published with Ionide works with AdaptiveLspServer!?)

@Krzysztof-Cieslak
Copy link
Member

CC: @TheAngryByrd any idea?

@TheAngryByrd
Copy link
Member

Seems like an environment issue. main seems to work on my machine. I'll give this branch a test out later tonight.

❯ dotnet --info
.NET SDK:
 Version:   7.0.100
 Commit:    e12b7af219

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.22000
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\7.0.100\

Host:
  Version:      7.0.0
  Architecture: x64
  Commit:       d099f075e4

.NET SDKs installed:
  5.0.404 [C:\Program Files\dotnet\sdk]
  6.0.203 [C:\Program Files\dotnet\sdk]
  6.0.401 [C:\Program Files\dotnet\sdk]
  6.0.405 [C:\Program Files\dotnet\sdk]
  7.0.100-rc.2.22477.23 [C:\Program Files\dotnet\sdk]
  7.0.100 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 5.0.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 7.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 5.0.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.0-rc.2.22472.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 5.0.13 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.10 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.13 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 7.0.0-rc.2.22472.13 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 7.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
  x86   [C:\Program Files (x86)\dotnet]

Environment variables:
  Not set

global.json file:
  C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\global.json

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download

@TheAngryByrd
Copy link
Member

TheAngryByrd commented Feb 13, 2023

Unchanged Issue with Adaptive Server: Rename fails when declaration or usage in not-loaded files

I do have that particular issue fixed in #1053. Incorporating the fixes from that branch seem to fix this.

Gif showing rename with the declaration file open, then close the file, reload VSCode to make sure it's cleared. Then rename still works.

RenameSymbolInFileNotOpen

@TheAngryByrd
Copy link
Member

This is super cool 😄

RenameGivesBackticks

@Booksbaum
Copy link
Contributor Author

Seems like an environment issue. main seems to work on my machine.

❯ dotnet --info
.NET SDK:
 Version:   7.0.100
 Commit:    e12b7af219

[...]

global.json file:
  C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\global.json

in main? global.json still points to dotnet 6:

❯ dotnet --info
.NET SDK (reflecting any global.json):
 Version:   6.0.405
 Commit:    27ab36058b

[...]

global.json file:
  [...]\FsAutoComplete\global.json
my `dotnet --info`

(inside FSAC)

❯ dotnet --info
.NET SDK (reflecting any global.json):
Version:   6.0.405
Commit:    27ab36058b

Runtime Environment:
OS Name:     Windows
OS Version:  10.0.22000
OS Platform: Windows
RID:         win10-x64
Base Path:   C:\Program Files\dotnet\sdk\6.0.405\

Host:
  Version:      7.0.2
  Architecture: x64
  Commit:       d037e070eb

.NET SDKs installed:
  2.1.818 [C:\Program Files\dotnet\sdk]
  3.1.101 [C:\Program Files\dotnet\sdk]
  3.1.426 [C:\Program Files\dotnet\sdk]
  6.0.100 [C:\Program Files\dotnet\sdk]
  6.0.201 [C:\Program Files\dotnet\sdk]
  6.0.202 [C:\Program Files\dotnet\sdk]
  6.0.405 [C:\Program Files\dotnet\sdk]
  7.0.100 [C:\Program Files\dotnet\sdk]
  7.0.102 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.All 2.1.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 7.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 7.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.1.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.13 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 7.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 7.0.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
  arm64 [C:\Program Files\dotnet]
    registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\arm64\InstallLocation]
  x86   [C:\Program Files (x86)\dotnet]
    registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables:
  Not set

global.json file:
  [...]\FsAutoComplete\global.json

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/downloa





Here the complete Error with Stack Trace for Adaptive Server: (Debug Build)

[01:39:54.140 ERR] [FsAutoComplete.Lsp.AdaptiveFSharpLspServer] Initialized Request Errored {"$type": "InitializedParams"}
System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
File name: 'System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
   at Ionide.ProjInfo.ProjectLoader.loadProject(String path, BinaryLogGeneration binaryLogs, FSharpList`1 globalProperties)
   at Ionide.ProjInfo.ProjectLoader.getProjectInfo(String path, FSharpList`1 globalProperties, BinaryLogGeneration binaryLogs, FSharpList`1 customProperties) in /_//src/Ionide.ProjInfo/Library.fs:line 659
   at <StartupCode$Ionide-ProjInfo>.$Library.loadProject@923(WorkspaceLoader __, FSharpList`1 customProperties, BinaryLogGeneration binaryLogs, Dictionary`2 cache, String p) in /_//src/Ionide.ProjInfo/Library.fs:line 924
   at <StartupCode$Ionide-ProjInfo>.$Library.loadProjectList@960(WorkspaceLoader __, FSharpList`1 customProperties, BinaryLogGeneration binaryLogs, Dictionary`2 cache, FSharpList`1 projectList) in /_//src/Ionide.ProjInfo/Library.fs:line 979
   at Ionide.ProjInfo.WorkspaceLoader.Ionide.ProjInfo.IWorkspaceLoader.LoadProjects(FSharpList`1 projects, FSharpList`1 customProperties, BinaryLogGeneration binaryLogs) in /_//src/Ionide.ProjInfo/Library.fs:line 987
   at <StartupCode$fsautocomplete>.$AdaptiveFSharpLspServer.-ctor@529-9.Invoke(FSharpHashMap`2 projects) in [...]\FsAutoComplete\src\FsAutoComplete\LspServers\AdaptiveFSharpLspServer.fs:line 539
   at FsAutoComplete.Lsp.AVal.mapWithAdditionalDependenies@109.Compute(AdaptiveToken token) in [...]\FsAutoComplete\src\FsAutoComplete\LspServers\AdaptiveFSharpLspServer.fs:line 114
   at FSharp.Data.Adaptive.AValModule.arg10@78[T](AbstractVal`1 x, AdaptiveToken token)
   at FSharp.Data.Adaptive.AValModule.AbstractVal`1.GetValue(AdaptiveToken token)
   at FSharp.Data.Adaptive.AValModule.AbstractVal`1.FSharp.Data.Adaptive.IAdaptiveValue<'T>.GetValue(AdaptiveToken t)
   at FSharp.Data.Adaptive.AValModule.Map2Val`3.Compute(AdaptiveToken token)
   at FSharp.Data.Adaptive.AValModule.arg10@78[T](AbstractVal`1 x, AdaptiveToken token)
   at FSharp.Data.Adaptive.AValModule.AbstractVal`1.GetValue(AdaptiveToken token)
   at FSharp.Data.Adaptive.AValModule.AbstractVal`1.FSharp.Data.Adaptive.IAdaptiveValue<'T>.GetValue(AdaptiveToken t)
   at FSharp.Data.Adaptive.AValModule.arg10@78[T](AbstractVal`1 x, AdaptiveToken token)
   at FSharp.Data.Adaptive.AValModule.AbstractVal`1.GetValue(AdaptiveToken token)
   at FSharp.Data.Adaptive.AValModule.AbstractVal`1.FSharp.Data.Adaptive.IAdaptiveValue<'T>.GetValue(AdaptiveToken t)
   at FSharp.Data.Adaptive.AValModule.arg10@78[T](AbstractVal`1 x, AdaptiveToken token)
   at FSharp.Data.Adaptive.AValModule.AbstractVal`1.GetValue(AdaptiveToken token)
   at FSharp.Data.Adaptive.AValModule.AbstractVal`1.FSharp.Data.Adaptive.IAdaptiveValue<'T>.GetValue(AdaptiveToken t)
   at <StartupCode$fsautocomplete>.$AdaptiveFSharpLspServer.FsAutoComplete-Lsp-IFSharpLspServer-Initialized@1791-1.Invoke(Unit unitVar) in [...]\FsAutoComplete\src\FsAutoComplete\LspServers\AdaptiveFSharpLspServer.fs:line 1793
   at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvoke[T,TResult](AsyncActivation`1 ctxt, TResult result1, FSharpFunc`2 part2) in D:\a\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 447
   at <StartupCode$fsautocomplete>.$AdaptiveFSharpLspServer.FsAutoComplete-Lsp-IFSharpLspServer-Initialized@1790-13.Invoke(AsyncActivation`1 ctxt)
   at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc`2 firstAction) in D:\a\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 104

Observations:

  • With normal Server (FsAutoComplete.Lsp, not AdaptiveFSharpLspServer) it works. Which is rather strange: Exception is inside WorkspaceLoader. But both should use same Workspace Loader? (here and here)
  • FSAC is currently dotnet 6. And I use a dotnet 6 project for testing. Why does it load dotnet 7 Runtime?

Seems like AdaptiveServer loads other dotnet dependencies (vs. old/current Server)?

@Booksbaum
Copy link
Contributor Author

When I compile FSAC with & to dotnet 7 Adaptive Server works.

Required steps:

  • global.json: Change version to 7.0.100
  • set env var BuildNet7 to true
    (Why does building for net7 already exists -- but isn't buildable with current global.json? I think we should adjust version in global.json)
  • Then use net7.0 dll -- NOT net6.0 (that fails with Adaptive Server)





I do have that particular issue fixed in #1053. Incorporating the fixes from that branch seem to fix this.

Seems to work perfectly 👍

@baronfel
Copy link
Contributor

If you check the CI pipelines we do shenanigans with the global.json to test specific TFMs, but if you have global.json set to allow 7.0 TFMs the test runners doesn't handle 6.0 testing in a way that's compatible with the way we look up MSBuild and the .net SDK. That's why we have the BuildNet7 shenanigans. I think for the .net 7 update we might end up dropping net6 support (or at least drop testing for it) to make the repo layout less confusing overall.

@TheAngryByrd
Copy link
Member

Then use net7.0 dll -- NOT net6.0 (that fails with Adaptive Server)

Very strange, Both 6.0 or 7.0 seem to work. (my giant work project is 6.0.404 only at the moment and that's what I tested against making most of the changes in the AdaptiveServer).

@Booksbaum
Copy link
Contributor Author

Booksbaum commented Feb 13, 2023

my giant work project is 6.0.404 only at the moment and that's what I tested against making most of the changes in the AdaptiveServer

the project target framework doesn't matter. FSAC target framework does.

So Adaptive Server in FSAC net6.0 inside net6.0 project fails, but Adaptive Server in FSAC net7.0 inside net6.0 projects works.

in .vscode/settings.json:

{
  "FSharp.enableAdaptiveLspServer": true,

  // error
  // "FSharp.fsac.netCoreDllPath": "[..]\\FsAutoComplete\\bin\\Debug\\net6.0\\fsautocomplete.dll",

  // ok
  "FSharp.fsac.netCoreDllPath": "[..]\\FsAutoComplete\\src\\FsAutoComplete\\bin\\Debug\\net7.0\\fsautocomplete.dll",
}

("FSharp.enableAdaptiveLspServer": false works with both targets)






The Published Ionide-Extension seems to use net7.0:

"C:\Program Files\dotnet\dotnet.exe" [...]\.vscode\extensions\ionide.ionide-fsharp-7.4.2\bin\net7.0\fsautocomplete.dll --state-directory [...]\AppData\Roaming\Code\User\workspaceStorage\[...]\Ionide.Ionide-fsharp

Which would explain why Adaptive Server works with current Ionide.

@TheAngryByrd
Copy link
Member

TheAngryByrd commented Feb 13, 2023

So Adaptive Server in FSAC net6.0 inside net6.0 project fails, but Adaptive Server in FSAC net7.0 inside net6.0 projects works

I can't reproduce that.

"FSharp.fsac.netCoreDllPath": "[..]\\FsAutoComplete\\src\\FsAutoComplete\\bin\\Debug\\net6.0\\fsautocomplete.dll"
"FSharp.enableAdaptiveLspServer": true

is what I have in my work project's .vscode/settings.json an it works for me locally.


Basically gonna need someone else to give this a try. @baronfel :)

@TheAngryByrd
Copy link
Member

While trying to use Find All References on my larger code base, I'm to get these errors (yeah the stack trace sucks):

[18:17:56.651 ERR] [FsAutoComplete.Lsp.AdaptiveFSharpLspServer] TextDocumentReferences
System.AggregateException: One or more errors occurred. (Value cannot be null. (Parameter 'buffer'))
 ---> System.ArgumentNullException: Value cannot be null. (Parameter 'buffer')
   at System.Reflection.Throw.ArgumentNull(String parameterName)
   at System.Reflection.Metadata.BlobBuilder.WriteBytes(Byte* buffer, Int32 byteCount)
   at FSharp.Compiler.EditorServices.SemanticClassificationKeyStoreBuilder.WriteAll(SemanticClassificationItem[] semanticClassification) in D:\a\_work\1\s\src\fsharp\service\SemanticClassificationKey.fs:line 55
   at <StartupCode$FSharp-Compiler-Service>.$IncrementalBuild.TypeCheck@492-7.Invoke(Tuple`2 _arg11) in D:\a\_work\1\s\src\fsharp\service\IncrementalBuild.fs:line 548
   at FSharp.Compiler.BuildGraph.Bind@53-1.Invoke(T x) in D:\a\_work\1\s\src\fsharp\BuildGraph.fs:line 53
   at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvokeNoHijackCheck[a,b](AsyncActivation`1 ctxt, b result1, FSharpFunc`2 userCode) in D:\a\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 465
   at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc`2 firstAction) in D:\a\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 104
   --- End of inner exception stack trace ---

I'll try to track it down a bit better.

@Booksbaum
Copy link
Contributor Author

I changed netCoreDllPath to the net6.0 included in Ionide (%USERPROFILE%\.vscode\extensions\ionide.ionide-fsharp-7.4.2\bin\net6.0\fsautocomplete.dll) -- and Adaptive Server works.
So there seems to be something wrong with my local build. But no idea what :/






Going further:
using produced dll from ./build.cmd Build doesn't work.
BUT: using produced dll from ./build.cmd LocalRelease does work.
(both from exactly same path: [..]\FsAutoComplete\src\FsAutoComplete\bin\Release\net6.0\fsautocomplete.dll)

Tracked the deciding argument down:
dotnet.exe pack src/FsAutoComplete doesn't work, dotnet.exe pack src/FsAutoComplete /p:PackAsTool=true does work (dotnet build src/FsAutoComplete /p:PackAsTool=true does work too)
-> so relevant switch is: PackAsTool

Diffing builds of dotnet build src/FsAutoComplete with /p:PackAsTool=true and /p:PackAsTool=false reveals the files are identical -- with two exceptions:

  • fsautocomplete.deps.json for PackAsTool=false specifies a bunch more dependencies for FsAutoComplete.Core and FsAutoComplete.Logging
  • fsautocomplete.runtimeconfig.json for PackAsTool=true has additional "rollForward": "LatestMajor",
    Removing that results in FSAC throwing exception again...

-> PackAsTool=true works .... because then it uses dotnet 7...

@TheAngryByrd
Copy link
Member

While trying to use Find All References on my larger code base, I'm to get these errors (yeah the stack trace sucks):

Adding this to tryFindReferencesInFile seems to resolve it.

          |> Async.Catch
          |> Async.map(Result.ofChoice >> Result.mapError string >> Result.bind id)

Copy link
Member

@TheAngryByrd TheAngryByrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor changes found through testing

src/FsAutoComplete.Core/Commands.fs Show resolved Hide resolved
src/FsAutoComplete.Core/Commands.fs Outdated Show resolved Hide resolved
Copy link
Member

@TheAngryByrd TheAngryByrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the parallel helps here quite a bit for bigger projects.

Really liking these changes so far by the way. :)

Comment on lines 868 to 878
let iterProject (project: FSharpProjectOptions) =
asyncResult {
//Enhancement: do in parallel?
for file in project.SourceFiles do
let file = UMX.tag file
do! tryFindReferencesInFile (file, project)
}

let! _ = getSymbolUsesInProjects (symbol, projects, onFound)
let iterProjects (projects: FSharpProjectOptions seq) =
asyncResult {
for project in projects do
do! iterProject project
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Been running this branch a few days on my work project. Yeah parallel helps a lot here with speed. I did something like:

        let iterProjects (projects: FSharpProjectOptions seq) =
            [
              for project in projects do
                for file in project.SourceFiles do
                  let file = UMX.tag file
                  tryFindReferencesInFile (file, project)
            ]
            |> Async.Parallel
            |> Async.Ignore<unit array>

and also changed the Dictionary to a ConcurrentDictionary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it feel from a load perspective? Async.Parallel queues all the items on the threadpool right, so you by default do NUM_CPUS at once? Would it make sense to limit to e.g. NUM_CPUs/2 or something to make sure there's headroom for other operations? What's the delay like in your experience?

QUESTIONS!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it feel from a load perspective? Async.Parallel queues all the items on the threadpool right, so you by default do NUM_CPUS at once?

Mostly the big wait time is the type checker so we're throttled by that until type checks complete for the solution. (mostly 1 core)

Otherwise the main part is looping through a bunch of files and adding them to a dictionary. This part parallelizes extremely well. Looping through a lot of files can be slow comparatively.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did something like:

let iterProjects (projects: FSharpProjectOptions seq) =
    [
      for project in projects do
        for file in project.SourceFiles do
          let file = UMX.tag file
          tryFindReferencesInFile (file, project)
    ]
    |> Async.Parallel
    |> Async.Ignore<unit array>

One disadvantage of this: it tryFindReferencesInFile for all files in all projects -- always.
Currently it stops on first error. (usually just for Rename when it cannot correct the range of a symbol). Is there already a Async.Parallel that stops on first error?

Though when deciding between these two: Parallel is probably preferred: It can only fail for Rename, not Find References -- which is most likely more often used. And now Adaptive Server loads all files -> not being able to correct range shouldn't happen that often

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the errors I run into are because I'm in a mixed C#/F# project and it tries to do things for cs file files so maybe we need to have more guards around file types.

Booksbaum added a commit to Booksbaum/FsAutoComplete that referenced this pull request Feb 17, 2023
fixed with ionide#1043 (Upgrade to dotnet 7 & new FCS)
(see ionide#1037 (comment) )
@TheAngryByrd
Copy link
Member

TheAngryByrd commented Feb 17, 2023

[01:39:54.140 ERR] [FsAutoComplete.Lsp.AdaptiveFSharpLspServer] Initialized Request Errored {"$type": "InitializedParams"}
System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
File name: 'System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

So I finally hit this, it seemed to be a global.json related issue. If you're building FSAC then reloading vscode to test the net6.0 TFM you'll need to make sure to constrain the global.json to 6.0 frameworks. You made mention of it but I wanted to call it out explicitly how to fix it if others see this. But why it would work for the other server i don't know.

Booksbaum added a commit to Booksbaum/FsAutoComplete that referenced this pull request Feb 21, 2023
fixed with ionide#1043 (Upgrade to dotnet 7 & new FCS)
(see ionide#1037 (comment) )
@Booksbaum
Copy link
Contributor Author

@TheAngryByrd

https://github.com/fsharp/FsAutoComplete/blob/1c57b52bc904b2e6b60dbf78de6616231c764ff7/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs#L1770-L1784

There was a change in Adaptive Server:
loadedProjectOptions doesn't return Project Options for Script files any more -> several find references tests fail because getAllProjectOptions doesn't return all project options any more and thus Commands.symbolUseWorkspace misses usages in Script files.

How do I get all project options including Scripts? Extract distinct Project Options from allProjectOptions?






(AHHH.... Github doesn't allow comments on unchanged code parts :/ )

@TheAngryByrd
Copy link
Member

There was a change in Adaptive Server:
loadedProjectOptions doesn't return Project Options for Script files any more -> several find references tests fail because getAllProjectOptions doesn't return all project options any more and thus Commands.symbolUseWorkspace misses usages in Script files.

How do I get all project options including Scripts? Extract distinct Project Options from [allProjectOptions](https://github.com/fsharp/FsAutoComplete/blob/24ceac2196627c1793df8affad43fc116d767bbe/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs#L1039-L1044)?

allProjectOptions should get you all the project options for projects we've loaded and for script files the editor has opened. However, we don't currently load all scripts in a workspace, which is maybe what you're asking for here. I think it makes sense. cc (@baronfel @Krzysztof-Cieslak )

Booksbaum and others added 23 commits March 3, 2023 15:52
-> same as `dotnet/fsharp`

Note:
`None` too if project for file not loaded!
`--debug` is already a Expecto flag (for example: prints message for test start & finished).

But when `--debug` is specified it
* gets reinterpreted by FSAC (-> FSAC debug logging)
* gets removed by FSAC (-> not passed on to Expecto)

Now: `--debug` gets ignored by FSAC and passed on to Expecto.

If you want debug FSAC logging: use `--log=verbose` instead
(Note: `verbose`: previously `--debug` was associated with `verbose` logging, NOT `debug` logging)
* Enhancement: Find references of external symbols
* Enhancement: better Range fixup
* Add `textDocument/prepareRename`
* Enhancement: Don't rename when not all exact ranges
* Add tests for FindReferences & Rename

Note: rename over unloaded files doesn't work in Adaptive LSP Server: Range fixup requires source text, but unlike in normal LSP Server the Adaptive LSP server doesn't autoload all F# files

Note: Rename for Active Pattern & Active Pattern Cases is disabled
includes now Prepare support
Co-authored-by: Jimmy Byrd <jimmybyrd@gmail.com>
Co-authored-by: Jimmy Byrd <jimmybyrd@gmail.com>
fixed with ionide#1043 (Upgrade to dotnet 7 & new FCS)
(see ionide#1037 (comment) )
Note:
Some AdaptiveLspServer Reference tests still fail: `allProjectOptions` doesn't return Project Options for Script files any more (at least untitled Script files) -> `Commands.symbolUseWorkspace` doesn't return usages inside Script file
…e Server)

Note:
There's still one bug/change compared to FSharpLspServer:
(-> `FSAC.lsp.Ionide WorkspaceLoader.AdaptiveLspServer.Find All References tests.solution.inside B/WorkingModule.fs.List.map` fails)
* FSharpLspServer: Caches script files that were once opened -> can find references script files that were once open but aren't any more
* AdaptiveLspServer: Only keeps open script files -> cannot find references in script files that were once open but aren't any more
Issue:
Unlike `FsharpLspServer`, AdaptiveLspServer doesn't keep closed scripts in cache
-> cannot find references inside closed script files
see ionide#1037 (comment)
@Booksbaum Booksbaum force-pushed the FindAllReferences branch from d154e2e to de6beb9 Compare March 3, 2023 18:22
@Booksbaum
Copy link
Contributor Author

Test is adjusted

(also: finding references in parallel incorporated)

@baronfel
Copy link
Contributor

baronfel commented Mar 3, 2023

Excellent work as always @Booksbaum - thank you for these great fixes!

@baronfel baronfel merged commit 0336e6e into ionide:main Mar 3, 2023
@Booksbaum Booksbaum deleted the FindAllReferences branch March 3, 2023 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants