diff --git a/R/session/vsc.R b/R/session/vsc.R index 0b881dc4d..9561e4a76 100644 --- a/R/session/vsc.R +++ b/R/session/vsc.R @@ -791,3 +791,5 @@ print.hsearch <- function(x, ...) { registerS3method(generic, class, method, envir = parent.frame()) invisible(NULL) } + +reg.finalizer(globalenv(), function(e) .vsc$request("detach"), onexit = TRUE) diff --git a/src/extension.ts b/src/extension.ts index d15bf329d..3a3de2468 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -36,6 +36,7 @@ export let enableSessionWatcher: boolean | undefined = undefined; export let globalHttpgdManager: httpgdViewer.HttpgdManager | undefined = undefined; export let rmdPreviewManager: rmarkdown.RMarkdownPreviewManager | undefined = undefined; export let rmdKnitManager: rmarkdown.RMarkdownKnitManager | undefined = undefined; +export let sessionStatusBarItem: vscode.StatusBarItem | undefined = undefined; // Called (once) when the extension is activated export async function activate(context: vscode.ExtensionContext): Promise { @@ -225,7 +226,7 @@ export async function activate(context: vscode.ExtensionContext): Promise('sessionWatcher')) { - removeSessionFiles(); + cleanupSession(); } } } diff --git a/src/session.ts b/src/session.ts index 34a086a81..f73793b7d 100644 --- a/src/session.ts +++ b/src/session.ts @@ -11,7 +11,7 @@ import { config, readContent, UriIcon } from './util'; import { purgeAddinPickerItems, dispatchRStudioAPICall } from './rstudioapi'; import { IRequest } from './liveShare/shareSession'; -import { homeExtDir, rWorkspace, globalRHelp, globalHttpgdManager, extensionContext } from './extension'; +import { homeExtDir, rWorkspace, globalRHelp, globalHttpgdManager, extensionContext, sessionStatusBarItem } from './extension'; import { UUID, rHostService, rGuestService, isLiveShare, isHost, isGuestSession, closeBrowser, guestResDir, shareBrowser, openVirtualDoc, shareWorkspace } from './liveShare'; export interface GlobalEnv { @@ -734,6 +734,7 @@ type ISessionRequest = { async function updateRequest(sessionStatusBarItem: StatusBarItem) { console.info('[updateRequest] Started'); console.info(`[updateRequest] requestFile: ${requestFile}`); + const lockContent = await fs.readFile(requestLockFile, 'utf8'); const newTimeStamp = Number.parseFloat(lockContent); if (newTimeStamp !== requestTimeStamp) { @@ -776,6 +777,15 @@ async function updateRequest(sessionStatusBarItem: StatusBarItem) { if (request.plot_url) { await globalHttpgdManager?.showViewer(request.plot_url); } + void watchProcess(Number(pid)).then((v) => { + if (v === Number(pid)) { + cleanupSession(); + } + }); + break; + } + case 'detach': { + cleanupSession(); break; } case 'browser': { @@ -815,3 +825,36 @@ async function updateRequest(sessionStatusBarItem: StatusBarItem) { } } } + +export function cleanupSession(): void { + if (sessionStatusBarItem) { + sessionStatusBarItem.text = 'R: (not attached)'; + sessionStatusBarItem.tooltip = 'Click to attach active terminal.'; + } + workspaceData.globalenv = {}; + workspaceData.loaded_namespaces = []; + workspaceData.search = []; + rWorkspace?.refresh(); + removeSessionFiles(); +} + +async function watchProcess(pid: number): Promise { + function pidIsRunning(pid: number) { + try { + process.kill(pid, 0); + return true; + } catch (e) { + return false; + } + } + + let res = true; + do { + res = pidIsRunning(pid); + await new Promise(resolve => { + setTimeout(resolve, 1000); + }); + + } while (res); + return pid; +}