Merge from vscode 31e03b8ffbb218a87e3941f2b63a249f061fe0e4 (#4986)

This commit is contained in:
Anthony Dresser
2019-04-10 16:29:23 -07:00
committed by GitHub
parent 18c54f41bd
commit 8315dacda4
320 changed files with 5540 additions and 3822 deletions

View File

@@ -1,59 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { isWindows } from 'vs/base/common/platform';
import { Emitter, Event } from 'vs/base/common/event';
import { IWindowService } from 'vs/platform/windows/common/windows';
export class AccessibilityService implements IAccessibilityService {
_serviceBrand: any;
private _accessibilitySupport = AccessibilitySupport.Unknown;
private readonly _onDidChangeAccessibilitySupport = new Emitter<void>();
readonly onDidChangeAccessibilitySupport: Event<void> = this._onDidChangeAccessibilitySupport.event;
constructor(
@IWindowService private readonly windowService: IWindowService
) { }
alwaysUnderlineAccessKeys(): Promise<boolean> {
if (!isWindows) {
return Promise.resolve(false);
}
return new Promise<boolean>(async (resolve) => {
const Registry = await import('vscode-windows-registry');
let value;
try {
value = Registry.GetStringRegKey('HKEY_CURRENT_USER', 'Control Panel\\Accessibility\\Keyboard Preference', 'On');
} catch {
resolve(false);
}
resolve(value === '1');
});
}
setAccessibilitySupport(accessibilitySupport: AccessibilitySupport): void {
if (this._accessibilitySupport === accessibilitySupport) {
return;
}
this._accessibilitySupport = accessibilitySupport;
this._onDidChangeAccessibilitySupport.fire();
}
getAccessibilitySupport(): AccessibilitySupport {
if (this._accessibilitySupport === AccessibilitySupport.Unknown) {
const config = this.windowService.getConfiguration();
this._accessibilitySupport = (config && config.accessibilitySupport) ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled;
}
return this._accessibilitySupport;
}
}

View File

@@ -9,9 +9,10 @@ import * as arrays from 'vs/base/common/arrays';
import * as types from 'vs/base/common/types';
import * as objects from 'vs/base/common/objects';
import { URI } from 'vs/base/common/uri';
import { OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
import { OVERRIDE_PROPERTY_PATTERN, ConfigurationScope, IConfigurationRegistry, Extensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { IOverrides, overrideIdentifierFromKey, addToValueTree, toValuesTree, IConfigurationModel, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, IConfigurationChangeEvent, ConfigurationTarget, removeFromValueTree, toOverrides } from 'vs/platform/configuration/common/configuration';
import { Workspace } from 'vs/platform/workspace/common/workspace';
import { Registry } from 'vs/platform/registry/common/platform';
export class ConfigurationModel implements IConfigurationModel {
@@ -195,10 +196,11 @@ export class DefaultConfigurationModel extends ConfigurationModel {
export class ConfigurationModelParser {
private _raw: any = null;
private _configurationModel: ConfigurationModel | null = null;
private _parseErrors: any[] = [];
constructor(protected readonly _name: string) { }
constructor(protected readonly _name: string, private _scopes?: ConfigurationScope[]) { }
get configurationModel(): ConfigurationModel {
return this._configurationModel || new ConfigurationModel();
@@ -208,15 +210,26 @@ export class ConfigurationModelParser {
return this._parseErrors;
}
public parse(content: string | null | undefined): void {
public parseContent(content: string | null | undefined): void {
if (content) {
const raw = this.parseContent(content);
const configurationModel = this.parseRaw(raw);
this._configurationModel = new ConfigurationModel(configurationModel.contents, configurationModel.keys, configurationModel.overrides);
const raw = this.doParseContent(content);
this.parseRaw(raw);
}
}
protected parseContent(content: string): any {
public parseRaw(raw: any): void {
this._raw = raw;
const configurationModel = this.doParseRaw(raw);
this._configurationModel = new ConfigurationModel(configurationModel.contents, configurationModel.keys, configurationModel.overrides);
}
public parse(): void {
if (this._raw) {
this.parseRaw(this._raw);
}
}
protected doParseContent(content: string): any {
let raw: any = {};
let currentProperty: string | null = null;
let currentParent: any = [];
@@ -273,12 +286,36 @@ export class ConfigurationModelParser {
return raw;
}
protected parseRaw(raw: any): IConfigurationModel {
protected doParseRaw(raw: any): IConfigurationModel {
if (this._scopes) {
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
raw = this.filterByScope(raw, configurationProperties, true, this._scopes);
}
const contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
const keys = Object.keys(raw);
const overrides: IOverrides[] = toOverrides(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
return { contents, keys, overrides };
}
private filterByScope(properties: {}, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }, filterOverriddenProperties: boolean, scopes: ConfigurationScope[]): {} {
const result = {};
for (let key in properties) {
if (OVERRIDE_PROPERTY_PATTERN.test(key) && filterOverriddenProperties) {
result[key] = this.filterByScope(properties[key], configurationProperties, false, scopes);
} else {
const scope = this.getScope(key, configurationProperties);
if (scopes.indexOf(scope) !== -1) {
result[key] = properties[key];
}
}
}
return result;
}
private getScope(key: string, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }): ConfigurationScope {
const propertySchema = configurationProperties[key];
return propertySchema && typeof propertySchema.scope !== 'undefined' ? propertySchema.scope : ConfigurationScope.WINDOW;
}
}
export class Configuration {

View File

@@ -27,7 +27,7 @@ export class NodeBasedUserConfiguration extends Disposable {
this.userConfigModelWatcher = new ConfigWatcher(this.settingsPath, {
changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new ConfigurationModelParser(this.settingsPath), parse: (content: string, parseErrors: any[]) => {
const userConfigModelParser = new ConfigurationModelParser(this.settingsPath);
userConfigModelParser.parse(content);
userConfigModelParser.parseContent(content);
parseErrors = [...userConfigModelParser.errors];
return userConfigModelParser;
}, initCallback: () => c(undefined)

View File

@@ -9,7 +9,6 @@ import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, compare, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration';
import { DefaultConfigurationModel, Configuration, ConfigurationChangeEvent, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { Event, Emitter } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { NodeBasedUserConfiguration } from 'vs/platform/configuration/node/configuration';
@@ -24,11 +23,11 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
constructor(
@IEnvironmentService environmentService: IEnvironmentService
configurationPath: string
) {
super();
this.userConfiguration = this._register(new NodeBasedUserConfiguration(environmentService.appSettingsPath));
this.userConfiguration = this._register(new NodeBasedUserConfiguration(configurationPath));
// Initialize
const defaults = new DefaultConfigurationModel();

View File

@@ -250,10 +250,10 @@ suite('CustomConfigurationModel', () => {
test('simple merge using models', () => {
let base = new ConfigurationModelParser('base');
base.parse(JSON.stringify({ 'a': 1, 'b': 2 }));
base.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
let add = new ConfigurationModelParser('add');
add.parse(JSON.stringify({ 'a': 3, 'c': 4 }));
add.parseContent(JSON.stringify({ 'a': 3, 'c': 4 }));
let result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 });
@@ -261,14 +261,14 @@ suite('CustomConfigurationModel', () => {
test('simple merge with an undefined contents', () => {
let base = new ConfigurationModelParser('base');
base.parse(JSON.stringify({ 'a': 1, 'b': 2 }));
base.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
let add = new ConfigurationModelParser('add');
let result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
base = new ConfigurationModelParser('base');
add = new ConfigurationModelParser('add');
add.parse(JSON.stringify({ 'a': 1, 'b': 2 }));
add.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
@@ -280,25 +280,25 @@ suite('CustomConfigurationModel', () => {
test('Recursive merge using config models', () => {
let base = new ConfigurationModelParser('base');
base.parse(JSON.stringify({ 'a': { 'b': 1 } }));
base.parseContent(JSON.stringify({ 'a': { 'b': 1 } }));
let add = new ConfigurationModelParser('add');
add.parse(JSON.stringify({ 'a': { 'b': 2 } }));
add.parseContent(JSON.stringify({ 'a': { 'b': 2 } }));
let result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
});
test('Test contents while getting an existing property', () => {
let testObject = new ConfigurationModelParser('test');
testObject.parse(JSON.stringify({ 'a': 1 }));
testObject.parseContent(JSON.stringify({ 'a': 1 }));
assert.deepEqual(testObject.configurationModel.getValue('a'), 1);
testObject.parse(JSON.stringify({ 'a': { 'b': 1 } }));
testObject.parseContent(JSON.stringify({ 'a': { 'b': 1 } }));
assert.deepEqual(testObject.configurationModel.getValue('a'), { 'b': 1 });
});
test('Test contents are undefined for non existing properties', () => {
const testObject = new ConfigurationModelParser('test');
testObject.parse(JSON.stringify({
testObject.parseContent(JSON.stringify({
awesome: true
}));
@@ -313,7 +313,7 @@ suite('CustomConfigurationModel', () => {
test('Test configWithOverrides gives all content merged with overrides', () => {
const testObject = new ConfigurationModelParser('test');
testObject.parse(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } }));
testObject.parseContent(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } }));
assert.deepEqual(testObject.configurationModel.override('b').contents, { 'a': 2, 'c': 1, '[b]': { 'a': 2 } });
});
@@ -326,17 +326,17 @@ suite('CustomConfigurationModel', () => {
test('Test update with empty data', () => {
const testObject = new ConfigurationModelParser('test');
testObject.parse('');
testObject.parseContent('');
assert.deepEqual(testObject.configurationModel.contents, {});
assert.deepEqual(testObject.configurationModel.keys, []);
testObject.parse(null!);
testObject.parseContent(null!);
assert.deepEqual(testObject.configurationModel.contents, {});
assert.deepEqual(testObject.configurationModel.keys, []);
testObject.parse(undefined!);
testObject.parseContent(undefined!);
assert.deepEqual(testObject.configurationModel.contents, {});
assert.deepEqual(testObject.configurationModel.keys, []);
@@ -472,7 +472,7 @@ suite('Configuration', () => {
test('Test update value', () => {
const parser = new ConfigurationModelParser('test');
parser.parse(JSON.stringify({ 'a': 1 }));
parser.parseContent(JSON.stringify({ 'a': 1 }));
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel());
testObject.updateValue('a', 2);
@@ -482,7 +482,7 @@ suite('Configuration', () => {
test('Test update value after inspect', () => {
const parser = new ConfigurationModelParser('test');
parser.parse(JSON.stringify({ 'a': 1 }));
parser.parseContent(JSON.stringify({ 'a': 1 }));
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel());
testObject.inspect('a', {}, undefined);

View File

@@ -10,29 +10,17 @@ import * as fs from 'fs';
import { Registry } from 'vs/platform/registry/common/platform';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import * as uuid from 'vs/base/common/uuid';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { testFile } from 'vs/base/test/node/utils';
class SettingsTestEnvironmentService extends EnvironmentService {
constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome: string) {
super(args, _execPath);
}
get appSettingsPath(): string { return this.customAppSettingsHome; }
}
suite('ConfigurationService - Node', () => {
test('simple', async () => {
const res = await testFile('config', 'config.json');
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
const config = service.getValue<{
foo: string;
}>();
@@ -49,7 +37,7 @@ suite('ConfigurationService - Node', () => {
fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
const config = service.getValue<{
testworkbench: {
editor: {
@@ -71,7 +59,7 @@ suite('ConfigurationService - Node', () => {
fs.writeFileSync(res.testFile, ',,,,');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
const config = service.getValue<{
foo: string;
}>();
@@ -87,7 +75,7 @@ suite('ConfigurationService - Node', () => {
const newDir = path.join(parentDir, 'config', id);
const testFile = path.join(newDir, 'config.json');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, testFile));
const service = new ConfigurationService(testFile);
const config = service.getValue<{ foo: string }>();
assert.ok(config);
@@ -98,7 +86,7 @@ suite('ConfigurationService - Node', () => {
test('trigger configuration change event', async () => {
const res = await testFile('config', 'config.json');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
return new Promise((c, e) => {
service.onDidChangeConfiguration(() => {
assert.equal(service.getValue('foo'), 'bar');
@@ -115,7 +103,7 @@ suite('ConfigurationService - Node', () => {
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
let config = service.getValue<{
foo: string;
}>();
@@ -163,7 +151,7 @@ suite('ConfigurationService - Node', () => {
}
});
let serviceWithoutFile = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, '__testFile'));
let serviceWithoutFile = new ConfigurationService('__testFile');
let setting = serviceWithoutFile.getValue<ITestSetting>();
assert.ok(setting);
@@ -172,7 +160,7 @@ suite('ConfigurationService - Node', () => {
return testFile('config', 'config.json').then(async res => {
fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
let setting = service.getValue<ITestSetting>();
@@ -205,7 +193,7 @@ suite('ConfigurationService - Node', () => {
});
const r = await testFile('config', 'config.json');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, r.testFile));
const service = new ConfigurationService(r.testFile);
let res = service.inspect('something.missing');
assert.strictEqual(res.value, undefined);
assert.strictEqual(res.default, undefined);
@@ -241,7 +229,7 @@ suite('ConfigurationService - Node', () => {
});
const r = await testFile('config', 'config.json');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, r.testFile));
const service = new ConfigurationService(r.testFile);
let res = service.inspect('lookup.service.testNullSetting');
assert.strictEqual(res.default, null);
assert.strictEqual(res.value, null);

View File

@@ -17,6 +17,7 @@ import { IContextMenuDelegate } from 'vs/base/browser/contextmenu';
import { EventType, $, removeNode } from 'vs/base/browser/dom';
import { attachMenuStyler } from 'vs/platform/theme/common/styler';
import { domEvent } from 'vs/base/browser/event';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
export interface IContextMenuHandlerOptions {
blockMouse: boolean;
@@ -83,6 +84,25 @@ export class ContextMenuHandler {
menu.onDidCancel(() => this.contextViewService.hideContextView(true), null, menuDisposables);
menu.onDidBlur(() => this.contextViewService.hideContextView(true), null, menuDisposables);
domEvent(window, EventType.BLUR)(() => { this.contextViewService.hideContextView(true); }, null, menuDisposables);
domEvent(window, EventType.MOUSE_DOWN)((e: MouseEvent) => {
let event = new StandardMouseEvent(e);
let element: HTMLElement | null = event.target;
// Don't do anything as we are likely creating a context menu
if (event.rightButton) {
return;
}
while (element) {
if (element === container) {
return;
}
element = element.parentElement;
}
this.contextViewService.hideContextView(true);
}, null, menuDisposables);
return combinedDisposable([...menuDisposables, menu]);
},

View File

@@ -30,6 +30,11 @@ export interface IRemoteDiagnosticInfo extends IDiagnosticInfo {
hostName: string;
}
export interface IRemoteDiagnosticError {
hostName: string;
errorMessage: string;
}
export interface IDiagnosticInfoOptions {
includeProcesses?: boolean;
folders?: UriComponents[];
@@ -46,4 +51,8 @@ export interface WorkspaceStats {
configFiles: WorkspaceStatItem[];
fileCount: number;
maxFilesReached: boolean;
}
export function isRemoteDiagnosticError(x: any): x is IRemoteDiagnosticError {
return !!x.hostName && !!x.errorMessage;
}

View File

@@ -15,7 +15,7 @@ import { app } from 'electron';
import { basename } from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IMachineInfo, WorkspaceStats, SystemInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
import { IMachineInfo, WorkspaceStats, SystemInfo, IRemoteDiagnosticInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
import { collectWorkspaceStats, getMachineInfo } from 'vs/platform/diagnostics/node/diagnosticsService';
import { ProcessItem } from 'vs/base/common/processes';
@@ -92,23 +92,28 @@ export class DiagnosticsService implements IDiagnosticsService {
try {
const remoteData = await launchService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true });
remoteData.forEach(diagnostics => {
processInfo += `\n\nRemote: ${diagnostics.hostName}`;
if (diagnostics.processes) {
processInfo += `\n${this.formatProcessList(info, diagnostics.processes)}`;
}
if (isRemoteDiagnosticError(diagnostics)) {
processInfo += `\n${diagnostics.errorMessage}`;
workspaceInfo += `\n${diagnostics.errorMessage}`;
} else {
processInfo += `\n\nRemote: ${diagnostics.hostName}`;
if (diagnostics.processes) {
processInfo += `\n${this.formatProcessList(info, diagnostics.processes)}`;
}
if (diagnostics.workspaceMetadata) {
workspaceInfo += `\n| Remote: ${diagnostics.hostName}`;
for (const folder of Object.keys(diagnostics.workspaceMetadata)) {
const metadata = diagnostics.workspaceMetadata[folder];
if (diagnostics.workspaceMetadata) {
workspaceInfo += `\n| Remote: ${diagnostics.hostName}`;
for (const folder of Object.keys(diagnostics.workspaceMetadata)) {
const metadata = diagnostics.workspaceMetadata[folder];
let countMessage = `${metadata.fileCount} files`;
if (metadata.maxFilesReached) {
countMessage = `more than ${countMessage}`;
let countMessage = `${metadata.fileCount} files`;
if (metadata.maxFilesReached) {
countMessage = `more than ${countMessage}`;
}
workspaceInfo += `| Folder (${folder}): ${countMessage}`;
workspaceInfo += this.formatWorkspaceStats(metadata);
}
workspaceInfo += `| Folder (${folder}): ${countMessage}`;
workspaceInfo += this.formatWorkspaceStats(metadata);
}
}
});
@@ -135,7 +140,7 @@ export class DiagnosticsService implements IDiagnosticsService {
processArgs: `${info.mainArguments.join(' ')}`,
gpuStatus: app.getGPUFeatureStatus(),
screenReader: `${app.isAccessibilitySupportEnabled() ? 'yes' : 'no'}`,
remoteData: await launchService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })
remoteData: (await launchService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })).filter((x): x is IRemoteDiagnosticInfo => !(x instanceof Error))
};
@@ -169,25 +174,29 @@ export class DiagnosticsService implements IDiagnosticsService {
try {
const data = await launchService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true });
data.forEach(diagnostics => {
output.push('\n\n');
output.push(`Remote: ${diagnostics.hostName}`);
output.push(this.formatMachineInfo(diagnostics.machineInfo));
if (isRemoteDiagnosticError(diagnostics)) {
output.push(`\n${diagnostics.errorMessage}`);
} else {
output.push('\n\n');
output.push(`Remote: ${diagnostics.hostName}`);
output.push(this.formatMachineInfo(diagnostics.machineInfo));
if (diagnostics.processes) {
output.push(this.formatProcessList(info, diagnostics.processes));
}
if (diagnostics.processes) {
output.push(this.formatProcessList(info, diagnostics.processes));
}
if (diagnostics.workspaceMetadata) {
for (const folder of Object.keys(diagnostics.workspaceMetadata)) {
const metadata = diagnostics.workspaceMetadata[folder];
if (diagnostics.workspaceMetadata) {
for (const folder of Object.keys(diagnostics.workspaceMetadata)) {
const metadata = diagnostics.workspaceMetadata[folder];
let countMessage = `${metadata.fileCount} files`;
if (metadata.maxFilesReached) {
countMessage = `more than ${countMessage}`;
let countMessage = `${metadata.fileCount} files`;
if (metadata.maxFilesReached) {
countMessage = `more than ${countMessage}`;
}
output.push(`Folder (${folder}): ${countMessage}`);
output.push(this.formatWorkspaceStats(metadata));
}
output.push(`Folder (${folder}): ${countMessage}`);
output.push(this.formatWorkspaceStats(metadata));
}
}
});
@@ -306,13 +315,13 @@ export class DiagnosticsService implements IDiagnosticsService {
output.push('CPU %\tMem MB\t PID\tProcess');
if (rootProcess) {
this.formatProcessItem(mapPidToWindowTitle, output, rootProcess, 0);
this.formatProcessItem(info.mainPID, mapPidToWindowTitle, output, rootProcess, 0);
}
return output.join('\n');
}
private formatProcessItem(mapPidToWindowTitle: Map<number, string>, output: string[], item: ProcessItem, indent: number): void {
private formatProcessItem(mainPid: number, mapPidToWindowTitle: Map<number, string>, output: string[], item: ProcessItem, indent: number): void {
const isRoot = (indent === 0);
const MB = 1024 * 1024;
@@ -320,7 +329,7 @@ export class DiagnosticsService implements IDiagnosticsService {
// Format name with indent
let name: string;
if (isRoot) {
name = `${product.applicationName} main`;
name = item.pid === mainPid ? `${product.applicationName} main` : 'remote agent';
} else {
name = `${repeat(' ', indent)} ${item.name}`;
@@ -333,7 +342,7 @@ export class DiagnosticsService implements IDiagnosticsService {
// Recurse into children if any
if (Array.isArray(item.children)) {
item.children.forEach(child => this.formatProcessItem(mapPidToWindowTitle, output, child, indent + 1));
item.children.forEach(child => this.formatProcessItem(mainPid, mapPidToWindowTitle, output, child, indent + 1));
}
}
}

View File

@@ -226,12 +226,12 @@ export async function registerWindowDriver(accessor: ServicesAccessor): Promise<
const windowDriverRegistryChannel = mainProcessService.getChannel('windowDriverRegistry');
const windowDriverRegistry = new WindowDriverRegistryChannelClient(windowDriverRegistryChannel);
await windowDriverRegistry.registerWindowDriver(windowService.getCurrentWindowId());
await windowDriverRegistry.registerWindowDriver(windowService.windowId);
// const options = await windowDriverRegistry.registerWindowDriver(windowId);
// if (options.verbose) {
// windowDriver.openDevTools();
// }
return toDisposable(() => windowDriverRegistry.reloadWindowDriver(windowService.getCurrentWindowId()));
return toDisposable(() => windowDriverRegistry.reloadWindowDriver(windowService.windowId));
}

View File

@@ -50,6 +50,7 @@ export interface ParsedArgs {
'show-versions'?: boolean;
'install-extension'?: string | string[];
'uninstall-extension'?: string | string[];
'locate-extension'?: string | string[];
'enable-proposed-api'?: string | string[];
'open-url'?: boolean;
'skip-getting-started'?: boolean;
@@ -109,6 +110,9 @@ export interface IEnvironmentService {
appSettingsPath: string;
appKeybindingsPath: string;
machineSettingsHome: string;
machineSettingsPath: string;
settingsSearchBuildId?: number;
settingsSearchUrl?: string;
@@ -124,7 +128,7 @@ export interface IEnvironmentService {
disableExtensions: boolean | string[];
builtinExtensionsPath: string;
extensionsPath: string;
extensionDevelopmentLocationURI?: URI | URI[];
extensionDevelopmentLocationURI?: URI[];
extensionTestsLocationURI?: URI;
debugExtensionHost: IExtensionHostDebugParams;

View File

@@ -66,6 +66,7 @@ export const options: Option[] = [
{ id: 'max-memory', type: 'string', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes).") },
{ id: 'remote', type: 'string' },
{ id: 'locate-extension', type: 'string' },
{ id: 'extensionDevelopmentPath', type: 'string' },
{ id: 'extensionTestsPath', type: 'string' },
{ id: 'debugId', type: 'string' },

View File

@@ -110,6 +110,12 @@ export class EnvironmentService implements IEnvironmentService {
@memoize
get appSettingsPath(): string { return path.join(this.appSettingsHome, 'settings.json'); }
@memoize
get machineSettingsHome(): string { return path.join(this.userDataPath, 'Machine'); }
@memoize
get machineSettingsPath(): string { return path.join(this.machineSettingsHome, 'settings.json'); }
@memoize
get globalStorageHome(): string { return path.join(this.appSettingsHome, 'globalStorage'); }
@@ -172,7 +178,7 @@ export class EnvironmentService implements IEnvironmentService {
}
@memoize
get extensionDevelopmentLocationURI(): URI | URI[] | undefined {
get extensionDevelopmentLocationURI(): URI[] | undefined {
const s = this._args.extensionDevelopmentPath;
if (Array.isArray(s)) {
return s.map(p => {
@@ -183,9 +189,9 @@ export class EnvironmentService implements IEnvironmentService {
});
} else if (s) {
if (/^[^:/?#]+?:\/\//.test(s)) {
return URI.parse(s);
return [URI.parse(s)];
}
return URI.file(path.normalize(s));
return [URI.file(path.normalize(s))];
}
return undefined;
}

View File

@@ -753,7 +753,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
loadAllDependencies(extensions: IExtensionIdentifier[], token: CancellationToken): Promise<IGalleryExtension[]> {
return this.getDependenciesReccursively(extensions.map(e => e.id), [], token);
return this.getDependenciesRecursively(extensions.map(e => e.id), [], token);
}
getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise<IGalleryExtensionVersion[]> {
@@ -812,7 +812,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
});
}
private getDependenciesReccursively(toGet: string[], result: IGalleryExtension[], token: CancellationToken): Promise<IGalleryExtension[]> {
private getDependenciesRecursively(toGet: string[], result: IGalleryExtension[], token: CancellationToken): Promise<IGalleryExtension[]> {
if (!toGet || !toGet.length) {
return Promise.resolve(result);
}
@@ -832,7 +832,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
result = distinct(result.concat(loadedDependencies), d => d.identifier.uuid);
const dependencies: string[] = [];
dependenciesSet.forEach(d => !ExtensionGalleryService.hasExtensionByName(result, d) && dependencies.push(d));
return this.getDependenciesReccursively(dependencies, result, token);
return this.getDependenciesRecursively(dependencies, result, token);
});
}
@@ -903,7 +903,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
if (version) {
return version;
}
return this.getLastValidExtensionVersionReccursively(extension, versions);
return this.getLastValidExtensionVersionRecursively(extension, versions);
}
private getLastValidExtensionVersionFromProperties(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise<IRawGalleryExtensionVersion> | null {
@@ -936,7 +936,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
.then(manifest => manifest ? manifest.engines.vscode : Promise.reject<string>('Error while reading manifest'));
}
private getLastValidExtensionVersionReccursively(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise<IRawGalleryExtensionVersion | null> {
private getLastValidExtensionVersionRecursively(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise<IRawGalleryExtensionVersion | null> {
if (!versions.length) {
return Promise.resolve(null);
}
@@ -945,7 +945,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return this.getEngine(version)
.then(engine => {
if (!isEngineValid(engine)) {
return this.getLastValidExtensionVersionReccursively(extension, versions.slice(1));
return this.getLastValidExtensionVersionRecursively(extension, versions.slice(1));
}
version.properties = version.properties || [];

View File

@@ -13,6 +13,7 @@ import { startsWithIgnoreCase } from 'vs/base/common/strings';
import { IDisposable } from 'vs/base/common/lifecycle';
import { isEqualOrParent, isEqual } from 'vs/base/common/resources';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer';
export const IFileService = createDecorator<IFileService>('fileService');
@@ -123,10 +124,15 @@ export interface IFileService {
*/
resolveStreamContent(resource: URI, options?: IResolveContentOptions): Promise<IStreamContent>;
/**
* @deprecated use writeFile instead
*/
updateContent(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise<IFileStatWithMetadata>;
/**
* Updates the content replacing its previous value.
*/
updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise<IFileStatWithMetadata>;
writeFile(resource: URI, bufferOrReadable: VSBuffer | VSBufferReadable, options?: IWriteFileOptions): Promise<IFileStatWithMetadata>;
/**
* Moves the file/folder to a new path identified by the resource.
@@ -143,12 +149,17 @@ export interface IFileService {
copy(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
/**
* Creates a new file with the given path. The returned promise
* @deprecated use createFile2 instead
*/
createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
/**
* Creates a new file with the given path and optional contents. The returned promise
* will have the stat model object as a result.
*
* The optional parameter content can be used as value to fill into the new file.
*/
createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
createFile2(resource: URI, bufferOrReadable?: VSBuffer | VSBufferReadable, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
/**
* Creates a new folder with the given path. The returned promise
@@ -650,17 +661,6 @@ export interface ITextSnapshot {
read(): string | null;
}
export class StringSnapshot implements ITextSnapshot {
private _value: string | null;
constructor(value: string) {
this._value = value;
}
read(): string | null {
let ret = this._value;
this._value = null;
return ret;
}
}
/**
* Helper method to convert a snapshot into its full string form.
*/
@@ -674,6 +674,35 @@ export function snapshotToString(snapshot: ITextSnapshot): string {
return chunks.join('');
}
export class TextSnapshotReadable implements VSBufferReadable {
private preambleHandled: boolean;
constructor(private snapshot: ITextSnapshot, private preamble?: string) { }
read(): VSBuffer | null {
let value = this.snapshot.read();
// Handle preamble if provided
if (!this.preambleHandled) {
this.preambleHandled = true;
if (typeof this.preamble === 'string') {
if (typeof value === 'string') {
value = this.preamble + value;
} else {
value = this.preamble;
}
}
}
if (typeof value === 'string') {
return VSBuffer.fromString(value);
}
return null;
}
}
/**
* Streamable content and meta information of a file.
*/
@@ -724,7 +753,20 @@ export interface IResolveContentOptions {
position?: number;
}
export interface IUpdateContentOptions {
export interface IWriteFileOptions {
/**
* The last known modification time of the file. This can be used to prevent dirty writes.
*/
mtime?: number;
/**
* The etag of the file. This can be used to prevent dirty writes.
*/
etag?: string;
}
export interface IWriteTextFileOptions extends IWriteFileOptions {
/**
* The encoding to use when updating a file.
@@ -746,21 +788,6 @@ export interface IUpdateContentOptions {
* ask the user to authenticate as super user.
*/
writeElevated?: boolean;
/**
* The last known modification time of the file. This can be used to prevent dirty writes.
*/
mtime?: number;
/**
* The etag of the file. This can be used to prevent dirty writes.
*/
etag?: string;
/**
* Run mkdirp before saving.
*/
mkdirp?: boolean;
}
export interface IResolveFileOptions {
@@ -797,7 +824,7 @@ export interface ICreateFileOptions {
}
export class FileOperationError extends Error {
constructor(message: string, public fileOperationResult: FileOperationResult, public options?: IResolveContentOptions & IUpdateContentOptions & ICreateFileOptions) {
constructor(message: string, public fileOperationResult: FileOperationResult, public options?: IResolveContentOptions & IWriteTextFileOptions & ICreateFileOptions) {
super(message);
}
@@ -1132,7 +1159,7 @@ export interface ILegacyFileService extends IDisposable {
resolveStreamContent(resource: URI, options?: IResolveContentOptions): Promise<IStreamContent>;
updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise<IFileStatWithMetadata>;
updateContent(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise<IFileStatWithMetadata>;
createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
}

View File

@@ -33,7 +33,7 @@ export class SharedProcessService implements ISharedProcessService {
@IEnvironmentService environmentService: IEnvironmentService
) {
this.withSharedProcessConnection = windowsService.whenSharedProcessReady()
.then(() => connect(environmentService.sharedIPCHandle, `window:${windowService.getCurrentWindowId()}`));
.then(() => connect(environmentService.sharedIPCHandle, `window:${windowService.windowId}`));
}
getChannel(channelName: string): IChannel {

View File

@@ -15,6 +15,8 @@ import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IWindowState } from 'vs/platform/windows/electron-main/windows';
import { listProcesses } from 'vs/base/node/ps';
import { isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
const DEFAULT_BACKGROUND_COLOR = '#1E1E1E';
@@ -44,6 +46,35 @@ export class IssueService implements IIssueService {
});
});
ipcMain.on('vscode:listProcesses', async (event: Event) => {
const processes = [];
try {
const mainPid = await this.launchService.getMainProcessId();
processes.push({ name: localize('local', "Local"), rootProcess: await listProcesses(mainPid) });
(await this.launchService.getRemoteDiagnostics({ includeProcesses: true }))
.forEach(data => {
if (isRemoteDiagnosticError(data)) {
processes.push({
name: data.hostName,
rootProcess: data
});
} else {
if (data.processes) {
processes.push({
name: data.hostName,
rootProcess: data.processes
});
}
}
});
} catch (e) {
this.logService.error(`Listing processes failed: ${e}`);
}
event.sender.send('vscode:listProcessesResponse', processes);
});
ipcMain.on('vscode:issuePerformanceInfoRequest', (event: Event) => {
this.getPerformanceInfo().then(msg => {
event.sender.send('vscode:issuePerformanceInfoResponse', msg);

View File

@@ -19,7 +19,7 @@ import { BrowserWindow, ipcMain, Event as IpcEvent } from 'electron';
import { Event } from 'vs/base/common/event';
import { hasArgs } from 'vs/platform/environment/node/argv';
import { coalesce } from 'vs/base/common/arrays';
import { IDiagnosticInfoOptions, IDiagnosticInfo, IRemoteDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
import { IDiagnosticInfoOptions, IDiagnosticInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
export const ID = 'launchService';
export const ILaunchService = createDecorator<ILaunchService>(ID);
@@ -71,7 +71,7 @@ export interface ILaunchService {
getMainProcessId(): Promise<number>;
getMainProcessInfo(): Promise<IMainProcessInfo>;
getLogsPath(): Promise<string>;
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<IRemoteDiagnosticInfo[]>;
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<(IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]>;
}
export class LaunchChannel implements IServerChannel {
@@ -299,9 +299,9 @@ export class LaunchService implements ILaunchService {
return Promise.resolve(this.environmentService.logsPath);
}
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<IRemoteDiagnosticInfo[]> {
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<(IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]> {
const windows = this.windowsMainService.getWindows();
const promises: Promise<IDiagnosticInfo | undefined>[] = windows.map(window => {
const promises: Promise<IDiagnosticInfo | IRemoteDiagnosticError | undefined>[] = windows.map(window => {
return new Promise((resolve, reject) => {
if (window.remoteAuthority) {
const replyChannel = `vscode:getDiagnosticInfoResponse${window.id}`;
@@ -315,18 +315,14 @@ export class LaunchService implements ILaunchService {
ipcMain.once(replyChannel, (_: IpcEvent, data: IRemoteDiagnosticInfo) => {
// No data is returned if getting the connection fails.
if (!data) {
resolve();
}
if (typeof (data) === 'string') {
reject(new Error(data));
resolve({ hostName: window.remoteAuthority!, errorMessage: `Unable to resolve connection to '${window.remoteAuthority}'.` });
}
resolve(data);
});
setTimeout(() => {
resolve();
resolve({ hostName: window.remoteAuthority!, errorMessage: `Fetching remote diagnostics for '${window.remoteAuthority}' timed out.` });
}, 5000);
} else {
resolve();
@@ -334,7 +330,7 @@ export class LaunchService implements ILaunchService {
});
});
return Promise.all(promises).then(diagnostics => diagnostics.filter((x): x is IRemoteDiagnosticInfo => !!x));
return Promise.all(promises).then(diagnostics => diagnostics.filter((x): x is IRemoteDiagnosticInfo | IRemoteDiagnosticError => !!x));
}
private getFolderURIs(window: ICodeWindow): URI[] {

View File

@@ -53,7 +53,7 @@ export class LifecycleService extends AbstractLifecycleService {
}
private registerListeners(): void {
const windowId = this.windowService.getCurrentWindowId();
const windowId = this.windowService.windowId;
// Main side indicates that window is about to unload, check for vetos
ipc.on('vscode:onBeforeUnload', (_event: unknown, reply: { okChannel: string, cancelChannel: string, reason: ShutdownReason }) => {

View File

@@ -29,7 +29,7 @@ import { ObjectTree, IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTr
import { ITreeEvent, ITreeRenderer, IAsyncDataSource, IDataSource, ITreeMouseEvent } from 'vs/base/browser/ui/tree/tree';
import { AsyncDataTree, IAsyncDataTreeOptions } from 'vs/base/browser/ui/tree/asyncDataTree';
import { DataTree, IDataTreeOptions } from 'vs/base/browser/ui/tree/dataTree';
import { IKeyboardNavigationEventFilter } from 'vs/base/browser/ui/tree/abstractTree';
import { IKeyboardNavigationEventFilter, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
export type ListWidget = List<any> | PagedList<any> | ITree | ObjectTree<any, any> | DataTree<any, any, any> | AsyncDataTree<any, any, any>;
@@ -659,8 +659,9 @@ export interface IOpenEvent<T> {
browserEvent?: UIEvent;
}
export interface IResourceResultsNavigationOptions {
openOnFocus: boolean;
export interface IResourceResultsNavigationOptions2 {
openOnFocus?: boolean;
openOnSelection?: boolean;
}
export interface SelectionKeyboardEvent extends KeyboardEvent {
@@ -676,14 +677,24 @@ export function getSelectionKeyboardEvent(typeArg = 'keydown', preserveFocus?: b
export class TreeResourceNavigator2<T, TFilterData> extends Disposable {
private options: IResourceResultsNavigationOptions2;
private readonly _onDidOpenResource = new Emitter<IOpenEvent<T | null>>();
readonly onDidOpenResource: Event<IOpenEvent<T | null>> = this._onDidOpenResource.event;
constructor(
private tree: WorkbenchObjectTree<T, TFilterData> | WorkbenchDataTree<any, T, TFilterData> | WorkbenchAsyncDataTree<any, T, TFilterData>,
private options?: IResourceResultsNavigationOptions
options?: IResourceResultsNavigationOptions2
) {
super();
this.options = {
...<IResourceResultsNavigationOptions2>{
openOnSelection: true
},
...(options || {})
};
this.registerListeners();
}
@@ -692,7 +703,10 @@ export class TreeResourceNavigator2<T, TFilterData> extends Disposable {
this._register(this.tree.onDidChangeFocus(e => this.onFocus(e)));
}
this._register(this.tree.onDidChangeSelection(e => this.onSelection(e)));
if (this.options && this.options.openOnSelection) {
this._register(this.tree.onDidChangeSelection(e => this.onSelection(e)));
}
this._register(this.tree.onDidOpen(e => this.onSelection(e)));
}
@@ -766,15 +780,9 @@ function createKeyboardNavigationEventFilter(container: HTMLElement, keybindingS
export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void> extends ObjectTree<T, TFilterData> {
readonly contextKeyService: IContextKeyService;
protected disposables: IDisposable[];
private hasSelectionOrFocus: IContextKey<boolean>;
private hasDoubleSelection: IContextKey<boolean>;
private hasMultiSelection: IContextKey<boolean>;
private _useAltAsMultipleSelectionModifier: boolean;
private internals: WorkbenchTreeInternals<any, T, TFilterData>;
get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; }
get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; }
constructor(
container: HTMLElement,
@@ -788,132 +796,19 @@ export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void>
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService
) {
WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService);
if (!didBindWorkbenchListAutomaticKeyboardNavigation) {
WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService);
didBindWorkbenchListAutomaticKeyboardNavigation = true;
}
const getAutomaticKeyboardNavigation = () => {
// give priority to the context key value to disable this completely
let automaticKeyboardNavigation = contextKeyService.getContextKeyValue<boolean>(WorkbenchListAutomaticKeyboardNavigationKey);
if (automaticKeyboardNavigation) {
automaticKeyboardNavigation = configurationService.getValue<boolean>(automaticKeyboardNavigationSettingKey);
}
return automaticKeyboardNavigation;
};
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService);
const openOnSingleClick = useSingleClickToOpen(configurationService);
const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService);
super(container, delegate, renderers, {
keyboardSupport: false,
styleController: new DefaultStyleController(getSharedListStyleSheet()),
...computeStyles(themeService.getTheme(), defaultListStyles),
...workbenchListOptions,
indent: configurationService.getValue(treeIndentKey),
automaticKeyboardNavigation: getAutomaticKeyboardNavigation(),
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter',
horizontalScrolling,
openOnSingleClick,
keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService)
});
this.disposables.push(workbenchListOptionsDisposable);
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService);
listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false));
this.hasSelectionOrFocus = WorkbenchListHasSelectionOrFocus.bindTo(this.contextKeyService);
this.hasDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService);
this.hasMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService);
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
const interestingContextKeys = new Set();
interestingContextKeys.add(WorkbenchListAutomaticKeyboardNavigationKey);
const updateKeyboardNavigation = () => {
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
this.updateOptions({
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter'
});
};
this.disposables.push(
this.contextKeyService,
(listService as ListService).register(this),
attachListStyler(this, themeService),
this.onDidChangeSelection(() => {
const selection = this.getSelection();
const focus = this.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
this.hasMultiSelection.set(selection.length > 1);
this.hasDoubleSelection.set(selection.length === 2);
}),
this.onDidChangeFocus(() => {
const selection = this.getSelection();
const focus = this.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
}),
configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(openModeSettingKey)) {
this.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) });
}
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
}
if (e.affectsConfiguration(treeIndentKey)) {
const indent = configurationService.getValue<number>(treeIndentKey);
this.updateOptions({ indent });
}
if (e.affectsConfiguration(keyboardNavigationSettingKey)) {
updateKeyboardNavigation();
}
if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) {
this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
}
}),
this.contextKeyService.onDidChangeContext(e => {
if (e.affectsSome(interestingContextKeys)) {
this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
}
}),
accessibilityService.onDidChangeAccessibilitySupport(() => updateKeyboardNavigation())
);
}
get useAltAsMultipleSelectionModifier(): boolean {
return this._useAltAsMultipleSelectionModifier;
}
dispose(): void {
super.dispose();
this.disposables = dispose(this.disposables);
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService);
super(container, delegate, renderers, treeOptions);
this.disposables.push(disposable);
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService);
this.disposables.push(this.internals);
}
}
export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<TInput, T, TFilterData> {
readonly contextKeyService: IContextKeyService;
private hasSelectionOrFocus: IContextKey<boolean>;
private hasDoubleSelection: IContextKey<boolean>;
private hasMultiSelection: IContextKey<boolean>;
private _useAltAsMultipleSelectionModifier: boolean;
private internals: WorkbenchTreeInternals<TInput, T, TFilterData>;
get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; }
get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; }
constructor(
container: HTMLElement,
@@ -928,127 +823,19 @@ export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<T
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService
) {
WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService);
if (!didBindWorkbenchListAutomaticKeyboardNavigation) {
WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService);
didBindWorkbenchListAutomaticKeyboardNavigation = true;
}
const getAutomaticKeyboardNavigation = () => {
// give priority to the context key value to disable this completely
let automaticKeyboardNavigation = contextKeyService.getContextKeyValue<boolean>(WorkbenchListAutomaticKeyboardNavigationKey);
if (automaticKeyboardNavigation) {
automaticKeyboardNavigation = configurationService.getValue<boolean>(automaticKeyboardNavigationSettingKey);
}
return automaticKeyboardNavigation;
};
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService);
const openOnSingleClick = useSingleClickToOpen(configurationService);
const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService);
super(container, delegate, renderers, dataSource, {
keyboardSupport: false,
styleController: new DefaultStyleController(getSharedListStyleSheet()),
...computeStyles(themeService.getTheme(), defaultListStyles),
...workbenchListOptions,
indent: configurationService.getValue(treeIndentKey),
automaticKeyboardNavigation: getAutomaticKeyboardNavigation(),
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter',
horizontalScrolling,
openOnSingleClick,
keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService)
});
this.disposables.push(workbenchListOptionsDisposable);
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService);
listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false));
this.hasSelectionOrFocus = WorkbenchListHasSelectionOrFocus.bindTo(this.contextKeyService);
this.hasDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService);
this.hasMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService);
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
const interestingContextKeys = new Set();
interestingContextKeys.add(WorkbenchListAutomaticKeyboardNavigationKey);
const updateKeyboardNavigation = () => {
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
this.updateOptions({
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter'
});
};
this.disposables.push(
this.contextKeyService,
(listService as ListService).register(this),
attachListStyler(this, themeService),
this.onDidChangeSelection(() => {
const selection = this.getSelection();
const focus = this.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
this.hasMultiSelection.set(selection.length > 1);
this.hasDoubleSelection.set(selection.length === 2);
}),
this.onDidChangeFocus(() => {
const selection = this.getSelection();
const focus = this.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
}),
configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(openModeSettingKey)) {
this.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) });
}
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
}
if (e.affectsConfiguration(treeIndentKey)) {
const indent = configurationService.getValue<number>(treeIndentKey);
this.updateOptions({ indent });
}
if (e.affectsConfiguration(keyboardNavigationSettingKey)) {
updateKeyboardNavigation();
}
if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) {
this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
}
}),
this.contextKeyService.onDidChangeContext(e => {
if (e.affectsSome(interestingContextKeys)) {
this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
}
}),
accessibilityService.onDidChangeAccessibilitySupport(() => updateKeyboardNavigation())
);
}
get useAltAsMultipleSelectionModifier(): boolean {
return this._useAltAsMultipleSelectionModifier;
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService);
super(container, delegate, renderers, dataSource, treeOptions);
this.disposables.push(disposable);
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService);
this.disposables.push(this.internals);
}
}
export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends AsyncDataTree<TInput, T, TFilterData> {
readonly contextKeyService: IContextKeyService;
private hasSelectionOrFocus: IContextKey<boolean>;
private hasDoubleSelection: IContextKey<boolean>;
private hasMultiSelection: IContextKey<boolean>;
private _useAltAsMultipleSelectionModifier: boolean;
private internals: WorkbenchTreeInternals<TInput, T, TFilterData>;
get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; }
get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; }
constructor(
container: HTMLElement,
@@ -1063,31 +850,51 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService
) {
WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService);
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService);
super(container, delegate, renderers, dataSource, treeOptions);
this.disposables.push(disposable);
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService);
this.disposables.push(this.internals);
}
}
if (!didBindWorkbenchListAutomaticKeyboardNavigation) {
WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService);
didBindWorkbenchListAutomaticKeyboardNavigation = true;
function workbenchTreeDataPreamble<T, TFilterData>(
container: HTMLElement,
options: IAbstractTreeOptions<T, TFilterData>,
contextKeyService: IContextKeyService,
themeService: IThemeService,
configurationService: IConfigurationService,
keybindingService: IKeybindingService,
accessibilityService: IAccessibilityService,
): { options: IAbstractTreeOptions<T, TFilterData>, getAutomaticKeyboardNavigation: () => boolean | undefined, disposable: IDisposable } {
WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService);
if (!didBindWorkbenchListAutomaticKeyboardNavigation) {
WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService);
didBindWorkbenchListAutomaticKeyboardNavigation = true;
}
const getAutomaticKeyboardNavigation = () => {
// give priority to the context key value to disable this completely
let automaticKeyboardNavigation = contextKeyService.getContextKeyValue<boolean>(WorkbenchListAutomaticKeyboardNavigationKey);
if (automaticKeyboardNavigation) {
automaticKeyboardNavigation = configurationService.getValue<boolean>(automaticKeyboardNavigationSettingKey);
}
const getAutomaticKeyboardNavigation = () => {
// give priority to the context key value to disable this completely
let automaticKeyboardNavigation = contextKeyService.getContextKeyValue<boolean>(WorkbenchListAutomaticKeyboardNavigationKey);
return automaticKeyboardNavigation;
};
if (automaticKeyboardNavigation) {
automaticKeyboardNavigation = configurationService.getValue<boolean>(automaticKeyboardNavigationSettingKey);
}
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService);
const openOnSingleClick = useSingleClickToOpen(configurationService);
const [workbenchListOptions, disposable] = toWorkbenchListOptions(options, configurationService, keybindingService);
return automaticKeyboardNavigation;
};
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService);
const openOnSingleClick = useSingleClickToOpen(configurationService);
const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService);
super(container, delegate, renderers, dataSource, {
return {
getAutomaticKeyboardNavigation,
disposable,
options: {
keyboardSupport: false,
styleController: new DefaultStyleController(getSharedListStyleSheet()),
...computeStyles(themeService.getTheme(), defaultListStyles),
@@ -1099,11 +906,30 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
horizontalScrolling,
openOnSingleClick,
keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService)
});
}
};
}
this.disposables.push(workbenchListOptionsDisposable);
class WorkbenchTreeInternals<TInput, T, TFilterData> {
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
readonly contextKeyService: IContextKeyService;
private hasSelectionOrFocus: IContextKey<boolean>;
private hasDoubleSelection: IContextKey<boolean>;
private hasMultiSelection: IContextKey<boolean>;
private _useAltAsMultipleSelectionModifier: boolean;
private disposables: IDisposable[] = [];
constructor(
tree: WorkbenchObjectTree<T, TFilterData> | WorkbenchDataTree<TInput, T, TFilterData> | WorkbenchAsyncDataTree<TInput, T, TFilterData>,
options: IAbstractTreeOptions<T, TFilterData>,
getAutomaticKeyboardNavigation: () => boolean | undefined,
@IContextKeyService contextKeyService: IContextKeyService,
@IListService listService: IListService,
@IThemeService themeService: IThemeService,
@IConfigurationService configurationService: IConfigurationService,
@IAccessibilityService accessibilityService: IAccessibilityService,
) {
this.contextKeyService = createScopedContextKeyService(contextKeyService, tree);
const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService);
listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false));
@@ -1119,7 +945,7 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
const updateKeyboardNavigation = () => {
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
this.updateOptions({
tree.updateOptions({
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter'
});
@@ -1127,43 +953,43 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
this.disposables.push(
this.contextKeyService,
(listService as ListService).register(this),
attachListStyler(this, themeService),
this.onDidChangeSelection(() => {
const selection = this.getSelection();
const focus = this.getFocus();
(listService as ListService).register(tree),
attachListStyler(tree, themeService),
tree.onDidChangeSelection(() => {
const selection = tree.getSelection();
const focus = tree.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
this.hasMultiSelection.set(selection.length > 1);
this.hasDoubleSelection.set(selection.length === 2);
}),
this.onDidChangeFocus(() => {
const selection = this.getSelection();
const focus = this.getFocus();
tree.onDidChangeFocus(() => {
const selection = tree.getSelection();
const focus = tree.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
}),
configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(openModeSettingKey)) {
this.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) });
tree.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) });
}
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
}
if (e.affectsConfiguration(treeIndentKey)) {
const indent = configurationService.getValue<number>(treeIndentKey);
this.updateOptions({ indent });
tree.updateOptions({ indent });
}
if (e.affectsConfiguration(keyboardNavigationSettingKey)) {
updateKeyboardNavigation();
}
if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) {
this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
tree.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
}
}),
this.contextKeyService.onDidChangeContext(e => {
if (e.affectsSome(interestingContextKeys)) {
this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
tree.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
}
}),
accessibilityService.onDidChangeAccessibilitySupport(() => updateKeyboardNavigation())
@@ -1173,6 +999,10 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
get useAltAsMultipleSelectionModifier(): boolean {
return this._useAltAsMultipleSelectionModifier;
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);

View File

@@ -11,7 +11,7 @@ export interface IRemoteAgentEnvironment {
pid: number;
appRoot: URI;
appSettingsHome: URI;
appSettingsPath: URI;
settingsPath: URI;
logsPath: URI;
extensionsPath: URI;
extensionHostLogsPath: URI;

View File

@@ -4,8 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
export const REMOTE_HOST_SCHEME = 'vscode-remote';
export const REMOTE_HOST_SCHEME = Schemas.vscodeRemote;
export function getRemoteAuthority(uri: URI): string | undefined {
return uri.scheme === REMOTE_HOST_SCHEME ? uri.authority : undefined;

View File

@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ITunnelService = createDecorator<ITunnelService>('tunnelService');
export interface RemoteTunnel {
readonly tunnelRemotePort: number;
readonly tunnelLocalPort: number;
dispose(): void;
}
export interface ITunnelService {
_serviceBrand: any;
openTunnel(remotePort: number): Promise<RemoteTunnel> | undefined;
}

View File

@@ -3,10 +3,42 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as net from 'net';
import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
import { IWebSocketFactory, IConnectCallback } from 'vs/platform/remote/common/remoteAgentConnection';
export const nodeWebSocketFactory = new class implements IWebSocketFactory {
connect(host: string, port: number, query: string, callback: IConnectCallback): void {
throw new Error(`Not implemented`);
const errorListener = (err: any) => callback(err, undefined);
const socket = net.createConnection({ host: host, port: port }, () => {
socket.removeListener('error', errorListener);
// https://tools.ietf.org/html/rfc6455#section-4
const buffer = Buffer.alloc(16);
for (let i = 0; i < 16; i++) {
buffer[i] = Math.round(Math.random() * 256);
}
const nonce = buffer.toString('base64');
let headers = [
`GET ws://${host}:${port}/?${query}&skipWebSocketFrames=true HTTP/1.1`,
`Connection: Upgrade`,
`Upgrade: websocket`,
`Sec-WebSocket-Key: ${nonce}`
];
socket.write(headers.join('\r\n') + '\r\n\r\n');
const onData = (data: Buffer) => {
const strData = data.toString();
if (strData.indexOf('\r\n\r\n') >= 0) {
// headers received OK
socket.off('data', onData);
callback(undefined, new NodeSocket(socket));
}
};
socket.on('data', onData);
});
socket.once('error', errorListener);
}
};

View File

@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
export class TunnelService implements ITunnelService {
_serviceBrand: any;
public constructor(
) {
}
openTunnel(remotePort: number): Promise<RemoteTunnel> | undefined {
return undefined;
}
}
registerSingleton(ITunnelService, TunnelService);

View File

@@ -67,13 +67,21 @@ export interface IStatusbarService {
_serviceBrand: any;
/**
* Adds an entry to the statusbar with the given alignment and priority. Use the returned IDisposable
* to remove the statusbar entry.
* Adds an entry to the statusbar with the given alignment and priority. Use the returned accessor
* to update or remove the statusbar entry.
*/
addEntry(entry: IStatusbarEntry, alignment: StatusbarAlignment, priority?: number): IDisposable;
addEntry(entry: IStatusbarEntry, alignment: StatusbarAlignment, priority?: number): IStatusbarEntryAccessor;
/**
* Prints something to the status bar area with optional auto dispose and delay.
*/
setStatusMessage(message: string, autoDisposeAfter?: number, delayBy?: number): IDisposable;
}
export interface IStatusbarEntryAccessor extends IDisposable {
/**
* Allows to update an existing status bar entry.
*/
update(properties: IStatusbarEntry): void;
}

View File

@@ -1,62 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITelemetryService, ITelemetryInfo, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Disposable } from 'vs/base/common/lifecycle';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IProductService } from 'vs/platform/product/common/product';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc';
import { ILogService } from 'vs/platform/log/common/log';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/node/workbenchCommonProperties';
import { TelemetryService as BaseTelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
export class TelemetryService extends Disposable implements ITelemetryService {
_serviceBrand: any;
private impl: ITelemetryService;
constructor(
@IEnvironmentService environmentService: IEnvironmentService,
@IProductService productService: IProductService,
@ISharedProcessService sharedProcessService: ISharedProcessService,
@ILogService logService: ILogService,
@IStorageService storageService: IStorageService,
@IConfigurationService configurationService: IConfigurationService,
@IWindowService windowService: IWindowService
) {
super();
if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.enableTelemetry) {
const channel = sharedProcessService.getChannel('telemetryAppender');
const config: ITelemetryServiceConfig = {
appender: combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)),
commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, windowService.getConfiguration().machineId, environmentService.installSourcePath),
piiPaths: [environmentService.appRoot, environmentService.extensionsPath]
};
this.impl = this._register(new BaseTelemetryService(config, configurationService));
} else {
this.impl = NullTelemetryService;
}
}
get isOptedIn(): boolean {
return this.impl.isOptedIn;
}
publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise<void> {
return this.impl.publicLog(eventName, data, anonymizeFilePaths);
}
getTelemetryInfo(): Promise<ITelemetryInfo> {
return this.impl.getTelemetryInfo();
}
}

View File

@@ -226,8 +226,8 @@ export interface IWindowService {
readonly hasFocus: boolean;
getConfiguration(): IWindowConfiguration;
getCurrentWindowId(): number;
readonly windowId: number;
pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void>;
pickFileAndOpen(options: INativeOpenDialogOptions): Promise<void>;
pickFolderAndOpen(options: INativeOpenDialogOptions): Promise<void>;
@@ -459,7 +459,7 @@ export class ActiveWindowManager implements IDisposable {
this.firstActiveWindowIdPromise = createCancelablePromise(_ => windowsService.getActiveWindowId());
this.firstActiveWindowIdPromise
.then(id => this.activeWindowId = id)
.then(id => this.activeWindowId = typeof this.activeWindowId === 'number' ? this.activeWindowId : id)
.finally(this.firstActiveWindowIdPromise = undefined);
}

View File

@@ -1,185 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { IWindowService, IWindowsService, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IWindowConfiguration, IDevToolsOptions, IOpenSettings, IURIToOpen, isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows';
import { IRecentlyOpened } from 'vs/platform/history/common/history';
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { URI } from 'vs/base/common/uri';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILabelService } from 'vs/platform/label/common/label';
export class WindowService extends Disposable implements IWindowService {
readonly onDidChangeFocus: Event<boolean>;
readonly onDidChangeMaximize: Event<boolean>;
_serviceBrand: any;
private windowId: number;
private _hasFocus: boolean;
get hasFocus(): boolean { return this._hasFocus; }
constructor(
private configuration: IWindowConfiguration,
@IWindowsService private readonly windowsService: IWindowsService,
@ILabelService private readonly labelService: ILabelService
) {
super();
this.windowId = configuration.windowId;
const onThisWindowFocus = Event.map(Event.filter(windowsService.onWindowFocus, id => id === this.windowId), _ => true);
const onThisWindowBlur = Event.map(Event.filter(windowsService.onWindowBlur, id => id === this.windowId), _ => false);
const onThisWindowMaximize = Event.map(Event.filter(windowsService.onWindowMaximize, id => id === this.windowId), _ => true);
const onThisWindowUnmaximize = Event.map(Event.filter(windowsService.onWindowUnmaximize, id => id === this.windowId), _ => false);
this.onDidChangeFocus = Event.any(onThisWindowFocus, onThisWindowBlur);
this.onDidChangeMaximize = Event.any(onThisWindowMaximize, onThisWindowUnmaximize);
this._hasFocus = document.hasFocus();
this.isFocused().then(focused => this._hasFocus = focused);
this._register(this.onDidChangeFocus(focus => this._hasFocus = focus));
}
getCurrentWindowId(): number {
return this.windowId;
}
getConfiguration(): IWindowConfiguration {
return this.configuration;
}
pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> {
options.windowId = this.windowId;
return this.windowsService.pickFileFolderAndOpen(options);
}
pickFileAndOpen(options: INativeOpenDialogOptions): Promise<void> {
options.windowId = this.windowId;
return this.windowsService.pickFileAndOpen(options);
}
pickFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> {
options.windowId = this.windowId;
return this.windowsService.pickFolderAndOpen(options);
}
pickWorkspaceAndOpen(options: INativeOpenDialogOptions): Promise<void> {
options.windowId = this.windowId;
return this.windowsService.pickWorkspaceAndOpen(options);
}
reloadWindow(args?: ParsedArgs): Promise<void> {
return this.windowsService.reloadWindow(this.windowId, args);
}
openDevTools(options?: IDevToolsOptions): Promise<void> {
return this.windowsService.openDevTools(this.windowId, options);
}
toggleDevTools(): Promise<void> {
return this.windowsService.toggleDevTools(this.windowId);
}
closeWorkspace(): Promise<void> {
return this.windowsService.closeWorkspace(this.windowId);
}
enterWorkspace(path: URI): Promise<IEnterWorkspaceResult | undefined> {
return this.windowsService.enterWorkspace(this.windowId, path);
}
openWindow(uris: IURIToOpen[], options: IOpenSettings = {}): Promise<void> {
if (!!this.configuration.remoteAuthority) {
uris.forEach(u => u.label = u.label || this.getRecentLabel(u));
}
return this.windowsService.openWindow(this.windowId, uris, options);
}
closeWindow(): Promise<void> {
return this.windowsService.closeWindow(this.windowId);
}
toggleFullScreen(): Promise<void> {
return this.windowsService.toggleFullScreen(this.windowId);
}
setRepresentedFilename(fileName: string): Promise<void> {
return this.windowsService.setRepresentedFilename(this.windowId, fileName);
}
getRecentlyOpened(): Promise<IRecentlyOpened> {
return this.windowsService.getRecentlyOpened(this.windowId);
}
focusWindow(): Promise<void> {
return this.windowsService.focusWindow(this.windowId);
}
isFocused(): Promise<boolean> {
return this.windowsService.isFocused(this.windowId);
}
isMaximized(): Promise<boolean> {
return this.windowsService.isMaximized(this.windowId);
}
maximizeWindow(): Promise<void> {
return this.windowsService.maximizeWindow(this.windowId);
}
unmaximizeWindow(): Promise<void> {
return this.windowsService.unmaximizeWindow(this.windowId);
}
minimizeWindow(): Promise<void> {
return this.windowsService.minimizeWindow(this.windowId);
}
onWindowTitleDoubleClick(): Promise<void> {
return this.windowsService.onWindowTitleDoubleClick(this.windowId);
}
setDocumentEdited(flag: boolean): Promise<void> {
return this.windowsService.setDocumentEdited(this.windowId, flag);
}
showMessageBox(options: Electron.MessageBoxOptions): Promise<IMessageBoxResult> {
return this.windowsService.showMessageBox(this.windowId, options);
}
showSaveDialog(options: Electron.SaveDialogOptions): Promise<string> {
return this.windowsService.showSaveDialog(this.windowId, options);
}
showOpenDialog(options: Electron.OpenDialogOptions): Promise<string[]> {
return this.windowsService.showOpenDialog(this.windowId, options);
}
updateTouchBar(items: ISerializableCommandAction[][]): Promise<void> {
return this.windowsService.updateTouchBar(this.windowId, items);
}
resolveProxy(url: string): Promise<string | undefined> {
return this.windowsService.resolveProxy(this.windowId, url);
}
private getRecentLabel(u: IURIToOpen): string {
if (isFolderToOpen(u)) {
return this.labelService.getWorkspaceLabel(u.folderUri, { verbose: true });
} else if (isWorkspaceToOpen(u)) {
return this.labelService.getWorkspaceLabel({ id: '', configPath: u.workspaceUri }, { verbose: true });
} else {
return this.labelService.getUriLabel(u.fileUri);
}
}
}