diff --git a/release/package.json b/release/package.json index 41ae1496..20f51b14 100644 --- a/release/package.json +++ b/release/package.json @@ -919,6 +919,11 @@ "description": "Path to the directory or solution file that should be loaded as a workspace. If set, no workspace probing or discovery is done by Ionide at all.", "scope": "window", "type": "string" + }, + "FSharp.lazyLoadWorkspace": { + "default": false, + "description": "Configures Ionide to not load any projects upfront but instead load them when .fs files are opened.", + "type": "boolean" } }, "title": "F#", @@ -1776,4 +1781,4 @@ "url": "https://github.com/ionide/ionide-vscode-fsharp.git" }, "version": "7.15.2" -} \ No newline at end of file +} diff --git a/src/Components/MSBuild.fs b/src/Components/MSBuild.fs index 5c685048..e52ea216 100644 --- a/src/Components/MSBuild.fs +++ b/src/Components/MSBuild.fs @@ -469,7 +469,7 @@ module MSBuild = let unlessIgnored (path: string) f = if Project.isIgnored path then unbox () else f path - let initWorkspace _n = Project.initWorkspace () + let initWorkspace _n = Project.initWorkspace context let solutionWatcher = workspace.createFileSystemWatcher (U2.Case1 "**/*.sln") diff --git a/src/Components/SolutionExplorer.fs b/src/Components/SolutionExplorer.fs index 365dc6e8..0043c001 100644 --- a/src/Components/SolutionExplorer.fs +++ b/src/Components/SolutionExplorer.fs @@ -257,7 +257,7 @@ module SolutionExplorer = setParentRef s result result | WorkspacePeekFound.Directory dir -> - let items = dir.Fsprojs |> Array.map getProjItem |> List.ofArray + let items = dir.Fsprojs |> Array.map (fun f -> getProjItem f.Path) |> List.ofArray let result = Workspace items setParentRefs items result @@ -714,7 +714,7 @@ module SolutionExplorer = | PackageReference _ | ProjectReference _ -> None - let newProject () = + let newProject (context: ExtensionContext) = promise { let! templates = LanguageService.dotnetNewList () @@ -777,7 +777,7 @@ module SolutionExplorer = | _ -> //If it's the first project in the workspace we need to init the workspace if Project.getInWorkspace().IsEmpty then - do! Project.initWorkspace () + do! Project.initWorkspace context () | _ -> () diff --git a/src/Core/DTO.fs b/src/Core/DTO.fs index 4be1a17e..03876239 100644 --- a/src/Core/DTO.fs +++ b/src/Core/DTO.fs @@ -271,12 +271,16 @@ module DTO = type WorkspacePeek = { Found: WorkspacePeekFound[] } + and WorkspacePeekFsproj = + { Path: string; CompileItems: string[] } + and WorkspacePeekFound = | Directory of WorkspacePeekFoundDirectory | Solution of WorkspacePeekFoundSolution and WorkspacePeekFoundDirectory = - { Directory: string; Fsprojs: string[] } + { Directory: string + Fsprojs: WorkspacePeekFsproj[] } and WorkspacePeekFoundSolution = { Path: string diff --git a/src/Core/Project.fs b/src/Core/Project.fs index c9c2e3aa..09133f56 100644 --- a/src/Core/Project.fs +++ b/src/Core/Project.fs @@ -58,6 +58,8 @@ module Project = let deepLevel = "FSharp.workspaceModePeekDeepLevel" |> Configuration.get 2 |> max 0 + let lazyLoadWorkspace = "FSharp.lazyLoadWorkspace" |> Configuration.get false + let isNetCoreApp (project: Project) = project.Info.TargetFramework :: project.Info.TargetFrameworks |> Seq.exists (fun tfm -> tfm = "net5.0" || tfm.StartsWith "netcoreapp") @@ -106,8 +108,7 @@ module Project = | Folder folder -> folder.Items |> Array.collect getProjs sln.Items |> Array.collect getProjs |> Array.toList - | Some(WorkspacePeekFound.Directory dir) -> dir.Fsprojs |> Array.toList - + | Some(WorkspacePeekFound.Directory dir) -> dir.Fsprojs |> Array.map (fun f -> f.Path) |> Array.toList let getNotLoaded () = let lst = @@ -250,7 +251,7 @@ module Project = let projs = match loadedWorkspace with | None -> Array.empty - | Some(WorkspacePeekFound.Directory dir) -> dir.Fsprojs + | Some(WorkspacePeekFound.Directory dir) -> dir.Fsprojs |> Array.map (fun f -> f.Path) | Some(WorkspacePeekFound.Solution sln) -> sln.Items |> Array.collect foldFsproj |> Array.map fst let loadingInProgress p = @@ -545,7 +546,7 @@ module Project = let wdir = { WorkspacePeekFoundDirectory.Directory = workspace.rootPath.Value - Fsprojs = fsprojs |> Array.ofList } + Fsprojs = fsprojs |> List.map (fun p -> { Path = p; CompileItems = [||] }) |> Array.ofList } return WorkspacePeekFound.Directory wdir } @@ -642,30 +643,50 @@ module Project = setAnyProjectContext true | None -> () - let private initWorkspaceHelper x = + let private initWorkspaceHelper (context: ExtensionContext) x = clearLoadedProjects () loadedWorkspace <- Some x workspaceChangedEmitter.fire x let projs = match x with - | WorkspacePeekFound.Directory dir -> dir.Fsprojs - | WorkspacePeekFound.Solution sln -> sln.Items |> Array.collect foldFsproj |> Array.map fst + | WorkspacePeekFound.Directory dir -> dir.Fsprojs |> Array.map (fun f -> (f.Path, f.CompileItems)) + | WorkspacePeekFound.Solution sln -> sln.Items |> Array.collect foldFsproj |> Array.map (fun (p, _) -> (p, [||])) match x with | WorkspacePeekFound.Solution _ -> setAnyProjectContext true | WorkspacePeekFound.Directory _ when not (projs |> Array.isEmpty) -> setAnyProjectContext true | _ -> () - projs |> List.ofArray |> LanguageService.workspaceLoad |> Promise.map ignore + if lazyLoadWorkspace then + //TODO: Register a file open event handler and load the project on demand + let fsFileToProj = Dictionary() + for (proj, compileItems) in projs do + for compileItem in compileItems do + fsFileToProj.[compileItem] <- proj + + let openFileHandler (e: TextDocument) = + + match tryFindInWorkspace e.uri.path with + | Some(ProjectLoadingState.Loaded _) -> () + | Some(ProjectLoadingState.Loading _) -> () + | _ -> LanguageService.workspaceLoad [ fsFileToProj[e.uri.path] ] |> ignore + + // workspace.onDidOpenTextDocument $ (openFileHandler, (), context.subscriptions) |> ignore + workspace.onDidOpenTextDocument.Invoke(unbox openFileHandler) + |> context.Subscribe + + Promise.lift () + else + projs |> Array.map(fun (p, _) -> p) |> List.ofArray |> LanguageService.workspaceLoad |> Promise.map ignore - let initWorkspace () = + let initWorkspace (context: ExtensionContext) = getWorkspace () |> Promise.bind (function | Some x -> Promise.lift x | None -> getWorkspaceForModeIonideSearch ()) - |> Promise.bind (initWorkspaceHelper) + |> Promise.bind (initWorkspaceHelper context) module internal ProjectStatus = let mutable timer = None @@ -744,7 +765,7 @@ module Project = workspacePeek () |> Promise.bind (fun x -> pickFSACWorkspace x (CurrentWorkspaceConfiguration.get ())) |> Promise.bind (function - | Some w -> initWorkspaceHelper w + | Some w -> initWorkspaceHelper context w | None -> Promise.empty) |> box |> Some) @@ -760,4 +781,4 @@ module Project = ) |> context.Subscribe - initWorkspace () + initWorkspace context diff --git a/src/fsharp.fs b/src/fsharp.fs index 1d01d7e0..0f741000 100644 --- a/src/fsharp.fs +++ b/src/fsharp.fs @@ -27,7 +27,7 @@ let private activateLanguageServiceRestart (context: ExtensionContext) = logger.Debug("Restarting F# language service") do! LanguageService.stop () do! LanguageService.start context - do! Project.initWorkspace () + do! Project.initWorkspace context } commands.registerCommand ("fsharp.restartLanguageService", restart |> objfy2)