Merge from vscode fb5dc0083bfa9a0e3da7ed1f86e1ecb9836fcc8b

This commit is contained in:
ADS Merger
2020-03-13 05:35:18 +00:00
parent 7658a5df28
commit a7e56d334f
88 changed files with 1627 additions and 553 deletions

View File

@@ -17,7 +17,7 @@ import { IStoredWorkspaceFolder, IWorkspaceIdentifier } from 'vs/platform/worksp
import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService';
import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { extname, join } from 'vs/base/common/path';
import { join } from 'vs/base/common/path';
import { equals } from 'vs/base/common/objects';
import { Schemas } from 'vs/base/common/network';
import { IConfigurationModel } from 'vs/platform/configuration/common/configuration';
@@ -30,7 +30,7 @@ export class UserConfiguration extends Disposable {
private readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
private readonly userConfiguration: MutableDisposable<UserSettings | FileServiceBasedConfigurationWithNames> = this._register(new MutableDisposable<UserSettings | FileServiceBasedConfigurationWithNames>());
private readonly userConfiguration: MutableDisposable<UserSettings | FileServiceBasedConfiguration> = this._register(new MutableDisposable<UserSettings | FileServiceBasedConfiguration>());
private readonly reloadConfigurationScheduler: RunOnceScheduler;
constructor(
@@ -52,8 +52,10 @@ export class UserConfiguration extends Disposable {
}
async reload(): Promise<ConfigurationModel> {
if (!(this.userConfiguration.value instanceof FileServiceBasedConfigurationWithNames)) {
this.userConfiguration.value = new FileServiceBasedConfigurationWithNames(resources.dirname(this.userSettingsResource), [FOLDER_SETTINGS_NAME, TASKS_CONFIGURATION_KEY], this.scopes, this.fileService);
if (!(this.userConfiguration.value instanceof FileServiceBasedConfiguration)) {
const folder = resources.dirname(this.userSettingsResource);
const standAloneConfigurationResources: [string, URI][] = [TASKS_CONFIGURATION_KEY].map(name => ([name, resources.joinPath(folder, `${name}.json`)]));
this.userConfiguration.value = new FileServiceBasedConfiguration(folder.toString(), [this.userSettingsResource], standAloneConfigurationResources, this.scopes, this.fileService);
this._register(this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule()));
}
return this.userConfiguration.value!.loadConfiguration();
@@ -64,24 +66,27 @@ export class UserConfiguration extends Disposable {
}
}
class FileServiceBasedConfigurationWithNames extends Disposable {
class FileServiceBasedConfiguration extends Disposable {
private readonly allResources: URI[];
private _folderSettingsModelParser: ConfigurationModelParser;
private _standAloneConfigurations: ConfigurationModel[];
private _cache: ConfigurationModel;
protected readonly configurationResources: URI[];
protected changeEventTriggerScheduler: RunOnceScheduler;
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
private readonly changeEventTriggerScheduler: RunOnceScheduler;
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = this._onDidChange.event;
constructor(protected readonly configurationFolder: URI,
private readonly configurationNames: string[],
constructor(
name: string,
private readonly settingsResources: URI[],
private readonly standAloneConfigurationResources: [string, URI][],
private readonly scopes: ConfigurationScope[] | undefined,
private fileService: IFileService) {
private fileService: IFileService
) {
super();
this.configurationResources = this.configurationNames.map(name => resources.joinPath(this.configurationFolder, `${name}.json`));
this._folderSettingsModelParser = new ConfigurationModelParser(this.configurationFolder.toString(), this.scopes);
this.allResources = [...this.settingsResources, ...this.standAloneConfigurationResources.map(([, resource]) => resource)];
this._folderSettingsModelParser = new ConfigurationModelParser(name, this.scopes);
this._standAloneConfigurations = [];
this._cache = new ConfigurationModel();
@@ -90,30 +95,37 @@ class FileServiceBasedConfigurationWithNames extends Disposable {
}
async loadConfiguration(): Promise<ConfigurationModel> {
const configurationContents = await Promise.all(this.configurationResources.map(async resource => {
try {
const content = await this.fileService.readFile(resource);
return content.value.toString();
} catch (error) {
if ((<FileOperationError>error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) {
errors.onUnexpectedError(error);
const resolveContents = async (resources: URI[]): Promise<(string | undefined)[]> => {
return Promise.all(resources.map(async resource => {
try {
const content = await this.fileService.readFile(resource);
return content.value.toString();
} catch (error) {
if ((<FileOperationError>error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) {
errors.onUnexpectedError(error);
}
}
}
return undefined;
}));
return undefined;
}));
};
const [settingsContents, standAloneConfigurationContents] = await Promise.all([
resolveContents(this.settingsResources),
resolveContents(this.standAloneConfigurationResources.map(([, resource]) => resource)),
]);
// reset
this._standAloneConfigurations = [];
this._folderSettingsModelParser.parseContent('');
// parse
if (configurationContents[0]) {
this._folderSettingsModelParser.parseContent(configurationContents[0]);
if (settingsContents[0]) {
this._folderSettingsModelParser.parseContent(settingsContents[0]);
}
for (let index = 1; index < configurationContents.length; index++) {
const contents = configurationContents[index];
for (let index = 0; index < standAloneConfigurationContents.length; index++) {
const contents = standAloneConfigurationContents[index];
if (contents) {
const standAloneConfigurationModelParser = new StandaloneConfigurationModelParser(this.configurationResources[index].toString(), this.configurationNames[index]);
const standAloneConfigurationModelParser = new StandaloneConfigurationModelParser(this.standAloneConfigurationResources[index][1].toString(), this.standAloneConfigurationResources[index][0]);
standAloneConfigurationModelParser.parseContent(contents);
this._standAloneConfigurations.push(standAloneConfigurationModelParser.configurationModel);
}
@@ -139,49 +151,22 @@ class FileServiceBasedConfigurationWithNames extends Disposable {
}
protected async handleFileEvents(event: FileChangesEvent): Promise<void> {
const events = event.changes;
let affectedByChanges = false;
// Find changes that affect workspace configuration files
for (let i = 0, len = events.length; i < len; i++) {
const resource = events[i].resource;
const basename = resources.basename(resource);
const isJson = extname(basename) === '.json';
const isConfigurationFolderDeleted = (events[i].type === FileChangeType.DELETED && resources.isEqual(resource, this.configurationFolder));
if (!isJson && !isConfigurationFolderDeleted) {
continue; // only JSON files or the actual settings folder
const isAffectedByChanges = (): boolean => {
// One of the resources has changed
if (this.allResources.some(resource => event.contains(resource))) {
return true;
}
const folderRelativePath = this.toFolderRelativePath(resource);
if (!folderRelativePath) {
continue; // event is not inside folder
// One of the resource's parent got deleted
if (this.allResources.some(resource => event.contains(resources.dirname(resource), FileChangeType.DELETED))) {
return true;
}
// Handle case where ".vscode" got deleted
if (isConfigurationFolderDeleted) {
affectedByChanges = true;
break;
}
// only valid workspace config files
if (this.configurationResources.some(configurationResource => resources.isEqual(configurationResource, resource))) {
affectedByChanges = true;
break;
}
}
if (affectedByChanges) {
return false;
};
if (isAffectedByChanges()) {
this.changeEventTriggerScheduler.schedule();
}
}
private toFolderRelativePath(resource: URI): string | undefined {
if (resources.isEqualOrParent(resource, this.configurationFolder)) {
return resources.relativePath(this.configurationFolder, resource);
}
return undefined;
}
}
export class RemoteUserConfiguration extends Disposable {
@@ -667,14 +652,6 @@ export interface IFolderConfiguration extends IDisposable {
reprocess(): ConfigurationModel;
}
class FileServiceBasedFolderConfiguration extends FileServiceBasedConfigurationWithNames implements IFolderConfiguration {
constructor(configurationFolder: URI, workbenchState: WorkbenchState, fileService: IFileService) {
super(configurationFolder, [FOLDER_SETTINGS_NAME /*First one should be settings */, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY], WorkbenchState.WORKSPACE === workbenchState ? FOLDER_SCOPES : WORKSPACE_SCOPES, fileService);
}
}
class CachedFolderConfiguration extends Disposable implements IFolderConfiguration {
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
@@ -742,13 +719,13 @@ export class FolderConfiguration extends Disposable implements IFolderConfigurat
this.configurationFolder = resources.joinPath(workspaceFolder.uri, configFolderRelativePath);
this.folderConfiguration = this.cachedFolderConfiguration = new CachedFolderConfiguration(workspaceFolder.uri, configFolderRelativePath, configurationCache);
if (workspaceFolder.uri.scheme === Schemas.file) {
this.folderConfiguration = new FileServiceBasedFolderConfiguration(this.configurationFolder, this.workbenchState, fileService);
this.folderConfiguration = this.createFileServiceBasedConfiguration(fileService);
} else {
whenProviderRegistered(workspaceFolder.uri, fileService)
.then(() => {
this.folderConfiguration.dispose();
this.folderConfigurationDisposable.dispose();
this.folderConfiguration = new FileServiceBasedFolderConfiguration(this.configurationFolder, this.workbenchState, fileService);
this.folderConfiguration = this.createFileServiceBasedConfiguration(fileService);
this._register(this.folderConfiguration.onDidChange(e => this.onDidFolderConfigurationChange()));
this.onDidFolderConfigurationChange();
});
@@ -769,8 +746,14 @@ export class FolderConfiguration extends Disposable implements IFolderConfigurat
this._onDidChange.fire();
}
private createFileServiceBasedConfiguration(fileService: IFileService) {
const settingsResources = [resources.joinPath(this.configurationFolder, `${FOLDER_SETTINGS_NAME}.json`)];
const standAloneConfigurationResources: [string, URI][] = [TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY].map(name => ([name, resources.joinPath(this.configurationFolder, `${name}.json`)]));
return new FileServiceBasedConfiguration(this.configurationFolder.toString(), settingsResources, standAloneConfigurationResources, WorkbenchState.WORKSPACE === this.workbenchState ? FOLDER_SCOPES : WORKSPACE_SCOPES, fileService);
}
private updateCache(): Promise<void> {
if (this.configurationFolder.scheme !== Schemas.file && this.folderConfiguration instanceof FileServiceBasedFolderConfiguration) {
if (this.configurationFolder.scheme !== Schemas.file && this.folderConfiguration instanceof FileServiceBasedConfiguration) {
return this.folderConfiguration.loadConfiguration()
.then(configurationModel => this.cachedFolderConfiguration.updateConfiguration(configurationModel));
}

View File

@@ -88,8 +88,8 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
}
async showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise<ConfirmResult> {
if (this.environmentService.isExtensionDevelopment) {
return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests)
if (this.environmentService.isExtensionDevelopment && this.environmentService.extensionTestsLocationURI) {
return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev testing mode because we cannot assume we run interactive
}
return this.doShowSaveConfirm(fileNamesOrResources);

View File

@@ -6,7 +6,7 @@
import { SaveDialogOptions, OpenDialogOptions } from 'electron';
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, IDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@@ -190,16 +190,6 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
// Don't allow untitled schema through.
return schema === Schemas.untitled ? [Schemas.file] : (schema !== Schemas.file ? [schema, Schemas.file] : [schema]);
}
async showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise<ConfirmResult> {
if (this.environmentService.isExtensionDevelopment) {
if (!this.environmentService.args['extension-development-confirm-save']) {
return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests)
}
}
return super.doShowSaveConfirm(fileNamesOrResources);
}
}
registerSingleton(IFileDialogService, FileDialogService, true);

View File

@@ -146,6 +146,10 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
return false;
}
}
if (extensionKind === 'web') {
// Web extensions are not yet supported to be disabled by kind
return false;
}
}
return true;
}

View File

@@ -490,6 +490,30 @@ suite('ExtensionEnablementService Test', () => {
assert.equal(testObject.canChangeEnablement(localWorkspaceExtension), true);
});
test('test web extension on local server is not disabled by kind', async () => {
instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService));
const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`) });
testObject = new TestExtensionEnablementService(instantiationService);
assert.ok(testObject.isEnabled(localWorkspaceExtension));
assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally);
});
test('test web extension on remote server is not disabled by kind', async () => {
instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService));
const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
testObject = new TestExtensionEnablementService(instantiationService);
assert.ok(testObject.isEnabled(localWorkspaceExtension));
assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally);
});
test('test web extension with no server is not disabled by kind', async () => {
instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService));
const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.https }) });
testObject = new TestExtensionEnablementService(instantiationService);
assert.ok(testObject.isEnabled(localWorkspaceExtension));
assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally);
});
});
function anExtensionManagementServer(authority: string, instantiationService: TestInstantiationService): IExtensionManagementServer {

View File

@@ -289,6 +289,11 @@ export const schema: IJSONSchema = {
body: 'onUri',
description: nls.localize('vscode.extension.activationEvents.onUri', 'An activation event emitted whenever a system-wide Uri directed towards this extension is open.'),
},
{
label: 'onCustomEditor',
body: 'onCustomEditor:${9:viewType}',
description: nls.localize('vscode.extension.activationEvents.onCustomEditor', 'An activation event emitted whenever the specified custom editor becomes visible.'),
},
{
label: '*',
description: nls.localize('vscode.extension.activationEvents.star', 'An activation event emitted on VS Code startup. To ensure a great end user experience, please use this activation event in your extension only when no other activation events combination works in your use-case.'),

View File

@@ -671,7 +671,7 @@ registerSingleton(IExtensionService, ExtensionService);
class RestartExtensionHostAction extends Action {
public static readonly ID = 'workbench.action.restartExtensionHost';
public static readonly LABEL = nls.localize('restartExtensionHost', "Developer: Restart Extension Host");
public static readonly LABEL = nls.localize('restartExtensionHost', "Restart Extension Host");
constructor(
id: string,
@@ -687,4 +687,4 @@ class RestartExtensionHostAction extends Action {
}
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(SyncActionDescriptor.create(RestartExtensionHostAction, RestartExtensionHostAction.ID, RestartExtensionHostAction.LABEL), 'Developer: Restart Extension Host');
registry.registerWorkbenchAction(SyncActionDescriptor.create(RestartExtensionHostAction, RestartExtensionHostAction.ID, RestartExtensionHostAction.LABEL), 'Developer: Restart Extension Host', nls.localize('developer', "Developer"));

View File

@@ -11,7 +11,6 @@ import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/cont
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IFilesConfiguration, AutoSaveConfiguration, HotExitConfiguration } from 'vs/platform/files/common/files';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { equals } from 'vs/base/common/objects';
import { URI } from 'vs/base/common/uri';
import { isWeb } from 'vs/base/common/platform';
@@ -83,8 +82,7 @@ export class FilesConfigurationService extends Disposable implements IFilesConfi
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
@IConfigurationService private readonly configurationService: IConfigurationService
) {
super();
@@ -203,7 +201,7 @@ export class FilesConfigurationService extends Disposable implements IFilesConfi
}
get isHotExitEnabled(): boolean {
return !this.environmentService.isExtensionDevelopment && this.currentHotExitConfig !== HotExitConfiguration.OFF;
return this.currentHotExitConfig !== HotExitConfiguration.OFF;
}
get hotExitConfiguration(): string {

View File

@@ -293,9 +293,11 @@ export class ColorThemeData implements IWorkbenchColorTheme {
const settings = tokenColors[i].settings;
if (score >= foregroundScore && settings.foreground) {
foreground = settings.foreground;
foregroundScore = score;
}
if (score >= fontStyleScore && types.isString(settings.fontStyle)) {
fontStyle = settings.fontStyle;
fontStyleScore = score;
}
}
}
@@ -657,7 +659,7 @@ function nameMatcher(identifers: string[], scope: ProbeScope): number {
let lastScopeIndex = scope.length - 1;
let lastIdentifierIndex = findInIdents(scope[lastScopeIndex--], identifers.length);
if (lastIdentifierIndex >= 0) {
const score = (lastIdentifierIndex + 1) * 0x10000 + scope.length;
const score = (lastIdentifierIndex + 1) * 0x10000 + identifers[lastIdentifierIndex].length;
while (lastScopeIndex >= 0) {
lastIdentifierIndex = findInIdents(scope[lastScopeIndex--], lastIdentifierIndex);
if (lastIdentifierIndex === -1) {

View File

@@ -289,6 +289,41 @@ suite('Themes - TokenStyleResolving', () => {
});
test('resolveScopes - match most specific', async () => {
const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test');
const customTokenColors: ITokenColorCustomizations = {
textMateRules: [
{
scope: 'entity.name.type',
settings: {
fontStyle: 'underline',
foreground: '#A6E22E'
}
},
{
scope: 'entity.name.type.class',
settings: {
foreground: '#FF00FF'
}
},
{
scope: 'entity.name',
settings: {
foreground: '#FFFFFF'
}
},
]
};
themeData.setCustomTokenColors(customTokenColors);
const tokenStyle = themeData.resolveScopes([['entity.name.type.class']]);
assertTokenStyle(tokenStyle, ts('#FF00FF', { underline: true }), 'entity.name.type.class');
});
test('rule matching', async () => {
const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test');
themeData.setCustomColors({ 'editor.foreground': '#000000' });