diff --git a/src/FsAutoComplete.BackgroundServices/Program.fs b/src/FsAutoComplete.BackgroundServices/Program.fs index 3757298ad..87d7e5f39 100644 --- a/src/FsAutoComplete.BackgroundServices/Program.fs +++ b/src/FsAutoComplete.BackgroundServices/Program.fs @@ -469,9 +469,10 @@ type BackgroundServiceServer(state: State, client: FsacClient) = return LspResult.success () } - member __.InitWorkspace() = + member __.InitWorkspace(workspaceStateDir) = async { do! client.Notify {Value = "Init workspace" } + SymbolCache.initCache workspaceStateDir do! Async.Sleep 100 let knownProjects = state.FileCheckOptions.Values |> Seq.distinctBy (fun o -> o.ProjectFileName) do! client.Notify {Value = sprintf "Init workspace - starting typechecking on %d projects" (knownProjects |> Seq.length) } @@ -498,21 +499,20 @@ module Program = |> Map.add "background/update" (requestHandling (fun s p -> s.UpdateTextFile(p) )) |> Map.add "background/project" (requestHandling (fun s p -> s.UpdateProject(p) )) |> Map.add "background/save" (requestHandling (fun s p -> s.FileSaved(p) )) - |> Map.add "background/init" (requestHandling (fun s p -> s.InitWorkspace() )) + |> Map.add "background/init" (requestHandling (fun s p -> s.InitWorkspace p)) Ionide.LanguageServerProtocol.Server.start requestsHandlings input output FsacClient (fun lspClient -> new BackgroundServiceServer(state, lspClient)) open FSharp.Compiler.IO + [] let main argv = - - let pid = Int32.Parse argv.[0] + let pid= Int32.Parse argv[0] let originalFs = FileSystemAutoOpens.FileSystem let fs = FsAutoComplete.FileSystem(originalFs, state.Files.TryFind) :> IFileSystem FileSystemAutoOpens.FileSystem <- fs ProcessWatcher.zombieCheckWithHostPID (fun () -> exit 0) pid - SymbolCache.initCache (Environment.CurrentDirectory) let _ = startCore() 0 diff --git a/src/FsAutoComplete.Core/BackgroundServices.fs b/src/FsAutoComplete.Core/BackgroundServices.fs index 305f4bba1..21a30005c 100644 --- a/src/FsAutoComplete.Core/BackgroundServices.fs +++ b/src/FsAutoComplete.Core/BackgroundServices.fs @@ -54,9 +54,9 @@ type BackgroundService = abstract UpdateFile : BackgroundFileCheckType * string * int -> unit abstract UpdateProject : string * FSharpProjectOptions -> unit abstract SaveFile : BackgroundFileCheckType -> unit - abstract InitWorkspace : unit -> unit + abstract InitWorkspace : workspaceStateDir: string -> unit abstract MessageReceived : IEvent - abstract Start : workspaceDir: string -> unit + abstract Start : unit -> unit abstract GetSymbols: string -> Async> abstract GetImplementation: string -> Async> @@ -99,9 +99,8 @@ type ActualBackgroundService() = ) interface BackgroundService with - member x.Start (workspaceDir) = + member x.Start () = logger.info (Log.setMessage "Starting background service") - SymbolCache.initCache workspaceDir client.Start() member x.UpdateFile(file, content, version) = @@ -123,8 +122,9 @@ type ActualBackgroundService() = let msg : FileParms = { File = file } client.SendNotification "background/save" msg - member x.InitWorkspace () = - client.SendNotification "background/init" {Ready = true} + member x.InitWorkspace (workspaceStateDir) = + SymbolCache.initCache workspaceStateDir + client.SendNotification "background/init" workspaceStateDir member x.MessageReceived = messageRecieved.Publish @@ -138,7 +138,7 @@ type MockBackgroundService() = let m = Event<_>() interface BackgroundService with - member x.Start _ = () + member x.Start () = () member x.UpdateFile(file, content, version) = () @@ -146,7 +146,7 @@ type MockBackgroundService() = member x.SaveFile(file) = () - member x.InitWorkspace () = () + member x.InitWorkspace workspaceStateDir = () member x.MessageReceived = m.Publish diff --git a/src/FsAutoComplete.Core/Commands.fs b/src/FsAutoComplete.Core/Commands.fs index 711b6d57d..e48870a30 100644 --- a/src/FsAutoComplete.Core/Commands.fs +++ b/src/FsAutoComplete.Core/Commands.fs @@ -573,7 +573,7 @@ type Commands commandsLogger.info ( Log.setMessage "Workspace ready - sending init request to background service" ) - backgroundService.InitWorkspace()) + backgroundService.InitWorkspace(state.WorkspaceStateDirectory.FullName)) member __.Notify = notify.Publish diff --git a/src/FsAutoComplete.Core/State.fs b/src/FsAutoComplete.Core/State.fs index 194f55171..38c823ce7 100644 --- a/src/FsAutoComplete.Core/State.fs +++ b/src/FsAutoComplete.Core/State.fs @@ -33,10 +33,12 @@ type State = ScriptProjectOptions: ConcurrentDictionary, int * FSharpProjectOptions> mutable ColorizationOutput: bool + + WorkspaceStateDirectory: System.IO.DirectoryInfo } member x.DebugString = $"{x.Files.Count} Files, {x.ProjectController.ProjectOptions |> Seq.length} Projects" - static member Initial toolsPath workspaceLoaderFactory = + static member Initial toolsPath workspaceStateDir workspaceLoaderFactory = { Files = ConcurrentDictionary() LastCheckedVersion = ConcurrentDictionary() ProjectController = new ProjectController(toolsPath, workspaceLoaderFactory) @@ -47,7 +49,8 @@ type State = CancellationTokens = ConcurrentDictionary() NavigationDeclarations = ConcurrentDictionary() ScriptProjectOptions = ConcurrentDictionary() - ColorizationOutput = false } + ColorizationOutput = false + WorkspaceStateDirectory = workspaceStateDir } member x.RefreshCheckerOptions(file: string, text: NamedText) : FSharpProjectOptions option = x.ProjectController.GetProjectOptions (UMX.untag file) diff --git a/src/FsAutoComplete/FsAutoComplete.Lsp.fs b/src/FsAutoComplete/FsAutoComplete.Lsp.fs index 61037caa4..d4b065c71 100644 --- a/src/FsAutoComplete/FsAutoComplete.Lsp.fs +++ b/src/FsAutoComplete/FsAutoComplete.Lsp.fs @@ -771,7 +771,7 @@ type FSharpLspServer(backgroundServiceEnabled: bool, state: State, lspClient: FS rootPath <- actualRootPath commands.SetWorkspaceRoot actualRootPath - rootPath |> Option.iter backgroundService.Start + backgroundService.Start() let c = p.InitializationOptions @@ -2722,7 +2722,7 @@ type FSharpLspServer(backgroundServiceEnabled: bool, state: State, lspClient: FS override x.Dispose() = x.Shutdown() |> Async.Start -let startCore backgroundServiceEnabled toolsPath workspaceLoaderFactory = +let startCore backgroundServiceEnabled toolsPath stateStorageDir workspaceLoaderFactory = use input = Console.OpenStandardInput() use output = Console.OpenStandardOutput() @@ -2758,7 +2758,7 @@ let startCore backgroundServiceEnabled toolsPath workspaceLoaderFactory = |> Map.add "fsharp/inlayHints" (requestHandling (fun s p -> s.FSharpInlayHints(p))) let state = - State.Initial toolsPath workspaceLoaderFactory + State.Initial toolsPath stateStorageDir workspaceLoaderFactory let originalFs = FSharp.Compiler.IO.FileSystemAutoOpens.FileSystem @@ -2768,12 +2768,12 @@ let startCore backgroundServiceEnabled toolsPath workspaceLoaderFactory = Ionide.LanguageServerProtocol.Server.start requestsHandlings input output FSharpLspClient (fun lspClient -> new FSharpLspServer(backgroundServiceEnabled, state, lspClient)) -let start backgroundServiceEnabled toolsPath workspaceLoaderFactory = +let start backgroundServiceEnabled toolsPath stateStorageDir workspaceLoaderFactory = let logger = LogProvider.getLoggerByName "Startup" try let result = - startCore backgroundServiceEnabled toolsPath workspaceLoaderFactory + startCore backgroundServiceEnabled toolsPath stateStorageDir workspaceLoaderFactory logger.info ( Log.setMessage "Start - Ending LSP mode with {reason}" diff --git a/src/FsAutoComplete/Parser.fs b/src/FsAutoComplete/Parser.fs index a880b3f71..afe5793f2 100644 --- a/src/FsAutoComplete/Parser.fs +++ b/src/FsAutoComplete/Parser.fs @@ -1,6 +1,7 @@ namespace FsAutoComplete open System +open System.IO open Serilog open Serilog.Core open Serilog.Events @@ -66,7 +67,7 @@ module Parser = let waitForDebuggerOption = Option( - "--wait-for-debugger", + [|"--wait-for-debugger"; "--attachdebugger"|], "Stop execution on startup until an external debugger to attach to this process" ) |> zero @@ -85,6 +86,9 @@ module Parser = ) |> zero + let stateLocationOption = + Option("--state-directory", getDefaultValue = Func<_> (fun () -> DirectoryInfo("C:\\Users\\chusk")), description = "Set the directory to store the state of the server. This should be a per-workspace location, not a shared-workspace location.") + let rootCommand = let rootCommand = RootCommand("An F# LSP server implementation") @@ -96,18 +100,20 @@ module Parser = rootCommand.AddOption backgroundServiceOption rootCommand.AddOption projectGraphOption rootCommand.AddOption logLevelOption + rootCommand.AddOption stateLocationOption + rootCommand.SetHandler( - Func<_,_,Task>(fun backgroundServiceEnabled projectGraphEnabled -> + Func<_,_,_,Task>(fun backgroundServiceEnabled projectGraphEnabled stateDirectory -> let workspaceLoaderFactory = if projectGraphEnabled then Ionide.ProjInfo.WorkspaceLoaderViaProjectGraph.Create else Ionide.ProjInfo.WorkspaceLoader.Create let toolsPath = Ionide.ProjInfo.Init.init (IO.DirectoryInfo Environment.CurrentDirectory) None use _compilerEventListener = new Debug.FSharpCompilerEventLogger.Listener() - let result = Lsp.start backgroundServiceEnabled toolsPath workspaceLoaderFactory + let result = Lsp.start backgroundServiceEnabled toolsPath stateDirectory workspaceLoaderFactory Task.FromResult result - ), backgroundServiceOption, projectGraphOption) + ), backgroundServiceOption, projectGraphOption, stateLocationOption) rootCommand let waitForDebugger = diff --git a/test/FsAutoComplete.Tests.Lsp/CoreTests.fs b/test/FsAutoComplete.Tests.Lsp/CoreTests.fs index 307c7b508..aaf87d6bc 100644 --- a/test/FsAutoComplete.Tests.Lsp/CoreTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/CoreTests.fs @@ -18,6 +18,7 @@ let initTests state = testCaseAsync "InitTest" (async { + let tempDir = Path.Combine(Path.GetTempPath(), "FsAutoComplete.Tests", Guid.NewGuid().ToString()) let (server, event) = createServer state let p: InitializeParams = diff --git a/test/FsAutoComplete.Tests.Lsp/Program.fs b/test/FsAutoComplete.Tests.Lsp/Program.fs index 907ea1759..5106903b7 100644 --- a/test/FsAutoComplete.Tests.Lsp/Program.fs +++ b/test/FsAutoComplete.Tests.Lsp/Program.fs @@ -14,6 +14,7 @@ open FsAutoComplete.Tests.InteractiveDirectivesTests open Ionide.ProjInfo open System.Threading open Serilog.Filters +open System.IO let testTimeout = Environment.GetEnvironmentVariable "TEST_TIMEOUT_MINUTES" @@ -41,10 +42,10 @@ let lspTests = [ for (name, workspaceLoaderFactory) in loaders do testList name - [ - Templates.tests () + [ Templates.tests () + let testRunDir = Path.Combine(Path.GetTempPath(), "FsAutoComplete.Tests", Guid.NewGuid().ToString()) |> DirectoryInfo let state () = - FsAutoComplete.State.Initial toolsPath workspaceLoaderFactory + FsAutoComplete.State.Initial toolsPath testRunDir workspaceLoaderFactory initTests state @@ -82,7 +83,7 @@ let lspTests = InfoPanelTests.docFormattingTest state DetectUnitTests.tests state XmlDocumentationGeneration.tests state - InlayHintTests.tests state + InlayHintTests.tests state ] ]