Archived
1
0

Update to VS Code 1.52.1

This commit is contained in:
Asher
2021-02-09 16:08:37 +00:00
1351 changed files with 56560 additions and 38990 deletions

View File

@ -126,7 +126,8 @@ const enum ProtocolMessageType {
Control = 2,
Ack = 3,
KeepAlive = 4,
Disconnect = 5
Disconnect = 5,
ReplayRequest = 6
}
export const enum ProtocolConstants {
@ -274,7 +275,11 @@ class ProtocolWriter {
}
public dispose(): void {
this.flush();
try {
this.flush();
} catch (err) {
// ignore error, since the socket could be already closed
}
this._isDisposed = true;
}
@ -601,6 +606,8 @@ export class PersistentProtocol implements IMessagePassingProtocol {
private _outgoingKeepAliveTimeout: any | null;
private _incomingKeepAliveTimeout: any | null;
private _lastReplayRequestTime: number;
private _socket: ISocket;
private _socketWriter: ProtocolWriter;
private _socketReader: ProtocolReader;
@ -642,6 +649,8 @@ export class PersistentProtocol implements IMessagePassingProtocol {
this._outgoingKeepAliveTimeout = null;
this._incomingKeepAliveTimeout = null;
this._lastReplayRequestTime = 0;
this._socketDisposables = [];
this._socket = socket;
this._socketWriter = new ProtocolWriter(this._socket);
@ -747,6 +756,8 @@ export class PersistentProtocol implements IMessagePassingProtocol {
this._onSocketTimeout.flushBuffer();
this._socket.dispose();
this._lastReplayRequestTime = 0;
this._socket = socket;
this._socketWriter = new ProtocolWriter(this._socket);
this._socketDisposables.push(this._socketWriter);
@ -792,17 +803,31 @@ export class PersistentProtocol implements IMessagePassingProtocol {
if (msg.type === ProtocolMessageType.Regular) {
if (msg.id > this._incomingMsgId) {
if (msg.id !== this._incomingMsgId + 1) {
console.error(`PROTOCOL CORRUPTION, LAST SAW MSG ${this._incomingMsgId} AND HAVE NOW RECEIVED MSG ${msg.id}`);
// in case we missed some messages we ask the other party to resend them
const now = Date.now();
if (now - this._lastReplayRequestTime > 10000) {
// send a replay request at most once every 10s
this._lastReplayRequestTime = now;
this._socketWriter.write(new ProtocolMessage(ProtocolMessageType.ReplayRequest, 0, 0, getEmptyBuffer()));
}
} else {
this._incomingMsgId = msg.id;
this._incomingMsgLastTime = Date.now();
this._sendAckCheck();
this._onMessage.fire(msg.data);
}
this._incomingMsgId = msg.id;
this._incomingMsgLastTime = Date.now();
this._sendAckCheck();
this._onMessage.fire(msg.data);
}
} else if (msg.type === ProtocolMessageType.Control) {
this._onControlMessage.fire(msg.data);
} else if (msg.type === ProtocolMessageType.Disconnect) {
this._onClose.fire();
} else if (msg.type === ProtocolMessageType.ReplayRequest) {
// Send again all unacknowledged messages
const toSend = this._outgoingUnackMsg.toArray();
for (let i = 0, len = toSend.length; i < len; i++) {
this._socketWriter.write(toSend[i]);
}
this._recvAckCheck();
}
}

View File

@ -130,6 +130,11 @@
padding: 5px 5px 2px 5px;
}
.quick-input-message > .codicon {
margin: 0 0.2em;
vertical-align: text-bottom;
}
.quick-input-progress.monaco-progress-container {
position: relative;
}

View File

@ -27,8 +27,10 @@ import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/lis
import { List, IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget';
import { IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox';
import { Color } from 'vs/base/common/color';
import { registerIcon, Codicon } from 'vs/base/common/codicons';
import { registerCodicon, Codicon } from 'vs/base/common/codicons';
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { escape } from 'vs/base/common/strings';
import { renderCodicons } from 'vs/base/browser/codicons';
export interface IQuickInputOptions {
idPrefix: string;
@ -70,7 +72,7 @@ const $ = dom.$;
type Writeable<T> = { -readonly [P in keyof T]: T[P] };
const backButtonIcon = registerIcon('quick-input-back', Codicon.arrowLeft);
const backButtonIcon = registerCodicon('quick-input-back', Codicon.arrowLeft, localize('backButtonIcon', 'Icon for the back button in the quick input dialog.'));
const backButton = {
iconClass: backButtonIcon.classNames,
@ -413,6 +415,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
private _valueSelection: Readonly<[number, number]> | undefined;
private valueSelectionUpdated = true;
private _validationMessage: string | undefined;
private _lastValidationMessage: string | undefined;
private _ok: boolean | 'default' = 'default';
private _customButton = false;
private _customButtonLabel: string | undefined;
@ -961,12 +964,11 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
this.selectedItemsToConfirm = null;
}
}
if (this.validationMessage) {
this.ui.message.textContent = this.validationMessage;
this.showMessageDecoration(Severity.Error);
} else {
this.ui.message.textContent = null;
this.showMessageDecoration(Severity.Ignore);
const validationMessage = this.validationMessage || '';
if (this._lastValidationMessage !== validationMessage) {
this._lastValidationMessage = validationMessage;
dom.reset(this.ui.message, ...renderCodicons(escape(validationMessage)));
this.showMessageDecoration(this.validationMessage ? Severity.Error : Severity.Ignore);
}
this.ui.customButton.label = this.customLabel || '';
this.ui.customButton.element.title = this.customHover || '';
@ -996,6 +998,7 @@ class InputBox extends QuickInput implements IInputBox {
private _prompt: string | undefined;
private noValidationMessage = InputBox.noPromptMessage;
private _validationMessage: string | undefined;
private _lastValidationMessage: string | undefined;
private readonly onDidValueChangeEmitter = this._register(new Emitter<string>());
private readonly onDidAcceptEmitter = this._register(new Emitter<void>());
@ -1097,13 +1100,11 @@ class InputBox extends QuickInput implements IInputBox {
if (this.ui.inputBox.password !== this.password) {
this.ui.inputBox.password = this.password;
}
if (!this.validationMessage && this.ui.message.textContent !== this.noValidationMessage) {
this.ui.message.textContent = this.noValidationMessage;
this.showMessageDecoration(Severity.Ignore);
}
if (this.validationMessage && this.ui.message.textContent !== this.validationMessage) {
this.ui.message.textContent = this.validationMessage;
this.showMessageDecoration(Severity.Error);
const validationMessage = this.validationMessage || this.noValidationMessage;
if (this._lastValidationMessage !== validationMessage) {
this._lastValidationMessage = validationMessage;
dom.reset(this.ui.message, ...renderCodicons(validationMessage));
this.showMessageDecoration(this.validationMessage ? Severity.Error : Severity.Ignore);
}
}
}
@ -1528,7 +1529,7 @@ export class QuickInputController extends Disposable {
ui.inputBox.showDecoration(Severity.Ignore);
ui.visibleCount.setCount(0);
ui.count.setCount(0);
ui.message.textContent = '';
dom.reset(ui.message);
ui.progressBar.stop();
ui.list.setElements([]);
ui.list.matchOnDescription = false;
@ -1696,7 +1697,7 @@ export class QuickInputController extends Disposable {
this.ui.container.style.backgroundColor = quickInputBackground ? quickInputBackground.toString() : '';
this.ui.container.style.color = quickInputForeground ? quickInputForeground.toString() : '';
this.ui.container.style.border = contrastBorder ? `1px solid ${contrastBorder}` : '';
this.ui.container.style.boxShadow = widgetShadow ? `0 5px 8px ${widgetShadow}` : '';
this.ui.container.style.boxShadow = widgetShadow ? `0 0 8px 2px ${widgetShadow}` : '';
this.ui.inputBox.style(this.styles.inputBox);
this.ui.count.style(this.styles.countBadge);
this.ui.ok.style(this.styles.button);

View File

@ -35,6 +35,17 @@
}
},
/**
* @param {string} channel
* @param {any[]} args
* @returns {Promise<any> | undefined}
*/
invoke(channel, ...args) {
if (validateIPC(channel)) {
return ipcRenderer.invoke(channel, ...args);
}
},
/**
* @param {string} channel
* @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener
@ -97,80 +108,48 @@
/**
* Support for a subset of access to node.js global `process`.
*
* Note: when `sandbox` is enabled, the only properties available
* are https://github.com/electron/electron/blob/master/docs/api/process.md#sandbox
*/
process: {
get platform() { return process.platform; },
get env() { return process.env; },
get versions() { return process.versions; },
get type() { return 'renderer'; },
get execPath() { return process.execPath; },
_whenEnvResolved: undefined,
whenEnvResolved:
/**
* @returns when the shell environment has been resolved.
*/
function () {
if (!this._whenEnvResolved) {
this._whenEnvResolved = resolveEnv();
}
/**
* @param {{[key: string]: string}} userEnv
* @returns {Promise<void>}
*/
resolveEnv(userEnv) {
return resolveEnv(userEnv);
},
return this._whenEnvResolved;
},
/**
* @returns {Promise<import('electron').ProcessMemoryInfo>}
*/
getProcessMemoryInfo() {
return process.getProcessMemoryInfo();
},
nextTick:
/**
* Adds callback to the "next tick queue". This queue is fully drained
* after the current operation on the JavaScript stack runs to completion
* and before the event loop is allowed to continue.
*
* @param {Function} callback
* @param {any[]} args
*/
function nextTick(callback, ...args) {
return process.nextTick(callback, ...args);
},
cwd:
/**
* @returns the current working directory.
*/
function () {
return process.cwd();
},
getuid:
/**
* @returns the numeric user identity of the process
*/
function () {
return process.getuid();
},
getProcessMemoryInfo:
/**
* @returns {Promise<import('electron').ProcessMemoryInfo>}
*/
function () {
return process.getProcessMemoryInfo();
},
on:
/**
* @param {string} type
* @param {() => void} callback
*/
function (type, callback) {
if (validateProcessEventType(type)) {
process.on(type, callback);
}
/**
* @param {string} type
* @param {() => void} callback
*/
on(type, callback) {
if (validateProcessEventType(type)) {
process.on(type, callback);
}
}
},
/**
* Some information about the context we are running in.
*/
context: {
get sandbox() { return process.argv.includes('--enable-sandbox'); }
get sandbox() { return process.sandboxed; }
}
};
@ -197,6 +176,7 @@
/**
* @param {string} channel
* @returns {true | never}
*/
function validateIPC(channel) {
if (!channel || !channel.startsWith('vscode:')) {
@ -218,32 +198,40 @@
return true;
}
/** @type {Promise<void> | undefined} */
let resolvedEnv = undefined;
/**
* If VSCode is not run from a terminal, we should resolve additional
* shell specific environment from the OS shell to ensure we are seeing
* all development related environment variables. We do this from the
* main process because it may involve spawning a shell.
*
* @param {{[key: string]: string}} userEnv
* @returns {Promise<void>}
*/
function resolveEnv() {
return new Promise(function (resolve) {
const handle = setTimeout(function () {
console.warn('Preload: Unable to resolve shell environment in a reasonable time');
function resolveEnv(userEnv) {
if (!resolvedEnv) {
// It took too long to fetch the shell environment, return
resolve();
}, 3000);
// Apply `userEnv` directly
Object.assign(process.env, userEnv);
ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) {
clearTimeout(handle);
// Resolve `shellEnv` from the main side
resolvedEnv = new Promise(function (resolve) {
ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) {
// Assign all keys of the shell environment to our process environment
Object.assign(process.env, shellEnv);
// Assign all keys of the shell environment to our process environment
// But make sure that the user environment wins in the end
Object.assign(process.env, shellEnv, userEnv);
resolve();
resolve();
});
ipcRenderer.send('vscode:fetchShellEnv');
});
}
ipcRenderer.send('vscode:fetchShellEnv');
});
return resolvedEnv;
}
//#endregion

View File

@ -12,45 +12,45 @@ export interface ISandboxNodeProcess extends INodeProcess {
* The process.platform property returns a string identifying the operating system platform
* on which the Node.js process is running.
*/
platform: 'win32' | 'linux' | 'darwin';
readonly platform: 'win32' | 'linux' | 'darwin';
/**
* The type will always be Electron renderer.
*/
type: 'renderer';
readonly type: 'renderer';
/**
* A list of versions for the current node.js/electron configuration.
*/
versions: { [key: string]: string | undefined };
readonly versions: { [key: string]: string | undefined };
/**
* The process.env property returns an object containing the user environment.
*/
env: IProcessEnvironment;
readonly env: IProcessEnvironment;
/**
* The current working directory.
* The `execPath` will be the location of the executable of this application.
*/
cwd(): string;
readonly execPath: string;
/**
* Returns the numeric user identity of the process.
* Resolve the true process environment to use and apply it to `process.env`.
*
* There are different layers of environment that will apply:
* - `process.env`: this is the actual environment of the process before this method
* - `shellEnv` : if the program was not started from a terminal, we resolve all shell
* variables to get the same experience as if the program was started from
* a terminal (Linux, macOS)
* - `userEnv` : this is instance specific environment, e.g. if the user started the program
* from a terminal and changed certain variables
*
* The order of overwrites is `process.env` < `shellEnv` < `userEnv`.
*
* It is critical that every process awaits this method early on startup to get the right
* set of environment in `process.env`.
*/
getuid(): number;
/**
* Allows to await resolving the full process environment by checking for the shell environment
* of the OS in certain cases (e.g. when the app is started from the Dock on macOS).
*/
whenEnvResolved(): Promise<void>;
/**
* Adds callback to the "next tick queue". This queue is fully drained
* after the current operation on the JavaScript stack runs to completion
* and before the event loop is allowed to continue.
*/
nextTick(callback: (...args: any[]) => void, ...args: any[]): void;
resolveEnv(userEnv: IProcessEnvironment): Promise<void>;
/**
* A listener on the process. Only a small subset of listener types are allowed.

View File

@ -43,9 +43,10 @@ export interface IStorageDatabase {
export interface IStorage extends IDisposable {
readonly onDidChangeStorage: Event<string>;
readonly items: Map<string, string>;
readonly size: number;
readonly onDidChangeStorage: Event<string>;
init(): Promise<void>;
@ -61,6 +62,8 @@ export interface IStorage extends IDisposable {
set(key: string, value: string | boolean | number | undefined | null): Promise<void>;
delete(key: string): Promise<void>;
whenFlushed(): Promise<void>;
close(): Promise<void>;
}
@ -86,6 +89,8 @@ export class Storage extends Disposable implements IStorage {
private pendingDeletes = new Set<string>();
private pendingInserts = new Map<string, string>();
private readonly whenFlushedCallbacks: Function[] = [];
constructor(
protected readonly database: IStorageDatabase,
private readonly options: IStorageOptions = Object.create(null)
@ -273,8 +278,12 @@ export class Storage extends Disposable implements IStorage {
await this.database.close(() => this.cache);
}
private get hasPending() {
return this.pendingInserts.size > 0 || this.pendingDeletes.size > 0;
}
private flushPending(): Promise<void> {
if (this.pendingInserts.size === 0 && this.pendingDeletes.size === 0) {
if (!this.hasPending) {
return Promise.resolve(); // return early if nothing to do
}
@ -285,8 +294,23 @@ export class Storage extends Disposable implements IStorage {
this.pendingDeletes = new Set<string>();
this.pendingInserts = new Map<string, string>();
// Update in storage
return this.database.updateItems(updateRequest);
// Update in storage and release any
// waiters we have once done
return this.database.updateItems(updateRequest).finally(() => {
if (!this.hasPending) {
while (this.whenFlushedCallbacks.length) {
this.whenFlushedCallbacks.pop()?.();
}
}
});
}
whenFlushed(): Promise<void> {
if (!this.hasPending) {
return Promise.resolve(); // return early if nothing to do
}
return new Promise(resolve => this.whenFlushedCallbacks.push(resolve));
}
}

View File

@ -45,11 +45,16 @@ suite('Storage Library', function () {
changes.add(key);
});
await storage.whenFlushed(); // returns immediately when no pending updates
// Simple updates
const set1Promise = storage.set('bar', 'foo');
const set2Promise = storage.set('barNumber', 55);
const set3Promise = storage.set('barBoolean', true);
let flushPromiseResolved = false;
storage.whenFlushed().then(() => flushPromiseResolved = true);
equal(storage.get('bar'), 'foo');
equal(storage.getNumber('barNumber'), 55);
equal(storage.getBoolean('barBoolean'), true);
@ -62,6 +67,7 @@ suite('Storage Library', function () {
let setPromiseResolved = false;
await Promise.all([set1Promise, set2Promise, set3Promise]).then(() => setPromiseResolved = true);
equal(setPromiseResolved, true);
equal(flushPromiseResolved, true);
changes = new Set<string>();
@ -166,6 +172,9 @@ suite('Storage Library', function () {
const set1Promise = storage.set('foo', 'bar');
const set2Promise = storage.set('bar', 'foo');
let flushPromiseResolved = false;
storage.whenFlushed().then(() => flushPromiseResolved = true);
equal(storage.get('foo'), 'bar');
equal(storage.get('bar'), 'foo');
@ -175,6 +184,7 @@ suite('Storage Library', function () {
await storage.close();
equal(setPromiseResolved, true);
equal(flushPromiseResolved, true);
storage = new Storage(new SQLiteStorageDatabase(join(storageDir, 'storage.db')));
await storage.init();
@ -226,6 +236,9 @@ suite('Storage Library', function () {
const set2Promise = storage.set('foo', 'bar2');
const set3Promise = storage.set('foo', 'bar3');
let flushPromiseResolved = false;
storage.whenFlushed().then(() => flushPromiseResolved = true);
equal(storage.get('foo'), 'bar3');
equal(changes.size, 1);
ok(changes.has('foo'));
@ -233,6 +246,7 @@ suite('Storage Library', function () {
let setPromiseResolved = false;
await Promise.all([set1Promise, set2Promise, set3Promise]).then(() => setPromiseResolved = true);
ok(setPromiseResolved);
ok(flushPromiseResolved);
changes = new Set<string>();