From 32987021a2efe158553b4c7299e077aaa7e21be9 Mon Sep 17 00:00:00 2001 From: "Daniel P. Purkhus" Date: Thu, 2 Nov 2023 15:25:04 +0000 Subject: [PATCH 1/2] WIP --- release/package.json | 7 ++++++- src/Components/MSBuild.fs | 2 +- src/Components/SolutionExplorer.fs | 4 ++-- src/Core/Project.fs | 26 ++++++++++++++++++++------ src/fsharp.fs | 2 +- 5 files changed, 30 insertions(+), 11 deletions(-) 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..4528cd0b 100644 --- a/src/Components/SolutionExplorer.fs +++ b/src/Components/SolutionExplorer.fs @@ -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/Project.fs b/src/Core/Project.fs index c9c2e3aa..b99dcb2f 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") @@ -642,7 +644,7 @@ module Project = setAnyProjectContext true | None -> () - let private initWorkspaceHelper x = + let private initWorkspaceHelper (context: ExtensionContext) x = clearLoadedProjects () loadedWorkspace <- Some x workspaceChangedEmitter.fire x @@ -657,15 +659,27 @@ module Project = | 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 openFileHandler (e: TextDocument) = + match tryFindInWorkspace e.uri.path with + | Some(ProjectLoadingState.Loaded _) -> () + | Some(ProjectLoadingState.Loading _) -> () + | _ -> LanguageService.workspaceLoad [ e.uri.path ] |> ignore + + // workspace.onDidOpenTextDocument $ (openFileHandler, (), context.subscriptions) |> ignore + workspace.onDidOpenTextDocument.Invoke(unbox openFileHandler)|> context.Subscribe + Promise.lift() + else + projs |> 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 +758,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 +774,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) From 0f51003759d5a29d0ceb30ef31eb168642f1d42b Mon Sep 17 00:00:00 2001 From: "Daniel P. Purkhus" Date: Fri, 3 Nov 2023 13:54:28 +0000 Subject: [PATCH 2/2] WIP --- src/Components/SolutionExplorer.fs | 2 +- src/Core/DTO.fs | 6 +++++- src/Core/Project.fs | 29 ++++++++++++++++++----------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/Components/SolutionExplorer.fs b/src/Components/SolutionExplorer.fs index 4528cd0b..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 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 b99dcb2f..09133f56 100644 --- a/src/Core/Project.fs +++ b/src/Core/Project.fs @@ -108,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 = @@ -252,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 = @@ -547,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 } @@ -651,8 +650,8 @@ module Project = 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 @@ -661,17 +660,25 @@ module Project = 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 [ e.uri.path ] |> ignore - + | _ -> LanguageService.workspaceLoad [ fsFileToProj[e.uri.path] ] |> ignore + // workspace.onDidOpenTextDocument $ (openFileHandler, (), context.subscriptions) |> ignore - workspace.onDidOpenTextDocument.Invoke(unbox openFileHandler)|> context.Subscribe - Promise.lift() + workspace.onDidOpenTextDocument.Invoke(unbox openFileHandler) + |> context.Subscribe + + Promise.lift () else - projs |> List.ofArray |> LanguageService.workspaceLoad |> Promise.map ignore + projs |> Array.map(fun (p, _) -> p) |> List.ofArray |> LanguageService.workspaceLoad |> Promise.map ignore let initWorkspace (context: ExtensionContext) =