Skip to content

Commit b45db8d

Browse files
author
Tal Hadad
committed
user button for detaching from session
1 parent 6f6199d commit b45db8d

File tree

4 files changed

+84
-26
lines changed

4 files changed

+84
-26
lines changed

package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,13 @@
322322
}
323323
],
324324
"commands": [
325+
{
326+
"command": "r.workspaceViewer.detachSession",
327+
"title": "Detach Session",
328+
"icon": "$(debug-disconnect)",
329+
"category": "R Workspace Viewer",
330+
"enablement": "rSessionActive"
331+
},
325332
{
326333
"command": "r.workspaceViewer.refreshEntry",
327334
"title": "Manual Refresh",
@@ -1365,6 +1372,11 @@
13651372
"group": "navigation@3",
13661373
"when": "r.WorkspaceViewer:show && view == workspaceViewer"
13671374
},
1375+
{
1376+
"command": "r.workspaceViewer.detachSession",
1377+
"group": "navigation@4",
1378+
"when": "r.WorkspaceViewer:show && view == workspaceViewer && !r.liveShare:isGuest"
1379+
},
13681380
{
13691381
"command": "r.helpPanel.showQuickPick",
13701382
"group": "navigation",

src/extension.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<apiImp
139139
'r.launchAddinPicker': rstudioapi.launchAddinPicker,
140140

141141
// workspace viewer
142+
'r.workspaceViewer.detachSession': session.detach,
142143
'r.workspaceViewer.refreshEntry': () => rWorkspace?.refresh(),
143144
'r.workspaceViewer.view': (node: workspaceViewer.GlobalEnvItem) => node?.label && workspaceViewer.viewItem(node.label),
144145
'r.workspaceViewer.remove': (node: workspaceViewer.GlobalEnvItem) => node?.label && workspaceViewer.removeItem(node.label),

src/session.ts

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { commands, StatusBarItem, Uri, ViewColumn, Webview, window, workspace, e
1212

1313
import { runTextInTerm } from './rTerminal';
1414
import { FSWatcher } from 'fs-extra';
15-
import { config, readContent, setContext, UriIcon } from './util';
15+
import { config, createWaiterForInvoker, readContent, setContext, UriIcon } from './util';
1616
import { purgeAddinPickerItems, dispatchRStudioAPICall } from './rstudioapi';
1717

1818
import { IRequest } from './liveShare/shareSession';
@@ -74,12 +74,28 @@ let activeBrowserExternalUri: Uri | undefined;
7474
export let incomingRequestServerAddressInfo: AddressInfo | undefined = undefined;
7575
export let attached = false;
7676

77-
class AnotherSocketConnectionError extends Error {
78-
finishCleaningCallback: () => void;
77+
enum InterruptReason {
78+
ANOTHER_CONNECTION,
79+
USER_REQUEST
80+
}
81+
82+
class InterruptSocketConnectionError extends Error {
83+
reason: InterruptReason;
84+
finishWaiter: Promise<void>;
85+
private finishInvoker: () => void;
7986

80-
constructor (finishCleaningCallback: () => void) {
87+
constructor (reason: InterruptReason) {
8188
super();
82-
this.finishCleaningCallback = finishCleaningCallback;
89+
90+
this.reason = reason;
91+
92+
const pair = createWaiterForInvoker();
93+
this.finishWaiter = pair.waiter;
94+
this.finishInvoker = pair.invoker;
95+
}
96+
97+
reportFinishHandling() {
98+
this.finishInvoker();
8399
}
84100
}
85101

@@ -339,6 +355,17 @@ function getBrowserHtml(uri: Uri): string {
339355
`;
340356
}
341357

358+
export async function detach() {
359+
if (incomingRequestServerCurrentSocket === null) {
360+
return;
361+
}
362+
// otherwise
363+
364+
const interrupt = new InterruptSocketConnectionError(InterruptReason.USER_REQUEST);
365+
incomingRequestServerCurrentSocket.destroy(interrupt);
366+
await interrupt.finishWaiter;
367+
}
368+
342369
export function refreshBrowser(): void {
343370
console.log('[refreshBrowser]');
344371
if (activeBrowserPanel) {
@@ -835,22 +862,9 @@ function startIncomingRequestServer(sessionStatusBarItem: StatusBarItem) {
835862
if (incomingRequestServerCurrentSocket !== null) {
836863
console.info('Closing existing connection to the incoming request server since a new one is pending.');
837864

838-
let resolveHandle: (() => void) | null = null;
839-
let wasHandledQuick = false;
840-
const promise = new Promise<void>((resolve) => {
841-
resolveHandle = resolve;
842-
});
843-
const callback = () => {
844-
if (resolveHandle === null) {
845-
wasHandledQuick = true;
846-
} else {
847-
resolveHandle();
848-
}
849-
};
850-
incomingRequestServerCurrentSocket.destroy(new AnotherSocketConnectionError(callback));
851-
if (!wasHandledQuick) {
852-
await promise;
853-
}
865+
const interrupt = new InterruptSocketConnectionError(InterruptReason.ANOTHER_CONNECTION);
866+
incomingRequestServerCurrentSocket.destroy(interrupt);
867+
await interrupt.finishWaiter;
854868
}
855869

856870
console.info('A new connection to the incoming request server has been established.');
@@ -893,20 +907,29 @@ function startIncomingRequestServer(sessionStatusBarItem: StatusBarItem) {
893907
contentToProcess = requests[requests.length - 1];
894908
}
895909
} catch (err) {
896-
if (err instanceof AnotherSocketConnectionError) {
897-
console.error(`Closing this TCP connection since another one is pending.`);
910+
if (err instanceof InterruptSocketConnectionError) {
911+
switch (err.reason) {
912+
case InterruptReason.ANOTHER_CONNECTION:
913+
console.log(`Closing this TCP connection since another one is pending.`);
914+
break;
915+
case InterruptReason.USER_REQUEST:
916+
console.log(`Closing this TCP connection because of a user request.`);
917+
break;
918+
}
898919
} else {
899920
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
900921
console.error(`Error while processing TCP connection: ${err}`);
901922
}
902923

903-
void promiseSocket.end();
924+
void promiseSocket.end().catch(() => {
925+
// For some reason, there is an error when ending this connection, so we're ignoring the error for now
926+
});
904927

905928
await cleanupSession();
906929
incomingRequestServerCurrentSocket = null;
907930

908-
if (err instanceof AnotherSocketConnectionError) {
909-
err.finishCleaningCallback();
931+
if (err instanceof InterruptSocketConnectionError) {
932+
err.reportFinishHandling();
910933
}
911934
}
912935
});

src/util.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,3 +642,25 @@ export function uniqueEntries<T>(array: T[], isIdentical: (x: T, y: T) => boolea
642642
}
643643
return array.filter(uniqueFunction);
644644
}
645+
646+
export function createWaiterForInvoker() {
647+
let resolveHandle: (() => void) | null = null;
648+
let wasHandledQuick = false;
649+
const invoker = () => {
650+
if (resolveHandle === null) {
651+
wasHandledQuick = true;
652+
} else {
653+
resolveHandle();
654+
}
655+
};
656+
657+
const waiter = new Promise<void>((resolve) => {
658+
if (wasHandledQuick) {
659+
resolve();
660+
} else {
661+
resolveHandle = resolve;
662+
}
663+
});
664+
665+
return { invoker, waiter };
666+
}

0 commit comments

Comments
 (0)