mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 1fbacccbc900bb59ba8a8f26a4128d48a1c97842
This commit is contained in:
@@ -13,6 +13,10 @@ import { OVERRIDE_PROPERTY_PATTERN, ConfigurationScope, IConfigurationRegistry,
|
||||
import { IOverrides, overrideIdentifierFromKey, addToValueTree, toValuesTree, IConfigurationModel, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, removeFromValueTree, toOverrides, IConfigurationValue, ConfigurationTarget, compare, IConfigurationChangeEvent, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
|
||||
import { Workspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { dirname } from 'vs/base/common/resources';
|
||||
|
||||
export class ConfigurationModel implements IConfigurationModel {
|
||||
|
||||
@@ -335,6 +339,40 @@ export class ConfigurationModelParser {
|
||||
}
|
||||
}
|
||||
|
||||
export class UserSettings extends Disposable {
|
||||
|
||||
private readonly parser: ConfigurationModelParser;
|
||||
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
constructor(
|
||||
private readonly userSettingsResource: URI,
|
||||
private readonly scopes: ConfigurationScope[] | undefined,
|
||||
private readonly fileService: IFileService
|
||||
) {
|
||||
super();
|
||||
this.parser = new ConfigurationModelParser(this.userSettingsResource.toString(), this.scopes);
|
||||
this._register(this.fileService.watch(dirname(this.userSettingsResource)));
|
||||
this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.userSettingsResource))(() => this._onDidChange.fire()));
|
||||
}
|
||||
|
||||
async loadConfiguration(): Promise<ConfigurationModel> {
|
||||
try {
|
||||
const content = await this.fileService.readFile(this.userSettingsResource);
|
||||
this.parser.parseContent(content.value.toString() || '{}');
|
||||
return this.parser.configurationModel;
|
||||
} catch (e) {
|
||||
return new ConfigurationModel();
|
||||
}
|
||||
}
|
||||
|
||||
reprocess(): ConfigurationModel {
|
||||
this.parser.parse();
|
||||
return this.parser.configurationModel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class Configuration {
|
||||
|
||||
private _workspaceConsolidatedConfiguration: ConfigurationModel | null = null;
|
||||
|
||||
@@ -7,54 +7,39 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
|
||||
import { DefaultConfigurationModel, Configuration, ConfigurationModel, ConfigurationModelParser, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { DefaultConfigurationModel, Configuration, ConfigurationModel, ConfigurationChangeEvent, UserSettings } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ConfigWatcher } from 'vs/base/node/config';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
||||
export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private configuration: Configuration;
|
||||
private userConfigModelWatcher: ConfigWatcher<ConfigurationModelParser> | undefined;
|
||||
private userConfiguration: UserSettings;
|
||||
private readonly reloadConfigurationScheduler: RunOnceScheduler;
|
||||
|
||||
private readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
|
||||
readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
|
||||
|
||||
constructor(
|
||||
private readonly settingsResource: URI
|
||||
private readonly settingsResource: URI,
|
||||
fileService: IFileService
|
||||
) {
|
||||
super();
|
||||
this.userConfiguration = this._register(new UserSettings(this.settingsResource, undefined, fileService));
|
||||
this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel());
|
||||
|
||||
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reloadConfiguration(), 50));
|
||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidUpdateConfiguration(configurationProperties => this.onDidDefaultConfigurationChange(configurationProperties)));
|
||||
this._register(this.userConfiguration.onDidChange(() => this.reloadConfigurationScheduler.schedule()));
|
||||
}
|
||||
|
||||
initialize(): Promise<void> {
|
||||
if (this.userConfigModelWatcher) {
|
||||
this.userConfigModelWatcher.dispose();
|
||||
}
|
||||
|
||||
if (this.settingsResource.scheme !== Schemas.file) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return new Promise<void>((c, e) => {
|
||||
this.userConfigModelWatcher = this._register(new ConfigWatcher(this.settingsResource.fsPath, {
|
||||
changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new ConfigurationModelParser(this.settingsResource.fsPath), parse: (content: string, parseErrors: any[]) => {
|
||||
const userConfigModelParser = new ConfigurationModelParser(this.settingsResource.fsPath);
|
||||
userConfigModelParser.parseContent(content);
|
||||
parseErrors = [...userConfigModelParser.errors];
|
||||
return userConfigModelParser;
|
||||
}, initCallback: () => {
|
||||
this.configuration = new Configuration(new DefaultConfigurationModel(), this.userConfigModelWatcher!.getConfig().configurationModel);
|
||||
this._register(this.userConfigModelWatcher!.onDidUpdateConfiguration(() => this.onDidChangeUserConfiguration(this.userConfigModelWatcher!.getConfig().configurationModel)));
|
||||
c();
|
||||
}
|
||||
}));
|
||||
});
|
||||
async initialize(): Promise<void> {
|
||||
const userConfiguration = await this.userConfiguration.loadConfiguration();
|
||||
this.configuration = new Configuration(new DefaultConfigurationModel(), userConfiguration);
|
||||
}
|
||||
|
||||
getConfigurationData(): IConfigurationData {
|
||||
@@ -92,14 +77,9 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
|
||||
return this.configuration.keys(undefined);
|
||||
}
|
||||
|
||||
reloadConfiguration(folder?: IWorkspaceFolder): Promise<void> {
|
||||
if (this.userConfigModelWatcher) {
|
||||
return new Promise<void>(c => this.userConfigModelWatcher!.reload(userConfigModelParser => {
|
||||
this.onDidChangeUserConfiguration(userConfigModelParser.configurationModel);
|
||||
c();
|
||||
}));
|
||||
}
|
||||
return this.initialize();
|
||||
async reloadConfiguration(): Promise<void> {
|
||||
const configurationModel = await this.userConfiguration.loadConfiguration();
|
||||
this.onDidChangeUserConfiguration(configurationModel);
|
||||
}
|
||||
|
||||
private onDidChangeUserConfiguration(userConfigurationModel: ConfigurationModel): void {
|
||||
|
||||
@@ -16,14 +16,32 @@ import { testFile } from 'vs/base/test/node/utils';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
suite('ConfigurationService - Node', () => {
|
||||
|
||||
let fileService: IFileService;
|
||||
const disposables: IDisposable[] = [];
|
||||
|
||||
setup(() => {
|
||||
const logService = new NullLogService();
|
||||
fileService = new FileService(logService);
|
||||
disposables.push(fileService);
|
||||
const diskFileSystemProvider = new DiskFileSystemProvider(logService);
|
||||
disposables.push(diskFileSystemProvider);
|
||||
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
|
||||
});
|
||||
|
||||
test('simple', async () => {
|
||||
const res = await testFile('config', 'config.json');
|
||||
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
|
||||
|
||||
const service = new ConfigurationService(URI.file(res.testFile));
|
||||
const service = new ConfigurationService(URI.file(res.testFile), fileService);
|
||||
await service.initialize();
|
||||
const config = service.getValue<{
|
||||
foo: string;
|
||||
@@ -41,7 +59,7 @@ suite('ConfigurationService - Node', () => {
|
||||
|
||||
fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }');
|
||||
|
||||
const service = new ConfigurationService(URI.file(res.testFile));
|
||||
const service = new ConfigurationService(URI.file(res.testFile), fileService);
|
||||
await service.initialize();
|
||||
const config = service.getValue<{
|
||||
testworkbench: {
|
||||
@@ -64,7 +82,7 @@ suite('ConfigurationService - Node', () => {
|
||||
|
||||
fs.writeFileSync(res.testFile, ',,,,');
|
||||
|
||||
const service = new ConfigurationService(URI.file(res.testFile));
|
||||
const service = new ConfigurationService(URI.file(res.testFile), fileService);
|
||||
await service.initialize();
|
||||
const config = service.getValue<{
|
||||
foo: string;
|
||||
@@ -81,7 +99,7 @@ suite('ConfigurationService - Node', () => {
|
||||
const newDir = path.join(parentDir, 'config', id);
|
||||
const testFile = path.join(newDir, 'config.json');
|
||||
|
||||
const service = new ConfigurationService(URI.file(testFile));
|
||||
const service = new ConfigurationService(URI.file(testFile), fileService);
|
||||
await service.initialize();
|
||||
|
||||
const config = service.getValue<{ foo: string }>();
|
||||
@@ -90,10 +108,10 @@ suite('ConfigurationService - Node', () => {
|
||||
service.dispose();
|
||||
});
|
||||
|
||||
test('trigger configuration change event', async () => {
|
||||
test('trigger configuration change event when file does not exist', async () => {
|
||||
const res = await testFile('config', 'config.json');
|
||||
|
||||
const service = new ConfigurationService(URI.file(res.testFile));
|
||||
const service = new ConfigurationService(URI.file(res.testFile), fileService);
|
||||
await service.initialize();
|
||||
return new Promise((c, e) => {
|
||||
const disposable = Event.filter(service.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => {
|
||||
@@ -108,12 +126,31 @@ suite('ConfigurationService - Node', () => {
|
||||
|
||||
});
|
||||
|
||||
test('trigger configuration change event when file exists', async () => {
|
||||
const res = await testFile('config', 'config.json');
|
||||
|
||||
const service = new ConfigurationService(URI.file(res.testFile), fileService);
|
||||
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
|
||||
await service.initialize();
|
||||
return new Promise((c, e) => {
|
||||
const disposable = Event.filter(service.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => {
|
||||
disposable.dispose();
|
||||
assert.equal(service.getValue('foo'), 'barz');
|
||||
service.dispose();
|
||||
await res.cleanUp();
|
||||
c();
|
||||
});
|
||||
fs.writeFileSync(res.testFile, '{ "foo": "barz" }');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('reloadConfiguration', async () => {
|
||||
const res = await testFile('config', 'config.json');
|
||||
|
||||
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
|
||||
|
||||
const service = new ConfigurationService(URI.file(res.testFile));
|
||||
const service = new ConfigurationService(URI.file(res.testFile), fileService);
|
||||
await service.initialize();
|
||||
let config = service.getValue<{
|
||||
foo: string;
|
||||
@@ -162,7 +199,7 @@ suite('ConfigurationService - Node', () => {
|
||||
}
|
||||
});
|
||||
|
||||
let serviceWithoutFile = new ConfigurationService(URI.file('__testFile'));
|
||||
let serviceWithoutFile = new ConfigurationService(URI.file('__testFile'), fileService);
|
||||
await serviceWithoutFile.initialize();
|
||||
let setting = serviceWithoutFile.getValue<ITestSetting>();
|
||||
|
||||
@@ -172,7 +209,7 @@ suite('ConfigurationService - Node', () => {
|
||||
return testFile('config', 'config.json').then(async res => {
|
||||
fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }');
|
||||
|
||||
const service = new ConfigurationService(URI.file(res.testFile));
|
||||
const service = new ConfigurationService(URI.file(res.testFile), fileService);
|
||||
|
||||
let setting = service.getValue<ITestSetting>();
|
||||
|
||||
@@ -205,7 +242,7 @@ suite('ConfigurationService - Node', () => {
|
||||
});
|
||||
|
||||
const r = await testFile('config', 'config.json');
|
||||
const service = new ConfigurationService(URI.file(r.testFile));
|
||||
const service = new ConfigurationService(URI.file(r.testFile), fileService);
|
||||
service.initialize();
|
||||
|
||||
let res = service.inspect('something.missing');
|
||||
@@ -243,7 +280,7 @@ suite('ConfigurationService - Node', () => {
|
||||
});
|
||||
|
||||
const r = await testFile('config', 'config.json');
|
||||
const service = new ConfigurationService(URI.file(r.testFile));
|
||||
const service = new ConfigurationService(URI.file(r.testFile), fileService);
|
||||
service.initialize();
|
||||
|
||||
let res = service.inspect('lookup.service.testNullSetting');
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ipcMain as ipc, app } from 'electron';
|
||||
import { ipcMain as ipc, app, BrowserWindow } from 'electron';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IStateService } from 'vs/platform/state/node/state';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
@@ -12,7 +12,7 @@ import { ICodeWindow } from 'vs/platform/windows/electron-main/windows';
|
||||
import { handleVetos } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
import { Barrier, timeout } from 'vs/base/common/async';
|
||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
|
||||
export const ILifecycleMainService = createDecorator<ILifecycleMainService>('lifecycleMainService');
|
||||
@@ -106,7 +106,7 @@ export interface ILifecycleMainService {
|
||||
/**
|
||||
* Forcefully shutdown the application. No livecycle event handlers are triggered.
|
||||
*/
|
||||
kill(code?: number): void;
|
||||
kill(code?: number): Promise<void>;
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves when a certain lifecycle phase
|
||||
@@ -550,9 +550,45 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
|
||||
this.quit().then(veto => quitVetoed = veto);
|
||||
}
|
||||
|
||||
kill(code?: number): void {
|
||||
async kill(code?: number): Promise<void> {
|
||||
this.logService.trace('Lifecycle#kill()');
|
||||
|
||||
// The kill() method is only used in 2 situations:
|
||||
// - when an instance fails to start at all
|
||||
// - when extension tests run from CLI to report proper exit code
|
||||
//
|
||||
// From extension tests we have seen issues where calling app.exit()
|
||||
// with an opened window can lead to native crashes (Linux) when webviews
|
||||
// are involved. As such, we should make sure to destroy any opened
|
||||
// window before calling app.exit().
|
||||
//
|
||||
// Note: Electron implements a similar logic here:
|
||||
// https://github.com/electron/electron/blob/fe5318d753637c3903e23fc1ed1b263025887b6a/spec-main/window-helpers.ts#L5
|
||||
|
||||
await Promise.race([
|
||||
|
||||
// still do not block more than 1s
|
||||
timeout(1000),
|
||||
|
||||
// destroy any opened window
|
||||
(async () => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window && !window.isDestroyed()) {
|
||||
let whenWindowClosed: Promise<void>;
|
||||
if (window.webContents && !window.webContents.isDestroyed()) {
|
||||
whenWindowClosed = new Promise(c => window.once('closed', c));
|
||||
} else {
|
||||
whenWindowClosed = Promise.resolve();
|
||||
}
|
||||
|
||||
window.destroy();
|
||||
await whenWindowClosed;
|
||||
}
|
||||
}
|
||||
})()
|
||||
]);
|
||||
|
||||
// Now exit either after 1s or all windows destroyed
|
||||
app.exit(code);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,11 +243,13 @@ export class WorkbenchList<T> extends List<T> {
|
||||
|
||||
readonly contextKeyService: IContextKeyService;
|
||||
private readonly configurationService: IConfigurationService;
|
||||
private readonly themeService: IThemeService;
|
||||
|
||||
private listHasSelectionOrFocus: IContextKey<boolean>;
|
||||
private listDoubleSelection: IContextKey<boolean>;
|
||||
private listMultiSelection: IContextKey<boolean>;
|
||||
|
||||
private _styler: IDisposable | undefined;
|
||||
private _useAltAsMultipleSelectionModifier: boolean;
|
||||
|
||||
constructor(
|
||||
@@ -278,6 +280,7 @@ export class WorkbenchList<T> extends List<T> {
|
||||
|
||||
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
|
||||
this.configurationService = configurationService;
|
||||
this.themeService = themeService;
|
||||
|
||||
const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService);
|
||||
listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false));
|
||||
@@ -292,7 +295,7 @@ export class WorkbenchList<T> extends List<T> {
|
||||
this.disposables.add((listService as ListService).register(this));
|
||||
|
||||
if (options.overrideStyles) {
|
||||
this.disposables.add(attachListStyler(this, themeService, options.overrideStyles));
|
||||
this.updateStyles(options.overrideStyles);
|
||||
}
|
||||
|
||||
this.disposables.add(this.onSelectionChange(() => {
|
||||
@@ -313,6 +316,29 @@ export class WorkbenchList<T> extends List<T> {
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
updateOptions(options: IWorkbenchListOptions<T>): void {
|
||||
super.updateOptions(options);
|
||||
|
||||
if (options.overrideStyles) {
|
||||
this.updateStyles(options.overrideStyles);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
if (this._styler) {
|
||||
this._styler.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private updateStyles(styles: IColorMapping): void {
|
||||
if (this._styler) {
|
||||
this._styler.dispose();
|
||||
}
|
||||
|
||||
this._styler = attachListStyler(this, this.themeService, styles);
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.disposables.add(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
|
||||
@@ -738,7 +764,7 @@ export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void>
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble<T, TFilterData, IWorkbenchObjectTreeOptions<T, TFilterData>>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
super(user, container, delegate, renderers, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
@@ -769,12 +795,20 @@ export class WorkbenchCompressibleObjectTree<T extends NonNullable<any>, TFilter
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble<T, TFilterData, IWorkbenchCompressibleObjectTreeOptions<T, TFilterData>>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
super(user, container, delegate, renderers, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.disposables.add(this.internals);
|
||||
}
|
||||
|
||||
updateOptions(options: IWorkbenchAsyncDataTreeOptions<T, TFilterData> = {}): void {
|
||||
super.updateOptions(options);
|
||||
|
||||
if (options.overrideStyles) {
|
||||
this.internals.updateStyleOverrides(options.overrideStyles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWorkbenchDataTreeOptions<T, TFilterData> extends IDataTreeOptions<T, TFilterData> {
|
||||
@@ -801,7 +835,7 @@ export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<T
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble<T, TFilterData, IWorkbenchDataTreeOptions<T, TFilterData>>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
super(user, container, delegate, renderers, dataSource, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
@@ -841,7 +875,7 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble<T, TFilterData, IWorkbenchAsyncDataTreeOptions<T, TFilterData>>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
super(user, container, delegate, renderers, dataSource, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
@@ -882,7 +916,7 @@ export class WorkbenchCompressibleAsyncDataTree<TInput, T, TFilterData = void> e
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble<T, TFilterData, IWorkbenchCompressibleAsyncDataTreeOptions<T, TFilterData>>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
super(user, container, virtualDelegate, compressionDelegate, renderers, dataSource, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
|
||||
@@ -64,7 +64,7 @@ class MarkerStats implements MarkerStatistics {
|
||||
this._data = undefined;
|
||||
}
|
||||
|
||||
private _update(resources: URI[]): void {
|
||||
private _update(resources: readonly URI[]): void {
|
||||
if (!this._data) {
|
||||
return;
|
||||
}
|
||||
@@ -343,7 +343,7 @@ export class MarkerService implements IMarkerService {
|
||||
|
||||
private static _dedupeMap: { [uri: string]: boolean };
|
||||
|
||||
private static _debouncer(last: URI[], event: URI[]): URI[] {
|
||||
private static _debouncer(last: URI[] | undefined, event: readonly URI[]): URI[] {
|
||||
if (!last) {
|
||||
MarkerService._dedupeMap = Object.create(null);
|
||||
last = [];
|
||||
|
||||
64
src/vs/platform/opener/browser/link.ts
Normal file
64
src/vs/platform/opener/browser/link.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { $ } from 'vs/base/browser/dom';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
export interface ILinkDescriptor {
|
||||
readonly label: string;
|
||||
readonly href: string;
|
||||
readonly title?: string;
|
||||
}
|
||||
|
||||
export interface ILinkStyles {
|
||||
readonly textLinkForeground?: Color;
|
||||
}
|
||||
|
||||
export class Link extends Disposable {
|
||||
|
||||
readonly el: HTMLAnchorElement;
|
||||
private styles: ILinkStyles = {
|
||||
textLinkForeground: Color.fromHex('#006AB1')
|
||||
};
|
||||
|
||||
constructor(
|
||||
link: ILinkDescriptor,
|
||||
@IOpenerService openerService: IOpenerService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.el = $<HTMLAnchorElement>('a', {
|
||||
tabIndex: 0,
|
||||
href: link.href,
|
||||
title: link.title
|
||||
}, link.label);
|
||||
|
||||
const onClick = domEvent(this.el, 'click');
|
||||
const onEnterPress = Event.chain(domEvent(this.el, 'keypress'))
|
||||
.map(e => new StandardKeyboardEvent(e))
|
||||
.filter(e => e.keyCode === KeyCode.Enter)
|
||||
.event;
|
||||
const onOpen = Event.any(onClick, onEnterPress);
|
||||
|
||||
this._register(onOpen(_ => openerService.open(link.href)));
|
||||
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
style(styles: ILinkStyles): void {
|
||||
this.styles = styles;
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
private applyStyles(): void {
|
||||
this.el.style.color = this.styles.textLinkForeground?.toString() || '';
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { IThemable, styleFn } from 'vs/base/common/styler';
|
||||
@@ -269,6 +269,16 @@ export function attachButtonStyler(widget: IThemable, themeService: IThemeServic
|
||||
} as IButtonStyleOverrides, widget);
|
||||
}
|
||||
|
||||
export interface ILinkStyleOverrides extends IStyleOverrides {
|
||||
textLinkForeground?: ColorIdentifier;
|
||||
}
|
||||
|
||||
export function attachLinkStyler(widget: IThemable, themeService: IThemeService, style?: ILinkStyleOverrides): IDisposable {
|
||||
return attachStyler(themeService, {
|
||||
textLinkForeground: (style && style.textLinkForeground) || textLinkForeground,
|
||||
} as ILinkStyleOverrides, widget);
|
||||
}
|
||||
|
||||
export interface IProgressBarStyleOverrides extends IStyleOverrides {
|
||||
progressBarBackground?: ColorIdentifier;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user