Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local terminal reconnection #116449

Merged
merged 79 commits into from
Feb 22, 2021
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
9fc381f
persist local terminals - not yet setting active instance or relative…
meganrogge Feb 8, 2021
4c690b3
remove log statement
meganrogge Feb 8, 2021
7dc31b4
active instance working for each tab
meganrogge Feb 8, 2021
b7882a8
touch up
meganrogge Feb 8, 2021
4e0bc11
relative sizes persist
meganrogge Feb 8, 2021
662bc9b
Merge branch 'merogge/localReconnect' into tyriar_megan_reconnect
meganrogge Feb 10, 2021
da39b7a
create PersistentTerminalProcess and move terminal and environmentVar…
meganrogge Feb 10, 2021
cd9704d
Merge branch 'tyriar_megan_reconnect' into tyriar/megan
meganrogge Feb 11, 2021
233890f
add log service
meganrogge Feb 11, 2021
ebebd48
adjust resizePanes
meganrogge Feb 11, 2021
1701859
add to do
meganrogge Feb 11, 2021
73e5f51
Merge branch 'tyriar/megan' into tyriar_megan_reconnect
meganrogge Feb 11, 2021
a35338b
move things back to the workbench and leave only necessary items in p…
meganrogge Feb 11, 2021
53ac8d3
Update src/vs/base/common/async.ts
meganrogge Feb 11, 2021
3bd49af
Update src/vs/platform/terminal/common/terminal.ts
meganrogge Feb 11, 2021
68a4751
move things back to platform and delete duplicate code
meganrogge Feb 11, 2021
8dd7a21
Merge branch 'tyriar/megan' into tyriar_megan_reconnect
meganrogge Feb 11, 2021
7e6e069
remove unused imports
meganrogge Feb 11, 2021
d3502ea
revert terminalTab change
meganrogge Feb 11, 2021
c87e017
remove unneeded stuff
meganrogge Feb 11, 2021
0c11d7c
undo move of lines in test files
meganrogge Feb 11, 2021
fb354b7
change import for terminalContributionsDescriptor
meganrogge Feb 11, 2021
1e46aab
Update src/vs/platform/terminal/common/terminal.ts
meganrogge Feb 11, 2021
ff20de6
move more stuff into/out of platform/terminal.ts
meganrogge Feb 11, 2021
de9c04c
remove storage service, try to hook up layout info communication, bad…
meganrogge Feb 12, 2021
af4a8f7
use TerminalInstanceService to access localPtyService
meganrogge Feb 12, 2021
8c1dbbb
clean some things up
meganrogge Feb 12, 2021
bfd8116
splits persisting, still some issues
meganrogge Feb 13, 2021
0de32ce
consolidate some code
meganrogge Feb 13, 2021
7420510
remoteAttach -> attachExisting, remove logs, fill in args
meganrogge Feb 16, 2021
f3e3ea9
rename remote -> persistent
meganrogge Feb 16, 2021
0f05597
don't re-create a persistent terminal process
meganrogge Feb 16, 2021
dcf7f71
add onProcessReplay to PersistentTerminalProcess
meganrogge Feb 17, 2021
5d08a56
add onProcessReplay to PtyService
meganrogge Feb 17, 2021
b89dedc
Add onProcessReplay to LocalPtyService and forward the event to PtySe…
meganrogge Feb 17, 2021
31e27a8
add trigger replay
meganrogge Feb 17, 2021
429fa74
reconnect processes
meganrogge Feb 17, 2021
3fbab26
only start process once
meganrogge Feb 18, 2021
13a9be8
removed unneeded events and fix reconnect title
meganrogge Feb 18, 2021
70c4b5d
fix initial terminal title
meganrogge Feb 18, 2021
e3dc574
move terminalDataBuffering from workbench to common
meganrogge Feb 18, 2021
7130344
Merge branch 'main' into tyriar_megan_reconnect
meganrogge Feb 18, 2021
0eaf2ba
enable flow control for local terminals
meganrogge Feb 18, 2021
8039b64
Update src/vs/workbench/contrib/terminal/browser/terminalInstanceServ…
meganrogge Feb 18, 2021
070a33c
Update src/vs/platform/terminal/common/terminal.ts
meganrogge Feb 18, 2021
e95a5c1
Update src/vs/platform/terminal/common/terminalDataBuffering.ts
meganrogge Feb 18, 2021
9c8e2ae
move stuff back to workbench and remove log statements and use pid
meganrogge Feb 18, 2021
88f459a
return terminal launch error
meganrogge Feb 18, 2021
dd2bb8c
Update src/vs/platform/terminal/electron-browser/localPtyService.ts
meganrogge Feb 18, 2021
1d3343a
remove stuff from process in platform
meganrogge Feb 18, 2021
a172be7
use persistentTerminalId instead of pid
meganrogge Feb 19, 2021
ba05eed
log something in ptyService
meganrogge Feb 19, 2021
88c471e
Merge remote-tracking branch 'origin/main' into tyriar_megan_reconnect
Tyriar Feb 19, 2021
5526061
Update src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
Tyriar Feb 19, 2021
d19fccb
Clean up
Tyriar Feb 19, 2021
f0ab2d8
Return Promise<void> from fetch proc
Tyriar Feb 19, 2021
392db91
fetch -> attach
Tyriar Feb 19, 2021
f9245a6
Tidy up persistent term events
Tyriar Feb 19, 2021
71ddff4
More ptyService clean up
Tyriar Feb 19, 2021
8a4a712
Simply onExit handling
Tyriar Feb 19, 2021
a59c8d2
Pty service better title handling
Tyriar Feb 19, 2021
fc28fad
Correct reconnected active terminal restoration
Tyriar Feb 19, 2021
89eb214
Clarify layout obj is not referencing pid
Tyriar Feb 19, 2021
0f6ef54
Fix replay position
Tyriar Feb 19, 2021
f3b46e3
Improve ptyService side log format
Tyriar Feb 19, 2021
1475cd0
Group common pty service interface methods
Tyriar Feb 19, 2021
04957f6
Handle shutting down all window processes when not a reload
Tyriar Feb 19, 2021
cf93ca4
Remove duplicate interfaces from workbench
Tyriar Feb 19, 2021
cc92705
Flow control constants to platform
Tyriar Feb 19, 2021
32a8133
Remove unneeded event declaration
Tyriar Feb 19, 2021
6153932
Merge remote-tracking branch 'origin/main' into tyriar_megan_reconnect
Tyriar Feb 22, 2021
35d3403
Remove log
Tyriar Feb 22, 2021
ad27576
Update distro
Tyriar Feb 22, 2021
3c9cfee
Prevent env var reload when attaching
Tyriar Feb 22, 2021
5e01284
Ensure pty host is shutdown when shared process goes down
Tyriar Feb 22, 2021
d8b302d
Merge remote-tracking branch 'origin/main' into tyriar_megan_reconnect
Tyriar Feb 22, 2021
1bddccd
Use less aggressive create process timeout
Tyriar Feb 22, 2021
d2a6dea
Disable conpty in integration tests again
Tyriar Feb 22, 2021
ce7cd0a
Make env var tests more resilient to other terminal events
Tyriar Feb 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/vs/base/common/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,25 @@ export class Barrier {
}
}

/**
* A barrier that is initially closed and then becomes opened permanently after a certain period of
* time or when open is called explicitly
*/
export class AutoOpenBarrier extends Barrier {
meganrogge marked this conversation as resolved.
Show resolved Hide resolved

private readonly _timeout: any;

constructor(autoOpenTimeMs: number) {
super();
this._timeout = setTimeout(() => this.open(), autoOpenTimeMs);
}

open(): void {
clearTimeout(this._timeout);
super.open();
}
}

export function timeout(millis: number): CancelablePromise<void>;
export function timeout(millis: number, token: CancellationToken): Promise<void>;
export function timeout(millis: number, token?: CancellationToken): CancelablePromise<void> | Promise<void> {
Expand Down
25 changes: 25 additions & 0 deletions src/vs/platform/terminal/common/environmentVariable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { ThemeIcon } from 'vs/platform/theme/common/themeService';

export enum EnvironmentVariableMutatorType {
Replace = 1,
Append = 2,
Prepend = 3
}
export interface IEnvironmentVariableMutator {
readonly value: string;
readonly type: EnvironmentVariableMutatorType;
}
/** [variable, mutator] */
export type ISerializableEnvironmentVariableCollection = [string, IEnvironmentVariableMutator][];

export interface IEnvironmentVariableInfo {
readonly requiresAction: boolean;
getInfo(): string;
getIcon(): ThemeIcon;
getActions?(): { label: string, iconClass?: string, run: () => void, commandId: string }[];
}
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
196 changes: 196 additions & 0 deletions src/vs/platform/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,171 @@
*--------------------------------------------------------------------------------------------*/

import { Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';

export interface IRemoteTerminalAttachTarget {
id: number;
pid: number;
title: string;
cwd: string;
workspaceId: string;
workspaceName: string;
isOrphan: boolean;
}
meganrogge marked this conversation as resolved.
Show resolved Hide resolved

export interface IRawTerminalInstanceLayoutInfo<T> {
relativeSize: number;
terminal: T;
}
export type ITerminalInstanceLayoutInfoById = IRawTerminalInstanceLayoutInfo<number>;
export type ITerminalInstanceLayoutInfo = IRawTerminalInstanceLayoutInfo<IPtyHostAttachTarget>;

export interface IRawTerminalTabLayoutInfo<T> {
isActive: boolean;
activeTerminalProcessId: number | undefined;
terminals: IRawTerminalInstanceLayoutInfo<T>[];
}

export type ITerminalTabLayoutInfoById = IRawTerminalTabLayoutInfo<number>;
export type ITerminalTabLayoutInfo = IRawTerminalTabLayoutInfo<IPtyHostAttachTarget | null>;

export interface IRawTerminalsLayoutInfo<T> {
tabs: IRawTerminalTabLayoutInfo<T>[];
}

export interface IPtyHostAttachTarget {
id: number;
pid: number;
title: string;
cwd: string;
workspaceId: string;
workspaceName: string;
isOrphan: boolean;
}

export type ITerminalsLayoutInfo = IRawTerminalsLayoutInfo<IPtyHostAttachTarget | null>;
export type ITerminalsLayoutInfoById = IRawTerminalsLayoutInfo<number>;

/**
* Provides access to native Windows calls that can be injected into non-native layers.
*/
export interface ITerminalNativeWindowsDelegate {
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
/**
* Gets the Windows build number, eg. this would be `19041` for Windows 10 version 2004
*/
getWindowsBuildNumber(): number;
/**
* Converts a regular Windows path into the WSL path equivalent, eg. `C:\` -> `/mnt/c`
* @param path The Windows path.
*/
getWslPath(path: string): Promise<string>;
}

export interface IShellDefinition {
label: string;
path: string;
}

export interface ICommandTracker {
scrollToPreviousCommand(): void;
scrollToNextCommand(): void;
selectToPreviousCommand(): void;
selectToNextCommand(): void;
selectToPreviousLine(): void;
selectToNextLine(): void;
}

export interface INavigationMode {
exitNavigationMode(): void;
focusPreviousLine(): void;
focusNextLine(): void;
}

export interface IBeforeProcessDataEvent {
/**
* The data of the event, this can be modified by the event listener to change what gets sent
* to the terminal.
*/
data: string;
}
meganrogge marked this conversation as resolved.
Show resolved Hide resolved


meganrogge marked this conversation as resolved.
Show resolved Hide resolved
export interface ITerminalProcessExtHostProxy extends IDisposable {
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
readonly terminalId: number;

emitData(data: string): void;
emitTitle(title: string): void;
emitReady(pid: number, cwd: string): void;
emitExit(exitCode: number | undefined): void;
emitOverrideDimensions(dimensions: ITerminalDimensions | undefined): void;
emitResolvedShellLaunchConfig(shellLaunchConfig: IShellLaunchConfig): void;
emitInitialCwd(initialCwd: string): void;
emitCwd(cwd: string): void;
emitLatency(latency: number): void;

onInput: Event<string>;
onResize: Event<{ cols: number, rows: number }>;
onAcknowledgeDataEvent: Event<number>;
onShutdown: Event<boolean>;
onRequestInitialCwd: Event<void>;
onRequestCwd: Event<void>;
onRequestLatency: Event<void>;
}

export interface ISpawnExtHostProcessRequest {
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
proxy: ITerminalProcessExtHostProxy;
shellLaunchConfig: IShellLaunchConfig;
activeWorkspaceRootUri: URI | undefined;
cols: number;
rows: number;
isWorkspaceShellAllowed: boolean;
callback: (error: ITerminalLaunchError | undefined) => void;
}

export interface IStartExtensionTerminalRequest {
proxy: ITerminalProcessExtHostProxy;
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
cols: number;
rows: number;
callback: (error: ITerminalLaunchError | undefined) => void;
}

export interface IAvailableShellsRequest {
callback: (shells: IShellDefinition[]) => void;
}

export interface IDefaultShellAndArgsRequest {
Tyriar marked this conversation as resolved.
Show resolved Hide resolved
useAutomationShell: boolean;
callback: (shell: string, args: string[] | string | undefined) => void;
}

export enum LinuxDistro {
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
Fedora,
Ubuntu,
Unknown
}

export enum TitleEventSource {
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
/** From the API or the rename command that overrides any other type */
Api,
/** From the process name property*/
Process,
/** From the VT sequence */
Sequence
}

export interface IWindowsShellHelper extends IDisposable {
readonly onShellNameChange: Event<string>;

getShellName(): Promise<string>;
}
meganrogge marked this conversation as resolved.
Show resolved Hide resolved

export interface IRawTerminalInstanceLayoutInfo<T> {
relativeSize: number;
terminal: T;
}

export enum TerminalIpcChannels {
/**
* Communicates between the renderer process and shared process.
Expand Down Expand Up @@ -206,6 +368,17 @@ export interface ITerminalChildProcess {
getLatency(): Promise<number>;
}

export const enum ReconnectConstants {
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
/**
* If there is no reconnection within this time-frame, consider the connection permanently closed...
*/
ReconnectionGraceTime = 5000, // 5 seconds
/**
* Maximal grace time between the first and the last reconnection...
*/
ReconnectionShortGraceTime = 1000, // 1 second
}

export const enum FlowControlConstants {
/**
* The number of _unacknowledged_ chars to have been sent before the pty is paused in order for
Expand Down Expand Up @@ -252,3 +425,26 @@ export interface ITerminalDimensionsOverride extends ITerminalDimensions {
*/
forceExactSize?: boolean;
}

export function printTime(ms: number): string {
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
let h = 0;
let m = 0;
let s = 0;
if (ms >= 1000) {
s = Math.floor(ms / 1000);
ms -= s * 1000;
}
if (s >= 60) {
m = Math.floor(s / 60);
s -= m * 60;
}
if (m >= 60) {
h = Math.floor(m / 60);
m -= h * 60;
}
const _h = h ? `${h}h` : ``;
const _m = m ? `${m}m` : ``;
const _s = s ? `${s}s` : ``;
const _ms = ms ? `${ms}ms` : ``;
return `${_h}${_m}${_s}${_ms}`;
}
65 changes: 65 additions & 0 deletions src/vs/platform/terminal/common/terminalDataBuffering.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IProcessDataEvent } from 'vs/platform/terminal/common/terminal';
interface TerminalDataBuffer extends IDisposable {
data: string[];
timeoutId: any;
}
export class TerminalDataBufferer implements IDisposable {
private readonly _terminalBufferMap = new Map<number, TerminalDataBuffer>();

constructor(private readonly _callback: (id: number, data: string) => void) {
}

dispose() {
for (const buffer of this._terminalBufferMap.values()) {
buffer.dispose();
}
}

startBuffering(id: number, event: Event<string | IProcessDataEvent>, throttleBy: number = 5): IDisposable {
let disposable: IDisposable;
disposable = event((e: string | IProcessDataEvent) => {
const data = (typeof e === 'string' ? e : e.data);
let buffer = this._terminalBufferMap.get(id);
if (buffer) {
buffer.data.push(data);

return;
}

const timeoutId = setTimeout(() => this._flushBuffer(id), throttleBy);
buffer = {
data: [data],
timeoutId: timeoutId,
dispose: () => {
clearTimeout(timeoutId);
this._flushBuffer(id);
disposable.dispose();
}
};
this._terminalBufferMap.set(id, buffer);
});
return disposable;
}

stopBuffering(id: number) {
const buffer = this._terminalBufferMap.get(id);
if (buffer) {
buffer.dispose();
}
}

private _flushBuffer(id: number): void {
const buffer = this._terminalBufferMap.get(id);
if (buffer) {
this._terminalBufferMap.delete(id);
this._callback(id, buffer.data.join(''));
}
}
}
Loading