mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-26 23:00:29 -04:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
28
src/vs/base/parts/ipc/common/ipc.ts
Normal file
28
src/vs/base/parts/ipc/common/ipc.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
/**
|
||||
* An `IChannel` is an abstraction over a collection of commands.
|
||||
* You can `call` several commands on a channel, each taking at
|
||||
* most one single argument. A `call` always returns a promise
|
||||
* with at most one single return value.
|
||||
*/
|
||||
export interface IChannel {
|
||||
call<T>(command: string, arg?: any, cancellationToken?: CancellationToken): Promise<T>;
|
||||
listen<T>(event: string, arg?: any): Event<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* An `IServerChannel` is the couter part to `IChannel`,
|
||||
* on the server-side. You should implement this interface
|
||||
* if you'd like to handle remote promises or events.
|
||||
*/
|
||||
export interface IServerChannel<TContext = string> {
|
||||
call<T>(ctx: TContext, command: string, arg?: any, cancellationToken?: CancellationToken): Promise<T>;
|
||||
listen<T>(ctx: TContext, event: string, arg?: any): Event<T>;
|
||||
}
|
||||
@@ -14,7 +14,7 @@ export class Client extends IPCClient implements IDisposable {
|
||||
private protocol: Protocol;
|
||||
|
||||
private static createProtocol(): Protocol {
|
||||
const onMessage = Event.fromNodeEventEmitter<string>(ipcRenderer, 'ipc:message', (_, message: string) => message);
|
||||
const onMessage = Event.fromNodeEventEmitter<Buffer>(ipcRenderer, 'ipc:message', (_, message: Buffer) => message);
|
||||
ipcRenderer.send('ipc:hello');
|
||||
return new Protocol(ipcRenderer, onMessage);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
interface IIPCEvent {
|
||||
event: { sender: Electron.WebContents; };
|
||||
message: string;
|
||||
message: Buffer | null;
|
||||
}
|
||||
|
||||
function createScopedOnMessageEvent(senderId: number, eventName: string): Event<string> {
|
||||
const onMessage = Event.fromNodeEventEmitter<IIPCEvent>(ipcMain, eventName, (event, message: string) => ({ event, message }));
|
||||
function createScopedOnMessageEvent(senderId: number, eventName: string): Event<Buffer | null> {
|
||||
const onMessage = Event.fromNodeEventEmitter<IIPCEvent>(ipcMain, eventName, (event, message) => ({ event, message }));
|
||||
const onMessageFromSender = Event.filter(onMessage, ({ event }) => event.sender.id === senderId);
|
||||
return Event.map(onMessageFromSender, ({ message }) => message);
|
||||
}
|
||||
@@ -38,7 +38,7 @@ export class Server extends IPCServer {
|
||||
const onDidClientReconnect = new Emitter<void>();
|
||||
Server.Clients.set(id, toDisposable(() => onDidClientReconnect.fire()));
|
||||
|
||||
const onMessage = createScopedOnMessageEvent(id, 'ipc:message');
|
||||
const onMessage = createScopedOnMessageEvent(id, 'ipc:message') as Event<Buffer>;
|
||||
const onDidClientDisconnect = Event.any(Event.signal(createScopedOnMessageEvent(id, 'ipc:disconnect')), onDidClientReconnect.event);
|
||||
const protocol = new Protocol(webContents, onMessage);
|
||||
|
||||
|
||||
@@ -5,14 +5,15 @@
|
||||
|
||||
import { ChildProcess, fork, ForkOptions } from 'child_process';
|
||||
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Delayer, always, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { Delayer, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { deepClone, assign } from 'vs/base/common/objects';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { createQueuedSender } from 'vs/base/node/processes';
|
||||
import { ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient, IChannel } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { isRemoteConsoleLog, log } from 'vs/base/node/console';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
|
||||
/**
|
||||
* This implementation doesn't perform well since it uses base64 encoding for buffers.
|
||||
@@ -132,7 +133,7 @@ export class Client implements IChannelClient, IDisposable {
|
||||
const disposable = toDisposable(() => result.cancel());
|
||||
this.activeRequests.add(disposable);
|
||||
|
||||
always(result, () => {
|
||||
result.finally(() => {
|
||||
cancellationTokenListener.dispose();
|
||||
this.activeRequests.delete(disposable);
|
||||
|
||||
|
||||
@@ -3,33 +3,20 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
||||
/**
|
||||
* This implementation doesn't perform well since it uses base64 encoding for buffers.
|
||||
* Electron 3.0 should have suport for buffers in IPC: https://github.com/electron/electron/pull/13055
|
||||
*/
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
export interface Sender {
|
||||
send(channel: string, msg: string | null): void;
|
||||
send(channel: string, msg: Buffer | null): void;
|
||||
}
|
||||
|
||||
export class Protocol implements IMessagePassingProtocol {
|
||||
|
||||
private listener: IDisposable;
|
||||
|
||||
private _onMessage = new Emitter<Buffer>();
|
||||
get onMessage(): Event<Buffer> { return this._onMessage.event; }
|
||||
|
||||
constructor(private sender: Sender, onMessageEvent: Event<string>) {
|
||||
onMessageEvent(msg => this._onMessage.fire(Buffer.from(msg, 'base64')));
|
||||
}
|
||||
constructor(private sender: Sender, readonly onMessage: Event<Buffer>) { }
|
||||
|
||||
send(message: Buffer): void {
|
||||
try {
|
||||
this.sender.send('ipc:message', message.toString('base64'));
|
||||
this.sender.send('ipc:message', message);
|
||||
} catch (e) {
|
||||
// systems are going down
|
||||
}
|
||||
@@ -37,6 +24,5 @@ export class Protocol implements IMessagePassingProtocol {
|
||||
|
||||
dispose(): void {
|
||||
this.sender.send('ipc:disconnect', null);
|
||||
this.listener = dispose(this.listener);
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,10 @@
|
||||
import { Socket, Server as NetServer, createConnection, createServer } from 'net';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IMessagePassingProtocol, ClientConnectionEvent, IPCServer, IPCClient } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { join } from 'path';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { tmpdir } from 'os';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
|
||||
export function generateRandomPipeName(): string {
|
||||
const randomSuffix = generateUuid();
|
||||
@@ -22,6 +21,80 @@ export function generateRandomPipeName(): string {
|
||||
}
|
||||
}
|
||||
|
||||
class ChunkStream {
|
||||
|
||||
private _chunks: Buffer[];
|
||||
private _totalLength: number;
|
||||
|
||||
public get byteLength() {
|
||||
return this._totalLength;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this._chunks = [];
|
||||
this._totalLength = 0;
|
||||
}
|
||||
|
||||
public acceptChunk(buff: Buffer) {
|
||||
this._chunks.push(buff);
|
||||
this._totalLength += buff.byteLength;
|
||||
}
|
||||
|
||||
public readUInt32BE(): number {
|
||||
let tmp = this.read(4);
|
||||
return tmp.readUInt32BE(0);
|
||||
}
|
||||
|
||||
public read(byteCount: number): Buffer {
|
||||
if (byteCount === 0) {
|
||||
return Buffer.allocUnsafe(0);
|
||||
}
|
||||
|
||||
if (byteCount > this._totalLength) {
|
||||
throw new Error(`Cannot read so many bytes!`);
|
||||
}
|
||||
|
||||
if (this._chunks[0].byteLength === byteCount) {
|
||||
// super fast path, precisely first chunk must be returned
|
||||
const result = this._chunks.shift()!;
|
||||
this._totalLength -= byteCount;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (this._chunks[0].byteLength > byteCount) {
|
||||
// fast path, the reading is entirely within the first chunk
|
||||
const result = this._chunks[0].slice(0, byteCount);
|
||||
this._chunks[0] = this._chunks[0].slice(byteCount);
|
||||
this._totalLength -= byteCount;
|
||||
return result;
|
||||
}
|
||||
|
||||
let result = Buffer.allocUnsafe(byteCount);
|
||||
let resultOffset = 0;
|
||||
while (byteCount > 0) {
|
||||
const chunk = this._chunks[0];
|
||||
if (chunk.byteLength > byteCount) {
|
||||
// this chunk will survive
|
||||
this._chunks[0] = chunk.slice(byteCount);
|
||||
|
||||
chunk.copy(result, resultOffset, 0, byteCount);
|
||||
resultOffset += byteCount;
|
||||
this._totalLength -= byteCount;
|
||||
byteCount -= byteCount;
|
||||
} else {
|
||||
// this chunk will be entirely read
|
||||
this._chunks.shift();
|
||||
|
||||
chunk.copy(result, resultOffset, 0, chunk.byteLength);
|
||||
resultOffset += chunk.byteLength;
|
||||
this._totalLength -= chunk.byteLength;
|
||||
byteCount -= chunk.byteLength;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A message has the following format:
|
||||
*
|
||||
@@ -35,9 +108,8 @@ export class Protocol implements IDisposable, IMessagePassingProtocol {
|
||||
private static readonly _headerLen = 4;
|
||||
|
||||
private _isDisposed: boolean;
|
||||
private _chunks: Buffer[];
|
||||
private _incomingData: ChunkStream;
|
||||
|
||||
private _firstChunkTimer: TimeoutTimer;
|
||||
private _socketDataListener: (data: Buffer) => void;
|
||||
private _socketEndListener: () => void;
|
||||
private _socketCloseListener: () => void;
|
||||
@@ -48,11 +120,9 @@ export class Protocol implements IDisposable, IMessagePassingProtocol {
|
||||
private _onClose = new Emitter<void>();
|
||||
readonly onClose: Event<void> = this._onClose.event;
|
||||
|
||||
constructor(private _socket: Socket, firstDataChunk?: Buffer) {
|
||||
constructor(private _socket: Socket) {
|
||||
this._isDisposed = false;
|
||||
this._chunks = [];
|
||||
|
||||
let totalLength = 0;
|
||||
this._incomingData = new ChunkStream();
|
||||
|
||||
const state = {
|
||||
readHead: true,
|
||||
@@ -61,24 +131,15 @@ export class Protocol implements IDisposable, IMessagePassingProtocol {
|
||||
|
||||
const acceptChunk = (data: Buffer) => {
|
||||
|
||||
this._chunks.push(data);
|
||||
totalLength += data.length;
|
||||
this._incomingData.acceptChunk(data);
|
||||
|
||||
while (totalLength > 0) {
|
||||
while (this._incomingData.byteLength > 0) {
|
||||
|
||||
if (state.readHead) {
|
||||
// expecting header -> read 5bytes for header
|
||||
// information: `bodyIsJson` and `bodyLen`
|
||||
if (totalLength >= Protocol._headerLen) {
|
||||
const all = Buffer.concat(this._chunks);
|
||||
|
||||
state.bodyLen = all.readUInt32BE(0);
|
||||
// expecting header -> read header
|
||||
if (this._incomingData.byteLength >= Protocol._headerLen) {
|
||||
state.bodyLen = this._incomingData.readUInt32BE();
|
||||
state.readHead = false;
|
||||
|
||||
const rest = all.slice(Protocol._headerLen);
|
||||
totalLength = rest.length;
|
||||
this._chunks = [rest];
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -87,15 +148,8 @@ export class Protocol implements IDisposable, IMessagePassingProtocol {
|
||||
if (!state.readHead) {
|
||||
// expecting body -> read bodyLen-bytes for
|
||||
// the actual message or wait for more data
|
||||
if (totalLength >= state.bodyLen) {
|
||||
|
||||
const all = Buffer.concat(this._chunks);
|
||||
const buffer = all.slice(0, state.bodyLen);
|
||||
|
||||
// ensure the getBuffer returns a valid value if invoked from the event listeners
|
||||
const rest = all.slice(state.bodyLen);
|
||||
totalLength = rest.length;
|
||||
this._chunks = [rest];
|
||||
if (this._incomingData.byteLength >= state.bodyLen) {
|
||||
const buffer = this._incomingData.read(state.bodyLen);
|
||||
|
||||
state.bodyLen = -1;
|
||||
state.readHead = true;
|
||||
@@ -113,28 +167,12 @@ export class Protocol implements IDisposable, IMessagePassingProtocol {
|
||||
}
|
||||
};
|
||||
|
||||
const acceptFirstDataChunk = () => {
|
||||
if (firstDataChunk && firstDataChunk.length > 0) {
|
||||
let tmp = firstDataChunk;
|
||||
firstDataChunk = undefined;
|
||||
acceptChunk(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
// Make sure to always handle the firstDataChunk if no more `data` event comes in
|
||||
this._firstChunkTimer = new TimeoutTimer();
|
||||
this._firstChunkTimer.setIfNotSet(() => {
|
||||
acceptFirstDataChunk();
|
||||
}, 0);
|
||||
|
||||
this._socketDataListener = (data: Buffer) => {
|
||||
acceptFirstDataChunk();
|
||||
acceptChunk(data);
|
||||
};
|
||||
_socket.on('data', this._socketDataListener);
|
||||
|
||||
this._socketEndListener = () => {
|
||||
acceptFirstDataChunk();
|
||||
};
|
||||
_socket.on('end', this._socketEndListener);
|
||||
|
||||
@@ -146,7 +184,6 @@ export class Protocol implements IDisposable, IMessagePassingProtocol {
|
||||
|
||||
dispose(): void {
|
||||
this._isDisposed = true;
|
||||
this._firstChunkTimer.dispose();
|
||||
this._socket.removeListener('data', this._socketDataListener);
|
||||
this._socket.removeListener('end', this._socketEndListener);
|
||||
this._socket.removeListener('close', this._socketCloseListener);
|
||||
@@ -156,8 +193,8 @@ export class Protocol implements IDisposable, IMessagePassingProtocol {
|
||||
this._socket.end();
|
||||
}
|
||||
|
||||
getBuffer(): Buffer {
|
||||
return Buffer.concat(this._chunks);
|
||||
readEntireBuffer(): Buffer {
|
||||
return this._incomingData.read(this._incomingData.byteLength);
|
||||
}
|
||||
|
||||
send(buffer: Buffer): void {
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
|
||||
import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter, Relay } from 'vs/base/common/event';
|
||||
import { always, CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async';
|
||||
import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
|
||||
export const enum RequestType {
|
||||
Promise = 100,
|
||||
@@ -51,27 +52,6 @@ enum State {
|
||||
Idle
|
||||
}
|
||||
|
||||
/**
|
||||
* An `IChannel` is an abstraction over a collection of commands.
|
||||
* You can `call` several commands on a channel, each taking at
|
||||
* most one single argument. A `call` always returns a promise
|
||||
* with at most one single return value.
|
||||
*/
|
||||
export interface IChannel {
|
||||
call<T>(command: string, arg?: any, cancellationToken?: CancellationToken): Promise<T>;
|
||||
listen<T>(event: string, arg?: any): Event<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* An `IServerChannel` is the couter part to `IChannel`,
|
||||
* on the server-side. You should implement this interface
|
||||
* if you'd like to handle remote promises or events.
|
||||
*/
|
||||
export interface IServerChannel<TContext = string> {
|
||||
call<T>(ctx: TContext, command: string, arg?: any, cancellationToken?: CancellationToken): Promise<T>;
|
||||
listen<T>(ctx: TContext, event: string, arg?: any): Event<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* An `IChannelServer` hosts a collection of channels. You are
|
||||
* able to register channels onto it, provided a channel name.
|
||||
@@ -448,9 +428,7 @@ export class ChannelClient implements IChannelClient, IDisposable {
|
||||
this.activeRequests.add(disposable);
|
||||
});
|
||||
|
||||
always(result, () => this.activeRequests.delete(disposable));
|
||||
|
||||
return result;
|
||||
return result.finally(() => this.activeRequests.delete(disposable));
|
||||
}
|
||||
|
||||
private requestEvent(channelName: string, name: string, arg?: any): Event<any> {
|
||||
@@ -690,7 +668,7 @@ export class IPCClient<TContext = string> implements IChannelClient, IChannelSer
|
||||
export function getDelayedChannel<T extends IChannel>(promise: Promise<T>): T {
|
||||
return {
|
||||
call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise<T> {
|
||||
return promise.then(c => c.call(command, arg, cancellationToken));
|
||||
return promise.then(c => c.call<T>(command, arg, cancellationToken));
|
||||
},
|
||||
|
||||
listen<T>(event: string, arg?: any): Event<T> {
|
||||
@@ -752,4 +730,4 @@ export class StaticRouter<TContext = string> implements IClientRouter<TContext>
|
||||
await Event.toPromise(hub.onDidChangeConnections);
|
||||
return await this.route(hub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Client } from 'vs/base/parts/ipc/node/ipc.cp';
|
||||
import { always } from 'vs/base/common/async';
|
||||
import { TestServiceClient } from './testService';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
|
||||
@@ -27,7 +26,7 @@ suite('IPC, Child Process', () => {
|
||||
assert.equal(r.outgoing, 'pong');
|
||||
});
|
||||
|
||||
return always(result, () => client.dispose());
|
||||
return result.finally(() => client.dispose());
|
||||
});
|
||||
|
||||
test('events', () => {
|
||||
@@ -49,7 +48,7 @@ suite('IPC, Child Process', () => {
|
||||
const request = service.marco();
|
||||
const result = Promise.all([request, event]);
|
||||
|
||||
return always(result, () => client.dispose());
|
||||
return result.finally(() => client.dispose());
|
||||
});
|
||||
|
||||
test('event dispose', () => {
|
||||
@@ -74,6 +73,6 @@ suite('IPC, Child Process', () => {
|
||||
assert.equal(count, 2);
|
||||
});
|
||||
|
||||
return always(result, () => client.dispose());
|
||||
return result.finally(() => client.dispose());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -85,39 +85,4 @@ suite('IPC, Socket Protocol', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('can devolve to a socket and evolve again without losing data', () => {
|
||||
let resolve: (v: void) => void;
|
||||
let result = new Promise<void>((_resolve, _reject) => {
|
||||
resolve = _resolve;
|
||||
});
|
||||
const sender = new Protocol(stream);
|
||||
const receiver1 = new Protocol(stream);
|
||||
|
||||
assert.equal(stream.listenerCount('data'), 2);
|
||||
assert.equal(stream.listenerCount('end'), 2);
|
||||
|
||||
receiver1.onMessage((msg) => {
|
||||
assert.equal(JSON.parse(msg.toString()).value, 1);
|
||||
|
||||
let buffer = receiver1.getBuffer();
|
||||
receiver1.dispose();
|
||||
|
||||
assert.equal(stream.listenerCount('data'), 1);
|
||||
assert.equal(stream.listenerCount('end'), 1);
|
||||
|
||||
const receiver2 = new Protocol(stream, buffer);
|
||||
receiver2.onMessage((msg) => {
|
||||
assert.equal(JSON.parse(msg.toString()).value, 2);
|
||||
resolve(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
const msg1 = { value: 1 };
|
||||
const msg2 = { value: 2 };
|
||||
sender.send(Buffer.from(JSON.stringify(msg1)));
|
||||
sender.send(Buffer.from(JSON.stringify(msg2)));
|
||||
|
||||
return result;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient, IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
@@ -71,7 +72,7 @@ class TestIPCClient extends IPCClient<string> {
|
||||
|
||||
class TestIPCServer extends IPCServer<string> {
|
||||
|
||||
private onDidClientConnect: Emitter<ClientConnectionEvent>;
|
||||
private readonly onDidClientConnect: Emitter<ClientConnectionEvent>;
|
||||
|
||||
constructor() {
|
||||
const onDidClientConnect = new Emitter<ClientConnectionEvent>();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import * as types from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode } from 'vs/base/parts/quickopen/common/quickOpen';
|
||||
import { Action, IAction, IActionRunner } from 'vs/base/common/actions';
|
||||
import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
|
||||
import { Action, IAction, IActionRunner, IActionItem } from 'vs/base/common/actions';
|
||||
import { compareAnything } from 'vs/base/common/comparers';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
@@ -35,15 +35,15 @@ let IDS = 0;
|
||||
|
||||
export class QuickOpenItemAccessorClass implements IItemAccessor<QuickOpenEntry> {
|
||||
|
||||
getItemLabel(entry: QuickOpenEntry): string {
|
||||
getItemLabel(entry: QuickOpenEntry): string | null {
|
||||
return entry.getLabel();
|
||||
}
|
||||
|
||||
getItemDescription(entry: QuickOpenEntry): string {
|
||||
getItemDescription(entry: QuickOpenEntry): string | null {
|
||||
return entry.getDescription();
|
||||
}
|
||||
|
||||
getItemPath(entry: QuickOpenEntry): string {
|
||||
getItemPath(entry: QuickOpenEntry): string | undefined {
|
||||
const resource = entry.getResource();
|
||||
|
||||
return resource ? resource.fsPath : undefined;
|
||||
@@ -55,8 +55,8 @@ export const QuickOpenItemAccessor = new QuickOpenItemAccessorClass();
|
||||
export class QuickOpenEntry {
|
||||
private id: string;
|
||||
private labelHighlights: IHighlight[];
|
||||
private descriptionHighlights: IHighlight[];
|
||||
private detailHighlights: IHighlight[];
|
||||
private descriptionHighlights?: IHighlight[];
|
||||
private detailHighlights?: IHighlight[];
|
||||
private hidden: boolean;
|
||||
|
||||
constructor(highlights: IHighlight[] = []) {
|
||||
@@ -75,14 +75,14 @@ export class QuickOpenEntry {
|
||||
/**
|
||||
* The label of the entry to identify it from others in the list
|
||||
*/
|
||||
getLabel(): string {
|
||||
getLabel(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The options for the label to use for this entry
|
||||
*/
|
||||
getLabelOptions(): IIconLabelValueOptions {
|
||||
getLabelOptions(): IIconLabelValueOptions | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -97,42 +97,42 @@ export class QuickOpenEntry {
|
||||
/**
|
||||
* Detail information about the entry that is optional and can be shown below the label
|
||||
*/
|
||||
getDetail(): string {
|
||||
getDetail(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The icon of the entry to identify it from others in the list
|
||||
*/
|
||||
getIcon(): string {
|
||||
getIcon(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A secondary description that is optional and can be shown right to the label
|
||||
*/
|
||||
getDescription(): string {
|
||||
getDescription(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tooltip to show when hovering over the entry.
|
||||
*/
|
||||
getTooltip(): string {
|
||||
getTooltip(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tooltip to show when hovering over the description portion of the entry.
|
||||
*/
|
||||
getDescriptionTooltip(): string {
|
||||
getDescriptionTooltip(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An optional keybinding to show for an entry.
|
||||
*/
|
||||
getKeybinding(): ResolvedKeybinding {
|
||||
getKeybinding(): ResolvedKeybinding | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ export class QuickOpenEntry {
|
||||
* A resource for this entry. Resource URIs can be used to compare different kinds of entries and group
|
||||
* them together.
|
||||
*/
|
||||
getResource(): URI {
|
||||
getResource(): URI | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ export class QuickOpenEntry {
|
||||
/**
|
||||
* Allows to return highlight ranges that should show up for the entry label and description.
|
||||
*/
|
||||
getHighlights(): [IHighlight[] /* Label */, IHighlight[] /* Description */, IHighlight[] /* Detail */] {
|
||||
getHighlights(): [IHighlight[] /* Label */, IHighlight[] | undefined /* Description */, IHighlight[] | undefined /* Detail */] {
|
||||
return [this.labelHighlights, this.descriptionHighlights, this.detailHighlights];
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ export class QuickOpenEntry {
|
||||
*
|
||||
* The context parameter provides additional context information how the run was triggered.
|
||||
*/
|
||||
run(mode: Mode, context: IContext): boolean {
|
||||
run(mode: Mode, context: IEntryRunContext): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -195,9 +195,9 @@ export class QuickOpenEntry {
|
||||
}
|
||||
|
||||
export class QuickOpenEntryGroup extends QuickOpenEntry {
|
||||
private entry: QuickOpenEntry;
|
||||
private groupLabel: string;
|
||||
private withBorder: boolean;
|
||||
private entry?: QuickOpenEntry;
|
||||
private groupLabel?: string;
|
||||
private withBorder?: boolean;
|
||||
|
||||
constructor(entry?: QuickOpenEntry, groupLabel?: string, withBorder?: boolean) {
|
||||
super();
|
||||
@@ -210,11 +210,11 @@ export class QuickOpenEntryGroup extends QuickOpenEntry {
|
||||
/**
|
||||
* The label of the group or null if none.
|
||||
*/
|
||||
getGroupLabel(): string {
|
||||
getGroupLabel(): string | undefined {
|
||||
return this.groupLabel;
|
||||
}
|
||||
|
||||
setGroupLabel(groupLabel: string): void {
|
||||
setGroupLabel(groupLabel: string | undefined): void {
|
||||
this.groupLabel = groupLabel;
|
||||
}
|
||||
|
||||
@@ -222,18 +222,18 @@ export class QuickOpenEntryGroup extends QuickOpenEntry {
|
||||
* Whether to show a border on top of the group entry or not.
|
||||
*/
|
||||
showBorder(): boolean {
|
||||
return this.withBorder;
|
||||
return !!this.withBorder;
|
||||
}
|
||||
|
||||
setShowBorder(showBorder: boolean): void {
|
||||
this.withBorder = showBorder;
|
||||
}
|
||||
|
||||
getLabel(): string {
|
||||
getLabel(): string | null {
|
||||
return this.entry ? this.entry.getLabel() : super.getLabel();
|
||||
}
|
||||
|
||||
getLabelOptions(): IIconLabelValueOptions {
|
||||
getLabelOptions(): IIconLabelValueOptions | null {
|
||||
return this.entry ? this.entry.getLabelOptions() : super.getLabelOptions();
|
||||
}
|
||||
|
||||
@@ -241,27 +241,27 @@ export class QuickOpenEntryGroup extends QuickOpenEntry {
|
||||
return this.entry ? this.entry.getAriaLabel() : super.getAriaLabel();
|
||||
}
|
||||
|
||||
getDetail(): string {
|
||||
getDetail(): string | null {
|
||||
return this.entry ? this.entry.getDetail() : super.getDetail();
|
||||
}
|
||||
|
||||
getResource(): URI {
|
||||
getResource(): URI | null {
|
||||
return this.entry ? this.entry.getResource() : super.getResource();
|
||||
}
|
||||
|
||||
getIcon(): string {
|
||||
getIcon(): string | null {
|
||||
return this.entry ? this.entry.getIcon() : super.getIcon();
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
getDescription(): string | null {
|
||||
return this.entry ? this.entry.getDescription() : super.getDescription();
|
||||
}
|
||||
|
||||
getEntry(): QuickOpenEntry {
|
||||
getEntry(): QuickOpenEntry | undefined {
|
||||
return this.entry;
|
||||
}
|
||||
|
||||
getHighlights(): [IHighlight[], IHighlight[], IHighlight[]] {
|
||||
getHighlights(): [IHighlight[], IHighlight[] | undefined, IHighlight[] | undefined] {
|
||||
return this.entry ? this.entry.getHighlights() : super.getHighlights();
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry {
|
||||
this.entry ? this.entry.setHidden(hidden) : super.setHidden(hidden);
|
||||
}
|
||||
|
||||
run(mode: Mode, context: IContext): boolean {
|
||||
run(mode: Mode, context: IEntryRunContext): boolean {
|
||||
return this.entry ? this.entry.run(mode, context) : super.run(mode, context);
|
||||
}
|
||||
}
|
||||
@@ -288,7 +288,7 @@ class NoActionProvider implements IActionProvider {
|
||||
return false;
|
||||
}
|
||||
|
||||
getActions(tree: ITree, element: any): IAction[] {
|
||||
getActions(tree: ITree, element: any): IAction[] | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -296,11 +296,11 @@ class NoActionProvider implements IActionProvider {
|
||||
return false;
|
||||
}
|
||||
|
||||
getSecondaryActions(tree: ITree, element: any): IAction[] {
|
||||
getSecondaryActions(tree: ITree, element: any): IAction[] | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
getActionItem(tree: ITree, element: any, action: Action) {
|
||||
getActionItem(tree: ITree, element: any, action: Action): IActionItem | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -316,7 +316,7 @@ export interface IQuickOpenEntryTemplateData {
|
||||
}
|
||||
|
||||
export interface IQuickOpenEntryGroupTemplateData extends IQuickOpenEntryTemplateData {
|
||||
group: HTMLDivElement;
|
||||
group?: HTMLDivElement;
|
||||
}
|
||||
|
||||
const templateEntry = 'quickOpenEntry';
|
||||
@@ -325,9 +325,9 @@ const templateEntryGroup = 'quickOpenEntryGroup';
|
||||
class Renderer implements IRenderer<QuickOpenEntry> {
|
||||
|
||||
private actionProvider: IActionProvider;
|
||||
private actionRunner: IActionRunner;
|
||||
private actionRunner?: IActionRunner;
|
||||
|
||||
constructor(actionProvider: IActionProvider = new NoActionProvider(), actionRunner: IActionRunner | null = null) {
|
||||
constructor(actionProvider: IActionProvider = new NoActionProvider(), actionRunner?: IActionRunner) {
|
||||
this.actionProvider = actionProvider;
|
||||
this.actionRunner = actionRunner;
|
||||
}
|
||||
@@ -356,7 +356,7 @@ class Renderer implements IRenderer<QuickOpenEntry> {
|
||||
// Entry
|
||||
const row1 = DOM.$('.quick-open-row');
|
||||
const row2 = DOM.$('.quick-open-row');
|
||||
const entry = DOM.$('.quick-open-entry', null, row1, row2);
|
||||
const entry = DOM.$('.quick-open-entry', undefined, row1, row2);
|
||||
entryContainer.appendChild(entry);
|
||||
|
||||
// Icon
|
||||
@@ -379,7 +379,7 @@ class Renderer implements IRenderer<QuickOpenEntry> {
|
||||
const detail = new HighlightedLabel(detailContainer, true);
|
||||
|
||||
// Entry Group
|
||||
let group: HTMLDivElement;
|
||||
let group: HTMLDivElement | undefined;
|
||||
if (templateId === templateEntryGroup) {
|
||||
group = document.createElement('div');
|
||||
DOM.addClass(group, 'results-group');
|
||||
@@ -442,7 +442,9 @@ class Renderer implements IRenderer<QuickOpenEntry> {
|
||||
// Border
|
||||
if (group.showBorder()) {
|
||||
DOM.addClass(groupData.container, 'results-group-separator');
|
||||
groupData.container.style.borderTopColor = styles.pickerGroupBorder.toString();
|
||||
if (styles.pickerGroupBorder) {
|
||||
groupData.container.style.borderTopColor = styles.pickerGroupBorder.toString();
|
||||
}
|
||||
} else {
|
||||
DOM.removeClass(groupData.container, 'results-group-separator');
|
||||
groupData.container.style.borderTopColor = null;
|
||||
@@ -450,8 +452,12 @@ class Renderer implements IRenderer<QuickOpenEntry> {
|
||||
|
||||
// Group Label
|
||||
const groupLabel = group.getGroupLabel() || '';
|
||||
groupData.group.textContent = groupLabel;
|
||||
groupData.group.style.color = styles.pickerGroupForeground.toString();
|
||||
if (groupData.group) {
|
||||
groupData.group.textContent = groupLabel;
|
||||
if (styles.pickerGroupForeground) {
|
||||
groupData.group.style.color = styles.pickerGroupForeground.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normal Entry
|
||||
@@ -465,31 +471,31 @@ class Renderer implements IRenderer<QuickOpenEntry> {
|
||||
// Label
|
||||
const options: IIconLabelValueOptions = entry.getLabelOptions() || Object.create(null);
|
||||
options.matches = labelHighlights || [];
|
||||
options.title = entry.getTooltip();
|
||||
options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription(); // tooltip over description because it could overflow
|
||||
options.title = types.withNullAsUndefined(entry.getTooltip());
|
||||
options.descriptionTitle = entry.getDescriptionTooltip() || types.withNullAsUndefined(entry.getDescription()); // tooltip over description because it could overflow
|
||||
options.descriptionMatches = descriptionHighlights || [];
|
||||
data.label.setLabel(entry.getLabel(), entry.getDescription(), options);
|
||||
data.label.setLabel(types.withNullAsUndefined(entry.getLabel()), types.withNullAsUndefined(entry.getDescription()), options);
|
||||
|
||||
// Meta
|
||||
data.detail.set(entry.getDetail(), detailHighlights);
|
||||
data.detail.set(types.withNullAsUndefined(entry.getDetail()), detailHighlights);
|
||||
|
||||
// Keybinding
|
||||
data.keybinding.set(entry.getKeybinding());
|
||||
data.keybinding.set(entry.getKeybinding()!);
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateId: string, templateData: IQuickOpenEntryGroupTemplateData): void {
|
||||
const data = templateData as IQuickOpenEntryGroupTemplateData;
|
||||
data.actionBar.dispose();
|
||||
data.actionBar = null;
|
||||
data.container = null;
|
||||
data.entry = null;
|
||||
data.keybinding = null;
|
||||
data.detail = null;
|
||||
data.group = null;
|
||||
data.icon = null;
|
||||
data.actionBar = null!;
|
||||
data.container = null!;
|
||||
data.entry = null!;
|
||||
data.keybinding = null!;
|
||||
data.detail = null!;
|
||||
data.group = null!;
|
||||
data.icon = null!;
|
||||
data.label.dispose();
|
||||
data.label = null;
|
||||
data.label = null!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,7 +568,7 @@ export class QuickOpenModel implements
|
||||
return entry.getId();
|
||||
}
|
||||
|
||||
getLabel(entry: QuickOpenEntry): string {
|
||||
getLabel(entry: QuickOpenEntry): string | null {
|
||||
return entry.getLabel();
|
||||
}
|
||||
|
||||
@@ -579,7 +585,7 @@ export class QuickOpenModel implements
|
||||
return !entry.isHidden();
|
||||
}
|
||||
|
||||
run(entry: QuickOpenEntry, mode: Mode, context: IContext): boolean {
|
||||
run(entry: QuickOpenEntry, mode: Mode, context: IEntryRunContext): boolean {
|
||||
return entry.run(mode, context);
|
||||
}
|
||||
}
|
||||
@@ -603,8 +609,8 @@ export function compareEntries(elementA: QuickOpenEntry, elementB: QuickOpenEntr
|
||||
}
|
||||
|
||||
// Fallback to the full path if labels are identical and we have associated resources
|
||||
let nameA = elementA.getLabel();
|
||||
let nameB = elementB.getLabel();
|
||||
let nameA = elementA.getLabel()!;
|
||||
let nameB = elementB.getLabel()!;
|
||||
if (nameA === nameB) {
|
||||
const resourceA = elementA.getResource();
|
||||
const resourceB = elementB.getResource();
|
||||
|
||||
@@ -24,7 +24,7 @@ export class DataSource implements IDataSource {
|
||||
|
||||
getId(tree: ITree, element: any): string {
|
||||
if (!element) {
|
||||
return null;
|
||||
return null!;
|
||||
}
|
||||
|
||||
const model = this.modelProvider.getModel();
|
||||
@@ -33,7 +33,7 @@ export class DataSource implements IDataSource {
|
||||
|
||||
hasChildren(tree: ITree, element: any): boolean {
|
||||
const model = this.modelProvider.getModel();
|
||||
return model && model === element && model.entries.length > 0;
|
||||
return !!(model && model === element && model.entries.length > 0);
|
||||
}
|
||||
|
||||
getChildren(tree: ITree, element: any): Promise<any[]> {
|
||||
@@ -49,10 +49,10 @@ export class DataSource implements IDataSource {
|
||||
export class AccessibilityProvider implements IAccessibilityProvider {
|
||||
constructor(private modelProvider: IModelProvider) { }
|
||||
|
||||
getAriaLabel(tree: ITree, element: any): string {
|
||||
getAriaLabel(tree: ITree, element: any): string | null {
|
||||
const model = this.modelProvider.getModel();
|
||||
|
||||
return model.accessibilityProvider && model.accessibilityProvider.getAriaLabel(element);
|
||||
return model.accessibilityProvider ? model.accessibilityProvider.getAriaLabel(element) : null;
|
||||
}
|
||||
|
||||
getPosInSet(tree: ITree, element: any): string {
|
||||
|
||||
@@ -36,7 +36,7 @@ export interface IQuickOpenCallbacks {
|
||||
export interface IQuickOpenOptions extends IQuickOpenStyles {
|
||||
minItemsToShow?: number;
|
||||
maxItemsToShow?: number;
|
||||
inputPlaceHolder: string;
|
||||
inputPlaceHolder?: string;
|
||||
inputAriaLabel?: string;
|
||||
actionProvider?: IActionProvider;
|
||||
keyboardSupport?: boolean;
|
||||
@@ -110,12 +110,12 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
private visible: boolean;
|
||||
private isLoosingFocus: boolean;
|
||||
private callbacks: IQuickOpenCallbacks;
|
||||
private quickNavigateConfiguration: IQuickNavigateConfiguration;
|
||||
private quickNavigateConfiguration: IQuickNavigateConfiguration | undefined;
|
||||
private container: HTMLElement;
|
||||
private treeElement: HTMLElement;
|
||||
private inputElement: HTMLElement;
|
||||
private layoutDimensions: DOM.Dimension;
|
||||
private model: IModel<any>;
|
||||
private model: IModel<any> | null;
|
||||
private inputChangingTimeoutHandle: any;
|
||||
private styles: IQuickOpenStyles;
|
||||
private renderer: Renderer;
|
||||
@@ -137,7 +137,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
}
|
||||
|
||||
getModel(): IModel<any> {
|
||||
return this.model;
|
||||
return this.model!;
|
||||
}
|
||||
|
||||
setCallbacks(callbacks: IQuickOpenCallbacks): void {
|
||||
@@ -181,7 +181,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
DOM.addClass(this.inputContainer, 'quick-open-input');
|
||||
this.element.appendChild(this.inputContainer);
|
||||
|
||||
this.inputBox = this._register(new InputBox(this.inputContainer, null, {
|
||||
this.inputBox = this._register(new InputBox(this.inputContainer, undefined, {
|
||||
placeholder: this.options.inputPlaceHolder || '',
|
||||
ariaLabel: DEFAULT_INPUT_ARIA_LABEL,
|
||||
inputBackground: this.styles.inputBackground,
|
||||
@@ -538,10 +538,15 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
}
|
||||
|
||||
// ARIA
|
||||
this.inputElement.setAttribute('aria-activedescendant', this.treeElement.getAttribute('aria-activedescendant'));
|
||||
const arivaActiveDescendant = this.treeElement.getAttribute('aria-activedescendant');
|
||||
if (arivaActiveDescendant) {
|
||||
this.inputElement.setAttribute('aria-activedescendant', arivaActiveDescendant);
|
||||
} else {
|
||||
this.inputElement.removeAttribute('aria-activedescendant');
|
||||
}
|
||||
|
||||
const context: IEntryRunContext = { event: event, keymods: this.extractKeyMods(event), quickNavigateConfiguration: this.quickNavigateConfiguration };
|
||||
this.model.runner.run(value, Mode.PREVIEW, context);
|
||||
this.model!.runner.run(value, Mode.PREVIEW, context);
|
||||
}
|
||||
|
||||
private elementSelected(value: any, event?: any, preferredMode?: Mode): void {
|
||||
@@ -553,7 +558,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
|
||||
const context: IEntryRunContext = { event, keymods: this.extractKeyMods(event), quickNavigateConfiguration: this.quickNavigateConfiguration };
|
||||
|
||||
hide = this.model.runner.run(value, mode, context);
|
||||
hide = this.model!.runner.run(value, mode, context);
|
||||
}
|
||||
|
||||
// Hide if command was run successfully
|
||||
@@ -603,7 +608,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
if (types.isString(param)) {
|
||||
this.doShowWithPrefix(param);
|
||||
} else {
|
||||
if (options.value) {
|
||||
if (options && options.value) {
|
||||
this.restoreLastInput(options.value);
|
||||
}
|
||||
this.doShowWithInput(param, options && options.autoFocus ? options.autoFocus : {});
|
||||
@@ -634,7 +639,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
this.setInput(input, autoFocus);
|
||||
}
|
||||
|
||||
private setInputAndLayout(input: IModel<any>, autoFocus: IAutoFocus): void {
|
||||
private setInputAndLayout(input: IModel<any>, autoFocus?: IAutoFocus): void {
|
||||
this.treeContainer.style.height = `${this.getHeight(input)}px`;
|
||||
|
||||
this.tree.setInput(null).then(() => {
|
||||
@@ -676,7 +681,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
const prefix = autoFocus.autoFocusPrefixMatch;
|
||||
const lowerCasePrefix = prefix.toLowerCase();
|
||||
for (const entry of entries) {
|
||||
const label = input.dataSource.getLabel(entry);
|
||||
const label = input.dataSource.getLabel(entry) || '';
|
||||
|
||||
if (!caseSensitiveMatch && label.indexOf(prefix) === 0) {
|
||||
caseSensitiveMatch = entry;
|
||||
@@ -747,13 +752,13 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
// Indicate entries to tree
|
||||
this.tree.layout();
|
||||
|
||||
const entries = input ? input.entries.filter(e => this.isElementVisible(input, e)) : [];
|
||||
const entries = input ? input.entries!.filter(e => this.isElementVisible(input!, e)) : [];
|
||||
this.updateResultCount(entries.length);
|
||||
|
||||
// Handle auto focus
|
||||
if (autoFocus) {
|
||||
if (entries.length) {
|
||||
this.autoFocus(input, entries, autoFocus);
|
||||
this.autoFocus(input!, entries, autoFocus);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -770,7 +775,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
|
||||
let height = 0;
|
||||
|
||||
let preferredItemsHeight: number;
|
||||
let preferredItemsHeight: number | undefined;
|
||||
if (this.layoutDimensions && this.layoutDimensions.height) {
|
||||
preferredItemsHeight = (this.layoutDimensions.height - 50 /* subtract height of input field (30px) and some spacing (drop shadow) to fit */) * 0.4 /* max 40% of screen */;
|
||||
}
|
||||
@@ -834,12 +839,12 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
}
|
||||
|
||||
if (this.callbacks.onHide) {
|
||||
this.callbacks.onHide(reason);
|
||||
this.callbacks.onHide(reason!);
|
||||
}
|
||||
}
|
||||
|
||||
getQuickNavigateConfiguration(): IQuickNavigateConfiguration {
|
||||
return this.quickNavigateConfiguration;
|
||||
return this.quickNavigateConfiguration!;
|
||||
}
|
||||
|
||||
setPlaceHolder(placeHolder: string): void {
|
||||
@@ -868,7 +873,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
}
|
||||
}
|
||||
|
||||
setInput(input: IModel<any>, autoFocus: IAutoFocus, ariaLabel?: string): void {
|
||||
setInput(input: IModel<any>, autoFocus?: IAutoFocus, ariaLabel?: string): void {
|
||||
if (!this.isVisible()) {
|
||||
return;
|
||||
}
|
||||
@@ -940,7 +945,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider {
|
||||
return this.inputBox;
|
||||
}
|
||||
|
||||
setExtraClass(clazz: string): void {
|
||||
setExtraClass(clazz: string | null): void {
|
||||
const previousClass = this.element.getAttribute('quick-open-extra-class');
|
||||
if (previousClass) {
|
||||
DOM.removeClasses(this.element, previousClass);
|
||||
|
||||
@@ -49,7 +49,7 @@ export const enum Mode {
|
||||
export interface IEntryRunContext {
|
||||
event: any;
|
||||
keymods: IKeyMods;
|
||||
quickNavigateConfiguration: IQuickNavigateConfiguration;
|
||||
quickNavigateConfiguration: IQuickNavigateConfiguration | undefined;
|
||||
}
|
||||
|
||||
export interface IKeyMods {
|
||||
@@ -59,7 +59,7 @@ export interface IKeyMods {
|
||||
|
||||
export interface IDataSource<T> {
|
||||
getId(entry: T): string;
|
||||
getLabel(entry: T): string;
|
||||
getLabel(entry: T): string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { compareAnything } from 'vs/base/common/comparers';
|
||||
import { matchesPrefix, IMatch, matchesCamelCase, isUpper } from 'vs/base/common/filters';
|
||||
import { nativeSep } from 'vs/base/common/paths';
|
||||
import { sep } from 'vs/base/common/path';
|
||||
import { isWindows, isLinux } from 'vs/base/common/platform';
|
||||
import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
@@ -156,7 +156,7 @@ function doScore(query: string, queryLower: string, queryLength: number, target:
|
||||
return [scores[queryLength * targetLength - 1], positions.reverse()];
|
||||
}
|
||||
|
||||
function computeCharScore(queryCharAtIndex, queryLowerCharAtIndex, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number {
|
||||
function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: string, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number {
|
||||
let score = 0;
|
||||
|
||||
if (queryLowerCharAtIndex !== targetLower[targetIndex]) {
|
||||
@@ -285,17 +285,17 @@ export interface IItemAccessor<T> {
|
||||
/**
|
||||
* Just the label of the item to score on.
|
||||
*/
|
||||
getItemLabel(item: T): string;
|
||||
getItemLabel(item: T): string | null;
|
||||
|
||||
/**
|
||||
* The optional description of the item to score on. Can be null.
|
||||
*/
|
||||
getItemDescription(item: T): string;
|
||||
getItemDescription(item: T): string | null;
|
||||
|
||||
/**
|
||||
* If the item is a file, the path of the file to score on. Can be null.
|
||||
*/
|
||||
getItemPath(file: T): string;
|
||||
getItemPath(file: T): string | undefined;
|
||||
}
|
||||
|
||||
const PATH_IDENTITY_SCORE = 1 << 18;
|
||||
@@ -320,11 +320,11 @@ export function prepareQuery(original: string): IPreparedQuery {
|
||||
|
||||
let value = stripWildcards(original).replace(/\s/g, ''); // get rid of all wildcards and whitespace
|
||||
if (isWindows) {
|
||||
value = value.replace(/\//g, nativeSep); // Help Windows users to search for paths when using slash
|
||||
value = value.replace(/\//g, sep); // Help Windows users to search for paths when using slash
|
||||
}
|
||||
|
||||
const lowercase = value.toLowerCase();
|
||||
const containsPathSeparator = value.indexOf(nativeSep) >= 0;
|
||||
const containsPathSeparator = value.indexOf(sep) >= 0;
|
||||
|
||||
return { original, value, lowercase, containsPathSeparator };
|
||||
}
|
||||
@@ -376,10 +376,10 @@ function createMatches(offsets: undefined | number[]): IMatch[] {
|
||||
return ret;
|
||||
}
|
||||
|
||||
function doScoreItem(label: string, description: string, path: string, query: IPreparedQuery, fuzzy: boolean): IItemScore {
|
||||
function doScoreItem(label: string, description: string | null, path: string | undefined, query: IPreparedQuery, fuzzy: boolean): IItemScore {
|
||||
|
||||
// 1.) treat identity matches on full path highest
|
||||
if (path && isLinux ? query.original === path : equalsIgnoreCase(query.original, path)) {
|
||||
if (path && (isLinux ? query.original === path : equalsIgnoreCase(query.original, path))) {
|
||||
return { score: PATH_IDENTITY_SCORE, labelMatch: [{ start: 0, end: label.length }], descriptionMatch: description ? [{ start: 0, end: description.length }] : undefined };
|
||||
}
|
||||
|
||||
@@ -410,7 +410,7 @@ function doScoreItem(label: string, description: string, path: string, query: IP
|
||||
if (description) {
|
||||
let descriptionPrefix = description;
|
||||
if (!!path) {
|
||||
descriptionPrefix = `${description}${nativeSep}`; // assume this is a file path
|
||||
descriptionPrefix = `${description}${sep}`; // assume this is a file path
|
||||
}
|
||||
|
||||
const descriptionPrefixLength = descriptionPrefix.length;
|
||||
@@ -469,8 +469,8 @@ export function compareItemsByScore<T>(itemA: T, itemB: T, query: IPreparedQuery
|
||||
return scoreA === LABEL_PREFIX_SCORE ? -1 : 1;
|
||||
}
|
||||
|
||||
const labelA = accessor.getItemLabel(itemA);
|
||||
const labelB = accessor.getItemLabel(itemB);
|
||||
const labelA = accessor.getItemLabel(itemA) || '';
|
||||
const labelB = accessor.getItemLabel(itemB) || '';
|
||||
|
||||
// prefer shorter names when both match on label prefix
|
||||
if (labelA.length !== labelB.length) {
|
||||
@@ -484,8 +484,8 @@ export function compareItemsByScore<T>(itemA: T, itemB: T, query: IPreparedQuery
|
||||
return scoreA === LABEL_CAMELCASE_SCORE ? -1 : 1;
|
||||
}
|
||||
|
||||
const labelA = accessor.getItemLabel(itemA);
|
||||
const labelB = accessor.getItemLabel(itemB);
|
||||
const labelA = accessor.getItemLabel(itemA) || '';
|
||||
const labelB = accessor.getItemLabel(itemB) || '';
|
||||
|
||||
// prefer more compact camel case matches over longer
|
||||
const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch);
|
||||
@@ -592,8 +592,8 @@ function compareByMatchLength(matchesA?: IMatch[], matchesB?: IMatch[]): number
|
||||
export function fallbackCompare<T>(itemA: T, itemB: T, query: IPreparedQuery, accessor: IItemAccessor<T>): number {
|
||||
|
||||
// check for label + description length and prefer shorter
|
||||
const labelA = accessor.getItemLabel(itemA);
|
||||
const labelB = accessor.getItemLabel(itemB);
|
||||
const labelA = accessor.getItemLabel(itemA) || '';
|
||||
const labelB = accessor.getItemLabel(itemB) || '';
|
||||
|
||||
const descriptionA = accessor.getItemDescription(itemA);
|
||||
const descriptionB = accessor.getItemDescription(itemB);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as assert from 'assert';
|
||||
import * as scorer from 'vs/base/parts/quickopen/common/quickOpenScorer';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { basename, dirname, nativeSep } from 'vs/base/common/paths';
|
||||
import { basename, dirname, sep } from 'vs/base/common/path';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
|
||||
class ResourceAccessorClass implements scorer.IItemAccessor<URI> {
|
||||
@@ -797,9 +797,9 @@ suite('Quick Open Scorer', () => {
|
||||
});
|
||||
|
||||
test('compareFilesByScore - avoid match scattering (bug #12095)', function () {
|
||||
const resourceA = URI.file('src/vs/workbench/parts/files/common/explorerViewModel.ts');
|
||||
const resourceB = URI.file('src/vs/workbench/parts/files/browser/views/explorerView.ts');
|
||||
const resourceC = URI.file('src/vs/workbench/parts/files/browser/views/explorerViewer.ts');
|
||||
const resourceA = URI.file('src/vs/workbench/contrib/files/common/explorerViewModel.ts');
|
||||
const resourceB = URI.file('src/vs/workbench/contrib/files/browser/views/explorerView.ts');
|
||||
const resourceC = URI.file('src/vs/workbench/contrib/files/browser/views/explorerViewer.ts');
|
||||
|
||||
let query = 'filesexplorerview.ts';
|
||||
|
||||
@@ -815,6 +815,6 @@ suite('Quick Open Scorer', () => {
|
||||
assert.equal(scorer.prepareQuery('model Tester.ts').value, 'modelTester.ts');
|
||||
assert.equal(scorer.prepareQuery('Model Tester.ts').lowercase, 'modeltester.ts');
|
||||
assert.equal(scorer.prepareQuery('ModelTester.ts').containsPathSeparator, false);
|
||||
assert.equal(scorer.prepareQuery('Model' + nativeSep + 'Tester.ts').containsPathSeparator, true);
|
||||
assert.equal(scorer.prepareQuery('Model' + sep + 'Tester.ts').containsPathSeparator, true);
|
||||
});
|
||||
});
|
||||
@@ -724,12 +724,12 @@ export interface IActionProvider {
|
||||
/**
|
||||
* Returns whether or not the element has actions. These show up in place right to the element in the tree.
|
||||
*/
|
||||
hasActions(tree: ITree, element: any): boolean;
|
||||
hasActions(tree: ITree | null, element: any): boolean;
|
||||
|
||||
/**
|
||||
* Returns a promise of an array with the actions of the element that should show up in place right to the element in the tree.
|
||||
*/
|
||||
getActions(tree: ITree, element: any): IAction[];
|
||||
getActions(tree: ITree | null, element: any): IAction[] | null;
|
||||
|
||||
/**
|
||||
* Returns whether or not the element has secondary actions. These show up once the user has expanded the element's action bar.
|
||||
@@ -739,7 +739,7 @@ export interface IActionProvider {
|
||||
/**
|
||||
* Returns a promise of an array with the secondary actions of the element that should show up once the user has expanded the element's action bar.
|
||||
*/
|
||||
getSecondaryActions(tree: ITree, element: any): IAction[];
|
||||
getSecondaryActions(tree: ITree, element: any): IAction[] | null;
|
||||
|
||||
/**
|
||||
* Returns an action item to render an action.
|
||||
|
||||
@@ -13,7 +13,7 @@ import * as mouse from 'vs/base/browser/mouseEvent';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import * as _ from 'vs/base/parts/tree/browser/tree';
|
||||
import { IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
import { KeyCode, KeyMod, Keybinding, SimpleKeybinding, createSimpleKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { KeyCode, KeyMod, Keybinding, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes';
|
||||
|
||||
export interface IKeyBindingCallback {
|
||||
(tree: _.ITree, event: IKeyboardEvent): void;
|
||||
@@ -49,7 +49,7 @@ export interface IControllerOptions {
|
||||
}
|
||||
|
||||
interface IKeybindingDispatcherItem {
|
||||
keybinding: Keybinding;
|
||||
keybinding: Keybinding | null;
|
||||
callback: IKeyBindingCallback;
|
||||
}
|
||||
|
||||
@@ -62,10 +62,12 @@ export class KeybindingDispatcher {
|
||||
}
|
||||
|
||||
public has(keybinding: KeyCode): boolean {
|
||||
let target = createSimpleKeybinding(keybinding, platform.OS);
|
||||
for (const a of this._arr) {
|
||||
if (target.equals(a.keybinding)) {
|
||||
return true;
|
||||
let target = createKeybinding(keybinding, platform.OS);
|
||||
if (target !== null) {
|
||||
for (const a of this._arr) {
|
||||
if (target.equals(a.keybinding)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -73,7 +75,7 @@ export class KeybindingDispatcher {
|
||||
|
||||
public set(keybinding: number, callback: IKeyBindingCallback) {
|
||||
this._arr.push({
|
||||
keybinding: createSimpleKeybinding(keybinding, platform.OS),
|
||||
keybinding: createKeybinding(keybinding, platform.OS),
|
||||
callback: callback
|
||||
});
|
||||
}
|
||||
@@ -82,7 +84,7 @@ export class KeybindingDispatcher {
|
||||
// Loop from the last to the first to handle overwrites
|
||||
for (let i = this._arr.length - 1; i >= 0; i--) {
|
||||
let item = this._arr[i];
|
||||
if (keybinding.equals(item.keybinding)) {
|
||||
if (keybinding.toChord().equals(item.keybinding)) {
|
||||
return item.callback;
|
||||
}
|
||||
}
|
||||
@@ -556,7 +558,7 @@ export class DefaultTreestyler implements _.ITreeStyler {
|
||||
export class CollapseAllAction extends Action {
|
||||
|
||||
constructor(private viewer: _.ITree, enabled: boolean) {
|
||||
super('vs.tree.collapse', nls.localize('collapse', "Collapse"), 'monaco-tree-action collapse-all', enabled);
|
||||
super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'monaco-tree-action collapse-all', enabled);
|
||||
}
|
||||
|
||||
public run(context?: any): Promise<any> {
|
||||
|
||||
@@ -1286,8 +1286,8 @@ export class TreeView extends HeightMap {
|
||||
element = this.model!.getInput();
|
||||
position = DOM.getDomNodePagePosition(this.inputItem.element);
|
||||
} else {
|
||||
let id = this.context.dataSource.getId(this.context.tree, element);
|
||||
let viewItem = this.items[id];
|
||||
const id = this.context.dataSource.getId(this.context.tree, element);
|
||||
const viewItem = this.items[id!];
|
||||
position = DOM.getDomNodePagePosition(viewItem.element);
|
||||
}
|
||||
|
||||
@@ -1422,6 +1422,8 @@ export class TreeView extends HeightMap {
|
||||
}
|
||||
|
||||
private onDragOver(e: DragEvent): boolean {
|
||||
e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
|
||||
|
||||
let event = new Mouse.DragMouseEvent(e);
|
||||
|
||||
let viewItem = this.getItemAround(event.target);
|
||||
|
||||
Reference in New Issue
Block a user