/// <summary> /// Waits for the workspace to finish loading and returns true if the workspace loaded successfully. /// Returns an application state and initialized providers. /// </summary> private bool TryWaitForWorkspaceLoaded(out AppState appState, out LanguageServiceProviders providers) { try { // LoadWorkspaceAsync returns a cached task. It means that if the initialization is finished // we'll get the result immediately. var result = LoadWorkspaceAsync().GetAwaiter().GetResult(); if (result.workspaceLoadingState == WorkspaceLoadingState.Success) { appState = result.appState; providers = result.languageServiceProviders; return(true); } } catch (TaskCanceledException) { } appState = null; providers = null; return(false); }
private bool ReloadWorkspaceAndWaitForCompletion(TextDocumentManager currentDocumentManager, out AppState appState, out LanguageServiceProviders providers) { // In case of adding/removing a document we should recompute the workspace and wait for completion, // some other code may rely that the workspace is ready when the method that calls this one is finished. ReloadWorkspace(currentDocumentManager); return(TryWaitForWorkspaceLoaded(out appState, out providers)); }
/// <summary> /// Creates the BuildXL workspace and initializes the language service providers. /// </summary> /// <remarks> /// This must be called when a text document is actually opened. The order VSCode /// calls the plugin is "Initialize" -> "DidConfigurationChange" -> DidOpenTextDocument. /// So, if you attempt to create the workspace, and initialize the providers during the /// initialization phase, then you will not have proper access to any configuration settings /// the user may have made. /// </remarks> private Task <(WorkspaceLoadingState workspaceLoadingState, AppState appState, LanguageServiceProviders languageServiceProviders)> LoadWorkspaceAsync(TextDocumentManager documentManager = null) { var workspaceLoadingTask = m_workspaceLoadingTask.GetOrCreate(async() => { var result = await Task.Run(() => { try { m_progressReporter.ReportWorkspaceInit(); WorkspaceLoadingState workspaceLoadingState; if (m_rootUri == null) { throw new ArgumentException("Root directory was not passed from VSCode via the rootUri configuration value"); } var appState = AppState.TryCreateWorkspace(documentManager, m_rootUri, (s, ea) => { // "Casting" the EventArgs object from the BuildXL library to the "local" version // to avoid pulling in all of its dependencies during build. var rpcEventArgs = BuildXL.Ide.JsonRpc.WorkspaceProgressEventArgs.Create( (BuildXL.Ide.JsonRpc.ProgressStage)ea.ProgressStage, ea.NumberOfProcessedSpecs, ea.TotalNumberOfSpecs); m_progressReporter.ReportWorkspaceInProgress(WorkspaceLoadingParams.InProgress(rpcEventArgs)); }, m_testContext, m_settings); if (appState == null || appState.HasUnrecoverableFailures()) { workspaceLoadingState = WorkspaceLoadingState.Failure; m_progressReporter.ReportWorkspaceFailure(m_tracer.LogFilePath, OpenLogFile); } else { workspaceLoadingState = WorkspaceLoadingState.Success; m_progressReporter.ReportWorkspaceSuccess(); } LanguageServiceProviders providers = null; lock (m_loadWorkspaceLock) { if (workspaceLoadingState == WorkspaceLoadingState.Success) { // In practice you should not make callouts while holding // a lock. Initializing the providers sets members of this // class and also relies on members of the app-state. // So, we do want to block callers until initialization of // the providers is complete, however, be warned that // if a provider ever calls back into the app it could // cause a deadlock. providers = InitializeProviders(appState); } } return(workspaceLoadingState, appState, providers); } catch (Exception e) { var errorMessage = e.ToStringDemystified(); Logger.LanguageServerUnhandledInternalError(LoggingContext, errorMessage); m_progressReporter.ReportWorkspaceFailure(m_tracer.LogFilePath, OpenLogFile); m_testContext.GetValueOrDefault().ErrorReporter?.Invoke(errorMessage); return(WorkspaceLoadingState.Failure, (AppState)null, (LanguageServiceProviders)null); } }); return(result); }); // Changing the workspace loading state once the task is finished. workspaceLoadingTask.ContinueWith(t => { m_workspaceLoadingState = t.Result.Item1; }); return(workspaceLoadingTask); }