Merge from vscode 3d67364fbfcf676d93be64f949e9b33e7f1b969e (#5028)

This commit is contained in:
Anthony Dresser
2019-04-14 22:29:14 -07:00
committed by GitHub
parent 6dbf757385
commit 57242a2e13
210 changed files with 4898 additions and 3018 deletions

View File

@@ -250,7 +250,8 @@ export class DebugHoverWidget implements IContentWidget {
}
private layoutTreeAndContainer(): void {
const treeHeight = Math.min(MAX_TREE_HEIGHT, this.tree.contentHeight);
const scrollBarHeight = 8;
const treeHeight = Math.min(MAX_TREE_HEIGHT, this.tree.contentHeight + scrollBarHeight);
this.treeContainer.style.height = `${treeHeight}px`;
this.tree.layout(treeHeight, 324);
}

View File

@@ -720,11 +720,25 @@ class ReplDelegate implements IListVirtualDelegate<IReplElement> {
getHeight(element: IReplElement): number {
// Give approximate heights. Repl has dynamic height so the tree will measure the actual height on its own.
const fontSize = this.configurationService.getValue<IDebugConfiguration>('debug').console.fontSize;
const rowHeight = Math.ceil(1.4 * fontSize);
if (element instanceof Expression && element.hasChildren) {
return Math.ceil(2 * 1.4 * fontSize);
return 2 * rowHeight;
}
return Math.ceil(1.4 * fontSize);
// In order to keep scroll position we need to give a good approximation to the tree
if (element instanceof SimpleReplElement) {
// For every 150 characters increase the number of lines needed
let count = Math.ceil(element.value.length / 150);
for (let i = 0; i < element.value.length; i++) {
if (element.value[i] === '\n' || element.value[i] === '\r\n') {
count++;
}
}
return Math.max(1, count) * rowHeight;
}
return rowHeight;
}
getTemplateId(element: IReplElement): string {

View File

@@ -10,6 +10,7 @@ import { Expression, SimpleReplElement, RawObjectReplElement } from 'vs/workbenc
import { isUndefinedOrNull, isObject } from 'vs/base/common/types';
import { basenameOrAuthority } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { endsWith } from 'vs/base/common/strings';
const MAX_REPL_LENGTH = 10000;
let topReplElementCounter = 0;
@@ -39,8 +40,13 @@ export class ReplModel {
}
if (typeof data === 'string') {
const element = new SimpleReplElement(`topReplElement:${topReplElementCounter++}`, data.trimRight(), sev, source);
this.addReplElement(element);
const previousElement = this.replElements.length ? this.replElements[this.replElements.length - 1] : undefined;
if (previousElement instanceof SimpleReplElement && previousElement.severity === sev && !endsWith(previousElement.value, '\n') && !endsWith(previousElement.value, '\r\n')) {
previousElement.value += data;
} else {
const element = new SimpleReplElement(`topReplElement:${topReplElementCounter++}`, data, sev, source);
this.addReplElement(element);
}
} else {
// TODO@Isidor hack, we should introduce a new type which is an output that can fetch children like an expression
(<any>data).severity = sev;

View File

@@ -509,7 +509,7 @@ export class DebugService implements IDebugService {
// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
if (isExtensionHostDebugging(session.configuration) && session.state === State.Running && session.configuration.noDebug) {
this.extensionHostDebugService.close(session.root.uri);
this.extensionHostDebugService.close(session.getId());
}
this.telemetryDebugSessionStop(session, adapterExitEvent);
@@ -556,8 +556,8 @@ export class DebugService implements IDebugService {
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? session.restart() : undefined);
}
if (isExtensionHostDebugging(session.configuration) && session.root) {
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.extensionHostDebugService.reload(session.root.uri) : undefined);
if (isExtensionHostDebugging(session.configuration)) {
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.extensionHostDebugService.reload(session.getId()) : undefined);
}
const shouldFocus = this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId();

View File

@@ -423,34 +423,41 @@ suite('Debug - Model', () => {
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
const repl = new ReplModel(session);
repl.appendToRepl('first line\n', severity.Error);
repl.appendToRepl('second line', severity.Error);
repl.appendToRepl('third line', severity.Warning);
repl.appendToRepl('second line ', severity.Error);
repl.appendToRepl('third line ', severity.Error);
repl.appendToRepl('fourth line', severity.Error);
let elements = <SimpleReplElement[]>repl.getReplElements();
assert.equal(elements.length, 4);
assert.equal(elements[0].value, 'first line');
assert.equal(elements.length, 2);
assert.equal(elements[0].value, 'first line\n');
assert.equal(elements[0].severity, severity.Error);
assert.equal(elements[1].value, 'second line');
assert.equal(elements[1].value, 'second line third line fourth line');
assert.equal(elements[1].severity, severity.Error);
assert.equal(elements[2].value, 'third line');
assert.equal(elements[2].severity, severity.Warning);
assert.equal(elements[3].value, 'fourth line');
assert.equal(elements[3].severity, severity.Error);
repl.appendToRepl('1', severity.Warning);
elements = <SimpleReplElement[]>repl.getReplElements();
assert.equal(elements.length, 5);
assert.equal(elements[4].value, '1');
assert.equal(elements[4].severity, severity.Warning);
assert.equal(elements.length, 3);
assert.equal(elements[2].value, '1');
assert.equal(elements[2].severity, severity.Warning);
const keyValueObject = { 'key1': 2, 'key2': 'value' };
repl.appendToRepl(new RawObjectReplElement('fakeid', 'fake', keyValueObject), severity.Info);
const element = <RawObjectReplElement>repl.getReplElements()[5];
const element = <RawObjectReplElement>repl.getReplElements()[3];
assert.equal(element.value, 'Object');
assert.deepEqual(element.valueObj, keyValueObject);
repl.removeReplExpressions();
assert.equal(repl.getReplElements().length, 0);
repl.appendToRepl('1\n', severity.Info);
repl.appendToRepl('2', severity.Info);
repl.appendToRepl('3\n4', severity.Info);
repl.appendToRepl('5\n', severity.Info);
repl.appendToRepl('6', severity.Info);
elements = <SimpleReplElement[]>repl.getReplElements();
assert.equal(elements.length, 3);
assert.equal(elements[0], '1\n');
assert.equal(elements[1], '23\n45\n');
assert.equal(elements[2], '6');
});
});

View File

@@ -28,7 +28,7 @@ import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension
import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/electron-browser/extensionsWidgets';
import { EditorOptions } from 'vs/workbench/common/editor';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, SystemDisabledLabelAction, SystemDisabledWarningAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, DisabledLabelAction, SystemDisabledWarningAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
@@ -395,7 +395,7 @@ export class ExtensionEditor extends BaseEditor {
this.instantiationService.createInstance(RemoteInstallAction),
combinedInstallAction,
systemDisabledWarningAction,
this.instantiationService.createInstance(SystemDisabledLabelAction, systemDisabledWarningAction),
this.instantiationService.createInstance(DisabledLabelAction, systemDisabledWarningAction),
this.instantiationService.createInstance(MaliciousStatusLabelAction, true),
];
const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets]);
@@ -623,9 +623,9 @@ export class ExtensionEditor extends BaseEditor {
renderDashboardContributions(content, manifest, layout)
];
const isEmpty = !renders.reduce((v, r) => r || v, false);
scrollableContent.scanDomNode();
const isEmpty = !renders.some(x => x);
if (isEmpty) {
append(content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions");
append(this.content, content);

View File

@@ -21,6 +21,7 @@ import { randomPort } from 'vs/base/node/ports';
import product from 'vs/platform/product/node/product';
import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
export class ExtensionHostProfileService extends Disposable implements IExtensionHostProfileService {
@@ -76,7 +77,8 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
return null;
}
if (!this._extensionService.canProfileExtensionHost()) {
const inspectPort = this._extensionService.getInspectPort();
if (!inspectPort) {
return this._dialogService.confirm({
type: 'info',
message: nls.localize('restart1', "Profile Extensions"),
@@ -92,7 +94,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
this._setState(ProfileSessionState.Starting);
return this._extensionService.startExtensionHostProfile().then((value) => {
return this._instantiationService.createInstance(ExtensionHostProfiler, inspectPort).start().then((value) => {
this._profileSession = value;
this._setState(ProfileSessionState.Running);
}, (err) => {

View File

@@ -251,6 +251,11 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
description: localize('extensionsPolicy', "Sets the security policy for downloading extensions."),
scope: ConfigurationScope.APPLICATION,
default: ExtensionsPolicy.allowAll
},
'extensions.showInstalledExtensionsByDefault': {
type: 'boolean',
description: localize('extensions.showInstalledExtensionsByDefault', "When enabled, extensions view shows installed extensions view by default."),
default: false
}
}
});

View File

@@ -16,7 +16,7 @@ import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
// {{SQL CARBON EDIT}}
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey, IExtensionContainer, EXTENSIONS_CONFIG, ExtensionsPolicy, ExtensionsPolicyKey } from 'vs/workbench/contrib/extensions/common/extensions';
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
import { IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
@@ -60,6 +60,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { isUIExtension } from 'vs/workbench/services/extensions/node/extensionsUtil';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
// {{SQL CARBON EDIT}}
@@ -183,7 +184,7 @@ export class InstallAction extends ExtensionAction {
return;
}
this.enabled = this.extensionsWorkbenchService.canInstall(this.extension) && this.extension.state === ExtensionState.Uninstalled;
this.enabled = this.extensionsWorkbenchService.canInstall(this.extension) && !this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, this.extension.identifier));
this.class = this.extension.state === ExtensionState.Installing ? InstallAction.InstallingClass : InstallAction.Class;
this.updateLabel();
}
@@ -720,7 +721,7 @@ export class ManageExtensionAction extends ExtensionDropDownAction {
]);
groups.push([this.instantiationService.createInstance(UninstallAction)]);
groups.push([this.instantiationService.createInstance(InstallAnotherVersionAction)]);
groups.push([this.instantiationService.createInstance(ExtensionInfoAction)]);
groups.push([this.instantiationService.createInstance(ExtensionInfoAction), this.instantiationService.createInstance(ExtensionSettingsAction)]);
groups.forEach(group => group.forEach(extensionAction => extensionAction.extension = this.extension));
@@ -829,6 +830,27 @@ export class ExtensionInfoAction extends ExtensionAction {
}
}
export class ExtensionSettingsAction extends ExtensionAction {
static readonly ID = 'extensions.extensionSettings';
static readonly LABEL = localize('extensionSettingsAction', "Configure Extension Settings");
constructor(
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(ExtensionSettingsAction.ID, ExtensionSettingsAction.LABEL);
this.update();
}
update(): void {
this.enabled = !!this.extension;
}
run(): Promise<any> {
this.preferencesService.openSettings(false, `@ext:${this.extension.identifier.id}`);
return Promise.resolve();
}
}
export class EnableForWorkspaceAction extends ExtensionAction {
static readonly ID = 'extensions.enableForWorkspace';
@@ -1195,9 +1217,10 @@ export class ReloadAction extends ExtensionAction {
}
const isUninstalled = this.extension.state === ExtensionState.Uninstalled;
const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
const isSameExtensionRunning = runningExtension && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation);
if (isUninstalled) {
if (runningExtension) {
if (isSameExtensionRunning) {
this.enabled = true;
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
@@ -1210,7 +1233,6 @@ export class ReloadAction extends ExtensionAction {
const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local);
if (runningExtension) {
// Extension is running
const isSameExtensionRunning = this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation);
const isSameVersionRunning = isSameExtensionRunning && this.extension.version === runningExtension.version;
if (isEnabled) {
if (!isSameVersionRunning && !this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))) {
@@ -2484,6 +2506,7 @@ export class StatusLabelAction extends Action implements IExtensionContainer {
return canAddExtension() ? this.initialStatus === ExtensionState.Installed ? localize('updated', "Updated") : localize('installed', "Installed") : null;
}
if (currentStatus === ExtensionState.Uninstalling && this.status === ExtensionState.Uninstalled) {
this.initialStatus = this.status;
return canRemoveExtension() ? localize('uninstalled', "Uninstalled") : null;
}
}
@@ -2533,7 +2556,7 @@ export class MaliciousStatusLabelAction extends ExtensionAction {
}
}
export class SystemDisabledLabelAction extends ExtensionAction {
export class DisabledLabelAction extends ExtensionAction {
private static readonly Class = 'disable-status';
@@ -2542,19 +2565,26 @@ export class SystemDisabledLabelAction extends ExtensionAction {
constructor(
private readonly warningAction: SystemDisabledWarningAction,
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService,
) {
super('extensions.systemDisabledLabel', warningAction.tooltip, `${SystemDisabledLabelAction.Class} hide`, false);
super('extensions.disabledLabel', warningAction.tooltip, `${DisabledLabelAction.Class} hide`, false);
warningAction.onDidChange(() => this.update(), this, this.disposables);
}
update(): void {
this.enabled = this.warningAction.enabled;
if (this.enabled) {
this.class = SystemDisabledLabelAction.Class;
this.class = `${DisabledLabelAction.Class} hide`;
this.label = '';
if (this.warningAction.enabled) {
this.enabled = true;
this.class = DisabledLabelAction.Class;
this.label = this.warningAction.tooltip;
} else {
this.class = `${SystemDisabledLabelAction.Class} hide`;
this.label = '';
return;
}
if (this.extension && this.extension.local && !this.extensionEnablementService.isEnabled(this.extension.local)) {
this.enabled = true;
this.class = DisabledLabelAction.Class;
this.label = localize('disabled by user', "This extension is disabled by the user.");
return;
}
}
@@ -2599,27 +2629,42 @@ export class SystemDisabledWarningAction extends ExtensionAction {
this.enabled = false;
this.class = `${SystemDisabledWarningAction.Class} hide`;
this.tooltip = '';
if (this.extension && this.extension.local && this._runningExtensions) {
if (this.extension && this.extension.local && this.extension.server && this._runningExtensions && this.workbenchEnvironmentService.configuration.remoteAuthority && this.extensionManagementServerService.remoteExtensionManagementServer) {
if (
// Remote Window
this.workbenchEnvironmentService.configuration.remoteAuthority
// Local Workspace Extension
&& this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)
// Extension does not exist in remote
&& !this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.remoteExtensionManagementServer)
this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)
) {
this.enabled = true;
this.class = `${SystemDisabledWarningAction.Class}`;
const host = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.workbenchEnvironmentService.configuration.remoteAuthority) || localize('remote', "Remote");
this.tooltip = localize('disabled workspace Extension', "This extension is disabled because it cannot run in a window connected to the remote server.", host, host);
if (this.extensionsWorkbenchService.canInstall(this.extension)) {
this.tooltip = `${this.tooltip} ${localize('Install in remote server', "Install it in '{0}' server to enable.", host)}`;
this.tooltip = localize('disabled workspace Extension', "This extension from {0} server is disabled because it cannot run in a window connected to the remote server.", this.getServerLabel(this.extensionManagementServerService.localExtensionManagementServer));
if (!this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.remoteExtensionManagementServer)
&& this.extensionsWorkbenchService.canInstall(this.extension)
) {
// Extension does not exist in remote
this.tooltip = `${this.tooltip} ${localize('Install in remote server', "Install it in {0} server to enable.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer))}`;
}
return;
}
const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation) : null;
if (
// Not same as running extension
runningExtensionServer && this.extension.server !== runningExtensionServer
) {
this.enabled = true;
this.class = `${SystemDisabledWarningAction.Class}`;
this.tooltip = localize('disabled because running in another server', "This extension from {0} server is disabled because another instance of same extension from {1} server is enabled.", this.getServerLabel(this.extension.server), this.getServerLabel(runningExtensionServer));
return;
}
}
}
private getServerLabel(server: IExtensionManagementServer): string {
if (server === this.extensionManagementServerService.remoteExtensionManagementServer) {
return this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.workbenchEnvironmentService.configuration.remoteAuthority) || localize('remote', "Remote");
}
return server.label;
}
run(): Promise<any> {
return Promise.resolve(null);
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IExtensionService, IResponsiveStateChangeEvent, ICpuProfilerTarget, IExtensionHostProfile, ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, IResponsiveStateChangeEvent, IExtensionHostProfile, ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
@@ -21,11 +21,12 @@ import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { createSlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions';
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchContribution {
private readonly _session = new Map<ICpuProfilerTarget, CancellationTokenSource>();
private readonly _blame = new Set<string>();
private _session: CancellationTokenSource | undefined;
constructor(
@IExtensionService private readonly _extensionService: IExtensionService,
@@ -41,26 +42,29 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
}
private async _onDidChangeResponsiveChange(event: IResponsiveStateChangeEvent): Promise<void> {
const { target } = event;
if (!target.canProfileExtensionHost()) {
const port = this._extensionService.getInspectPort();
if (!port) {
return;
}
if (event.isResponsive && this._session.has(target)) {
if (event.isResponsive && this._session) {
// stop profiling when responsive again
this._session.get(target)!.cancel();
this._session.cancel();
} else if (!event.isResponsive && !this._session.has(target)) {
} else if (!event.isResponsive && !this._session) {
// start profiling if not yet profiling
const token = new CancellationTokenSource();
this._session.set(target, token);
const cts = new CancellationTokenSource();
this._session = cts;
let session: ProfileSession;
try {
session = await target.startExtensionHostProfile();
session = await this._instantiationService.createInstance(ExtensionHostProfiler, port).start();
} catch (err) {
this._session.delete(target);
this._session = undefined;
// fail silent as this is often
// caused by another party being
// connected already
@@ -69,7 +73,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
// wait 5 seconds or until responsive again
await new Promise(resolve => {
token.token.onCancellationRequested(resolve);
cts.token.onCancellationRequested(resolve);
setTimeout(resolve, 5e3);
});
@@ -79,7 +83,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
} catch (err) {
onUnexpectedError(err);
} finally {
this._session.delete(target);
this._session = undefined;
}
}
}

View File

@@ -13,9 +13,9 @@ import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
import { Event } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, DisabledLabelAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/electron-browser/extensionsWidgets';
import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, TooltipWidget } from 'vs/workbench/contrib/extensions/electron-browser/extensionsWidgets';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -88,13 +88,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
});
actionbar.onDidRun(({ error }) => error && this.notificationService.error(error));
const widgets = [
recommendationWidget,
badgeWidget,
this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version),
this.instantiationService.createInstance(InstallCountWidget, installCount, true),
this.instantiationService.createInstance(RatingsWidget, ratings, true)
];
const systemDisabledWarningAction = this.instantiationService.createInstance(SystemDisabledWarningAction);
const actions = [
this.instantiationService.createInstance(StatusLabelAction),
this.instantiationService.createInstance(UpdateAction),
@@ -102,10 +96,20 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
this.instantiationService.createInstance(InstallAction),
this.instantiationService.createInstance(RemoteInstallAction),
this.instantiationService.createInstance(MaliciousStatusLabelAction, false),
this.instantiationService.createInstance(SystemDisabledWarningAction),
systemDisabledWarningAction,
this.instantiationService.createInstance(ManageExtensionAction)
];
const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets]);
const disabledLabelAction = this.instantiationService.createInstance(DisabledLabelAction, systemDisabledWarningAction);
const tooltipWidget = this.instantiationService.createInstance(TooltipWidget, root, disabledLabelAction, recommendationWidget);
const widgets = [
recommendationWidget,
badgeWidget,
tooltipWidget,
this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version),
this.instantiationService.createInstance(InstallCountWidget, installCount, true),
this.instantiationService.createInstance(RatingsWidget, ratings, true)
];
const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets, disabledLabelAction]);
actionbar.push(actions, actionOptions);
const disposables = [...actions, ...widgets, actionbar, extensionContainers];

View File

@@ -53,7 +53,6 @@ import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { RemoteAuthorityContext as RemoteAuthorityContext } from 'vs/workbench/common/contextkeys';
import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
interface SearchInputEvent extends Event {
@@ -64,7 +63,10 @@ interface SearchInputEvent extends Event {
const NonEmptyWorkspaceContext = new RawContextKey<boolean>('nonEmptyWorkspace', false);
const DefaultViewsContext = new RawContextKey<boolean>('defaultExtensionViews', true);
const SearchMarketplaceExtensionsContext = new RawContextKey<boolean>('searchMarketplaceExtensions', false);
const SearchServerExtensionsContext = new RawContextKey<boolean>('searchServerExtensions', false);
const SearchIntalledExtensionsContext = new RawContextKey<boolean>('searchInstalledExtensions', false);
const SearchOutdatedExtensionsContext = new RawContextKey<boolean>('searchOutdatedExtensions', false);
const SearchEnabledExtensionsContext = new RawContextKey<boolean>('searchEnabledExtensions', false);
const SearchDisabledExtensionsContext = new RawContextKey<boolean>('searchDisabledExtensions', false);
const HasInstalledExtensionsContext = new RawContextKey<boolean>('hasInstalledExtensions', true);
const SearchBuiltInExtensionsContext = new RawContextKey<boolean>('searchBuiltInExtensions', false);
const RecommendedExtensionsContext = new RawContextKey<boolean>('recommendedExtensions', false);
@@ -72,10 +74,12 @@ const DefaultRecommendedExtensionsContext = new RawContextKey<boolean>('defaultR
const viewIdNameMappings: { [id: string]: string } = {
'extensions.listView': localize('marketPlace', "Marketplace"),
'extensions.enabledExtensionList': localize('enabledExtensions', "Enabled"),
'extensions.enabledExtensionList2': localize('enabledExtensions', "Enabled"),
'extensions.disabledExtensionList': localize('disabledExtensions', "Disabled"),
'extensions.disabledExtensionList2': localize('disabledExtensions', "Disabled"),
// {{SQL CARBON EDIT}}
// 'extensions.popularExtensionsList': localize('popularExtensions', "Popular"),
'extensions.recommendedList': localize('recommendedExtensions', "Marketplace"),
'extensions.recommendedList': localize('recommendedExtensions', "Recommended"),
'extensions.otherrecommendedList': localize('otherRecommendedExtensions', "Other Recommendations"),
'extensions.workspaceRecommendedList': localize('workspaceRecommendedExtensions', "Workspace Recommendations"),
'extensions.builtInExtensionsList': localize('builtInExtensions', "Features"),
@@ -98,6 +102,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
viewDescriptors.push(this.createDefaultDisabledExtensionsListViewDescriptor());
// {{SQL CARBON EDIT}}
// viewDescriptors.push(this.createDefaultPopularExtensionsListViewDescriptor());
viewDescriptors.push(this.createEnabledExtensionsListViewDescriptor());
viewDescriptors.push(this.createDisabledExtensionsListViewDescriptor());
viewDescriptors.push(this.createBuiltInExtensionsListViewDescriptor());
viewDescriptors.push(this.createBuiltInBasicsExtensionsListViewDescriptor());
viewDescriptors.push(this.createBuiltInThemesExtensionsListViewDescriptor());
@@ -133,7 +139,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: EnabledExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.isEqualTo('')),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), ContextKeyExpr.not('config.extensions.showInstalledExtensionsByDefault')),
weight: 40,
canToggleVisibility: true,
order: 1
@@ -148,7 +154,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: DisabledExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.isEqualTo('')),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), ContextKeyExpr.not('config.extensions.showInstalledExtensionsByDefault')),
weight: 10,
canToggleVisibility: true,
order: 3,
@@ -175,15 +181,21 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
private createExtensionsViewDescriptorsForServer(server: IExtensionManagementServer): IViewDescriptor[] {
return [{
id: `extensions.${server.authority}.installed`,
name: server.label,
name: localize('installed', "Installed"),
ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server] },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchServerExtensions')),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')),
weight: 100
}, {
id: `extensions.${server.authority}.outdated`,
name: localize('outdated', "Outdated"),
ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server] },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchOutdatedExtensions')),
weight: 100
}, {
id: `extensions.${server.authority}.default`,
name: server.label,
name: localize('installed', "Installed"),
ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server] },
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.notEqualsTo('')),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), ContextKeyExpr.has('config.extensions.showInstalledExtensionsByDefault')),
weight: 40,
order: 1
}];
@@ -235,6 +247,33 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
};
}
private createEnabledExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.enabledExtensionList2';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: EnabledExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchEnabledExtensions')),
weight: 40,
canToggleVisibility: true,
order: 1
};
}
private createDisabledExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.disabledExtensionList2';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: DisabledExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchDisabledExtensions')),
weight: 10,
canToggleVisibility: true,
order: 3,
collapsed: true
};
}
private createBuiltInExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.builtInExtensionsList';
return {
@@ -278,7 +317,10 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
private nonEmptyWorkspaceContextKey: IContextKey<boolean>;
private defaultViewsContextKey: IContextKey<boolean>;
private searchMarketplaceExtensionsContextKey: IContextKey<boolean>;
private searchServerExtensionsContextKey: IContextKey<boolean>;
private searchInstalledExtensionsContextKey: IContextKey<boolean>;
private searchOutdatedExtensionsContextKey: IContextKey<boolean>;
private searchEnabledExtensionsContextKey: IContextKey<boolean>;
private searchDisabledExtensionsContextKey: IContextKey<boolean>;
private hasInstalledExtensionsContextKey: IContextKey<boolean>;
private searchBuiltInExtensionsContextKey: IContextKey<boolean>;
private recommendedExtensionsContextKey: IContextKey<boolean>;
@@ -317,7 +359,10 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
this.nonEmptyWorkspaceContextKey = NonEmptyWorkspaceContext.bindTo(contextKeyService);
this.defaultViewsContextKey = DefaultViewsContext.bindTo(contextKeyService);
this.searchMarketplaceExtensionsContextKey = SearchMarketplaceExtensionsContext.bindTo(contextKeyService);
this.searchServerExtensionsContextKey = SearchServerExtensionsContext.bindTo(contextKeyService);
this.searchInstalledExtensionsContextKey = SearchIntalledExtensionsContext.bindTo(contextKeyService);
this.searchOutdatedExtensionsContextKey = SearchOutdatedExtensionsContext.bindTo(contextKeyService);
this.searchEnabledExtensionsContextKey = SearchEnabledExtensionsContext.bindTo(contextKeyService);
this.searchDisabledExtensionsContextKey = SearchDisabledExtensionsContext.bindTo(contextKeyService);
this.hasInstalledExtensionsContextKey = HasInstalledExtensionsContext.bindTo(contextKeyService);
this.searchBuiltInExtensionsContextKey = SearchBuiltInExtensionsContext.bindTo(contextKeyService);
this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService);
@@ -457,7 +502,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
protected saveState(): void {
const value = this.searchBox.getValue();
if (ExtensionsListView.isInstalledExtensionsQuery(value)) {
if (ExtensionsListView.isLocalExtensionsQuery(value)) {
this.searchViewletState['query.value'] = value;
} else {
this.searchViewletState['query.value'] = '';
@@ -468,13 +513,14 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
private doSearch(): Promise<void> {
const value = this.normalizedQuery();
this.defaultViewsContextKey.set(!value);
const isServerExtensionsQuery = ExtensionsListView.isServerExtensionsQuery(value);
const isBuiltInExtensionsQuery = ExtensionsListView.isBuiltInExtensionsQuery(value);
const isRecommendedExtensionsQuery = ExtensionsListView.isRecommendedExtensionsQuery(value);
this.searchServerExtensionsContextKey.set(isServerExtensionsQuery);
this.searchBuiltInExtensionsContextKey.set(isBuiltInExtensionsQuery);
this.searchInstalledExtensionsContextKey.set(ExtensionsListView.isInstalledExtensionsQuery(value));
this.searchOutdatedExtensionsContextKey.set(ExtensionsListView.isOutdatedExtensionsQuery(value));
this.searchEnabledExtensionsContextKey.set(ExtensionsListView.isEnabledExtensionsQuery(value));
this.searchDisabledExtensionsContextKey.set(ExtensionsListView.isDisabledExtensionsQuery(value));
this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value));
this.recommendedExtensionsContextKey.set(isRecommendedExtensionsQuery);
this.searchMarketplaceExtensionsContextKey.set(!!value && !isServerExtensionsQuery && !isBuiltInExtensionsQuery && !isRecommendedExtensionsQuery);
this.searchMarketplaceExtensionsContextKey.set(!!value && !ExtensionsListView.isLocalExtensionsQuery(value) && !isRecommendedExtensionsQuery);
this.nonEmptyWorkspaceContextKey.set(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY);
return this.progress(Promise.all(this.panels.map(view =>

View File

@@ -9,7 +9,7 @@ import { assign } from 'vs/base/common/objects';
import { Event, Emitter } from 'vs/base/common/event';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging';
import { SortBy, SortOrder, IQueryOptions, IExtensionTipsService, IExtensionRecommendation, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement';
import { SortBy, SortOrder, IQueryOptions, IExtensionTipsService, IExtensionRecommendation, IExtensionManagementServer, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
@@ -41,7 +41,7 @@ import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list';
import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IAction } from 'vs/base/common/actions';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { ExtensionType, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import product from 'vs/platform/product/node/product';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
@@ -96,7 +96,8 @@ export class ExtensionsListView extends ViewletPanel {
@IConfigurationService configurationService: IConfigurationService,
@IWorkspaceContextService protected contextService: IWorkspaceContextService,
@IExperimentService private readonly experimentService: IExperimentService,
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService
) {
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService);
this.server = options.server;
@@ -234,7 +235,7 @@ export class ExtensionsListView extends ViewletPanel {
if (ids.length) {
return this.queryByIds(ids, options, token);
}
if (ExtensionsListView.isInstalledExtensionsQuery(query.value) || /@builtin/.test(query.value)) {
if (ExtensionsListView.isLocalExtensionsQuery(query.value) || /@builtin/.test(query.value)) {
return this.queryLocal(query, options);
}
return this.queryGallery(query, options, token);
@@ -329,7 +330,23 @@ export class ExtensionsListView extends ViewletPanel {
&& (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1)
&& (!categories.length || categories.some(category => (e.local && e.local.manifest.categories || []).some(c => c.toLowerCase() === category))));
return this.getPagedModel(this.sortExtensions(result, options));
if (options.sortBy !== undefined) {
result = this.sortExtensions(result, options);
} else {
const runningExtensions = await this.extensionService.getExtensions();
const runningExtensionsById = runningExtensions.reduce((result, e) => { result.set(ExtensionIdentifier.toKey(e.identifier.value), e); return result; }, new Map<string, IExtensionDescription>());
result = result.sort((e1, e2) => {
const running1 = runningExtensionsById.get(ExtensionIdentifier.toKey(e1.identifier.id));
const isE1Running = running1 && this.extensionManagementServerService.getExtensionManagementServer(running1.extensionLocation) === e1.server;
const running2 = runningExtensionsById.get(ExtensionIdentifier.toKey(e2.identifier.id));
const isE2Running = running2 && this.extensionManagementServerService.getExtensionManagementServer(running2.extensionLocation) === e2.server;
if ((isE1Running && isE2Running) || (!isE1Running && !isE2Running)) {
return e1.displayName.localeCompare(e2.displayName);
}
return isE1Running ? -1 : 1;
});
}
return this.getPagedModel(result);
}
@@ -785,12 +802,28 @@ export class ExtensionsListView extends ViewletPanel {
return /^\s*@builtin\s*$/i.test(query);
}
static isInstalledExtensionsQuery(query: string): boolean {
return /@installed|@outdated|@enabled|@disabled/i.test(query);
static isLocalExtensionsQuery(query: string): boolean {
return this.isInstalledExtensionsQuery(query)
|| this.isOutdatedExtensionsQuery(query)
|| this.isEnabledExtensionsQuery(query)
|| this.isDisabledExtensionsQuery(query)
|| this.isBuiltInExtensionsQuery(query);
}
static isServerExtensionsQuery(query: string): boolean {
return /@installed|@outdated/i.test(query);
static isInstalledExtensionsQuery(query: string): boolean {
return /@installed/i.test(query);
}
static isOutdatedExtensionsQuery(query: string): boolean {
return /@outdated/i.test(query);
}
static isEnabledExtensionsQuery(query: string): boolean {
return /@enabled/i.test(query);
}
static isDisabledExtensionsQuery(query: string): boolean {
return /@disabled/i.test(query);
}
static isRecommendedExtensionsQuery(query: string): boolean {
@@ -827,8 +860,12 @@ export class ExtensionsListView extends ViewletPanel {
}
}
function getServerLabel(server: IExtensionManagementServer, labelService: ILabelService, workbenchEnvironmentService: IWorkbenchEnvironmentService): string {
return workbenchEnvironmentService.configuration.remoteAuthority === server.authority ? labelService.getHostLabel(REMOTE_HOST_SCHEME, server.authority) || server.label : server.label;
function getViewTitleForServer(viewTitle: string, server: IExtensionManagementServer, labelService: ILabelService, workbenchEnvironmentService: IWorkbenchEnvironmentService): string {
const serverLabel = workbenchEnvironmentService.configuration.remoteAuthority === server.authority ? labelService.getHostLabel(REMOTE_HOST_SCHEME, server.authority) || server.label : server.label;
if (viewTitle && workbenchEnvironmentService.configuration.remoteAuthority) {
return `${serverLabel} - ${viewTitle}`;
}
return viewTitle ? viewTitle : serverLabel;
}
export class ServerExtensionsView extends ExtensionsListView {
@@ -852,17 +889,19 @@ export class ServerExtensionsView extends ExtensionsListView {
@IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService,
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
@ILabelService labelService: ILabelService,
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService
) {
options.title = getServerLabel(server, labelService, workbenchEnvironmentService);
const viewTitle = options.title;
options.title = getViewTitleForServer(viewTitle, server, labelService, workbenchEnvironmentService);
options.server = server;
super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, modeService, telemetryService, configurationService, contextService, experimentService, workbenchThemeService);
this.disposables.push(labelService.onDidChangeFormatters(() => this.updateTitle(getServerLabel(server, labelService, workbenchEnvironmentService))));
super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, modeService, telemetryService, configurationService, contextService, experimentService, workbenchThemeService, extensionManagementServerService);
this.disposables.push(labelService.onDidChangeFormatters(() => this.updateTitle(getViewTitleForServer(viewTitle, server, labelService, workbenchEnvironmentService))));
}
async show(query: string): Promise<IPagedModel<IExtension>> {
query = query ? query : '@installed';
if (!ExtensionsListView.isInstalledExtensionsQuery(query) && !ExtensionsListView.isBuiltInExtensionsQuery(query)) {
if (!ExtensionsListView.isLocalExtensionsQuery(query) && !ExtensionsListView.isBuiltInExtensionsQuery(query)) {
query = query += ' @installed';
}
return super.show(query.trim());

View File

@@ -11,12 +11,13 @@ import * as platform from 'vs/base/common/platform';
import { localize } from 'vs/nls';
import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ILabelService } from 'vs/platform/label/common/label';
import { extensionButtonProminentBackground, extensionButtonProminentForeground } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { extensionButtonProminentBackground, extensionButtonProminentForeground, DisabledLabelAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { Emitter, Event } from 'vs/base/common/event';
export abstract class ExtensionWidget extends Disposable implements IExtensionContainer {
private _extension: IExtension;
@@ -142,11 +143,53 @@ export class RatingsWidget extends ExtensionWidget {
}
}
export class TooltipWidget extends ExtensionWidget {
constructor(
private readonly parent: HTMLElement,
private readonly extensionLabelAction: DisabledLabelAction,
private readonly recommendationWidget: RecommendationWidget
) {
super();
this._register(this.extensionLabelAction.onDidChange(() => this.render()));
this._register(this.recommendationWidget.onDidChangeTooltip(() => this.render()));
}
render(): void {
this.parent.title = '';
this.parent.removeAttribute('aria-label');
if (this.extension) {
const title = this.getTitle();
this.parent.title = title;
this.parent.setAttribute('aria-label', localize('extension-arialabel', "{0}. {1} Press enter for extension details.", this.extension.displayName));
}
}
private getTitle(): string {
if (this.extensionLabelAction.enabled) {
return this.extensionLabelAction.label;
}
return this.recommendationWidget.tooltip;
}
}
export class RecommendationWidget extends ExtensionWidget {
private element?: HTMLElement;
private disposables: IDisposable[] = [];
private _tooltip: string;
get tooltip(): string { return this._tooltip; }
set tooltip(tooltip: string) {
if (this._tooltip !== tooltip) {
this._tooltip = tooltip;
this._onDidChangeTooltip.fire();
}
}
private _onDidChangeTooltip: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeTooltip: Event<void> = this._onDidChangeTooltip.event;
constructor(
private parent: HTMLElement,
@IThemeService private readonly themeService: IThemeService,
@@ -159,7 +202,7 @@ export class RecommendationWidget extends ExtensionWidget {
}
private clear(): void {
this.parent.title = '';
this.tooltip = '';
this.parent.setAttribute('aria-label', this.extension ? localize('viewExtensionDetailsAria', "{0}. Press enter for extension details.", this.extension.displayName) : '');
if (this.element) {
this.parent.removeChild(this.element);
@@ -186,8 +229,7 @@ export class RecommendationWidget extends ExtensionWidget {
};
applyBookmarkStyle(this.themeService.getTheme());
this.themeService.onThemeChange(applyBookmarkStyle, this, this.disposables);
this.parent.title = extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText;
this.parent.setAttribute('aria-label', localize('viewRecommendedExtensionDetailsAria', "{0}. {1} Press enter for extension details.", this.extension.displayName, extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText));
this.tooltip = extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText;
}
}
@@ -227,7 +269,7 @@ export class RemoteBadgeWidget extends ExtensionWidget {
}
if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) {
this.element = append(this.parent, $('div.extension-remote-badge'));
append(this.element, $('span.octicon.octicon-file-symlink-directory'));
append(this.element, $('span.octicon.octicon-remote'));
const applyBadgeStyle = () => {
if (!this.element) {

View File

@@ -700,7 +700,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
}
private fromGallery(gallery: IGalleryExtension, maliciousExtensionSet: Set<string>): IExtension {
Promise.all([this.localExtensions.syncLocalWithGalleryExtension(gallery, maliciousExtensionSet), this.remoteExtensions ? this.localExtensions.syncLocalWithGalleryExtension(gallery, maliciousExtensionSet) : Promise.resolve(false)])
Promise.all([this.localExtensions.syncLocalWithGalleryExtension(gallery, maliciousExtensionSet), this.remoteExtensions ? this.remoteExtensions.syncLocalWithGalleryExtension(gallery, maliciousExtensionSet) : Promise.resolve(false)])
.then(result => {
if (result[0] || result[1]) {
this.eventuallyAutoUpdateExtensions();

View File

@@ -12,7 +12,7 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/com
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/node/extensionsWorkbenchService';
import {
IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension, IQueryOptions,
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionManagementServerService, EnablementState, ExtensionRecommendationReason, SortBy
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionManagementServerService, EnablementState, ExtensionRecommendationReason, SortBy, IExtensionManagementServer
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
@@ -39,6 +39,7 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService';
suite('ExtensionsListView Tests', () => {
@@ -89,11 +90,14 @@ suite('ExtensionsListView Tests', () => {
instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event);
instantiationService.stub(IRemoteAgentService, RemoteAgentService);
instantiationService.stub(IExtensionManagementServerService, <IExtensionManagementServerService>{
localExtensionManagementServer: {
extensionManagementService: instantiationService.get(IExtensionManagementService)
instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService {
private _localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', authority: 'vscode-local' };
constructor() {
super(instantiationService.get(ISharedProcessService), instantiationService.get(IRemoteAgentService));
}
});
get localExtensionManagementServer(): IExtensionManagementServer { return this._localExtensionManagementServer; }
set localExtensionManagementServer(server: IExtensionManagementServer) { }
}());
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
@@ -151,14 +155,14 @@ suite('ExtensionsListView Tests', () => {
test('Test query types', () => {
assert.equal(ExtensionsListView.isBuiltInExtensionsQuery('@builtin'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@installed'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@enabled'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@disabled'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@outdated'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@installed searchText'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@enabled searchText'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@disabled searchText'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@outdated searchText'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@installed'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@enabled'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@disabled'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@outdated'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@installed searchText'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@enabled searchText'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@disabled searchText'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@outdated searchText'), true);
});
test('Test empty query equates to sort by install count', () => {
@@ -200,8 +204,8 @@ suite('ExtensionsListView Tests', () => {
await testableView.show('@installed first').then(result => {
assert.equal(result.length, 2, 'Unexpected number of results for @installed query');
assert.equal(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with search text.');
assert.equal(result.get(1).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with search text.');
assert.equal(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with search text.');
assert.equal(result.get(1).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with search text.');
});
await testableView.show('@disabled').then(result => {
@@ -242,27 +246,27 @@ suite('ExtensionsListView Tests', () => {
test('Test installed query with category', async () => {
await testableView.show('@installed category:themes').then(result => {
assert.equal(result.length, 2, 'Unexpected number of results for @installed query with category');
assert.equal(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with category.');
assert.equal(result.get(1).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with category.');
assert.equal(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with category.');
assert.equal(result.get(1).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with category.');
});
await testableView.show('@installed category:"themes"').then(result => {
assert.equal(result.length, 2, 'Unexpected number of results for @installed query with quoted category');
assert.equal(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with quoted category.');
assert.equal(result.get(1).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with quoted category.');
assert.equal(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with quoted category.');
assert.equal(result.get(1).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with quoted category.');
});
await testableView.show('@installed category:"programming languages"').then(result => {
assert.equal(result.length, 2, 'Unexpected number of results for @installed query with quoted category including space');
assert.equal(result.get(0).name, localDisabledLanguage.manifest.name, 'Unexpected extension for @installed query with quoted category inlcuding space.');
assert.equal(result.get(1).name, localEnabledLanguage.manifest.name, 'Unexpected extension for @installed query with quoted category including space.');
assert.equal(result.get(0).name, localEnabledLanguage.manifest.name, 'Unexpected extension for @installed query with quoted category including space.');
assert.equal(result.get(1).name, localDisabledLanguage.manifest.name, 'Unexpected extension for @installed query with quoted category inlcuding space.');
});
await testableView.show('@installed category:themes category:random').then(result => {
assert.equal(result.length, 3, 'Unexpected number of results for @installed query with multiple category');
assert.equal(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with multiple category.');
assert.equal(result.get(1).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with multiple category.');
assert.equal(result.get(2).name, localRandom.manifest.name, 'Unexpected extension for @installed query with multiple category.');
assert.equal(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with multiple category.');
assert.equal(result.get(1).name, localRandom.manifest.name, 'Unexpected extension for @installed query with multiple category.');
assert.equal(result.get(2).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with multiple category.');
});
await testableView.show('@enabled category:themes').then(result => {

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from 'vs/workbench/browser/viewlet';
import * as nls from 'vs/nls';
import { sep } from 'vs/base/common/path';
@@ -159,7 +159,7 @@ class FileEditorInputFactory implements IEditorInputFactory {
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): FileEditorInput {
return instantiationService.invokeFunction<FileEditorInput>(accessor => {
const fileInput: ISerializedFileInput = JSON.parse(serializedEditorInput);
const resource = !!fileInput.resourceJSON ? URI.revive(fileInput.resourceJSON) : URI.parse(fileInput.resource);
const resource = !!fileInput.resourceJSON ? URI.revive(<UriComponents>fileInput.resourceJSON) : URI.parse(fileInput.resource);
const encoding = fileInput.encoding;
return accessor.get(IEditorService).createInput({ resource, encoding, forceFile: true }) as FileEditorInput;

View File

@@ -160,9 +160,9 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I
if (isReadonly) {
if (triedToMakeWriteable) {
message = isWindows ? nls.localize('readonlySaveErrorAdmin', "Failed to save '{0}': File is write protected. Select 'Overwrite as Admin' to retry as administrator.", basename(resource)) : nls.localize('readonlySaveErrorSudo', "Failed to save '{0}': File is write protected. Select 'Overwrite as Sudo' to retry as superuser.", basename(resource));
message = isWindows ? nls.localize('readonlySaveErrorAdmin', "Failed to save '{0}': File is read-only. Select 'Overwrite as Admin' to retry as administrator.", basename(resource)) : nls.localize('readonlySaveErrorSudo', "Failed to save '{0}': File is read-only. Select 'Overwrite as Sudo' to retry as superuser.", basename(resource));
} else {
message = nls.localize('readonlySaveError', "Failed to save '{0}': File is write protected. Select 'Overwrite' to attempt to remove protection.", basename(resource));
message = nls.localize('readonlySaveError', "Failed to save '{0}': File is read-only. Select 'Overwrite' to attempt to make it writeable.", basename(resource));
}
} else if (isPermissionDenied) {
message = isWindows ? nls.localize('permissionDeniedSaveError', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Admin' to retry as administrator.", basename(resource)) : nls.localize('permissionDeniedSaveErrorSudo', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Sudo' to retry as superuser.", basename(resource));

View File

@@ -6,9 +6,9 @@
import { Registry } from 'vs/platform/registry/common/platform';
import * as nls from 'vs/nls';
import product from 'vs/platform/product/node/product';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { SyncActionDescriptor, ICommandAction, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
import { OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer } from 'vs/workbench/contrib/issue/electron-browser/issueActions';
import { ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer } from 'vs/workbench/contrib/issue/electron-browser/issueActions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issue';
import { WorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issueService';
@@ -19,8 +19,27 @@ const helpCategory = nls.localize('help', "Help");
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
if (!!product.reportIssueUrl) {
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenIssueReporterAction, OpenIssueReporterAction.ID, OpenIssueReporterAction.LABEL), 'Help: Open Issue Reporter', helpCategory);
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReportPerformanceIssueUsingReporterAction, ReportPerformanceIssueUsingReporterAction.ID, ReportPerformanceIssueUsingReporterAction.LABEL), 'Help: Report Performance Issue', helpCategory);
const OpenIssueReporterActionId = 'workbench.action.openIssueReporter';
const OpenIssueReporterActionLabel = nls.localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue");
CommandsRegistry.registerCommand(OpenIssueReporterActionId, function (accessor, args?: [string]) {
let extensionId: string | undefined;
if (args && Array.isArray(args)) {
[extensionId] = args;
}
return accessor.get(IWorkbenchIssueService).openReporter({ extensionId });
});
const command: ICommandAction = {
id: OpenIssueReporterActionId,
title: { value: OpenIssueReporterActionLabel, original: 'Help: Open Issue Reporter' },
category: helpCategory
};
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command });
}
const developerCategory = nls.localize('developer', "Developer");

View File

@@ -8,23 +8,6 @@ import * as nls from 'vs/nls';
import { IssueType } from 'vs/platform/issue/common/issue';
import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issue';
export class OpenIssueReporterAction extends Action {
static readonly ID = 'workbench.action.openIssueReporter';
static readonly LABEL = nls.localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue");
constructor(
id: string,
label: string,
@IWorkbenchIssueService private readonly issueService: IWorkbenchIssueService
) {
super(id, label);
}
run(): Promise<boolean> {
return this.issueService.openReporter().then(() => true);
}
}
export class OpenProcessExplorer extends Action {
static readonly ID = 'workbench.action.openProcessExplorer';
static readonly LABEL = nls.localize('openProcessExplorer', "Open Process Explorer");

View File

@@ -48,7 +48,7 @@ export class OpenSettings2Action extends Action {
}
run(event?: any): Promise<any> {
return this.preferencesService.openSettings(false);
return this.preferencesService.openSettings(false, undefined);
}
}
@@ -66,7 +66,7 @@ export class OpenSettingsJsonAction extends Action {
}
run(event?: any): Promise<any> {
return this.preferencesService.openSettings(true);
return this.preferencesService.openSettings(true, undefined);
}
}

View File

@@ -256,8 +256,8 @@ export class PreferencesEditor extends BaseEditor {
}
const promise: Promise<boolean> = this.input && this.input.isDirty() ? this.input.save() : Promise.resolve(true);
promise.then(() => {
if (target === ConfigurationTarget.USER) {
this.preferencesService.switchSettings(ConfigurationTarget.USER, this.preferencesService.userSettingsResource, true);
if (target === ConfigurationTarget.USER_LOCAL) {
this.preferencesService.switchSettings(ConfigurationTarget.USER_LOCAL, this.preferencesService.userSettingsResource, true);
} else if (target === ConfigurationTarget.WORKSPACE) {
this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE, this.preferencesService.workspaceSettingsResource!, true);
} else if (target instanceof URI) {
@@ -507,7 +507,7 @@ class PreferencesRenderersController extends Disposable {
private searchAllSettingsTargets(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise<void> {
const searchPs = [
this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.WORKSPACE, groupId, groupLabel, groupOrder, token),
this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.USER, groupId, groupLabel, groupOrder, token)
this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.USER_LOCAL, groupId, groupLabel, groupOrder, token)
];
for (const folder of this.workspaceContextService.getWorkspace().folders) {
@@ -541,9 +541,10 @@ class PreferencesRenderersController extends Disposable {
}
private async getPreferencesEditorModel(target: SettingsTarget | undefined): Promise<ISettingsEditorModel | undefined> {
const resource = target === ConfigurationTarget.USER ? this.preferencesService.userSettingsResource :
target === ConfigurationTarget.WORKSPACE ? this.preferencesService.workspaceSettingsResource :
target;
const resource = target === ConfigurationTarget.USER_LOCAL ? this.preferencesService.userSettingsResource :
target === ConfigurationTarget.USER_REMOTE ? this.preferencesService.userSettingsResource :
target === ConfigurationTarget.WORKSPACE ? this.preferencesService.workspaceSettingsResource :
target;
if (!resource) {
return undefined;
@@ -821,7 +822,7 @@ class SideBySidePreferencesWidget extends Widget {
this.editablePreferencesEditorContainer = DOM.$('.editable-preferences-editor-container');
const editablePreferencesHeaderContainer = DOM.append(this.editablePreferencesEditorContainer, DOM.$('.preferences-header-container'));
this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, editablePreferencesHeaderContainer));
this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, editablePreferencesHeaderContainer, undefined));
this._register(this.settingsTargetsWidget.onDidTargetChange(target => this._onDidSettingsTargetChange.fire(target)));
this._register(attachStylerCallback(this.themeService, { scrollbarShadow }, colors => {
@@ -865,7 +866,7 @@ class SideBySidePreferencesWidget extends Widget {
private getDefaultPreferencesHeaderText(target: ConfigurationTarget): string {
switch (target) {
case ConfigurationTarget.USER:
case ConfigurationTarget.USER_LOCAL:
return nls.localize('defaultUserSettings', "Default User Settings");
case ConfigurationTarget.WORKSPACE:
return nls.localize('defaultWorkspaceSettings', "Default Workspace Settings");
@@ -942,7 +943,7 @@ class SideBySidePreferencesWidget extends Widget {
private getSettingsTarget(resource: URI): SettingsTarget {
if (this.preferencesService.userSettingsResource.toString() === resource.toString()) {
return ConfigurationTarget.USER;
return ConfigurationTarget.USER_LOCAL;
}
const workspaceSettingsResource = this.preferencesService.workspaceSettingsResource;
@@ -955,7 +956,7 @@ class SideBySidePreferencesWidget extends Widget {
return folder.uri;
}
return ConfigurationTarget.USER;
return ConfigurationTarget.USER_LOCAL;
}
private disposeEditors(): void {
@@ -1202,7 +1203,7 @@ class SettingsEditorContribution extends AbstractSettingsEditorContribution impl
.then<any>(settingsModel => {
if (settingsModel instanceof SettingsEditorModel && this.editor.getModel()) {
switch (settingsModel.configurationTarget) {
case ConfigurationTarget.USER:
case ConfigurationTarget.USER_LOCAL:
return this.instantiationService.createInstance(UserSettingsRenderer, this.editor, settingsModel);
case ConfigurationTarget.WORKSPACE:
return this.instantiationService.createInstance(WorkspaceSettingsRenderer, this.editor, settingsModel);

View File

@@ -24,11 +24,14 @@ import { ConfigurationTarget } from 'vs/platform/configuration/common/configurat
import { IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILabelService } from 'vs/platform/label/common/label';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { activeContrastBorder, badgeBackground, badgeForeground, contrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND } from 'vs/workbench/common/theme';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences';
export class SettingsHeaderWidget extends Widget implements IViewZone {
@@ -464,14 +467,20 @@ export class FolderSettingsActionItem extends BaseActionItem {
}
}
export type SettingsTarget = ConfigurationTarget.USER | ConfigurationTarget.WORKSPACE | URI;
export type SettingsTarget = ConfigurationTarget.USER_LOCAL | ConfigurationTarget.USER_REMOTE | ConfigurationTarget.WORKSPACE | URI;
export interface ISettingsTargetsWidgetOptions {
enableRemoteSettings?: boolean;
}
export class SettingsTargetsWidget extends Widget {
private settingsSwitcherBar: ActionBar;
private userSettings: Action;
private userLocalSettings: Action;
private userRemoteSettings: Action;
private workspaceSettings: Action;
private folderSettings: FolderSettingsActionItem;
private options: ISettingsTargetsWidgetOptions;
private _settingsTarget: SettingsTarget;
@@ -480,10 +489,14 @@ export class SettingsTargetsWidget extends Widget {
constructor(
parent: HTMLElement,
options: ISettingsTargetsWidgetOptions | undefined,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IInstantiationService private readonly instantiationService: IInstantiationService
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@ILabelService private readonly labelService: ILabelService
) {
super();
this.options = options || {};
this.create(parent);
this._register(this.contextService.onDidChangeWorkbenchState(() => this.onWorkbenchStateChanged()));
this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.update()));
@@ -498,18 +511,25 @@ export class SettingsTargetsWidget extends Widget {
actionItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : undefined
}));
this.userSettings = new Action('userSettings', localize('userSettings', "User Settings"), '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER));
this.userSettings.tooltip = this.userSettings.label;
this.userLocalSettings = new Action('userSettings', localize('userSettings', "User Settings"), '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_LOCAL));
this.userLocalSettings.tooltip = this.userLocalSettings.label;
const remoteAuthority = this.environmentService.configuration.remoteAuthority;
const hostLabel = remoteAuthority && this.labelService.getHostLabel(REMOTE_HOST_SCHEME, remoteAuthority);
const remoteSettingsLabel = localize('userSettingsRemote', "Remote Settings") +
(hostLabel ? ` (${hostLabel})` : '');
this.userRemoteSettings = new Action('userSettingsRemote', remoteSettingsLabel, '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_REMOTE));
this.userRemoteSettings.tooltip = this.userRemoteSettings.label;
this.workspaceSettings = new Action('workspaceSettings', localize('workspaceSettings', "Workspace Settings"), '.settings-tab', false, () => this.updateTarget(ConfigurationTarget.WORKSPACE));
this.workspaceSettings.tooltip = this.workspaceSettings.label;
const folderSettingsAction = new Action('folderSettings', localize('folderSettings', "Folder Settings"), '.settings-tab', false, (folder: IWorkspaceFolder) => this.updateTarget(folder ? folder.uri : ConfigurationTarget.USER));
const folderSettingsAction = new Action('folderSettings', localize('folderSettings', "Folder Settings"), '.settings-tab', false, (folder: IWorkspaceFolder) => this.updateTarget(folder.uri));
this.folderSettings = this.instantiationService.createInstance(FolderSettingsActionItem, folderSettingsAction);
this.update();
this.settingsSwitcherBar.push([this.userSettings, this.workspaceSettings, folderSettingsAction]);
this.settingsSwitcherBar.push([this.userLocalSettings, this.userRemoteSettings, this.workspaceSettings, folderSettingsAction]);
}
get settingsTarget(): SettingsTarget {
@@ -518,7 +538,8 @@ export class SettingsTargetsWidget extends Widget {
set settingsTarget(settingsTarget: SettingsTarget) {
this._settingsTarget = settingsTarget;
this.userSettings.checked = ConfigurationTarget.USER === this.settingsTarget;
this.userLocalSettings.checked = ConfigurationTarget.USER_LOCAL === this.settingsTarget;
this.userRemoteSettings.checked = ConfigurationTarget.USER_REMOTE === this.settingsTarget;
this.workspaceSettings.checked = ConfigurationTarget.WORKSPACE === this.settingsTarget;
if (this.settingsTarget instanceof URI) {
this.folderSettings.getAction().checked = true;
@@ -536,13 +557,13 @@ export class SettingsTargetsWidget extends Widget {
}
this.workspaceSettings.label = label;
} else if (settingsTarget === ConfigurationTarget.USER) {
} else if (settingsTarget === ConfigurationTarget.USER_LOCAL) {
let label = localize('userSettings', "User Settings");
if (count) {
label += ` (${count})`;
}
this.userSettings.label = label;
this.userLocalSettings.label = label;
} else if (settingsTarget instanceof URI) {
this.folderSettings.setCount(settingsTarget, count);
}
@@ -552,21 +573,27 @@ export class SettingsTargetsWidget extends Widget {
this.folderSettings.folder = null;
this.update();
if (this.settingsTarget === ConfigurationTarget.WORKSPACE && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
this.updateTarget(ConfigurationTarget.USER);
this.updateTarget(ConfigurationTarget.USER_LOCAL);
}
}
updateTarget(settingsTarget: SettingsTarget): Promise<void> {
const isSameTarget = this.settingsTarget === settingsTarget || settingsTarget instanceof URI && this.settingsTarget instanceof URI && this.settingsTarget.toString() === settingsTarget.toString();
const isSameTarget = this.settingsTarget === settingsTarget ||
settingsTarget instanceof URI &&
this.settingsTarget instanceof URI &&
this.settingsTarget.toString() === settingsTarget.toString();
if (!isSameTarget) {
this.settingsTarget = settingsTarget;
this._onDidTargetChange.fire(this.settingsTarget);
}
return Promise.resolve(undefined);
}
private update(): void {
DOM.toggleClass(this.settingsSwitcherBar.domNode, 'empty-workbench', this.contextService.getWorkbenchState() === WorkbenchState.EMPTY);
this.userRemoteSettings.enabled = !!(this.options.enableRemoteSettings && this.environmentService.configuration.remoteAuthority);
this.workspaceSettings.enabled = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY;
this.folderSettings.getAction().enabled = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && this.contextService.getWorkspace().folders.length > 0;
}

View File

@@ -1174,7 +1174,7 @@ export class SettingsTreeFilter implements ITreeFilter<SettingsTreeElement> {
}
// Non-user scope selected
if (element instanceof SettingsTreeSettingElement && this.viewState.settingsTarget !== ConfigurationTarget.USER) {
if (element instanceof SettingsTreeSettingElement && this.viewState.settingsTarget !== ConfigurationTarget.USER_LOCAL) {
if (!element.matchesScope(this.viewState.settingsTarget)) {
return false;
}

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as arrays from 'vs/base/common/arrays';
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import { isArray, withUndefinedAsNull } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
@@ -19,6 +20,7 @@ export const ONLINE_SERVICES_SETTING_TAG = 'usesOnlineServices';
export interface ISettingsEditorViewState {
settingsTarget: SettingsTarget;
tagFilters?: Set<string>;
extensionFilters?: Set<string>;
filterToCategory?: SettingsTreeGroupElement;
}
@@ -228,6 +230,24 @@ export class SettingsTreeSettingElement extends SettingsTreeElement {
return true;
}
matchesAnyExtension(extensionFilters?: Set<string>): boolean {
if (!extensionFilters || !extensionFilters.size) {
return true;
}
if (!this.setting.extensionInfo) {
return false;
}
for (let extensionId of extensionFilters) {
if (extensionId.toLowerCase() === this.setting.extensionInfo.id.toLowerCase()) {
return true;
}
}
return false;
}
}
export class SettingsTreeModel {
@@ -337,9 +357,10 @@ interface IInspectResult {
function inspectSetting(key: string, target: SettingsTarget, configurationService: IConfigurationService): IInspectResult {
const inspectOverrides = URI.isUri(target) ? { resource: target } : undefined;
const inspected = configurationService.inspect(key, inspectOverrides);
const targetSelector = target === ConfigurationTarget.USER ? 'user' :
target === ConfigurationTarget.WORKSPACE ? 'workspace' :
'workspaceFolder';
const targetSelector = target === ConfigurationTarget.USER_LOCAL ? 'userLocal' :
target === ConfigurationTarget.USER_REMOTE ? 'userRemote' :
target === ConfigurationTarget.WORKSPACE ? 'workspace' :
'workspaceFolder';
const isConfigured = typeof inspected[targetSelector] !== 'undefined';
return { isConfigured, inspected, targetSelector };
@@ -494,7 +515,7 @@ export class SearchResultModel extends SettingsTreeModel {
// Save time, filter children in the search model instead of relying on the tree filter, which still requires heights to be calculated.
this.root.children = this.root.children
.filter(child => child instanceof SettingsTreeSettingElement && child.matchesAllTags(this._viewState.tagFilters) && child.matchesScope(this._viewState.settingsTarget));
.filter(child => child instanceof SettingsTreeSettingElement && child.matchesAllTags(this._viewState.tagFilters) && child.matchesScope(this._viewState.settingsTarget) && child.matchesAnyExtension(this._viewState.extensionFilters));
if (this.newExtensionSearchResults && this.newExtensionSearchResults.filterMatches.length) {
const newExtElement = new SettingsTreeNewExtensionsElement();
@@ -527,11 +548,14 @@ export class SearchResultModel extends SettingsTreeModel {
export interface IParsedQuery {
tags: string[];
query: string;
extensionFilters: string[];
}
const tagRegex = /(^|\s)@tag:("([^"]*)"|[^"]\S*)/g;
const extensionRegex = /(^|\s)@ext:("([^"]*)"|[^"]\S*)?/g;
export function parseQuery(query: string): IParsedQuery {
const tags: string[] = [];
let extensions: string[] = [];
query = query.replace(tagRegex, (_, __, quotedTag, tag) => {
tags.push(tag || quotedTag);
return '';
@@ -542,10 +566,19 @@ export function parseQuery(query: string): IParsedQuery {
return '';
});
query = query.replace(extensionRegex, (_, __, quotedExtensionId, extensionId) => {
let extensionIdQuery: string = extensionId || quotedExtensionId;
if (extensionIdQuery) {
extensions.push(...extensionIdQuery.split(',').map(s => s.trim()).filter(s => !isFalsyOrWhitespace(s)));
}
return '';
});
query = query.trim();
return {
tags,
extensionFilters: extensions,
query
};
}

View File

@@ -81,7 +81,7 @@ export class TOCTreeModel {
}
// Check everything that the SettingsFilter checks except whether it's filtered by a category
return child.matchesScope(this._viewState.settingsTarget) && child.matchesAllTags(this._viewState.tagFilters);
return child.matchesScope(this._viewState.settingsTarget) && child.matchesAllTags(this._viewState.tagFilters) && child.matchesAnyExtension(this._viewState.extensionFilters);
}).length;
}
}

View File

@@ -108,5 +108,6 @@ export const KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS = 'keybindings.editor.show
export const DEFAULT_SETTINGS_EDITOR_SETTING = 'workbench.settings.openDefaultSettings';
export const MODIFIED_SETTING_TAG = 'modified';
export const EXTENSION_SETTING_TAG = 'ext:';
export const SETTINGS_COMMAND_OPEN_SETTINGS = 'workbench.action.openSettings';

View File

@@ -217,8 +217,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
weight: KeybindingWeight.WorkbenchContrib,
when: null,
primary: KeyMod.CtrlCmd | KeyCode.US_COMMA,
handler: (accessor, args: any) => {
accessor.get(IPreferencesService).openSettings();
handler: (accessor, args: string | undefined) => {
accessor.get(IPreferencesService).openSettings(undefined, typeof args === 'string' ? args : undefined);
}
});

View File

@@ -11,6 +11,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance
import * as collections from 'vs/base/common/collections';
import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors';
import { Iterator } from 'vs/base/common/iterator';
import * as strings from 'vs/base/common/strings';
import { isArray, withNullAsUndefined } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import 'vs/css!./media/settingsEditor2';
@@ -35,7 +36,7 @@ import { AbstractSettingRenderer, ISettingLinkClickEvent, ISettingOverrideClickE
import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels';
import { settingsTextInputBorder } from 'vs/workbench/contrib/preferences/browser/settingsWidgets';
import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree';
import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences';
import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, EXTENSION_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEditorOptions, SettingsEditorOptions, SettingValueType } from 'vs/workbench/services/preferences/common/preferences';
import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
@@ -69,7 +70,7 @@ export class SettingsEditor2 extends BaseEditor {
private static SETTING_UPDATE_SLOW_DEBOUNCE: number = 1000;
private static readonly SUGGESTIONS: string[] = [
`@${MODIFIED_SETTING_TAG}`, '@tag:usesOnlineServices'
`@${MODIFIED_SETTING_TAG}`, '@tag:usesOnlineServices', `@${EXTENSION_SETTING_TAG}`
];
private static shouldSettingUpdateFast(type: SettingValueType | SettingValueType[]): boolean {
@@ -149,7 +150,7 @@ export class SettingsEditor2 extends BaseEditor {
this.delayedFilterLogging = new Delayer<void>(1000);
this.localSearchDelayer = new Delayer(300);
this.remoteSearchThrottle = new ThrottledDelayer(200);
this.viewState = { settingsTarget: ConfigurationTarget.USER };
this.viewState = { settingsTarget: ConfigurationTarget.USER_LOCAL };
this.settingFastUpdateDelayer = new Delayer<void>(SettingsEditor2.SETTING_UPDATE_FAST_DEBOUNCE);
this.settingSlowUpdateDelayer = new Delayer<void>(SettingsEditor2.SETTING_UPDATE_SLOW_DEBOUNCE);
@@ -212,10 +213,10 @@ export class SettingsEditor2 extends BaseEditor {
if (!options) {
if (!this.viewState.settingsTarget) {
// Persist?
options = SettingsEditorOptions.create({ target: ConfigurationTarget.USER });
options = SettingsEditorOptions.create({ target: ConfigurationTarget.USER_LOCAL });
}
} else if (!options.target) {
options.target = ConfigurationTarget.USER;
options.target = ConfigurationTarget.USER_LOCAL;
}
this._setOptions(options);
@@ -371,7 +372,7 @@ export class SettingsEditor2 extends BaseEditor {
this.searchWidget = this._register(this.instantiationService.createInstance(SuggestEnabledInput, `${SettingsEditor2.ID}.searchbox`, searchContainer, {
triggerCharacters: ['@'],
provideResults: (query: string) => {
return SettingsEditor2.SUGGESTIONS.filter(tag => query.indexOf(tag) === -1).map(tag => tag + ' ');
return SettingsEditor2.SUGGESTIONS.filter(tag => query.indexOf(tag) === -1).map(tag => strings.endsWith(tag, ':') ? tag : tag + ' ');
}
}, searchBoxLabel, 'settingseditor:searchinput' + SettingsEditor2.NUM_INSTANCES++, {
placeholderText: searchBoxLabel,
@@ -406,8 +407,8 @@ export class SettingsEditor2 extends BaseEditor {
const headerControlsContainer = DOM.append(this.headerContainer, $('.settings-header-controls'));
const targetWidgetContainer = DOM.append(headerControlsContainer, $('.settings-target-container'));
this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, targetWidgetContainer));
this.settingsTargetsWidget.settingsTarget = ConfigurationTarget.USER;
this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, targetWidgetContainer, { enableRemoteSettings: true }));
this.settingsTargetsWidget.settingsTarget = ConfigurationTarget.USER_LOCAL;
this.settingsTargetsWidget.onDidTargetChange(target => this.onDidSettingsTargetChange(target));
}
@@ -458,8 +459,10 @@ export class SettingsEditor2 extends BaseEditor {
const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget;
const options: ISettingsEditorOptions = { query };
if (currentSettingsTarget === ConfigurationTarget.USER) {
if (currentSettingsTarget === ConfigurationTarget.USER_LOCAL) {
return this.preferencesService.openGlobalSettings(true, options);
} else if (currentSettingsTarget === ConfigurationTarget.USER_REMOTE) {
return this.preferencesService.openRemoteSettings();
} else if (currentSettingsTarget === ConfigurationTarget.WORKSPACE) {
return this.preferencesService.openWorkspaceSettings(true, options);
} else {
@@ -615,8 +618,10 @@ export class SettingsEditor2 extends BaseEditor {
this._register(this.settingRenderers.onDidClickOverrideElement((element: ISettingOverrideClickEvent) => {
if (ConfigurationTargetToString(ConfigurationTarget.WORKSPACE) === element.scope.toUpperCase()) {
this.settingsTargetsWidget.updateTarget(ConfigurationTarget.WORKSPACE);
} else if (ConfigurationTargetToString(ConfigurationTarget.USER) === element.scope.toUpperCase()) {
this.settingsTargetsWidget.updateTarget(ConfigurationTarget.USER);
} else if (ConfigurationTargetToString(ConfigurationTarget.USER_LOCAL) === element.scope.toUpperCase()) {
this.settingsTargetsWidget.updateTarget(ConfigurationTarget.USER_LOCAL);
} else if (ConfigurationTargetToString(ConfigurationTarget.USER_REMOTE) === element.scope.toUpperCase()) {
this.settingsTargetsWidget.updateTarget(ConfigurationTarget.USER_REMOTE);
}
this.searchWidget.setValue(element.targetKey);
@@ -792,9 +797,10 @@ export class SettingsEditor2 extends BaseEditor {
}
}
const reportedTarget = props.settingsTarget === ConfigurationTarget.USER ? 'user' :
props.settingsTarget === ConfigurationTarget.WORKSPACE ? 'workspace' :
'folder';
const reportedTarget = props.settingsTarget === ConfigurationTarget.USER_LOCAL ? 'user' :
props.settingsTarget === ConfigurationTarget.USER_REMOTE ? 'user_remote' :
props.settingsTarget === ConfigurationTarget.WORKSPACE ? 'workspace' :
'folder';
const data = {
key: props.key,
@@ -1035,17 +1041,19 @@ export class SettingsEditor2 extends BaseEditor {
private triggerSearch(query: string): Promise<void> {
this.viewState.tagFilters = new Set<string>();
this.viewState.extensionFilters = new Set<string>();
if (query) {
const parsedQuery = parseQuery(query);
query = parsedQuery.query;
parsedQuery.tags.forEach(tag => this.viewState.tagFilters!.add(tag));
parsedQuery.extensionFilters.forEach(extensionId => this.viewState.extensionFilters!.add(extensionId));
}
if (query && query !== '@') {
query = this.parseSettingFromJSON(query) || query;
return this.triggerFilterPreferences(query);
} else {
if (this.viewState.tagFilters && this.viewState.tagFilters.size) {
if ((this.viewState.tagFilters && this.viewState.tagFilters.size) || (this.viewState.extensionFilters && this.viewState.extensionFilters.size)) {
this.searchResultModel = this.createFilterModel();
} else {
this.searchResultModel = null;

View File

@@ -116,6 +116,7 @@ suite('SettingsTree', () => {
'',
<IParsedQuery>{
tags: [],
extensionFilters: [],
query: ''
});
@@ -123,6 +124,7 @@ suite('SettingsTree', () => {
'@modified',
<IParsedQuery>{
tags: ['modified'],
extensionFilters: [],
query: ''
});
@@ -130,6 +132,7 @@ suite('SettingsTree', () => {
'@tag:foo',
<IParsedQuery>{
tags: ['foo'],
extensionFilters: [],
query: ''
});
@@ -137,6 +140,7 @@ suite('SettingsTree', () => {
'@modified foo',
<IParsedQuery>{
tags: ['modified'],
extensionFilters: [],
query: 'foo'
});
@@ -144,6 +148,7 @@ suite('SettingsTree', () => {
'@tag:foo @modified',
<IParsedQuery>{
tags: ['foo', 'modified'],
extensionFilters: [],
query: ''
});
@@ -151,6 +156,7 @@ suite('SettingsTree', () => {
'@tag:foo @modified my query',
<IParsedQuery>{
tags: ['foo', 'modified'],
extensionFilters: [],
query: 'my query'
});
@@ -158,6 +164,7 @@ suite('SettingsTree', () => {
'test @modified query',
<IParsedQuery>{
tags: ['modified'],
extensionFilters: [],
query: 'test query'
});
@@ -165,6 +172,7 @@ suite('SettingsTree', () => {
'test @modified',
<IParsedQuery>{
tags: ['modified'],
extensionFilters: [],
query: 'test'
});
@@ -172,7 +180,24 @@ suite('SettingsTree', () => {
'query has @ for some reason',
<IParsedQuery>{
tags: [],
extensionFilters: [],
query: 'query has @ for some reason'
});
testParseQuery(
'@ext:github.vscode-pull-request-github',
<IParsedQuery>{
tags: [],
extensionFilters: ['github.vscode-pull-request-github'],
query: ''
});
testParseQuery(
'@ext:github.vscode-pull-request-github,vscode.git',
<IParsedQuery>{
tags: [],
extensionFilters: ['github.vscode-pull-request-github', 'vscode.git'],
query: ''
});
});
});

View File

@@ -85,7 +85,8 @@ class PartsSplash {
colorInfo,
layoutInfo,
baseTheme
})
}),
{ encoding: 'utf8', overwriteEncoding: true }
);
if (baseTheme !== this._lastBaseTheme || colorInfo.editorBackground !== this._lastBackground) {

View File

@@ -533,7 +533,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
const workspace = this.contextService.getWorkspace();
// Handle top-level workspace files for local single folder workspace
if (state === WorkbenchState.FOLDER && workspace.folders[0].uri.scheme === Schemas.file) {
if (state === WorkbenchState.FOLDER) {
const workspaceFiles = rootFiles.filter(hasWorkspaceFileExtension);
if (workspaceFiles.length > 0) {
this.doHandleWorkspaceFiles(workspace.folders[0].uri, workspaceFiles);

View File

@@ -184,6 +184,9 @@ module.exports = function createWebviewManager(host) {
let isHandlingScroll = false;
const handleInnerScroll = (event) => {
if (!event.target || !event.target.body) {
return;
}
if (isHandlingScroll) {
return;
}
@@ -205,6 +208,10 @@ module.exports = function createWebviewManager(host) {
};
document.addEventListener('DOMContentLoaded', () => {
if (!document.body) {
return;
}
host.onMessage('styles', (_event, variables, activeTheme) => {
initData.styles = variables;
initData.activeTheme = activeTheme;
@@ -214,7 +221,9 @@ module.exports = function createWebviewManager(host) {
return;
}
applyStyles(target.contentDocument, target.contentDocument.body);
if (target.contentDocument) {
applyStyles(target.contentDocument, target.contentDocument.body);
}
});
// propagate focus
@@ -346,15 +355,15 @@ module.exports = function createWebviewManager(host) {
return false;
};
let onLoad = (contentDocument, contentWindow) => {
if (contentDocument.body) {
const onLoad = (contentDocument, contentWindow) => {
if (contentDocument && contentDocument.body) {
// Workaround for https://github.com/Microsoft/vscode/issues/12865
// check new scrollY and reset if neccessary
setInitialScrollPosition(contentDocument.body, contentWindow);
}
const newFrame = getPendingFrame();
if (newFrame && newFrame.contentDocument === contentDocument) {
if (newFrame && newFrame.contentDocument && newFrame.contentDocument === contentDocument) {
const oldActiveFrame = getActiveFrame();
if (oldActiveFrame) {
document.body.removeChild(oldActiveFrame);

View File

@@ -46,7 +46,7 @@ export class WebviewEditorInputFactory implements IEditorInputFactory {
title: input.getName(),
options: input.options,
extensionLocation: input.extension ? input.extension.location : undefined,
extensionId: input.extension ? input.extension.id.value : undefined,
extensionId: input.extension && input.extension.id ? input.extension.id.value : undefined,
state: input.state,
iconPath: input.iconPath ? { light: input.iconPath.light, dark: input.iconPath.dark, } : undefined,
group: input.group