Merge from vscode 31e03b8ffbb218a87e3941f2b63a249f061fe0e4 (#4986)

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

View File

@@ -235,9 +235,9 @@ export class FocusSessionActionItem extends SelectActionItem {
}
protected getSessions(): ReadonlyArray<IDebugSession> {
const hideSubSessions = this.configurationService.getValue<IDebugConfiguration>('debug').hideSubSessions;
const showSubSessions = this.configurationService.getValue<IDebugConfiguration>('debug').showSubSessionsInToolBar;
const sessions = this.debugService.getModel().getSessions();
return hideSubSessions ? sessions.filter(s => !s.parentSession) : sessions;
return showSubSessions ? sessions : sessions.filter(s => !s.parentSession);
}
}

View File

@@ -185,9 +185,9 @@ export function registerCommands(): void {
if (!session || !session.getId) {
session = debugService.getViewModel().focusedSession;
const configurationService = accessor.get(IConfigurationService);
const hideSubSessions = configurationService.getValue<IDebugConfiguration>('debug').hideSubSessions;
const showSubSessions = configurationService.getValue<IDebugConfiguration>('debug').showSubSessionsInToolBar;
// Stop should be sent to the root parent session
while (hideSubSessions && session && session.parentSession) {
while (!showSubSessions && session && session.parentSession) {
session = session.parentSession;
}
}

View File

@@ -150,6 +150,9 @@ export interface IDebugSession extends ITreeElement {
readonly state: State;
readonly root: IWorkspaceFolder;
readonly parentSession: IDebugSession | undefined;
readonly subId: string | undefined;
setSubId(subId: string | undefined): void;
getLabel(): string;
@@ -424,7 +427,7 @@ export interface IDebugConfiguration {
internalConsoleOptions: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart';
extensionHostDebugAdapter: boolean;
enableAllHovers: boolean;
hideSubSessions: boolean;
showSubSessionsInToolBar: boolean;
console: {
fontSize: number;
fontFamily: string;

View File

@@ -229,6 +229,11 @@ configurationRegistry.registerConfiguration({
description: nls.localize({ comment: ['This is the description for a setting'], key: 'enableAllHovers' }, "Controls whether the non-debug hovers should be enabled while debugging. When enabled the hover providers will be called to provide a hover. Regular hovers will not be shown even if this setting is enabled."),
default: false
},
'debug.showSubSessionsInToolBar': {
type: 'boolean',
description: nls.localize({ comment: ['This is the description for a setting'], key: 'showSubSessionsInToolBar' }, "Controls whether the debug sub-sessions are shown in the debug tool bar. When this setting is false the stop command on a sub-session will also stop the parent session."),
default: false
},
'debug.console.fontSize': {
type: 'number',
description: nls.localize('debug.console.fontSize', "Controls the font size in pixels in the debug console."),

View File

@@ -34,6 +34,7 @@ import { launchSchema, debuggersExtPoint, breakpointsExtPoint } from 'vs/workben
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { onUnexpectedError } from 'vs/base/common/errors';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
jsonRegistry.registerSchema(launchSchemaId, launchSchema);
@@ -535,6 +536,7 @@ class Launch extends AbstractLaunch implements ILaunch {
private configurationManager: ConfigurationManager,
public workspace: IWorkspaceFolder,
@IFileService private readonly fileService: IFileService,
@ITextFileService private readonly textFileService: ITextFileService,
@IEditorService private readonly editorService: IEditorService,
@IConfigurationService private readonly configurationService: IConfigurationService
) {
@@ -574,7 +576,7 @@ class Launch extends AbstractLaunch implements ILaunch {
}
created = true; // pin only if config file is created #8727
return this.fileService.updateContent(resource, content).then(() => {
return this.textFileService.write(resource, content).then(() => {
// convert string into IContent; see #32135
return content;
});

View File

@@ -135,27 +135,28 @@ export class DebugService implements IDebugService {
this.toDispose.push(this.storageService.onWillSaveState(this.saveState, this));
this.lifecycleService.onShutdown(this.dispose, this);
this.toDispose.push(this.extensionHostDebugService.onAttachSession(data => {
const session = this.model.getSession(data.id, true);
this.toDispose.push(this.extensionHostDebugService.onAttachSession(event => {
const session = this.model.getSession(event.sessionId, true);
if (session) {
// EH was started in debug mode -> attach to it
session.configuration.request = 'attach';
session.configuration.port = data.port;
session.configuration.port = event.port;
session.setSubId(event.subId);
this.launchOrAttachToSession(session).then(undefined, errors.onUnexpectedError);
}
}));
this.toDispose.push(this.extensionHostDebugService.onTerminateSession(sessionId => {
const session = this.model.getSession(sessionId);
if (session) {
this.toDispose.push(this.extensionHostDebugService.onTerminateSession(event => {
const session = this.model.getSession(event.sessionId);
if (session && session.subId === event.subId) {
session.disconnect().then(undefined, errors.onUnexpectedError);
}
}));
this.toDispose.push(this.extensionHostDebugService.onLogToSession(data => {
const session = this.model.getSession(data.id, true);
this.toDispose.push(this.extensionHostDebugService.onLogToSession(event => {
const session = this.model.getSession(event.sessionId, true);
if (session) {
// extension logged output -> show it in REPL
const sev = data.log.severity === 'warn' ? severity.Warning : data.log.severity === 'error' ? severity.Error : severity.Info;
const { args, stack } = parse(data.log);
const sev = event.log.severity === 'warn' ? severity.Warning : event.log.severity === 'error' ? severity.Error : severity.Info;
const { args, stack } = parse(event.log);
const frame = !!stack ? getFirstFrame(stack) : undefined;
session.logToRepl(sev, args, frame);
}
@@ -439,9 +440,9 @@ export class DebugService implements IDebugService {
}
this.viewModel.firstSessionStart = false;
const hideSubSessions = this.configurationService.getValue<IDebugConfiguration>('debug').hideSubSessions;
const showSubSessions = this.configurationService.getValue<IDebugConfiguration>('debug').showSubSessionsInToolBar;
const sessions = this.model.getSessions();
const shownSessions = hideSubSessions ? sessions.filter(s => !s.parentSession) : sessions;
const shownSessions = showSubSessions ? sessions : sessions.filter(s => !s.parentSession);
if (shownSessions.length > 1) {
this.viewModel.setMultiSessionView(true);
}

View File

@@ -35,7 +35,9 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { INotificationService } from 'vs/platform/notification/common/notification';
export class DebugSession implements IDebugSession {
private id: string;
private _subId: string | undefined;
private raw: RawDebugSession | undefined;
private initialized = false;
@@ -76,6 +78,14 @@ export class DebugSession implements IDebugSession {
return this.id;
}
setSubId(subId: string | undefined) {
this._subId = subId;
}
get subId(): string | undefined {
return this._subId;
}
get configuration(): IConfig {
return this._configuration.resolved;
}

View File

@@ -124,6 +124,12 @@ export class MockDebugService implements IDebugService {
export class MockSession implements IDebugSession {
subId: string | undefined;
setSubId(subId: string | undefined): void {
throw new Error('Method not implemented.');
}
get parentSession(): IDebugSession | undefined {
return undefined;
}

View File

@@ -7,7 +7,7 @@ import * as assert from 'assert';
import { Emitter } from 'vs/base/common/event';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { INotificationService, IPromptChoice, Severity, IPromptOptions } from 'vs/platform/notification/common/notification';
import { INotificationService, IPromptChoice, IPromptOptions, Severity } from 'vs/platform/notification/common/notification';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -37,7 +37,6 @@ suite('Experimental Prompts', () => {
commands: [
{
text: 'Yes',
externalLink: 'https://code.visualstudio.com'
},
{
text: 'No'
@@ -83,30 +82,30 @@ suite('Experimental Prompts', () => {
});
// test('Show experimental prompt if experiment should be run. Choosing option with link should mark experiment as complete', () => {
test('Show experimental prompt if experiment should be run. Choosing option with link should mark experiment as complete', () => {
// storageData = {
// enabled: true,
// state: ExperimentState.Run
// };
storageData = {
enabled: true,
state: ExperimentState.Run
};
// instantiationService.stub(INotificationService, {
// prompt: (a: Severity, b: string, c: IPromptChoice[], options: IPromptOptions) => {
// assert.equal(b, promptText);
// assert.equal(c.length, 2);
// c[0].run();
// return undefined!;
// }
// });
instantiationService.stub(INotificationService, {
prompt: (a: Severity, b: string, c: IPromptChoice[], options: IPromptOptions) => {
assert.equal(b, promptText);
assert.equal(c.length, 2);
c[0].run();
return undefined!;
}
});
// experimentalPrompt = instantiationService.createInstance(ExperimentalPrompts);
// onExperimentEnabledEvent.fire(experiment);
experimentalPrompt = instantiationService.createInstance(ExperimentalPrompts);
onExperimentEnabledEvent.fire(experiment);
// return Promise.resolve(null).then(result => {
// assert.equal(storageData['state'], ExperimentState.Complete);
// });
return Promise.resolve(null).then(result => {
assert.equal(storageData['state'], ExperimentState.Complete);
});
// });
});
test('Show experimental prompt if experiment should be run. Choosing negative option should mark experiment as complete', () => {

View File

@@ -7,7 +7,7 @@ import { IViewlet } from 'vs/workbench/common/viewlet';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { IPager } from 'vs/base/common/paging';
import { IQueryOptions, EnablementState, ILocalExtension, IGalleryExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IQueryOptions, EnablementState, ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views';
import { Registry } from 'vs/platform/registry/common/platform';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -32,40 +32,41 @@ export const enum ExtensionState {
}
export interface IExtension {
type?: ExtensionType;
state: ExtensionState;
name: string;
displayName: string;
identifier: IExtensionIdentifier;
publisher: string;
publisherDisplayName: string;
version: string;
latestVersion: string;
description: string;
url?: string;
readonly type?: ExtensionType;
readonly state: ExtensionState;
readonly name: string;
readonly displayName: string;
readonly identifier: IExtensionIdentifier;
readonly publisher: string;
readonly publisherDisplayName: string;
readonly version: string;
readonly latestVersion: string;
readonly description: string;
readonly url?: string;
// {{SQL CARBON EDIT}}
downloadPage: string;
repository?: string;
iconUrl: string;
iconUrlFallback: string;
licenseUrl?: string;
installCount?: number;
rating?: number;
ratingCount?: number;
outdated: boolean;
enablementState: EnablementState;
dependencies: string[];
extensionPack: string[];
telemetryData: any;
preview: boolean;
readonly downloadPage?: string;
readonly repository?: string;
readonly iconUrl: string;
readonly iconUrlFallback: string;
readonly licenseUrl?: string;
readonly installCount?: number;
readonly rating?: number;
readonly ratingCount?: number;
readonly outdated: boolean;
readonly enablementState: EnablementState;
readonly dependencies: string[];
readonly extensionPack: string[];
readonly telemetryData: any;
readonly preview: boolean;
getManifest(token: CancellationToken): Promise<IExtensionManifest | null>;
getReadme(token: CancellationToken): Promise<string>;
hasReadme(): boolean;
getChangelog(token: CancellationToken): Promise<string>;
hasChangelog(): boolean;
local?: ILocalExtension;
readonly server?: IExtensionManagementServer;
readonly local?: ILocalExtension;
gallery?: IGalleryExtension;
isMalicious: boolean;
readonly isMalicious: boolean;
}
export interface IExtensionDependencies {
@@ -84,7 +85,8 @@ export interface IExtensionsWorkbenchService {
_serviceBrand: any;
onChange: Event<IExtension | undefined>;
local: IExtension[];
queryLocal(): Promise<IExtension[]>;
outdated: IExtension[];
queryLocal(server?: IExtensionManagementServer): Promise<IExtension[]>;
queryGallery(token: CancellationToken): Promise<IPager<IExtension>>;
queryGallery(options: IQueryOptions, token: CancellationToken): Promise<IPager<IExtension>>;
canInstall(extension: IExtension): boolean;
@@ -128,6 +130,7 @@ export enum ExtensionsPolicy {
export interface IExtensionContainer {
extension: IExtension | null;
updateWhenCounterExtensionChanges?: boolean;
update(): void;
}
@@ -149,7 +152,11 @@ export class ExtensionContainers extends Disposable {
for (const container of this.containers) {
if (extension && container.extension) {
if (areSameExtensions(container.extension.identifier, extension.identifier)) {
container.extension = extension;
if (!container.extension.server || container.extension.server === extension.server) {
container.extension = extension;
} else if (container.updateWhenCounterExtensionChanges) {
container.update();
}
}
} else {
container.update();

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 } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
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 { 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';
@@ -215,6 +215,8 @@ export class ExtensionEditor extends BaseEditor {
createEditor(parent: HTMLElement): void {
const root = append(parent, $('.extension-editor'));
root.tabIndex = 0; // this is required for the focus tracker on the editor
root.style.outline = 'none';
this.header = append(root, $('.header'));
this.iconContainer = append(this.header, $('.icon-container'));
@@ -380,6 +382,8 @@ export class ExtensionEditor extends BaseEditor {
// this.instantiationService.createInstance(RatingsWidget, this.rating, false)
];
const reloadAction = this.instantiationService.createInstance(ReloadAction);
const combinedInstallAction = this.instantiationService.createInstance(CombinedInstallAction);
const systemDisabledWarningAction = this.instantiationService.createInstance(SystemDisabledWarningAction);
const actions = [
reloadAction,
this.instantiationService.createInstance(StatusLabelAction),
@@ -388,7 +392,10 @@ export class ExtensionEditor extends BaseEditor {
this.instantiationService.createInstance(SetFileIconThemeAction, fileIconThemes),
this.instantiationService.createInstance(EnableDropDownAction),
this.instantiationService.createInstance(DisableDropDownAction, runningExtensions),
this.instantiationService.createInstance(CombinedInstallAction),
this.instantiationService.createInstance(RemoteInstallAction),
combinedInstallAction,
systemDisabledWarningAction,
this.instantiationService.createInstance(SystemDisabledLabelAction, systemDisabledWarningAction),
this.instantiationService.createInstance(MaliciousStatusLabelAction, true),
];
const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets]);
@@ -410,6 +417,9 @@ export class ExtensionEditor extends BaseEditor {
this.extensionManifest.get()
.promise
.then(manifest => {
if (manifest) {
combinedInstallAction.manifest = manifest;
}
if (extension.extensionPack.length) {
this.navbar.push(NavbarSection.ExtensionPack, localize('extensionPack', "Extension Pack"), localize('extensionsPack', "Set of extensions that can be installed together"));
}
@@ -543,19 +553,20 @@ export class ExtensionEditor extends BaseEditor {
.then(renderBody)
.then(removeEmbeddedSVGs)
.then(body => {
const wbeviewElement = this.instantiationService.createInstance(WebviewElement,
const webviewElement = this.instantiationService.createInstance(WebviewElement,
{
enableFindWidget: true,
},
{
svgWhiteList: this.extensionsWorkbenchService.allowedBadgeProviders
});
wbeviewElement.mountTo(this.content);
const removeLayoutParticipant = arrays.insert(this.layoutParticipants, wbeviewElement);
webviewElement.mountTo(this.content);
this.contentDisposables.push(webviewElement.onDidFocus(() => this.fireOnDidFocus()));
const removeLayoutParticipant = arrays.insert(this.layoutParticipants, webviewElement);
this.contentDisposables.push(toDisposable(removeLayoutParticipant));
wbeviewElement.contents = body;
webviewElement.contents = body;
wbeviewElement.onDidClickLink(link => {
this.contentDisposables.push(webviewElement.onDidClickLink(link => {
if (!link) {
return;
}
@@ -563,9 +574,9 @@ export class ExtensionEditor extends BaseEditor {
if (['http', 'https', 'mailto'].indexOf(link.scheme) >= 0 || (link.scheme === 'command' && link.path === ShowCurrentReleaseNotesAction.ID)) {
this.openerService.open(link);
}
}, null, this.contentDisposables);
this.contentDisposables.push(wbeviewElement);
return wbeviewElement;
}, null, this.contentDisposables));
this.contentDisposables.push(webviewElement);
return webviewElement;
})
.then(undefined, () => {
const p = append(this.content, $('p.nocontent'));

View File

@@ -16,9 +16,9 @@ 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 } from 'vs/platform/extensionManagement/common/extensionManagement';
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 { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ShowViewletAction } from 'vs/workbench/browser/viewlet';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
@@ -56,6 +56,11 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la
import { alert } from 'vs/base/browser/ui/aria/aria';
import { coalesce } from 'vs/base/common/arrays';
import { IWorkbenchThemeService, COLOR_THEME_SETTING, ICON_THEME_SETTING, IFileIconTheme, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
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 { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
// {{SQL CARBON EDIT}}
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
@@ -145,16 +150,29 @@ export class InstallAction extends ExtensionAction {
private static readonly Class = 'extension-action prominent install';
private static readonly InstallingClass = 'extension-action install installing';
private disposables: IDisposable[] = [];
private _manifest: IExtensionManifest | null;
set manifest(manifest: IExtensionManifest) {
this._manifest = manifest;
this.updateLabel();
}
constructor(
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@INotificationService private readonly notificationService: INotificationService,
@IOpenerService private readonly openerService: IOpenerService,
@IExtensionService private readonly runtimeExtensionService: IExtensionService,
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@ILabelService private readonly labelService: ILabelService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
) {
super(`extensions.install`, InstallAction.INSTALL_LABEL, InstallAction.Class, false);
this.update();
this.labelService.onDidChangeFormatters(() => this.updateLabel(), this, this.disposables);
}
update(): void {
@@ -166,15 +184,28 @@ export class InstallAction extends ExtensionAction {
}
this.enabled = this.extensionsWorkbenchService.canInstall(this.extension) && this.extension.state === ExtensionState.Uninstalled;
this.class = this.extension.state === ExtensionState.Installing ? InstallAction.InstallingClass : InstallAction.Class;
this.updateLabel();
}
private updateLabel(): void {
if (this.extension.state === ExtensionState.Installing) {
this.label = InstallAction.INSTALLING_LABEL;
this.class = InstallAction.InstallingClass;
this.tooltip = InstallAction.INSTALLING_LABEL;
} else {
this.label = InstallAction.INSTALL_LABEL;
this.class = InstallAction.Class;
this.tooltip = InstallAction.INSTALL_LABEL;
if (this._manifest && this.workbenchEnvironmentService.configuration.remoteAuthority) {
if (isUIExtension(this._manifest, this.configurationService)) {
this.label = `${InstallAction.INSTALL_LABEL} (${this.extensionManagementServerService.localExtensionManagementServer.label})`;
this.tooltip = `${InstallAction.INSTALL_LABEL} (${this.extensionManagementServerService.localExtensionManagementServer.label})`;
} else {
const host = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.workbenchEnvironmentService.configuration.remoteAuthority) || localize('remote', "Remote");
this.label = `${InstallAction.INSTALL_LABEL} (${host})`;
this.tooltip = `${InstallAction.INSTALL_LABEL} (${host})`;
}
} else {
this.label = InstallAction.INSTALL_LABEL;
this.tooltip = InstallAction.INSTALL_LABEL;
}
}
}
@@ -185,7 +216,8 @@ export class InstallAction extends ExtensionAction {
const extension = await this.install(this.extension);
// {{SQL CARBON EDIT}}
alert(localize('installExtensionComplete', "Installing extension {0} is completed. Please reload Azure Data Studio to enable it.", this.extension.displayName));
// Add extension object check since ADS third party extensions will be directed to a download page
// and the extension object will be undefined.
if (extension && extension.local) {
@@ -246,6 +278,93 @@ export class InstallAction extends ExtensionAction {
}
return null;
}
dispose(): void {
this.disposables = dispose(this.disposables);
super.dispose();
}
}
export class RemoteInstallAction extends ExtensionAction {
private static INSTALL_LABEL = localize('install', "Install");
private static INSTALLING_LABEL = localize('installing', "Installing");
private static readonly Class = 'extension-action prominent install';
private static readonly InstallingClass = 'extension-action install installing';
updateWhenCounterExtensionChanges: boolean = true;
private disposables: IDisposable[] = [];
private installing: boolean = false;
constructor(
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@ILabelService private readonly labelService: ILabelService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IConfigurationService private readonly configurationService: IConfigurationService,
) {
super(`extensions.remoteinstall`, RemoteInstallAction.INSTALL_LABEL, RemoteInstallAction.Class, false);
this.labelService.onDidChangeFormatters(() => this.updateLabel(), this, this.disposables);
this.updateLabel();
this.update();
}
private updateLabel(): void {
if (this.installing) {
this.label = RemoteInstallAction.INSTALLING_LABEL;
return;
}
const remoteAuthority = this.environmentService.configuration.remoteAuthority;
if (remoteAuthority) {
const host = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority) || localize('remote', "Remote");
this.label = `${RemoteInstallAction.INSTALL_LABEL} (${host})`;
return;
}
}
update(): void {
this.enabled = false;
this.class = RemoteInstallAction.Class;
if (this.installing) {
this.enabled = true;
this.class = RemoteInstallAction.InstallingClass;
this.updateLabel();
return;
}
if (this.environmentService.configuration.remoteAuthority
// Installed User Extension
&& this.extension && this.extension.local && this.extension.type === ExtensionType.User && this.extension.state === ExtensionState.Installed
// 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.extensionsWorkbenchService.canInstall(this.extension)
) {
this.enabled = true;
this.updateLabel();
return;
}
}
async run(): Promise<void> {
if (this.extensionManagementServerService.remoteExtensionManagementServer && !this.installing) {
this.installing = true;
this.update();
this.extensionsWorkbenchService.open(this.extension);
alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));
if (this.extension.gallery) {
await this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(this.extension.gallery);
this.installing = false;
this.update();
}
}
}
dispose(): void {
this.disposables = dispose(this.disposables);
super.dispose();
}
}
export class UninstallAction extends ExtensionAction {
@@ -281,19 +400,12 @@ export class UninstallAction extends ExtensionAction {
this.label = UninstallAction.UninstallLabel;
this.class = UninstallAction.UninstallClass;
const installedExtensions = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier));
if (!installedExtensions.length) {
this.enabled = false;
return;
}
if (state !== ExtensionState.Installed) {
this.enabled = false;
return;
}
if (installedExtensions[0].type !== ExtensionType.User) {
if (this.extension.type !== ExtensionType.User) {
this.enabled = false;
return;
}
@@ -330,6 +442,8 @@ export class CombinedInstallAction extends ExtensionAction {
this.update();
}
set manifest(manifiest: IExtensionManifest) { this.installAction.manifest = manifiest; this.update(); }
update(): void {
this.installAction.extension = this.extension;
this.uninstallAction.extension = this.extension;
@@ -901,36 +1015,32 @@ export class CheckForUpdatesAction extends Action {
}
private checkUpdatesAndNotify(): void {
this.extensionsWorkbenchService.queryLocal().then(
extensions => {
const outdatedExtensions = extensions.filter(ext => ext.outdated === true);
if (!outdatedExtensions.length) {
this.notificationService.info(localize('noUpdatesAvailable', "All Extensions are up to date."));
return;
}
const outdated = this.extensionsWorkbenchService.outdated;
if (!outdated.length) {
this.notificationService.info(localize('noUpdatesAvailable', "All Extensions are up to date."));
return;
}
let msgAvailableExtensions = outdatedExtensions.length === 1 ? localize('singleUpdateAvailable', "An extension update is available.") : localize('updatesAvailable', "{0} extension updates are available.", outdatedExtensions.length);
let msgAvailableExtensions = outdated.length === 1 ? localize('singleUpdateAvailable', "An extension update is available.") : localize('updatesAvailable', "{0} extension updates are available.", outdated.length);
const disabledExtensionsCount = outdatedExtensions.filter(ext => ext.enablementState === EnablementState.Disabled || ext.enablementState === EnablementState.WorkspaceDisabled).length;
if (disabledExtensionsCount) {
if (outdatedExtensions.length === 1) {
msgAvailableExtensions = localize('singleDisabledUpdateAvailable', "An update to an extension which is disabled is available.");
} else if (disabledExtensionsCount === 1) {
msgAvailableExtensions = localize('updatesAvailableOneDisabled', "{0} extension updates are available. One of them is for a disabled extension.", outdatedExtensions.length);
} else if (disabledExtensionsCount === outdatedExtensions.length) {
msgAvailableExtensions = localize('updatesAvailableAllDisabled', "{0} extension updates are available. All of them are for disabled extensions.", outdatedExtensions.length);
} else {
msgAvailableExtensions = localize('updatesAvailableIncludingDisabled', "{0} extension updates are available. {1} of them are for disabled extensions.", outdatedExtensions.length, disabledExtensionsCount);
}
}
this.viewletService.openViewlet(VIEWLET_ID, true)
.then(viewlet => viewlet as IExtensionsViewlet)
.then(viewlet => viewlet.search(''));
this.notificationService.info(msgAvailableExtensions);
const disabledExtensionsCount = outdated.filter(ext => ext.enablementState === EnablementState.Disabled || ext.enablementState === EnablementState.WorkspaceDisabled).length;
if (disabledExtensionsCount) {
if (outdated.length === 1) {
msgAvailableExtensions = localize('singleDisabledUpdateAvailable', "An update to an extension which is disabled is available.");
} else if (disabledExtensionsCount === 1) {
msgAvailableExtensions = localize('updatesAvailableOneDisabled', "{0} extension updates are available. One of them is for a disabled extension.", outdated.length);
} else if (disabledExtensionsCount === outdated.length) {
msgAvailableExtensions = localize('updatesAvailableAllDisabled', "{0} extension updates are available. All of them are for disabled extensions.", outdated.length);
} else {
msgAvailableExtensions = localize('updatesAvailableIncludingDisabled', "{0} extension updates are available. {1} of them are for disabled extensions.", outdated.length, disabledExtensionsCount);
}
);
}
this.viewletService.openViewlet(VIEWLET_ID, true)
.then(viewlet => viewlet as IExtensionsViewlet)
.then(viewlet => viewlet.search(''));
this.notificationService.info(msgAvailableExtensions);
}
run(): Promise<any> {
@@ -1009,16 +1119,12 @@ export class UpdateAllAction extends Action {
this.update();
}
private get outdated(): IExtension[] {
return this.extensionsWorkbenchService.local.filter(e => e.outdated && e.state !== ExtensionState.Installing);
}
private update(): void {
this.enabled = this.outdated.length > 0;
this.enabled = this.extensionsWorkbenchService.outdated.length > 0;
}
run(): Promise<any> {
return Promise.all(this.outdated.map(e => this.install(e)));
return Promise.all(this.extensionsWorkbenchService.outdated.map(e => this.install(e)));
}
private install(extension: IExtension): Promise<any> {
@@ -1044,16 +1150,18 @@ export class ReloadAction extends ExtensionAction {
private static readonly EnabledClass = 'extension-action reload';
private static readonly DisabledClass = `${ReloadAction.EnabledClass} disabled`;
updateWhenCounterExtensionChanges: boolean = true;
private disposables: IDisposable[] = [];
private _runningExtensions: IExtensionDescription[] = [];
private get runningExtensions(): IExtensionDescription[] { return this._runningExtensions; }
private set runningExtensions(runningExtensions: IExtensionDescription[]) { this._runningExtensions = runningExtensions; this.update(); }
private _runningExtensions: IExtensionDescription[] | null = null;
constructor(
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IWindowService private readonly windowService: IWindowService,
@IExtensionService private readonly extensionService: IExtensionService,
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IConfigurationService private readonly configurationService: IConfigurationService
) {
super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false);
this.extensionService.onDidChangeExtensions(this.updateRunningExtensions, this, this.disposables);
@@ -1061,81 +1169,90 @@ export class ReloadAction extends ExtensionAction {
}
private updateRunningExtensions(): void {
this.extensionService.getExtensions().then(runningExtensions => this.runningExtensions = runningExtensions);
this.extensionService.getExtensions().then(runningExtensions => { this._runningExtensions = runningExtensions; this.update(); });
}
update(): void {
this.enabled = false;
this.tooltip = '';
if (!this.extension) {
if (!this.extension || !this._runningExtensions) {
return;
}
const state = this.extension.state;
if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) {
return;
}
const installed = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier))[0];
const local = this.extension.local || (installed && installed.local);
if (local && local.manifest && local.manifest.contributes && local.manifest.contributes.localizations && local.manifest.contributes.localizations.length > 0) {
if (this.extension.local && this.extension.local.manifest && this.extension.local.manifest.contributes && this.extension.local.manifest.contributes.localizations && this.extension.local.manifest.contributes.localizations.length > 0) {
return;
}
this.computeReloadState(installed);
this.computeReloadState();
this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass;
}
private computeReloadState(installed: IExtension): void {
private computeReloadState(): void {
if (!this._runningExtensions) {
return;
}
const isUninstalled = this.extension.state === ExtensionState.Uninstalled;
const isDisabled = this.extension.local ? !this.extensionEnablementService.isEnabled(this.extension.local) : false;
const isEnabled = this.extension.local ? this.extensionEnablementService.isEnabled(this.extension.local) : false;
const runningExtension = this.runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
if (installed && installed.local) {
if (isUninstalled) {
if (runningExtension) {
const isDifferentVersionRunning = this.extension.version !== runningExtension.version;
if (isDifferentVersionRunning && !isDisabled) {
if (!(this.extension.local && this.extensionService.canAddExtension(toExtensionDescription(this.extension.local)))) {
// Requires reload to run the updated extension
this.enabled = true;
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
this.tooltip = localize('postUpdateTooltip', "Please reload Azure Data Studio to complete the updating of this extension.");
}
return;
}
if (isDisabled) {
// Requires reload to disable the extension
this.enabled = true;
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
this.tooltip = localize('postDisableTooltip', "Please reload Azure Data Studio to complete the disabling of this extension.");
return;
}
} else {
if (!isDisabled && !(this.extension.local && this.extensionService.canAddExtension(toExtensionDescription(this.extension.local)))) {
this.enabled = true;
if (isEnabled) {
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
this.tooltip = localize('postEnableTooltip', "Please reload Azure Data Studio to complete the enabling of this extension.");
} else {
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
this.tooltip = localize('postInstallTooltip', "Please reload Azure Data Studio to complete the installation of this extension.");
alert(localize('installExtensionComplete', "Installing extension {0} is completed. Please reload Azure Data Studio to enable it.", this.extension.displayName));
}
}
this.enabled = true;
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
this.tooltip = localize('postUninstallTooltip', "Please reload Azure Data Studio to complete the uninstallation of this extension.");
alert(localize('uninstallExtensionComplete', "Please reload Azure Data Studio to complete the uninstallation of the extension {0}.", this.extension.displayName));
}
return;
}
if (isUninstalled && runningExtension) {
// Requires reload to deactivate the extension
this.enabled = true;
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
this.tooltip = localize('postUninstallTooltip', "Please reload Azure Data Studio to complete the uninstallation of this extension.");
alert(localize('uninstallExtensionComplete', "Please reload Azure Data Studio to complete the uninstallation of the extension {0}.", this.extension.displayName));
return;
if (this.extension.local) {
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))) {
this.enabled = true;
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
this.tooltip = localize('postUpdateTooltip', "Please reload Azure Data Studio to enable the updated extension.");
}
} else {
if (isSameExtensionRunning) {
this.enabled = true;
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
this.tooltip = localize('postDisableTooltip', "Please reload Azure Data Studio to disable this extension.");
}
}
return;
} else {
// Extension is not running
if (isEnabled && !this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))) {
this.enabled = true;
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
this.tooltip = localize('postEnableTooltip', "Please reload Azure Data Studio to enable this extension.");
return;
}
if (this.workbenchEnvironmentService.configuration.remoteAuthority
// Local Workspace Extension
&& this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)
) {
const remoteExtension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.remoteExtensionManagementServer)[0];
// Extension exist in remote and enabled
if (remoteExtension && remoteExtension.local && this.extensionEnablementService.isEnabled(remoteExtension.local)) {
this.enabled = true;
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
this.tooltip = localize('postEnableTooltip', "Please reload Azure Data Studio to enable this extension.");
alert(localize('installExtensionComplete', "Installing extension {0} is completed. Please reload Azure Data Studio to enable it.", this.extension.displayName));
return;
}
}
}
}
}
@@ -1394,10 +1511,11 @@ export class ClearExtensionsInputAction extends Action {
id: string,
label: string,
onSearchChange: Event<string>,
value: string,
@IViewletService private readonly viewletService: IViewletService
) {
super(id, label, 'clear-extensions', true);
this.enabled = false;
this.onSearchChange(value);
onSearchChange(this.onSearchChange, this, this.disposables);
}
@@ -1893,6 +2011,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
label: string,
@IWorkspaceContextService protected contextService: IWorkspaceContextService,
@IFileService private readonly fileService: IFileService,
@ITextFileService private readonly textFileService: ITextFileService,
@IEditorService protected editorService: IEditorService,
@IJSONEditingService private readonly jsonEditingService: IJSONEditingService,
@ITextModelService private readonly textModelResolverService: ITextModelService
@@ -2041,7 +2160,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
return Promise.resolve(this.fileService.resolveContent(extensionsFileResource)).then(content => {
return { created: false, extensionsFileResource, content: content.value };
}, err => {
return this.fileService.updateContent(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => {
return this.textFileService.write(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => {
return { created: true, extensionsFileResource, content: ExtensionsConfigurationInitialContent };
});
});
@@ -2059,12 +2178,13 @@ export class ConfigureWorkspaceRecommendedExtensionsAction extends AbstractConfi
id: string,
label: string,
@IFileService fileService: IFileService,
@ITextFileService textFileService: ITextFileService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IEditorService editorService: IEditorService,
@IJSONEditingService jsonEditingService: IJSONEditingService,
@ITextModelService textModelResolverService: ITextModelService
) {
super(id, label, contextService, fileService, editorService, jsonEditingService, textModelResolverService);
super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);
this.contextService.onDidChangeWorkbenchState(() => this.update(), this, this.disposables);
this.update();
}
@@ -2100,13 +2220,14 @@ export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends Abstrac
id: string,
label: string,
@IFileService fileService: IFileService,
@ITextFileService textFileService: ITextFileService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IEditorService editorService: IEditorService,
@IJSONEditingService jsonEditingService: IJSONEditingService,
@ITextModelService textModelResolverService: ITextModelService,
@ICommandService private readonly commandService: ICommandService
) {
super(id, label, contextService, fileService, editorService, jsonEditingService, textModelResolverService);
super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);
this.contextService.onDidChangeWorkspaceFolders(() => this.update(), this, this.disposables);
this.update();
}
@@ -2145,6 +2266,7 @@ export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigure
id: string,
label: string,
@IFileService fileService: IFileService,
@ITextFileService textFileService: ITextFileService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IEditorService editorService: IEditorService,
@IJSONEditingService jsonEditingService: IJSONEditingService,
@@ -2152,7 +2274,7 @@ export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigure
@ICommandService private readonly commandService: ICommandService,
@INotificationService private readonly notificationService: INotificationService
) {
super(id, label, contextService, fileService, editorService, jsonEditingService, textModelResolverService);
super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);
}
run(shouldRecommend: boolean): Promise<void> {
@@ -2228,13 +2350,14 @@ export class AddToWorkspaceRecommendationsAction extends AbstractConfigureRecomm
id: string,
label: string,
@IFileService fileService: IFileService,
@ITextFileService textFileService: ITextFileService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IEditorService editorService: IEditorService,
@IJSONEditingService jsonEditingService: IJSONEditingService,
@ITextModelService textModelResolverService: ITextModelService,
@INotificationService private readonly notificationService: INotificationService
) {
super(id, label, contextService, fileService, editorService, jsonEditingService, textModelResolverService);
super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);
}
run(shouldRecommend: boolean): Promise<void> {
@@ -2410,6 +2533,103 @@ export class MaliciousStatusLabelAction extends ExtensionAction {
}
}
export class SystemDisabledLabelAction extends ExtensionAction {
private static readonly Class = 'disable-status';
updateWhenCounterExtensionChanges: boolean = true;
private disposables: IDisposable[] = [];
constructor(
private readonly warningAction: SystemDisabledWarningAction,
) {
super('extensions.systemDisabledLabel', warningAction.tooltip, `${SystemDisabledLabelAction.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.label = this.warningAction.tooltip;
} else {
this.class = `${SystemDisabledLabelAction.Class} hide`;
this.label = '';
}
}
run(): Promise<any> {
return Promise.resolve(null);
}
dispose(): void {
dispose(this.disposables);
super.dispose();
}
}
export class SystemDisabledWarningAction extends ExtensionAction {
private static readonly Class = 'disable-warning';
updateWhenCounterExtensionChanges: boolean = true;
private disposables: IDisposable[] = [];
private _runningExtensions: IExtensionDescription[] | null = null;
constructor(
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@ILabelService private readonly labelService: ILabelService,
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionService private readonly extensionService: IExtensionService,
) {
super('extensions.install', '', `${SystemDisabledWarningAction.Class} hide`, false);
this.labelService.onDidChangeFormatters(() => this.update(), this, this.disposables);
this.extensionService.onDidChangeExtensions(this.updateRunningExtensions, this, this.disposables);
this.updateRunningExtensions();
this.update();
}
private updateRunningExtensions(): void {
this.extensionService.getExtensions().then(runningExtensions => { this._runningExtensions = runningExtensions; this.update(); });
}
update(): void {
this.enabled = false;
this.class = `${SystemDisabledWarningAction.Class} hide`;
this.tooltip = '';
if (this.extension && this.extension.local && this._runningExtensions) {
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.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)}`;
}
return;
}
}
}
run(): Promise<any> {
return Promise.resolve(null);
}
dispose(): void {
dispose(this.disposables);
super.dispose();
}
}
export class DisableAllAction extends Action {
static readonly ID = 'workbench.extensions.action.disableAll';

View File

@@ -12,8 +12,8 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
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, IExtensionsWorkbenchService, ExtensionContainers } from 'vs/workbench/contrib/extensions/common/extensions';
import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionItem, StatusLabelAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
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 { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/electron-browser/extensionsWidgets';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@@ -54,9 +54,9 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
private extensionViewState: IExtensionsViewState,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@INotificationService private readonly notificationService: INotificationService,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionService private readonly extensionService: IExtensionService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService
) { }
get templateId() { return 'extension'; }
@@ -100,7 +100,9 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
this.instantiationService.createInstance(UpdateAction),
this.instantiationService.createInstance(ReloadAction),
this.instantiationService.createInstance(InstallAction),
this.instantiationService.createInstance(RemoteInstallAction),
this.instantiationService.createInstance(MaliciousStatusLabelAction, false),
this.instantiationService.createInstance(SystemDisabledWarningAction),
this.instantiationService.createInstance(ManageExtensionAction)
];
const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets]);
@@ -136,23 +138,18 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
renderElement(extension: IExtension, index: number, data: ITemplateData): void {
removeClass(data.element, 'loading');
if (extension.state !== ExtensionState.Uninstalled && !extension.server) {
// Get the extension if it is installed and has no server information
extension = this.extensionsWorkbenchService.local.filter(e => e.server === extension.server && areSameExtensions(e.identifier, extension.identifier))[0] || extension;
}
data.extensionDisposables = dispose(data.extensionDisposables);
const updateEnablement = async () => {
const runningExtensions = await this.extensionService.getExtensions();
const installed = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, extension.identifier))[0];
if (installed && installed.local) {
const installedExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(installed.local.location);
const isSameExtensionRunning = runningExtensions.some(e => {
if (!areSameExtensions({ id: e.identifier.value }, extension.identifier)) {
return false;
}
const runningExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(e.extensionLocation);
if (!installedExtensionServer || !runningExtensionServer) {
return false;
}
return installedExtensionServer.authority === runningExtensionServer.authority;
});
if (extension.local) {
const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, extension.identifier))[0];
const isSameExtensionRunning = runningExtension && extension.server === this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation);
toggleClass(data.root, 'disabled', !isSameExtensionRunning);
} else {
removeClass(data.root, 'disabled');

View File

@@ -24,9 +24,9 @@ import {
ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction,
EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction
} from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { IExtensionManagementService, IExtensionManagementServerService, IExtensionManagementServer, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementService, IExtensionManagementServerService, IExtensionManagementServer, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInExtensionsView, BuiltInThemesExtensionsView, BuiltInBasicsExtensionsView, GroupByServerExtensionsView, DefaultRecommendedExtensionsView } from './extensionsViews';
import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInExtensionsView, BuiltInThemesExtensionsView, BuiltInBasicsExtensionsView, ServerExtensionsView, DefaultRecommendedExtensionsView } from './extensionsViews';
import { OpenGlobalSettingsAction } from 'vs/workbench/contrib/preferences/browser/preferencesActions';
import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
@@ -35,7 +35,6 @@ import { IActivityService, ProgressBadge, NumberBadge } from 'vs/workbench/servi
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IViewsRegistry, IViewDescriptor, Extensions } from 'vs/workbench/common/views';
import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IContextKeyService, ContextKeyExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
@@ -54,6 +53,8 @@ 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 {
target: HTMLInputElement;
@@ -61,12 +62,13 @@ interface SearchInputEvent extends Event {
}
const NonEmptyWorkspaceContext = new RawContextKey<boolean>('nonEmptyWorkspace', false);
const SearchExtensionsContext = new RawContextKey<boolean>('searchExtensions', false);
const DefaultViewsContext = new RawContextKey<boolean>('defaultExtensionViews', true);
const SearchMarketplaceExtensionsContext = new RawContextKey<boolean>('searchMarketplaceExtensions', false);
const SearchServerExtensionsContext = new RawContextKey<boolean>('searchServerExtensions', false);
const HasInstalledExtensionsContext = new RawContextKey<boolean>('hasInstalledExtensions', true);
const SearchBuiltInExtensionsContext = new RawContextKey<boolean>('searchBuiltInExtensions', false);
const RecommendedExtensionsContext = new RawContextKey<boolean>('recommendedExtensions', false);
const DefaultRecommendedExtensionsContext = new RawContextKey<boolean>('defaultRecommendedExtensions', false);
const GroupByServersContext = new RawContextKey<boolean>('groupByServersContext', false);
const viewIdNameMappings: { [id: string]: string } = {
'extensions.listView': localize('marketPlace', "Marketplace"),
'extensions.enabledExtensionList': localize('enabledExtensions', "Enabled"),
@@ -92,10 +94,10 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
private registerViews(): void {
let viewDescriptors: IViewDescriptor[] = [];
viewDescriptors.push(this.createMarketPlaceExtensionsListViewDescriptor());
viewDescriptors.push(this.createEnabledExtensionsListViewDescriptor());
viewDescriptors.push(this.createDisabledExtensionsListViewDescriptor());
viewDescriptors.push(this.createDefaultEnabledExtensionsListViewDescriptor());
viewDescriptors.push(this.createDefaultDisabledExtensionsListViewDescriptor());
// {{SQL CARBON EDIT}}
// viewDescriptors.push(this.createPopularExtensionsListViewDescriptor());
// viewDescriptors.push(this.createDefaultPopularExtensionsListViewDescriptor());
viewDescriptors.push(this.createBuiltInExtensionsListViewDescriptor());
viewDescriptors.push(this.createBuiltInBasicsExtensionsListViewDescriptor());
viewDescriptors.push(this.createBuiltInThemesExtensionsListViewDescriptor());
@@ -103,8 +105,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
viewDescriptors.push(this.createOtherRecommendedExtensionsListViewDescriptor());
viewDescriptors.push(this.createWorkspaceRecommendedExtensionsListViewDescriptor());
viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.localExtensionManagementServer));
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.localExtensionManagementServer));
viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.remoteExtensionManagementServer));
}
@@ -118,20 +120,20 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: ExtensionsListView },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchExtensions'), ContextKeyExpr.not('searchInstalledExtensions'), ContextKeyExpr.not('searchBuiltInExtensions'), ContextKeyExpr.not('recommendedExtensions'), ContextKeyExpr.not('groupByServersContext')),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchMarketplaceExtensions')),
weight: 100
};
}
// Separate view for enabled extensions required as we need to show enabled, disabled and recommended sections
// in the default view when there is no search text, but user has installed extensions.
private createEnabledExtensionsListViewDescriptor(): IViewDescriptor {
private createDefaultEnabledExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.enabledExtensionList';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: EnabledExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('hasInstalledExtensions')),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.isEqualTo('')),
weight: 40,
canToggleVisibility: true,
order: 1
@@ -140,13 +142,13 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
// Separate view for disabled extensions required as we need to show enabled, disabled and recommended sections
// in the default view when there is no search text, but user has installed extensions.
private createDisabledExtensionsListViewDescriptor(): IViewDescriptor {
private createDefaultDisabledExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.disabledExtensionList';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: DisabledExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('hasInstalledExtensions')),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.isEqualTo('')),
weight: 10,
canToggleVisibility: true,
order: 3,
@@ -154,28 +156,36 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
};
}
// {{SQL CARBON EDIT}}
// // Separate view for popular extensions required as we need to show popular and recommended sections
// // in the default view when there is no search text, and user has no installed extensions.
// private createPopularExtensionsListViewDescriptor(): IViewDescriptor {
// const id = 'extensions.popularExtensionsList';
// return {
// id,
// name: viewIdNameMappings[id],
// ctorDescriptor: { ctor: ExtensionsListView },
// when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.not('hasInstalledExtensions')),
// weight: 60,
// order: 1
// };
// }
/* // {{SQL CARBON EDIT}}
// Separate view for popular extensions required as we need to show popular and recommended sections
// in the default view when there is no search text, and user has no installed extensions.
private createDefaultPopularExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.popularExtensionsList';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: ExtensionsListView },
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')),
weight: 60,
order: 1
};
}
*/
private createExtensionsViewDescriptorsForServer(server: IExtensionManagementServer): IViewDescriptor[] {
return [{
id: `server.extensionsList.${server.authority}`,
id: `extensions.${server.authority}.installed`,
name: server.label,
ctorDescriptor: { ctor: GroupByServerExtensionsView },
when: ContextKeyExpr.has('groupByServersContext'),
ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server] },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchServerExtensions')),
weight: 100
}, {
id: `extensions.${server.authority}.default`,
name: server.label,
ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server] },
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.notEqualsTo('')),
weight: 40,
order: 1
}];
}
@@ -188,7 +198,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: DefaultRecommendedExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('defaultRecommendedExtensions')),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('defaultRecommendedExtensions')),
weight: 40,
order: 2,
canToggleVisibility: true
@@ -266,10 +276,11 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
private onSearchChange: EventOf<string>;
private nonEmptyWorkspaceContextKey: IContextKey<boolean>;
private searchExtensionsContextKey: IContextKey<boolean>;
private defaultViewsContextKey: IContextKey<boolean>;
private searchMarketplaceExtensionsContextKey: IContextKey<boolean>;
private searchServerExtensionsContextKey: IContextKey<boolean>;
private hasInstalledExtensionsContextKey: IContextKey<boolean>;
private searchBuiltInExtensionsContextKey: IContextKey<boolean>;
private groupByServersContextKey: IContextKey<boolean>;
private recommendedExtensionsContextKey: IContextKey<boolean>;
private defaultRecommendedExtensionsContextKey: IContextKey<boolean>;
@@ -304,11 +315,12 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
this.searchDelayer = new Delayer(500);
this.nonEmptyWorkspaceContextKey = NonEmptyWorkspaceContext.bindTo(contextKeyService);
this.searchExtensionsContextKey = SearchExtensionsContext.bindTo(contextKeyService);
this.defaultViewsContextKey = DefaultViewsContext.bindTo(contextKeyService);
this.searchMarketplaceExtensionsContextKey = SearchMarketplaceExtensionsContext.bindTo(contextKeyService);
this.searchServerExtensionsContextKey = SearchServerExtensionsContext.bindTo(contextKeyService);
this.hasInstalledExtensionsContextKey = HasInstalledExtensionsContext.bindTo(contextKeyService);
this.searchBuiltInExtensionsContextKey = SearchBuiltInExtensionsContext.bindTo(contextKeyService);
this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService);
this.groupByServersContextKey = GroupByServersContext.bindTo(contextKeyService);
this.defaultRecommendedExtensionsContextKey = DefaultRecommendedExtensionsContext.bindTo(contextKeyService);
this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue<boolean>(ShowRecommendationsOnlyOnDemandKey));
this.disposables.push(this.viewletService.onDidViewletOpen(this.onViewletOpen, this, this.disposables));
@@ -393,7 +405,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
getActions(): IAction[] {
if (!this.primaryActions) {
this.primaryActions = [
this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange)
this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox.getValue())
];
}
return this.primaryActions;
@@ -455,19 +467,20 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
private doSearch(): Promise<void> {
const value = this.normalizedQuery();
this.searchExtensionsContextKey.set(!!value);
this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value));
this.groupByServersContextKey.set(ExtensionsListView.isGroupByServersExtensionsQuery(value));
this.recommendedExtensionsContextKey.set(ExtensionsListView.isRecommendedExtensionsQuery(value));
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.recommendedExtensionsContextKey.set(isRecommendedExtensionsQuery);
this.searchMarketplaceExtensionsContextKey.set(!!value && !isServerExtensionsQuery && !isBuiltInExtensionsQuery && !isRecommendedExtensionsQuery);
this.nonEmptyWorkspaceContextKey.set(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY);
if (value) {
return this.progress(Promise.all(this.panels.map(view =>
(<ExtensionsListView>view).show(this.normalizedQuery())
.then(model => this.alertSearchResult(model.length, view.id))
))).then(() => undefined);
}
return Promise.resolve();
return this.progress(Promise.all(this.panels.map(view =>
(<ExtensionsListView>view).show(this.normalizedQuery())
.then(model => this.alertSearchResult(model.length, view.id))
))).then(() => undefined);
}
protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] {
@@ -565,7 +578,8 @@ export class StatusUpdater implements IWorkbenchContribution {
constructor(
@IActivityService private readonly activityService: IActivityService,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
) {
extensionsWorkbenchService.onChange(this.onServiceChange, this, this.disposables);
}
@@ -579,7 +593,7 @@ export class StatusUpdater implements IWorkbenchContribution {
return;
}
const outdated = this.extensionsWorkbenchService.local.reduce((r, e) => r + (e.outdated && e.enablementState !== EnablementState.Disabled && e.enablementState !== EnablementState.WorkspaceDisabled ? 1 : 0), 0);
const outdated = this.extensionsWorkbenchService.outdated.reduce((r, e) => r + (this.extensionEnablementService.isEnabled(e.local!) ? 1 : 0), 0);
if (outdated > 0) {
const badge = new NumberBadge(outdated, n => localize('outdatedExtensions', '{0} Outdated Extensions', n));
this.badgeHandle = this.activityService.showActivity(VIEWLET_ID, badge, 'extensions-badge count-badge');

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 } from 'vs/platform/extensionManagement/common/extensionManagement';
import { SortBy, SortOrder, IQueryOptions, IExtensionTipsService, IExtensionRecommendation, IExtensionManagementServer } 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';
@@ -45,6 +45,9 @@ import { ExtensionType } 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';
import { ILabelService } from 'vs/platform/label/common/label';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
class ExtensionsViewState extends Disposable implements IExtensionsViewState {
@@ -63,8 +66,13 @@ class ExtensionsViewState extends Disposable implements IExtensionsViewState {
}
}
export interface ExtensionsListViewOptions extends IViewletViewOptions {
server?: IExtensionManagementServer;
}
export class ExtensionsListView extends ViewletPanel {
private readonly server: IExtensionManagementServer | undefined;
private messageBox: HTMLElement;
private extensionsList: HTMLElement;
private badge: CountBadge;
@@ -73,7 +81,7 @@ export class ExtensionsListView extends ViewletPanel {
private queryRequest: { query: string, request: CancelablePromise<IPagedModel<IExtension>> } | null;
constructor(
private options: IViewletViewOptions,
options: ExtensionsListViewOptions,
@INotificationService protected notificationService: INotificationService,
@IKeybindingService keybindingService: IKeybindingService,
@IContextMenuService contextMenuService: IContextMenuService,
@@ -91,6 +99,7 @@ export class ExtensionsListView extends ViewletPanel {
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService
) {
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService);
this.server = options.server;
}
protected renderHeader(container: HTMLElement): void {
@@ -98,7 +107,7 @@ export class ExtensionsListView extends ViewletPanel {
}
renderHeaderTitle(container: HTMLElement): void {
super.renderHeaderTitle(container, this.options.title);
super.renderHeaderTitle(container, this.title);
this.badgeContainer = append(container, $('.count-badge-wrapper'));
this.badge = new CountBadge(this.badgeContainer);
@@ -233,7 +242,7 @@ export class ExtensionsListView extends ViewletPanel {
private async queryByIds(ids: string[], options: IQueryOptions, token: CancellationToken): Promise<IPagedModel<IExtension>> {
const idsSet: Set<string> = ids.reduce((result, id) => { result.add(id.toLowerCase()); return result; }, new Set<string>());
const result = (await this.extensionsWorkbenchService.queryLocal())
const result = (await this.extensionsWorkbenchService.queryLocal(this.server))
.filter(e => idsSet.has(e.identifier.id.toLowerCase()));
if (result.length) {
@@ -261,7 +270,7 @@ export class ExtensionsListView extends ViewletPanel {
}
value = value.replace(/@builtin/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase();
let result = await this.extensionsWorkbenchService.queryLocal();
let result = await this.extensionsWorkbenchService.queryLocal(this.server);
result = result
.filter(e => e.type === ExtensionType.System && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1));
@@ -313,7 +322,7 @@ export class ExtensionsListView extends ViewletPanel {
// Show installed extensions
value = value.replace(/@installed/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase();
let result = await this.extensionsWorkbenchService.queryLocal();
let result = await this.extensionsWorkbenchService.queryLocal(this.server);
result = result
.filter(e => e.type === ExtensionType.User
@@ -327,7 +336,7 @@ export class ExtensionsListView extends ViewletPanel {
if (/@outdated/i.test(value)) {
value = value.replace(/@outdated/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase();
const local = await this.extensionsWorkbenchService.queryLocal();
const local = await this.extensionsWorkbenchService.queryLocal(this.server);
const result = local
.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName))
.filter(extension => extension.outdated
@@ -340,7 +349,7 @@ export class ExtensionsListView extends ViewletPanel {
if (/@disabled/i.test(value)) {
value = value.replace(/@disabled/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase();
const local = await this.extensionsWorkbenchService.queryLocal();
const local = await this.extensionsWorkbenchService.queryLocal(this.server);
const runningExtensions = await this.extensionService.getExtensions();
const result = local
@@ -355,7 +364,7 @@ export class ExtensionsListView extends ViewletPanel {
if (/@enabled/i.test(value)) {
value = value ? value.replace(/@enabled/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase() : '';
const local = (await this.extensionsWorkbenchService.queryLocal()).filter(e => e.type === ExtensionType.User);
const local = (await this.extensionsWorkbenchService.queryLocal(this.server)).filter(e => e.type === ExtensionType.User);
const runningExtensions = await this.extensionService.getExtensions();
const result = local
@@ -485,7 +494,7 @@ export class ExtensionsListView extends ViewletPanel {
private getAllRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise<IPagedModel<IExtension>> {
const value = query.value.replace(/@recommended:all/g, '').replace(/@recommended/g, '').trim().toLowerCase();
return this.extensionsWorkbenchService.queryLocal()
return this.extensionsWorkbenchService.queryLocal(this.server)
.then(result => result.filter(e => e.type === ExtensionType.User))
.then(local => {
const fileBasedRecommendations = this.tipsService.getFileBasedRecommendations();
@@ -540,7 +549,7 @@ export class ExtensionsListView extends ViewletPanel {
private getRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise<IPagedModel<IExtension>> {
const value = query.value.replace(/@recommended/g, '').trim().toLowerCase();
return this.extensionsWorkbenchService.queryLocal()
return this.extensionsWorkbenchService.queryLocal(this.server)
.then(result => result.filter(e => e.type === ExtensionType.User))
.then(local => {
let fileBasedRecommendations = this.tipsService.getFileBasedRecommendations();
@@ -780,8 +789,8 @@ export class ExtensionsListView extends ViewletPanel {
return /@installed|@outdated|@enabled|@disabled/i.test(query);
}
static isGroupByServersExtensionsQuery(query: string): boolean {
return !!Query.parse(query).groupBy;
static isServerExtensionsQuery(query: string): boolean {
return /@installed|@outdated/i.test(query);
}
static isRecommendedExtensionsQuery(query: string): boolean {
@@ -818,10 +827,40 @@ export class ExtensionsListView extends ViewletPanel {
}
}
export class GroupByServerExtensionsView extends ExtensionsListView {
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;
}
export class ServerExtensionsView extends ExtensionsListView {
constructor(
server: IExtensionManagementServer,
options: ExtensionsListViewOptions,
@INotificationService notificationService: INotificationService,
@IKeybindingService keybindingService: IKeybindingService,
@IContextMenuService contextMenuService: IContextMenuService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IExtensionService extensionService: IExtensionService,
@IEditorService editorService: IEditorService,
@IExtensionTipsService tipsService: IExtensionTipsService,
@IModeService modeService: IModeService,
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService configurationService: IConfigurationService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IExperimentService experimentService: IExperimentService,
@IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService,
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
@ILabelService labelService: ILabelService,
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService
) {
options.title = getServerLabel(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))));
}
async show(query: string): Promise<IPagedModel<IExtension>> {
query = query.replace(/@group:server/g, '').trim();
query = query ? query : '@installed';
if (!ExtensionsListView.isInstalledExtensionsQuery(query) && !ExtensionsListView.isBuiltInExtensionsQuery(query)) {
query = query += ' @installed';

View File

@@ -16,7 +16,7 @@ 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 { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
export abstract class ExtensionWidget extends Disposable implements IExtensionContainer {
private _extension: IExtension;
@@ -205,7 +205,7 @@ export class RemoteBadgeWidget extends ExtensionWidget {
@IThemeService private readonly themeService: IThemeService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@IWindowService private readonly windowService: IWindowService
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) {
super();
this.render();
@@ -222,11 +222,10 @@ export class RemoteBadgeWidget extends ExtensionWidget {
render(): void {
this.clear();
if (!this.extension || !this.extension.local) {
if (!this.extension || !this.extension.local || !this.extension.server) {
return;
}
const server = this.extensionManagementServerService.getExtensionManagementServer(this.extension.local.location);
if (server === this.extensionManagementServerService.remoteExtensionManagementServer) {
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'));
@@ -245,7 +244,7 @@ export class RemoteBadgeWidget extends ExtensionWidget {
const updateTitle = () => {
if (this.element) {
this.element.title = localize('remote extension title', "Extension in {0}", this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.windowService.getConfiguration().remoteAuthority));
this.element.title = localize('remote extension title', "Extension in {0}", this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority));
}
};
this.labelService.onDidChangeFormatters(() => updateTitle(), this, this.disposables);

View File

@@ -35,6 +35,7 @@
.monaco-action-bar .action-item.disabled .action-label.extension-action.extension-editor-dropdown-action,
.monaco-action-bar .action-item.disabled .action-label.extension-action.reload,
.monaco-action-bar .action-item.disabled .action-label.disable-status.hide,
.monaco-action-bar .action-item.disabled .action-label.disable-warning.hide,
.monaco-action-bar .action-item.disabled .action-label.extension-status-label.hide,
.monaco-action-bar .action-item.disabled .action-label.malicious-status.not-malicious {
display: none;
@@ -61,6 +62,29 @@
font-style: italic;
}
.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .action-label.disable-status {
margin-left: 0;
padding-left: 0;
}
.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .action-label.disable-warning,
.extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.disable-warning {
cursor: default;
margin: 0.1em;
}
.monaco-action-bar .action-item .action-label.disable-warning.icon {
opacity: 1;
height: 18px;
width: 10px;
background: url('status-warning.svg') center center no-repeat;
margin-top: 0.15em
}
.vs-dark .monaco-action-bar .action-item .action-label.disable-warning.icon {
background: url('status-warning-inverse.svg') center center no-repeat;
}
.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.extension-status-label,
.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.disable-status,
.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.malicious-status {

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" height="16" width="16"><path fill="#1E1E1E" d="M7.5 2L2 12l2 2h9l2-2L9.5 2z"/><path d="M9 3H8l-4.5 9 1 1h8l1-1L9 3zm0 9H8v-1h1v1zm0-2H8V6h1v4z" fill="#fc0"/><path d="M9 10H8V6h1v4zm0 1H8v1h1v-1z"/></svg>

After

Width:  |  Height:  |  Size: 263 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><path fill="#F6F6F6" d="M7.5 2L2 12l2 2h9l2-2L9.5 2z"/><path d="M9 3H8l-4.5 9 1 1h8l1-1L9 3zm0 9H8v-1h1v1zm0-2H8V6h1v4z" fill="#fc0"/><path d="M9 10H8V6h1v4zm0 1H8v1h1v-1z"/></svg>

After

Width:  |  Height:  |  Size: 297 B

View File

@@ -43,6 +43,7 @@ import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { SlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
export const IExtensionHostProfileService = createDecorator<IExtensionHostProfileService>('extensionHostProfileService');
export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey<string>('profileSessionState', 'none');
@@ -119,7 +120,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
@IStorageService storageService: IStorageService,
@ILabelService private readonly _labelService: ILabelService,
@IWindowService private readonly _windowService: IWindowService
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService
) {
super(RuntimeExtensionsEditor.ID, telemetryService, themeService, storageService);
@@ -381,7 +382,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
el.innerHTML = renderOcticons(`$(rss) ${element.description.extensionLocation.authority}`);
data.msgContainer.appendChild(el);
const hostLabel = this._labelService.getHostLabel(REMOTE_HOST_SCHEME, this._windowService.getConfiguration().remoteAuthority);
const hostLabel = this._labelService.getHostLabel(REMOTE_HOST_SCHEME, this._environmentService.configuration.remoteAuthority);
if (hostLabel) {
el.innerHTML = renderOcticons(`$(rss) ${hostLabel}`);
}

View File

@@ -9,15 +9,15 @@ import { Event, Emitter } from 'vs/base/common/event';
import { index, distinct } from 'vs/base/common/arrays';
import { ThrottledDelayer } from 'vs/base/common/async';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
// {{SQL CARBON EDIT}}
import {
IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions,
InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionManagementServerService, INSTALL_ERROR_INCOMPATIBLE
InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, INSTALL_ERROR_INCOMPATIBLE
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWindowService } from 'vs/platform/windows/common/windows';
@@ -37,7 +37,6 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IFileService } from 'vs/platform/files/common/files';
import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion, IExtension as IPlatformExtension } from 'vs/platform/extensions/common/extensions';
import { isUIExtension } from 'vs/workbench/services/extensions/node/extensionsUtil';
// {{SQL CARBON EDIT}}
import { isEngineValid } from 'vs/platform/extensions/node/extensionValidator';
@@ -55,6 +54,7 @@ class Extension implements IExtension {
constructor(
private galleryService: IExtensionGalleryService,
private stateProvider: IExtensionStateProvider<ExtensionState>,
public readonly server: IExtensionManagementServer | undefined,
public local: ILocalExtension | undefined,
public gallery: IGalleryExtension | undefined,
private telemetryService: ITelemetryService,
@@ -370,14 +370,179 @@ class ExtensionDependencies implements IExtensionDependencies {
}
}
export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, IURLHandler {
class Extensions extends Disposable {
private static readonly SyncPeriod = 1000 * 60 * 60 * 12; // 12 hours
_serviceBrand: any;
private stateProvider: IExtensionStateProvider<ExtensionState>;
private readonly _onChange: Emitter<Extension | undefined> = new Emitter<Extension | undefined>();
get onChange(): Event<Extension | undefined> { return this._onChange.event; }
private readonly stateProvider: IExtensionStateProvider<ExtensionState>;
private installing: Extension[] = [];
private uninstalling: Extension[] = [];
private installed: Extension[] = [];
constructor(
private readonly server: IExtensionManagementServer,
@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@ILogService private readonly logService: ILogService,
@IFileService private readonly fileService: IFileService,
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
) {
super();
this.stateProvider = ext => this.getExtensionState(ext);
this._register(server.extensionManagementService.onInstallExtension(e => this.onInstallExtension(e)));
this._register(server.extensionManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e)));
this._register(server.extensionManagementService.onUninstallExtension(e => this.onUninstallExtension(e)));
this._register(server.extensionManagementService.onDidUninstallExtension(e => this.onDidUninstallExtension(e)));
this._register(extensionEnablementService.onEnablementChanged(e => this.onEnablementChanged(e)));
}
get local(): IExtension[] {
const installing = this.installing
.filter(e => !this.installed.some(installed => areSameExtensions(installed.identifier, e.identifier)))
.map(e => e);
return [...this.installed, ...installing];
}
async queryInstalled(): Promise<IExtension[]> {
const installed = await this.server.extensionManagementService.getInstalled();
const byId = index(this.installed, e => e.identifier.id);
this.installed = installed.map(local => {
const extension = byId[local.identifier.id] || new Extension(this.galleryService, this.stateProvider, this.server, local, undefined, this.telemetryService, this.logService, this.fileService);
extension.local = local;
extension.enablementState = this.extensionEnablementService.getEnablementState(local);
return extension;
});
this._onChange.fire(undefined);
return this.local;
}
async syncLocalWithGalleryExtension(gallery: IGalleryExtension, maliciousExtensionSet: Set<string>): Promise<boolean> {
const extension = this.getInstalledExtensionMatchingGallery(gallery);
if (!extension) {
return false;
}
if (maliciousExtensionSet.has(extension.identifier.id)) {
extension.isMalicious = true;
}
// Loading the compatible version only there is an engine property
// Otherwise falling back to old way so that we will not make many roundtrips
const compatible = gallery.properties.engine ? await this.galleryService.getCompatibleExtension(gallery) : gallery;
if (!compatible) {
return false;
}
// Sync the local extension with gallery extension if local extension doesnot has metadata
if (extension.local) {
const local = extension.local.metadata ? extension.local : await this.server.extensionManagementService.updateMetadata(extension.local, { id: compatible.identifier.uuid, publisherDisplayName: compatible.publisherDisplayName, publisherId: compatible.publisherId });
extension.local = local;
extension.gallery = compatible;
this._onChange.fire(extension);
return true;
}
return false;
}
private getInstalledExtensionMatchingGallery(gallery: IGalleryExtension): Extension | null {
for (const installed of this.installed) {
if (installed.uuid) { // Installed from Gallery
if (installed.uuid === gallery.identifier.uuid) {
return installed;
}
} else {
if (areSameExtensions(installed.identifier, gallery.identifier)) { // Installed from other sources
return installed;
}
}
}
return null;
}
private onInstallExtension(event: InstallExtensionEvent): void {
const { gallery } = event;
if (gallery) {
const extension = this.installed.filter(e => areSameExtensions(e.identifier, gallery.identifier))[0]
|| new Extension(this.galleryService, this.stateProvider, this.server, undefined, gallery, this.telemetryService, this.logService, this.fileService);
this.installing.push(extension);
this._onChange.fire(extension);
}
}
private onDidInstallExtension(event: DidInstallExtensionEvent): void {
const { local, zipPath, error, gallery } = event;
const installingExtension = gallery ? this.installing.filter(e => areSameExtensions(e.identifier, gallery.identifier))[0] : null;
this.installing = installingExtension ? this.installing.filter(e => e !== installingExtension) : this.installing;
let extension: Extension | undefined = installingExtension ? installingExtension : zipPath ? new Extension(this.galleryService, this.stateProvider, this.server, local, undefined, this.telemetryService, this.logService, this.fileService) : undefined;
if (extension) {
if (local) {
const installed = this.installed.filter(e => areSameExtensions(e.identifier, extension!.identifier))[0];
if (installed) {
extension = installed;
} else {
this.installed.push(extension);
}
extension.local = local;
if (!extension.gallery) {
extension.gallery = gallery;
}
}
}
this._onChange.fire(error ? undefined : extension);
}
private onUninstallExtension(identifier: IExtensionIdentifier): void {
const extension = this.installed.filter(e => areSameExtensions(e.identifier, identifier))[0];
if (extension) {
const uninstalling = this.uninstalling.filter(e => areSameExtensions(e.identifier, identifier))[0] || extension;
this.uninstalling = [uninstalling, ...this.uninstalling.filter(e => !areSameExtensions(e.identifier, identifier))];
this._onChange.fire(uninstalling);
}
}
private onDidUninstallExtension({ identifier, error }: DidUninstallExtensionEvent): void {
if (!error) {
this.installed = this.installed.filter(e => !areSameExtensions(e.identifier, identifier));
}
const uninstalling = this.uninstalling.filter(e => areSameExtensions(e.identifier, identifier))[0];
this.uninstalling = this.uninstalling.filter(e => !areSameExtensions(e.identifier, identifier));
if (uninstalling) {
this._onChange.fire(uninstalling);
}
}
private onEnablementChanged(platformExtensions: IPlatformExtension[]) {
const extensions = this.local.filter(e => platformExtensions.some(p => areSameExtensions(e.identifier, p.identifier)));
for (const extension of extensions) {
if (extension.local) {
const enablementState = this.extensionEnablementService.getEnablementState(extension.local);
if (enablementState !== extension.enablementState) {
(extension as Extension).enablementState = enablementState;
this._onChange.fire(extension as Extension);
}
}
}
}
getExtensionState(extension: Extension): ExtensionState {
if (extension.gallery && this.installing.some(e => !!e.gallery && areSameExtensions(e.gallery.identifier, extension.gallery!.identifier))) {
return ExtensionState.Installing;
}
if (this.uninstalling.some(e => areSameExtensions(e.identifier, extension.identifier))) {
return ExtensionState.Uninstalling;
}
const local = this.installed.filter(e => e === extension || (e.gallery && extension.gallery && areSameExtensions(e.gallery.identifier, extension.gallery.identifier)))[0];
return local ? ExtensionState.Installed : ExtensionState.Uninstalled;
}
}
export class ExtensionsWorkbenchService extends Disposable implements IExtensionsWorkbenchService, IURLHandler {
private static readonly SyncPeriod = 1000 * 60 * 60 * 12; // 12 hours
_serviceBrand: any;
private readonly localExtensions: Extensions;
private readonly remoteExtensions: Extensions | null;
private syncDelayer: ThrottledDelayer<void>;
private autoUpdateDelayer: ThrottledDelayer<void>;
private disposables: IDisposable[] = [];
@@ -404,13 +569,15 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService,
@IStorageService private readonly storageService: IStorageService,
@IFileService private readonly fileService: IFileService
) {
this.stateProvider = ext => this.getExtensionState(ext);
extensionService.onInstallExtension(this.onInstallExtension, this, this.disposables);
extensionService.onDidInstallExtension(this.onDidInstallExtension, this, this.disposables);
extensionService.onUninstallExtension(this.onUninstallExtension, this, this.disposables);
extensionService.onDidUninstallExtension(this.onDidUninstallExtension, this, this.disposables);
extensionEnablementService.onEnablementChanged(this.onEnablementChanged, this, this.disposables);
super();
this.localExtensions = this._register(instantiationService.createInstance(Extensions, extensionManagementServerService.localExtensionManagementServer));
this._register(this.localExtensions.onChange(e => this._onChange.fire(e)));
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
this.remoteExtensions = this._register(instantiationService.createInstance(Extensions, extensionManagementServerService.remoteExtensionManagementServer));
this._register(this.remoteExtensions.onChange(e => this._onChange.fire(e)));
} else {
this.remoteExtensions = null;
}
this.syncDelayer = new ThrottledDelayer<void>(ExtensionsWorkbenchService.SyncPeriod);
this.autoUpdateDelayer = new ThrottledDelayer<void>(1000);
@@ -438,29 +605,40 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService,
}
get local(): IExtension[] {
const installing = this.installing
.filter(e => !this.installed.some(installed => areSameExtensions(installed.identifier, e.identifier)))
.map(e => e);
return [...this.installed, ...installing];
const result = [...this.localExtensions.local];
if (!this.remoteExtensions) {
return result;
}
result.push(...this.remoteExtensions.local);
const byId = groupByExtension(result, r => r.identifier);
return byId.reduce((result, extensions) => { result.push(this.getPrimaryExtension(extensions)); return result; }, []);
}
queryLocal(): Promise<IExtension[]> {
return this.extensionService.getInstalled()
.then(installed => {
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
installed = installed.filter(installed => this.belongsToWindow(installed));
}
const installedById = index(this.installed, e => e.identifier.id);
this.installed = installed.map(local => {
const extension = installedById[local.identifier.id] || new Extension(this.galleryService, this.stateProvider, local, undefined, this.telemetryService, this.logService, this.fileService);
extension.enablementState = this.extensionEnablementService.getEnablementState(local);
return extension;
});
get outdated(): IExtension[] {
const allLocal = [...this.localExtensions.local];
if (this.remoteExtensions) {
allLocal.push(...this.remoteExtensions.local);
}
return allLocal.filter(e => e.outdated && e.local && e.state === ExtensionState.Installed);
}
this._onChange.fire(undefined);
return this.local;
});
async queryLocal(server?: IExtensionManagementServer): Promise<IExtension[]> {
if (server) {
if (this.extensionManagementServerService.localExtensionManagementServer === server) {
return this.localExtensions.queryInstalled();
}
if (this.remoteExtensions && this.extensionManagementServerService.remoteExtensionManagementServer === server) {
return this.remoteExtensions.queryInstalled();
}
}
await this.localExtensions.queryInstalled();
if (this.remoteExtensions) {
await Promise.all([this.localExtensions.queryInstalled(), this.remoteExtensions.queryInstalled()]);
} else {
await this.localExtensions.queryInstalled();
}
return this.local;
}
queryGallery(token: CancellationToken): Promise<IPager<IExtension>>;
@@ -509,50 +687,41 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService,
return Promise.resolve(this.editorService.openEditor(this.instantiationService.createInstance(ExtensionsInput, extension), undefined, sideByside ? SIDE_GROUP : ACTIVE_GROUP));
}
private belongsToWindow(extension: ILocalExtension): boolean {
if (!this.extensionManagementServerService.remoteExtensionManagementServer) {
return true;
private getPrimaryExtension(extensions: IExtension[]): IExtension {
if (extensions.length === 1) {
return extensions[0];
}
const extensionManagementServer = this.extensionManagementServerService.getExtensionManagementServer(extension.location);
if (isUIExtension(extension.manifest, this.configurationService)) {
if (this.extensionManagementServerService.localExtensionManagementServer === extensionManagementServer) {
return true;
}
} else {
if (this.extensionManagementServerService.remoteExtensionManagementServer === extensionManagementServer) {
return true;
}
}
return false;
const pickRemoteOrFirstExtension = (from: IExtension[]): IExtension => {
const remoteExtension = from.filter(e => e.server === this.extensionManagementServerService.remoteExtensionManagementServer)[0];
return remoteExtension ? remoteExtension : from[0];
};
const enabledExtensions = extensions.filter(e => e.local && this.extensionEnablementService.isEnabled(e.local));
return enabledExtensions.length === 1 ? enabledExtensions[0] : pickRemoteOrFirstExtension(extensions);
}
private fromGallery(gallery: IGalleryExtension, maliciousExtensionSet: Set<string>): Extension {
let result = this.getInstalledExtensionMatchingGallery(gallery);
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)])
.then(result => {
if (result[0] || result[1]) {
this.eventuallyAutoUpdateExtensions();
}
});
if (result) {
// Loading the compatible version only there is an engine property
// Otherwise falling back to old way so that we will not make many roundtrips
if (gallery.properties.engine) {
this.galleryService.getCompatibleExtension(gallery)
.then(compatible => compatible ? this.syncLocalWithGalleryExtension(result!, compatible) : null);
} else {
this.syncLocalWithGalleryExtension(result, gallery);
}
} else {
result = new Extension(this.galleryService, this.stateProvider, undefined, gallery, this.telemetryService, this.logService, this.fileService);
const installed = this.getInstalledExtensionMatchingGallery(gallery);
if (installed) {
return installed;
}
if (maliciousExtensionSet.has(result.identifier.id)) {
result.isMalicious = true;
const extension = new Extension(this.galleryService, ext => this.getExtensionState(ext), undefined, undefined, gallery, this.telemetryService, this.logService, this.fileService);
if (maliciousExtensionSet.has(extension.identifier.id)) {
extension.isMalicious = true;
}
return result;
return extension;
}
private getInstalledExtensionMatchingGallery(gallery: IGalleryExtension): Extension | null {
for (const installed of this.installed) {
if (installed.uuid) { // Installed from Gallery
if (installed.uuid === gallery.identifier.uuid) {
private getInstalledExtensionMatchingGallery(gallery: IGalleryExtension): IExtension | null {
for (const installed of this.local) {
if (installed.identifier.uuid) { // Installed from Gallery
if (installed.identifier.uuid === gallery.identifier.uuid) {
return installed;
}
} else {
@@ -564,19 +733,14 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService,
return null;
}
private syncLocalWithGalleryExtension(extension: Extension, gallery: IGalleryExtension) {
// Sync the local extension with gallery extension if local extension doesnot has metadata
if (extension.local) {
(extension.local.metadata ? Promise.resolve(extension.local) : this.extensionService.updateMetadata(extension.local, { id: gallery.identifier.uuid, publisherDisplayName: gallery.publisherDisplayName, publisherId: gallery.publisherId }))
.then(local => {
extension.local = local;
extension.gallery = gallery;
this._onChange.fire(extension);
this.eventuallyAutoUpdateExtensions();
});
} else {
this._onChange.fire(extension);
private getExtensionState(extension: Extension): ExtensionState {
if (this.remoteExtensions) {
const state = this.remoteExtensions.getExtensionState(extension);
if (state !== ExtensionState.Uninstalled) {
return state;
}
}
return this.localExtensions.getExtensionState(extension);
}
checkForUpdates(): Promise<void> {
@@ -602,10 +766,10 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService,
private syncWithGallery(): Promise<void> {
const ids: string[] = [], names: string[] = [];
for (const installed of this.installed) {
for (const installed of this.local) {
if (installed.type === ExtensionType.User) {
if (installed.uuid) {
ids.push(installed.uuid);
if (installed.identifier.uuid) {
ids.push(installed.identifier.uuid);
} else {
names.push(installed.identifier.id);
}
@@ -634,9 +798,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService,
}
// {{SQL CARBON EDIT}} - Add && !e.downloadPage condition
const toUpdate = this.local.filter(e =>
e.outdated && e.state !== ExtensionState.Installing
&& e.local && !this.isAutoUpdateIgnored(new ExtensionIdentifierWithVersion(e.identifier, e.version)) && !e.downloadPage);
const toUpdate = this.outdated.filter(e => !this.isAutoUpdateIgnored(new ExtensionIdentifierWithVersion(e.identifier, e.version)) && !e.downloadPage);
return Promise.all(toUpdate.map(e => this.install(e)));
}
@@ -689,7 +851,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService,
// The check is added here because we want to fail fast instead of downloading the VSIX and then fail.
if (gallery.properties.engine && (!isEngineValid(gallery.properties.engine, product.vscodeVersion)
|| (gallery.properties.azDataEngine && !isEngineValid(gallery.properties.azDataEngine, pkg.version)))) {
return Promise.reject(new ExtensionManagementError(nls.localize('incompatible', "Unable to install version '{2}' of extension '{0}' as it is not compatible with Azure Data Studio '{1}'.", extension.gallery!.identifier.id, pkg.version, gallery.version), INSTALL_ERROR_INCOMPATIBLE));
return Promise.reject(new ExtensionManagementError(nls.localize('incompatible2', "Unable to install version '{2}' of extension '{0}' as it is not compatible with Azure Data Studio '{1}'.", extension.gallery!.identifier.id, pkg.version, gallery.version), INSTALL_ERROR_INCOMPATIBLE));
}
return this.installWithProgress(async () => {
@@ -723,14 +885,14 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService,
}
uninstall(extension: IExtension): Promise<void> {
const ext = extension.local ? extension : this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0];
const ext = extension.local ? extension : this.local.filter(e => areSameExtensions(e.identifier, extension.identifier))[0];
const toUninstall: ILocalExtension | null = ext && ext.local ? ext.local : null;
if (!toUninstall) {
return Promise.reject(new Error('Missing local'));
}
this.logService.info(`Requested uninstalling the extension ${extension.identifier.id} from window ${this.windowService.getCurrentWindowId()}`);
this.logService.info(`Requested uninstalling the extension ${extension.identifier.id} from window ${this.windowService.windowId}`);
return this.progressService.withProgress({
location: ProgressLocation.Extensions,
title: nls.localize('uninstallingExtension', 'Uninstalling extension....'),
@@ -765,7 +927,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService,
}
reinstall(extension: IExtension): Promise<IExtension> {
const ext = extension.local ? extension : this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0];
const ext = extension.local ? extension : this.local.filter(e => areSameExtensions(e.identifier, extension.identifier))[0];
const toReinstall: ILocalExtension | null = ext && ext.local ? ext.local : null;
if (!toReinstall) {
@@ -925,101 +1087,6 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService,
return this._extensionAllowedBadgeProviders;
}
private onInstallExtension(event: InstallExtensionEvent): void {
const { gallery } = event;
if (!gallery) {
return;
}
let extension = this.installed.filter(e => areSameExtensions(e.identifier, gallery.identifier))[0];
if (!extension) {
extension = new Extension(this.galleryService, this.stateProvider, undefined, gallery, this.telemetryService, this.logService, this.fileService);
}
this.installing.push(extension);
this._onChange.fire(extension);
}
private onDidInstallExtension(event: DidInstallExtensionEvent): void {
const { local, zipPath, error, gallery } = event;
const installingExtension = gallery ? this.installing.filter(e => areSameExtensions(e.identifier, gallery.identifier))[0] : null;
this.installing = installingExtension ? this.installing.filter(e => e !== installingExtension) : this.installing;
if (local && !this.belongsToWindow(local)) {
return;
}
let extension: Extension | undefined = installingExtension ? installingExtension : zipPath ? new Extension(this.galleryService, this.stateProvider, local, undefined, this.telemetryService, this.logService, this.fileService) : undefined;
if (extension) {
if (local) {
const installed = this.installed.filter(e => areSameExtensions(e.identifier, extension!.identifier))[0];
if (installed) {
extension = installed;
} else {
this.installed.push(extension);
}
extension.local = local;
extension.gallery = gallery;
}
}
this._onChange.fire(error ? undefined : extension);
}
private onUninstallExtension(identifier: IExtensionIdentifier): void {
const extension = this.installed.filter(e => areSameExtensions(e.identifier, identifier))[0];
if (!extension) {
return;
}
const uninstalling = this.uninstalling.filter(e => areSameExtensions(e.identifier, identifier))[0] || extension;
this.uninstalling = [uninstalling, ...this.uninstalling.filter(e => !areSameExtensions(e.identifier, identifier))];
this._onChange.fire(uninstalling);
}
private onDidUninstallExtension({ identifier, error }: DidUninstallExtensionEvent): void {
if (!error) {
this.installed = this.installed.filter(e => !areSameExtensions(e.identifier, identifier));
}
const uninstalling = this.uninstalling.filter(e => areSameExtensions(e.identifier, identifier))[0];
this.uninstalling = this.uninstalling.filter(e => !areSameExtensions(e.identifier, identifier));
if (!uninstalling) {
return;
}
this._onChange.fire(uninstalling);
}
private onEnablementChanged(platformExtensions: IPlatformExtension[]) {
const extensions = this.local.filter(e => platformExtensions.some(p => areSameExtensions(e.identifier, p.identifier)));
for (const extension of extensions) {
if (extension.local) {
const enablementState = this.extensionEnablementService.getEnablementState(extension.local);
if (enablementState !== extension.enablementState) {
extension.enablementState = enablementState;
this._onChange.fire(extension);
}
}
}
}
private getExtensionState(extension: Extension): ExtensionState {
if (extension.gallery && this.installing.some(e => !!e.gallery && areSameExtensions(e.gallery.identifier, extension.gallery!.identifier))) {
return ExtensionState.Installing;
}
if (this.uninstalling.some(e => areSameExtensions(e.identifier, extension.identifier))) {
return ExtensionState.Uninstalling;
}
const local = this.installed.filter(e => e === extension || (e.gallery && extension.gallery && areSameExtensions(e.gallery.identifier, extension.gallery.identifier)))[0];
return local ? ExtensionState.Installed : ExtensionState.Uninstalled;
}
private onError(err: any): void {
if (isPromiseCanceledError(err)) {
return;

View File

@@ -6,12 +6,12 @@
import * as assert from 'assert';
import { assign } from 'vs/base/common/objects';
import { generateUuid } from 'vs/base/common/uuid';
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
import { IExtensionsWorkbenchService, ExtensionContainers } from 'vs/workbench/contrib/extensions/common/extensions';
import * as ExtensionsActions from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/node/extensionsWorkbenchService';
import {
IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension,
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, EnablementState, InstallOperation, IExtensionManagementServerService
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, EnablementState, InstallOperation, IExtensionManagementServerService, IExtensionManagementServer
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
@@ -33,12 +33,13 @@ import { IWindowService } from 'vs/platform/windows/common/windows';
import { URLService } from 'vs/platform/url/common/urlService';
import { URI } from 'vs/base/common/uri';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
import { ExtensionIdentifier, IExtensionContributions, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ILabelService } from 'vs/platform/label/common/label';
import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService';
suite('ExtensionsActions Test', () => {
@@ -74,9 +75,17 @@ suite('ExtensionsActions Test', () => {
instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event);
instantiationService.stub(IRemoteAgentService, RemoteAgentService);
instantiationService.stub(IExtensionManagementServerService, instantiationService.createInstance(ExtensionManagementServerService));
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));
instantiationService.stub(ILabelService, { onDidChangeFormatters: new Emitter<void>().event });
instantiationService.set(IExtensionTipsService, instantiationService.createInstance(ExtensionTipsService));
instantiationService.stub(IURLService, URLService);
@@ -105,7 +114,7 @@ suite('ExtensionsActions Test', () => {
test('Test Install action when state is installed', () => {
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
return workbenchService.queryLocal()
@@ -124,7 +133,7 @@ suite('ExtensionsActions Test', () => {
test('Test Install action when state is installing', () => {
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
return workbenchService.queryGallery(CancellationToken.None)
@@ -141,7 +150,7 @@ suite('ExtensionsActions Test', () => {
test('Test Install action when state is uninstalled', () => {
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
return workbenchService.queryGallery(CancellationToken.None)
@@ -154,7 +163,7 @@ suite('ExtensionsActions Test', () => {
test('Test Install action when extension is system action', () => {
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a', {}, { type: ExtensionType.System });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -169,7 +178,7 @@ suite('ExtensionsActions Test', () => {
test('Test Install action when extension doesnot has gallery', () => {
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -184,14 +193,14 @@ suite('ExtensionsActions Test', () => {
test('Uninstall action is disabled when there is no extension', () => {
const testObject: ExtensionsActions.UninstallAction = instantiationService.createInstance(ExtensionsActions.UninstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
assert.ok(!testObject.enabled);
});
test('Test Uninstall action when state is uninstalling', () => {
const testObject: ExtensionsActions.UninstallAction = instantiationService.createInstance(ExtensionsActions.UninstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -207,7 +216,7 @@ suite('ExtensionsActions Test', () => {
test('Test Uninstall action when state is installed and is user extension', () => {
const testObject: ExtensionsActions.UninstallAction = instantiationService.createInstance(ExtensionsActions.UninstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -222,7 +231,7 @@ suite('ExtensionsActions Test', () => {
test('Test Uninstall action when state is installed and is system extension', () => {
const testObject: ExtensionsActions.UninstallAction = instantiationService.createInstance(ExtensionsActions.UninstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a', {}, { type: ExtensionType.System });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -237,7 +246,7 @@ suite('ExtensionsActions Test', () => {
test('Test Uninstall action when state is installing and is user extension', () => {
const testObject: ExtensionsActions.UninstallAction = instantiationService.createInstance(ExtensionsActions.UninstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -254,7 +263,7 @@ suite('ExtensionsActions Test', () => {
test('Test Uninstall action after extension is installed', () => {
const testObject: ExtensionsActions.UninstallAction = instantiationService.createInstance(ExtensionsActions.UninstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
@@ -273,7 +282,7 @@ suite('ExtensionsActions Test', () => {
test('Test CombinedInstallAction when there is no extension', () => {
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
assert.ok(!testObject.enabled);
assert.equal('extension-action prominent install no-extension', testObject.class);
@@ -281,7 +290,7 @@ suite('ExtensionsActions Test', () => {
test('Test CombinedInstallAction when extension is system extension', () => {
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a', {}, { type: ExtensionType.System });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -296,7 +305,7 @@ suite('ExtensionsActions Test', () => {
test('Test CombinedInstallAction when installAction is enabled', () => {
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
@@ -311,7 +320,7 @@ suite('ExtensionsActions Test', () => {
test('Test CombinedInstallAction when unInstallAction is enabled', () => {
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -326,7 +335,7 @@ suite('ExtensionsActions Test', () => {
test('Test CombinedInstallAction when state is installing', () => {
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
@@ -343,7 +352,7 @@ suite('ExtensionsActions Test', () => {
test('Test CombinedInstallAction when state is installing during update', () => {
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -362,7 +371,7 @@ suite('ExtensionsActions Test', () => {
test('Test CombinedInstallAction when state is uninstalling', () => {
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -378,14 +387,14 @@ suite('ExtensionsActions Test', () => {
test('Test UpdateAction when there is no extension', () => {
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
assert.ok(!testObject.enabled);
});
test('Test UpdateAction when extension is uninstalled', () => {
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const gallery = aGalleryExtension('a', { version: '1.0.0' });
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
return instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None)
@@ -397,7 +406,7 @@ suite('ExtensionsActions Test', () => {
test('Test UpdateAction when extension is installed and not outdated', () => {
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a', { version: '1.0.0' });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -412,7 +421,7 @@ suite('ExtensionsActions Test', () => {
test('Test UpdateAction when extension is installed outdated and system extension', () => {
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a', { version: '1.0.0' }, { type: ExtensionType.System });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -427,7 +436,7 @@ suite('ExtensionsActions Test', () => {
test('Test UpdateAction when extension is installed outdated and user extension', () => {
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a', { version: '1.0.0' });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -450,7 +459,7 @@ suite('ExtensionsActions Test', () => {
test('Test UpdateAction when extension is installing and outdated and user extension', () => {
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a', { version: '1.0.0' });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -469,14 +478,14 @@ suite('ExtensionsActions Test', () => {
test('Test ManageExtensionAction when there is no extension', () => {
const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
assert.ok(!testObject.enabled);
});
test('Test ManageExtensionAction when extension is installed', () => {
const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -491,7 +500,7 @@ suite('ExtensionsActions Test', () => {
test('Test ManageExtensionAction when extension is uninstalled', () => {
const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
@@ -506,7 +515,7 @@ suite('ExtensionsActions Test', () => {
test('Test ManageExtensionAction when extension is installing', () => {
const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
@@ -523,7 +532,7 @@ suite('ExtensionsActions Test', () => {
test('Test ManageExtensionAction when extension is queried from gallery and installed', () => {
const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
@@ -541,7 +550,7 @@ suite('ExtensionsActions Test', () => {
test('Test ManageExtensionAction when extension is system extension', () => {
const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a', {}, { type: ExtensionType.System });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -556,7 +565,7 @@ suite('ExtensionsActions Test', () => {
test('Test ManageExtensionAction when extension is uninstalling', () => {
const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -767,7 +776,7 @@ suite('ExtensionsActions Test', () => {
.then(page => {
const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction);
testObject.extension = page.firstPage[0];
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
installEvent.fire({ identifier: gallery.identifier, gallery });
assert.ok(!testObject.enabled);
@@ -951,7 +960,7 @@ suite('ExtensionsActions Test', () => {
.then(page => {
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
testObject.extension = page.firstPage[0];
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
installEvent.fire({ identifier: gallery.identifier, gallery });
assert.ok(!testObject.enabled);
});
@@ -965,7 +974,7 @@ suite('ExtensionsActions Test', () => {
.then(extensions => {
const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
testObject.extension = extensions[0];
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
uninstallEvent.fire(local.identifier);
assert.ok(!testObject.enabled);
});
@@ -1046,14 +1055,14 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when there is no extension', () => {
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
assert.ok(!testObject.enabled);
});
test('Test ReloadAction when extension state is installing', () => {
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
@@ -1068,7 +1077,7 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when extension state is uninstalling', () => {
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -1083,7 +1092,7 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when extension is newly installed', async () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
@@ -1094,7 +1103,7 @@ suite('ExtensionsActions Test', () => {
return new Promise(c => {
testObject.onDidChange(() => {
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
if (testObject.enabled && testObject.tooltip === 'Please reload Azure Data Studio to complete the installation of this extension.') {
if (testObject.enabled && testObject.tooltip === 'Please reload Azure Data Studio to enable this extension.') {
c();
}
});
@@ -1106,7 +1115,7 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when extension is installed and uninstalled', () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
return instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None)
@@ -1123,9 +1132,9 @@ suite('ExtensionsActions Test', () => {
});
test('Test ReloadAction when extension is uninstalled', async () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a'), version: '1.0.0' }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
const extensions = await instantiationService.get(IExtensionsWorkbenchService).queryLocal();
@@ -1146,7 +1155,7 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when extension is uninstalled and installed', () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), version: '1.0.0', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
@@ -1167,7 +1176,7 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when extension is updated while running', async () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), version: '1.0.1', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a', { version: '1.0.1' });
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -1177,7 +1186,7 @@ suite('ExtensionsActions Test', () => {
return new Promise(c => {
testObject.onDidChange(() => {
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
if (testObject.enabled && testObject.tooltip === 'Please reload Azure Data Studio to complete the updating of this extension.') {
if (testObject.enabled && testObject.tooltip === 'Please reload Azure Data Studio to enable the updated extension.') {
c();
}
});
@@ -1193,7 +1202,7 @@ suite('ExtensionsActions Test', () => {
return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled)
.then(() => {
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
return workbenchService.queryLocal()
@@ -1212,7 +1221,7 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when extension is disabled when running', () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -1223,7 +1232,7 @@ suite('ExtensionsActions Test', () => {
.then(() => {
assert.ok(testObject.enabled);
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
assert.equal('Please reload Azure Data Studio to complete the disabling of this extension.', testObject.tooltip);
assert.equal('Please reload Azure Data Studio to disable this extension.', testObject.tooltip);
});
});
});
@@ -1231,7 +1240,7 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when extension enablement is toggled when running', () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), version: '1.0.0', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a');
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -1250,7 +1259,7 @@ suite('ExtensionsActions Test', () => {
return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled)
.then(() => {
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
return workbenchService.queryLocal()
@@ -1261,7 +1270,7 @@ suite('ExtensionsActions Test', () => {
.then(() => {
assert.ok(testObject.enabled);
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
assert.equal('Please reload Azure Data Studio to complete the enabling of this extension.', testObject.tooltip);
assert.equal('Please reload Azure Data Studio to enable this extension.', testObject.tooltip);
});
});
});
@@ -1273,7 +1282,7 @@ suite('ExtensionsActions Test', () => {
return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled)
.then(() => {
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
return workbenchService.queryLocal()
@@ -1292,7 +1301,7 @@ suite('ExtensionsActions Test', () => {
return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled)
.then(() => {
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
return workbenchService.queryLocal()
@@ -1307,7 +1316,7 @@ suite('ExtensionsActions Test', () => {
.then(() => {
assert.ok(testObject.enabled);
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
assert.equal('Please reload Azure Data Studio to complete the enabling of this extension.', testObject.tooltip);
assert.equal('Please reload Azure Data Studio to enable this extension.', testObject.tooltip);
});
});
@@ -1317,7 +1326,7 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when a localization extension is newly installed', async () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
@@ -1333,7 +1342,7 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when a localization extension is updated while running', async () => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), version: '1.0.1', extensionLocation: URI.file('pub.a') }]);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update());
instantiationService.createInstance(ExtensionContainers, [testObject]);
const local = aLocalExtension('a', { version: '1.0.1', contributes: <IExtensionContributions>{ localizations: [{ languageId: 'de', translations: [] }] } });
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);

View File

@@ -37,7 +37,6 @@ import { SinonStub } from 'sinon';
import { IExperimentService, ExperimentService, ExperimentState, ExperimentActionType } from 'vs/workbench/contrib/experiments/node/experimentService';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService';
import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
@@ -90,7 +89,11 @@ suite('ExtensionsListView Tests', () => {
instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event);
instantiationService.stub(IRemoteAgentService, RemoteAgentService);
instantiationService.stub(IExtensionManagementServerService, instantiationService.createInstance(ExtensionManagementServerService));
instantiationService.stub(IExtensionManagementServerService, <IExtensionManagementServerService>{
localExtensionManagementServer: {
extensionManagementService: instantiationService.get(IExtensionManagementService)
}
});
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));

View File

@@ -37,7 +37,6 @@ import { URLService } from 'vs/platform/url/common/urlService';
import { URI } from 'vs/base/common/uri';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
@@ -77,7 +76,6 @@ suite('ExtensionsWorkbenchServiceTest', () => {
});
instantiationService.stub(IRemoteAgentService, RemoteAgentService);
instantiationService.stub(IExtensionManagementServerService, instantiationService.createInstance(ExtensionManagementServerService));
instantiationService.stub(IExtensionManagementService, ExtensionManagementService);
instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event);
@@ -85,6 +83,12 @@ suite('ExtensionsWorkbenchServiceTest', () => {
instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event);
instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event);
instantiationService.stub(IExtensionManagementServerService, <IExtensionManagementServerService>{
localExtensionManagementServer: {
extensionManagementService: instantiationService.get(IExtensionManagementService)
}
});
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
instantiationService.set(IExtensionTipsService, instantiationService.createInstance(ExtensionTipsService));

View File

@@ -310,6 +310,12 @@ export class FeedbackDropdown extends Dropdown {
};
}
private updateFeedbackDescription() {
if (this.feedbackDescriptionInput && this.feedbackDescriptionInput.textLength > this.maxFeedbackCharacters) {
this.feedbackDescriptionInput.value = this.feedbackDescriptionInput.value.substring(0, this.maxFeedbackCharacters);
}
}
private getCharCountText(charCount: number): string {
const remaining = this.maxFeedbackCharacters - charCount;
const text = (remaining === 1)
@@ -349,6 +355,7 @@ export class FeedbackDropdown extends Dropdown {
this.sentiment = smile ? 1 : 0;
this.maxFeedbackCharacters = this.feedbackDelegate.getCharacterLimit(this.sentiment);
this.updateFeedbackDescription();
this.updateCharCountText();
if (this.feedbackDescriptionInput) {
this.feedbackDescriptionInput.maxLength = this.maxFeedbackCharacters;

View File

@@ -186,7 +186,7 @@ export class TextFileEditor extends BaseTextEditor {
return Promise.reject(createErrorWithActions(toErrorMessage(error), {
actions: [
new Action('workbench.files.action.createMissingFile', nls.localize('createFile', "Create File"), undefined, true, () => {
return this.fileService.updateContent(input.getResource(), '').then(() => this.editorService.openEditor({
return this.textFileService.create(input.getResource()).then(() => this.editorService.openEditor({
resource: input.getResource(),
options: {
pinned: true // new file gets pinned by default

View File

@@ -968,6 +968,7 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole
const listService = accessor.get(IListService);
const explorerService = accessor.get(IExplorerService);
const fileService = accessor.get(IFileService);
const textFileService = accessor.get(ITextFileService);
const editorService = accessor.get(IEditorService);
const viewletService = accessor.get(IViewletService);
const activeViewlet = viewletService.getActiveViewlet();
@@ -995,7 +996,7 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole
folder.addChild(newStat);
const onSuccess = async (value: string) => {
const createPromise = isFolder ? fileService.createFolder(resources.joinPath(folder.resource, value)) : fileService.createFile(resources.joinPath(folder.resource, value));
const createPromise = isFolder ? fileService.createFolder(resources.joinPath(folder.resource, value)) : textFileService.create(resources.joinPath(folder.resource, value));
return createPromise.then(created => {
refreshIfSeparator(value, explorerService);
return isFolder ? explorerService.select(created.resource, true)

View File

@@ -40,8 +40,9 @@ import { IEditorService, SIDE_GROUP, IResourceEditorReplacement } from 'vs/workb
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ILabelService } from 'vs/platform/label/common/label';
import { onUnexpectedError } from 'vs/base/common/errors';
import { basename } from 'vs/base/common/resources';
import { basename, toLocalResource } from 'vs/base/common/resources';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
// {{SQL CARBON EDIT}}
import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService';
@@ -104,7 +105,8 @@ function save(
untitledEditorService: IUntitledEditorService,
textFileService: ITextFileService,
editorGroupService: IEditorGroupsService,
queryEditorService: IQueryEditorService
queryEditorService: IQueryEditorService,
environmentService: IWorkbenchEnvironmentService
): Promise<any> {
function ensureForcedSave(options?: ISaveOptions): ISaveOptions {
@@ -146,9 +148,9 @@ function save(
// Special case: an untitled file with associated path gets saved directly unless "saveAs" is true
let savePromise: Promise<URI | undefined>;
if (!isSaveAs && resource.scheme === Schemas.untitled && untitledEditorService.hasAssociatedFilePath(resource)) {
savePromise = textFileService.save(resource, options).then((result) => {
savePromise = textFileService.save(resource, options).then(result => {
if (result) {
return resource.with({ scheme: Schemas.file });
return toLocalResource(resource, environmentService.configuration.remoteAuthority);
}
return undefined;
@@ -165,7 +167,7 @@ function save(
savePromise = textFileService.saveAs(resource, undefined, options);
}
return savePromise.then((target) => {
return savePromise.then(target => {
if (!target || target.toString() === resource.toString()) {
return false; // save canceled or same resource used
}
@@ -238,7 +240,7 @@ function saveAll(saveAllArguments: any, editorService: IEditorService, untitledE
});
// Save all
return textFileService.saveAll(saveAllArguments).then((result) => {
return textFileService.saveAll(saveAllArguments).then(result => {
groupIdToUntitledResourceInput.forEach((inputs, groupId) => {
// {{SQL CARBON EDIT}}
// Update untitled resources to the saved ones, so we open the proper files
@@ -325,6 +327,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
const instantiationService = accessor.get(IInstantiationService);
const textModelService = accessor.get(ITextModelService);
const editorService = accessor.get(IEditorService);
const fileService = accessor.get(IFileService);
// Register provider at first as needed
let registerEditorListener = false;
@@ -336,9 +339,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
providerDisposables.push(textModelService.registerTextModelContentProvider(COMPARE_WITH_SAVED_SCHEMA, provider));
}
// Open editor (only files supported)
// Open editor (only resources that can be handled by file service are supported)
const uri = getResourceForCommand(resource, accessor.get(IListService), editorService);
if (uri && uri.scheme === Schemas.file /* only files on disk supported for now */) {
if (uri && fileService.canHandleResource(uri)) {
const name = basename(uri);
const editorLabel = nls.localize('modifiedLabel', "{0} (on disk) ↔ {1}", name, name);
@@ -542,7 +545,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
}
// {{SQL CARBON EDIT}}
return save(resource, true, undefined, editorService, accessor.get(IFileService), accessor.get(IUntitledEditorService), accessor.get(ITextFileService), accessor.get(IEditorGroupsService), accessor.get(IQueryEditorService));
return save(resource, true, undefined, editorService, accessor.get(IFileService), accessor.get(IUntitledEditorService), accessor.get(ITextFileService), accessor.get(IEditorGroupsService), accessor.get(IQueryEditorService), accessor.get(IWorkbenchEnvironmentService));
}
});
@@ -558,7 +561,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
if (resources.length === 1) {
// If only one resource is selected explictly call save since the behavior is a bit different than save all #41841
// {{SQL CARBON EDIT}}
return save(resources[0], false, undefined, editorService, accessor.get(IFileService), accessor.get(IUntitledEditorService), accessor.get(ITextFileService), accessor.get(IEditorGroupsService), accessor.get(IQueryEditorService));
return save(resources[0], false, undefined, editorService, accessor.get(IFileService), accessor.get(IUntitledEditorService), accessor.get(ITextFileService), accessor.get(IEditorGroupsService), accessor.get(IQueryEditorService), accessor.get(IWorkbenchEnvironmentService));
}
return saveAll(resources, editorService, accessor.get(IUntitledEditorService), accessor.get(ITextFileService), accessor.get(IEditorGroupsService));
}
@@ -576,7 +579,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
const resource = toResource(editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource) {
// {{SQL CARBON EDIT}}
return save(resource, false, { skipSaveParticipants: true }, editorService, accessor.get(IFileService), accessor.get(IUntitledEditorService), accessor.get(ITextFileService), accessor.get(IEditorGroupsService), accessor.get(IQueryEditorService));
return save(resource, false, { skipSaveParticipants: true }, editorService, accessor.get(IFileService), accessor.get(IUntitledEditorService), accessor.get(ITextFileService), accessor.get(IEditorGroupsService), accessor.get(IQueryEditorService), accessor.get(IWorkbenchEnvironmentService));
}
return undefined;

View File

@@ -32,6 +32,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { Event } from 'vs/base/common/event';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { isWindows } from 'vs/base/common/platform';
import { Schemas } from 'vs/base/common/network';
export const CONFLICT_RESOLUTION_CONTEXT = 'saveConflictResolutionContext';
export const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution';
@@ -136,13 +137,13 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I
const triedToMakeWriteable = isReadonly && fileOperationError.options && fileOperationError.options.overwriteReadonly;
const isPermissionDenied = fileOperationError.fileOperationResult === FileOperationResult.FILE_PERMISSION_DENIED;
// Save Elevated
if (isPermissionDenied || triedToMakeWriteable) {
// Save Elevated (TODO@remote cannot write elevated https://github.com/Microsoft/vscode/issues/48659)
if (resource.scheme === Schemas.file && (isPermissionDenied || triedToMakeWriteable)) {
actions.primary!.push(this.instantiationService.createInstance(SaveElevatedAction, model, triedToMakeWriteable));
}
// Overwrite
else if (isReadonly) {
// Overwrite (TODO@remote cannot overwrite readonly https://github.com/Microsoft/vscode/issues/48659)
else if (resource.scheme === Schemas.file && isReadonly) {
actions.primary!.push(this.instantiationService.createInstance(OverwriteReadonlyAction, model));
}

View File

@@ -6,7 +6,7 @@
import { localize } from 'vs/nls';
import { memoize } from 'vs/base/common/decorators';
import { basename } from 'vs/base/common/path';
import { basenameOrAuthority, dirname } from 'vs/base/common/resources';
import { dirname } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { EncodingMode, ConfirmResult, EditorInput, IFileEditorInput, ITextEditorModel, Verbosity, IRevertOptions } from 'vs/workbench/common/editor';
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
@@ -118,7 +118,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
getName(): string {
if (!this.name) {
this.name = basenameOrAuthority(this.resource);
this.name = basename(this.labelService.getUriLabel(this.resource));
}
return this.decorateLabel(this.name);
@@ -195,6 +195,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
if (model && model.hasState(ModelState.ORPHAN)) {
return localize('orphanedFile', "{0} (deleted from disk)", label);
}
if (model && model.isReadonly()) {
return localize('readonlyFile', "{0} (read-only)", label);
}

View File

@@ -23,6 +23,8 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel';
import { once } from 'vs/base/common/functional';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { toLocalResource } from 'vs/base/common/resources';
/**
* Explorer viewlet id.
@@ -138,12 +140,13 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider {
@ITextFileService private readonly textFileService: ITextFileService,
@IFileService private readonly fileService: IFileService,
@IModeService private readonly modeService: IModeService,
@IModelService private readonly modelService: IModelService
@IModelService private readonly modelService: IModelService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) {
}
provideTextContent(resource: URI): Promise<ITextModel> {
const fileOnDiskResource = resource.with({ scheme: Schemas.file });
const savedFileResource = toLocalResource(resource, this.environmentService.configuration.remoteAuthority);
// Make sure our file from disk is resolved up to date
return this.resolveEditorModel(resource).then(codeEditorModel => {
@@ -151,7 +154,7 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider {
// Make sure to keep contents on disk up to date when it changes
if (!this.fileWatcherDisposable) {
this.fileWatcherDisposable = this.fileService.onFileChanges(changes => {
if (changes.contains(fileOnDiskResource, FileChangeType.UPDATED)) {
if (changes.contains(savedFileResource, FileChangeType.UPDATED)) {
this.resolveEditorModel(resource, false /* do not create if missing */); // update model when resource changes
}
});
@@ -171,20 +174,20 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider {
private resolveEditorModel(resource: URI, createAsNeeded?: true): Promise<ITextModel>;
private resolveEditorModel(resource: URI, createAsNeeded?: boolean): Promise<ITextModel | null>;
private resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise<ITextModel | null> {
const fileOnDiskResource = resource.with({ scheme: Schemas.file });
const savedFileResource = toLocalResource(resource, this.environmentService.configuration.remoteAuthority);
return this.textFileService.resolveTextContent(fileOnDiskResource).then(content => {
return this.textFileService.resolve(savedFileResource).then(content => {
let codeEditorModel = this.modelService.getModel(resource);
if (codeEditorModel) {
this.modelService.updateModel(codeEditorModel, content.value);
} else if (createAsNeeded) {
const fileOnDiskModel = this.modelService.getModel(fileOnDiskResource);
const fileOnDiskModel = this.modelService.getModel(savedFileResource);
let languageSelector: ILanguageSelection;
if (fileOnDiskModel) {
languageSelector = this.modeService.create(fileOnDiskModel.getModeId());
} else {
languageSelector = this.modeService.createByFilepathOrFirstLine(fileOnDiskResource.fsPath);
languageSelector = this.modeService.createByFilepathOrFirstLine(savedFileResource.fsPath);
}
codeEditorModel = this.modelService.createModel(content.value, languageSelector, resource);

View File

@@ -36,7 +36,7 @@ class DefaultFormatter extends Disposable implements IWorkbenchContribution {
static configName = 'editor.defaultFormatter';
static extensionIds: string[] = [];
static extensionIds: (string | null)[] = [];
static extensionDescriptions: string[] = [];
constructor(
@@ -60,6 +60,10 @@ class DefaultFormatter extends Disposable implements IWorkbenchContribution {
DefaultFormatter.extensionIds.length = 0;
DefaultFormatter.extensionDescriptions.length = 0;
DefaultFormatter.extensionIds.push(null);
DefaultFormatter.extensionDescriptions.push(nls.localize('nullFormatterDescription', "None"));
for (const extension of extensions) {
if (extension.main) {
DefaultFormatter.extensionIds.push(extension.identifier.value);
@@ -127,7 +131,8 @@ class DefaultFormatter extends Disposable implements IWorkbenchContribution {
const picks = formatter.map((formatter, index) => {
return <IIndexedPick>{
index,
label: formatter.displayName || formatter.extensionId || '?'
label: formatter.displayName || formatter.extensionId || '?',
description: formatter.extensionId && formatter.extensionId.value
};
});
const langName = this._modeService.getLanguageName(document.getModeId()) || document.getModeId();
@@ -156,7 +161,7 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).regis
properties: {
[DefaultFormatter.configName]: {
description: nls.localize('formatter.default', "Defines a default formatter which takes precedence over all other formatter settings. Must be the identifier of an extension contributing a formatter."),
type: 'string',
type: ['string', 'null'],
default: null,
enum: DefaultFormatter.extensionIds,
markdownEnumDescriptions: DefaultFormatter.extensionDescriptions
@@ -253,8 +258,8 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction {
const model = editor.getModel();
const provider = getRealAndSyntheticDocumentFormattersOrdered(model);
const pick = await instaService.invokeFunction(showFormatterPick, model, provider);
if (pick) {
await instaService.invokeFunction(formatDocumentWithProvider, provider[pick], editor, CancellationToken.None);
if (typeof pick === 'number') {
await instaService.invokeFunction(formatDocumentWithProvider, provider[pick], editor, FormattingMode.Explicit, CancellationToken.None);
}
logFormatterTelemetry(telemetryService, 'document', provider, typeof pick === 'number' && provider[pick] || undefined);
}
@@ -291,7 +296,7 @@ registerEditorAction(class FormatSelectionMultipleAction extends EditorAction {
const provider = DocumentRangeFormattingEditProviderRegistry.ordered(model);
const pick = await instaService.invokeFunction(showFormatterPick, model, provider);
if (pick) {
if (typeof pick === 'number') {
await instaService.invokeFunction(formatDocumentRangeWithProvider, provider[pick], editor, range, CancellationToken.None);
}

View File

@@ -11,8 +11,8 @@ import { IExtensionManagementService, IExtensionEnablementService } from 'vs/pla
import { webFrame } from 'electron';
import { assign } from 'vs/base/common/objects';
import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issue';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
export class WorkbenchIssueService implements IWorkbenchIssueService {
_serviceBrand: any;
@@ -22,7 +22,7 @@ export class WorkbenchIssueService implements IWorkbenchIssueService {
@IThemeService private readonly themeService: IThemeService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService,
@IWindowService private readonly windowService: IWindowService
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) { }
openReporter(dataOverrides: Partial<IssueReporterData> = {}): Promise<void> {
@@ -60,7 +60,7 @@ export class WorkbenchIssueService implements IWorkbenchIssueService {
openProcessExplorer(): Promise<void> {
const theme = this.themeService.getTheme();
const data: ProcessExplorerData = {
pid: this.windowService.getConfiguration().mainPid,
pid: this.environmentService.configuration.mainPid,
zoomLevel: webFrame.getZoomLevel(),
styles: {
backgroundColor: getColor(theme, editorBackground),

View File

@@ -9,8 +9,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/contrib/output/common/output';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import * as Constants from 'vs/workbench/contrib/logs/common/logConstants';
@@ -22,15 +21,14 @@ import { ILogService, LogLevel } from 'vs/platform/log/common/log';
class LogOutputChannels extends Disposable implements IWorkbenchContribution {
constructor(
@IWindowService windowService: IWindowService,
@IEnvironmentService environmentService: IEnvironmentService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@ILogService logService: ILogService
) {
super();
let outputChannelRegistry = Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels);
outputChannelRegistry.registerChannel({ id: Constants.mainLogChannelId, label: nls.localize('mainLog', "Main"), file: URI.file(join(environmentService.logsPath, `main.log`)), log: true });
outputChannelRegistry.registerChannel({ id: Constants.sharedLogChannelId, label: nls.localize('sharedLog', "Shared"), file: URI.file(join(environmentService.logsPath, `sharedprocess.log`)), log: true });
outputChannelRegistry.registerChannel({ id: Constants.rendererLogChannelId, label: nls.localize('rendererLog', "Window"), file: URI.file(join(environmentService.logsPath, `renderer${windowService.getCurrentWindowId()}.log`)), log: true });
outputChannelRegistry.registerChannel({ id: Constants.rendererLogChannelId, label: nls.localize('rendererLog', "Window"), file: URI.file(join(environmentService.logsPath, `renderer${environmentService.configuration.windowId}.log`)), log: true });
// {{SQL CARBON EDIT}}
let toolsServiceLogFile : string = join(environmentService.logsPath, '..', '..', 'mssql', `sqltools_${Date.now()}.log`);

View File

@@ -13,8 +13,8 @@ import * as nls from 'vs/nls';
import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { WorkbenchStateContext, IsRemoteContext } from 'vs/workbench/common/contextkeys';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { WorkbenchStateContext, RemoteAuthorityContext } from 'vs/workbench/common/contextkeys';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
@@ -40,7 +40,6 @@ import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEdito
import { ExplorerRootContext, ExplorerFolderContext } from 'vs/workbench/contrib/files/common/files';
import { ILabelService } from 'vs/platform/label/common/label';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
registerSingleton(IPreferencesSearchService, PreferencesSearchService, true);
@@ -387,12 +386,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
class PreferencesActionsContribution extends Disposable implements IWorkbenchContribution {
constructor(
@IEnvironmentService environmentService: IEnvironmentService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IPreferencesService private readonly preferencesService: IPreferencesService,
@IWorkspaceContextService private readonly workpsaceContextService: IWorkspaceContextService,
@ILabelService labelService: ILabelService,
@IExtensionService extensionService: IExtensionService,
@IWindowService windowService: IWindowService
) {
super();
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
@@ -431,7 +429,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
extensionService.whenInstalledExtensionsRegistered()
.then(() => {
const remoteAuthority = windowService.getConfiguration().remoteAuthority;
const remoteAuthority = environmentService.configuration.remoteAuthority;
const hostLabel = labelService.getHostLabel(REMOTE_HOST_SCHEME, remoteAuthority) || remoteAuthority;
const label = nls.localize('openRemoteSettings', "Open User Settings ({0})", hostLabel);
CommandsRegistry.registerCommand(OpenRemoteSettingsAction.ID, serviceAccessor => {
@@ -443,7 +441,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
title: { value: label, original: `Preferences: Open User Settings (${hostLabel})` },
category: nls.localize('preferencesCategory', "Preferences")
},
when: IsRemoteContext
when: RemoteAuthorityContext.notEqualsTo('')
});
});
}

View File

@@ -1211,7 +1211,8 @@ export class SettingsEditor2 extends BaseEditor {
}
private renderResultCountMessages() {
if (!this.currentSettingsModel) {
if (!this.currentSettingsModel || !this.searchResultModel) {
this.countElement.style.display = 'none';
return;
}

View File

@@ -377,7 +377,8 @@ export class CommandsHandler extends QuickOpenHandler {
private commandHistoryEnabled: boolean;
private commandsHistory: CommandsHistory;
private extensionsRegistered: boolean;
private waitedForExtensionsRegistered: boolean;
constructor(
@IEditorService private readonly editorService: IEditorService,
@@ -391,7 +392,7 @@ export class CommandsHandler extends QuickOpenHandler {
this.commandsHistory = this.instantiationService.createInstance(CommandsHistory);
this.extensionService.whenInstalledExtensionsRegistered().then(() => this.extensionsRegistered = true);
this.extensionService.whenInstalledExtensionsRegistered().then(() => this.waitedForExtensionsRegistered = true);
this.configurationService.onDidChangeConfiguration(e => this.updateConfiguration());
this.updateConfiguration();
@@ -402,7 +403,7 @@ export class CommandsHandler extends QuickOpenHandler {
}
getResults(searchValue: string, token: CancellationToken): Promise<QuickOpenModel> {
if (this.extensionsRegistered) {
if (this.waitedForExtensionsRegistered) {
return this.doGetResults(searchValue, token);
}
@@ -410,7 +411,11 @@ export class CommandsHandler extends QuickOpenHandler {
// a chance to register so that the complete set of commands shows up as result
// We do not want to delay functionality beyond that time though to keep the commands
// functional.
return Promise.race([timeout(800), this.extensionService.whenInstalledExtensionsRegistered().then(() => undefined)]).then(() => this.doGetResults(searchValue, token));
return Promise.race([timeout(800), this.extensionService.whenInstalledExtensionsRegistered().then(() => undefined)]).then(() => {
this.waitedForExtensionsRegistered = true;
return this.doGetResults(searchValue, token);
});
}
private doGetResults(searchValue: string, token: CancellationToken): Promise<QuickOpenModel> {

View File

@@ -18,6 +18,7 @@ import { isEqual } from 'vs/base/common/resources';
import { isLinux, isMacintosh } from 'vs/base/common/platform';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
interface IConfiguration extends IWindowsConfiguration {
update: { mode: string; };
@@ -151,7 +152,7 @@ export class WorkspaceChangeExtHostRelauncher extends Disposable implements IWor
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IExtensionService extensionService: IExtensionService,
@IWindowService windowSevice: IWindowService,
@IEnvironmentService environmentService: IEnvironmentService
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
) {
super();
@@ -159,7 +160,7 @@ export class WorkspaceChangeExtHostRelauncher extends Disposable implements IWor
if (!!environmentService.extensionTestsLocationURI) {
return; // no restart when in tests: see https://github.com/Microsoft/vscode/issues/66936
}
if (windowSevice.getConfiguration().remoteAuthority) {
if (environmentService.configuration.remoteAuthority) {
windowSevice.reloadWindow(); // TODO aeschli, workaround
} else {
extensionService.restartExtensionHost();

View File

@@ -32,7 +32,7 @@ export class OpenAnythingHandler extends QuickOpenHandler {
static readonly ID = 'workbench.picker.anything';
private static readonly LINE_COLON_PATTERN = /[#:\(](\d*)([#:,](\d*))?\)?$/;
private static readonly LINE_COLON_PATTERN = /[#:\(](\d*)([#:,](\d*))?\)?\s*$/;
private static readonly TYPING_SEARCH_DELAY = 200; // This delay accommodates for the user typing a word and then stops typing to start searching

View File

@@ -41,7 +41,7 @@ import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition
import { OpenAnythingHandler } from 'vs/workbench/contrib/search/browser/openAnythingHandler';
import { OpenSymbolHandler } from 'vs/workbench/contrib/search/browser/openSymbolHandler';
import { registerContributions as replaceContributions } from 'vs/workbench/contrib/search/browser/replaceContributions';
import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FindInFilesAction, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand } from 'vs/workbench/contrib/search/browser/searchActions';
import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand } from 'vs/workbench/contrib/search/browser/searchActions';
import { SearchPanel } from 'vs/workbench/contrib/search/browser/searchPanel';
import { SearchView } from 'vs/workbench/contrib/search/browser/searchView';
import { SearchViewlet } from 'vs/workbench/contrib/search/browser/searchViewlet';
@@ -523,7 +523,14 @@ const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.Workbenc
// Show Search and Find in Files are redundant, but we can't break keybindings by removing one. So it's the same action, same keybinding, registered to different IDs.
// Show Search 'when' is redundant but if the two conflict with exactly the same keybinding and 'when' clause, then they can show up as "unbound" - #51780
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSearchViewletAction, VIEWLET_ID, OpenSearchViewletAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, Constants.SearchViewVisibleKey.toNegated()), 'View: Show Search', nls.localize('view', "View"));
registry.registerWorkbenchAction(new SyncActionDescriptor(FindInFilesAction, Constants.FindInFilesActionId, nls.localize('findInFiles', "Find in Files"), { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }), 'Find in Files', category);
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: Constants.FindInFilesActionId,
weight: KeybindingWeight.WorkbenchContrib,
when: null,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F,
handler: FindInFilesCommand
});
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: Constants.FindInFilesActionId, title: { value: nls.localize('findInFiles', "Find in Files"), original: 'Find in Files' }, category } });
MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, {
group: '4_find_global',
command: {

View File

@@ -160,19 +160,35 @@ export abstract class FindOrReplaceInFilesAction extends Action {
});
}
}
export class FindInFilesAction extends FindOrReplaceInFilesAction {
static readonly LABEL = nls.localize('findInFiles', "Find in Files");
constructor(id: string, label: string,
@IViewletService viewletService: IViewletService,
@IPanelService panelService: IPanelService,
@IConfigurationService configurationService: IConfigurationService
) {
super(id, label, viewletService, panelService, configurationService, /*expandSearchReplaceWidget=*/false);
}
export interface IFindInFilesArgs {
query?: string;
replace?: string;
triggerSearch?: boolean;
filesToInclude?: string;
filesToExclude?: string;
isRegex?: boolean;
isCaseSensitive?: boolean;
matchWholeWord?: boolean;
}
export const FindInFilesCommand: ICommandHandler = (accessor, args: IFindInFilesArgs = {}) => {
const viewletService = accessor.get(IViewletService);
const panelService = accessor.get(IPanelService);
const configurationService = accessor.get(IConfigurationService);
openSearchView(viewletService, panelService, configurationService, false).then(openedView => {
if (openedView) {
const searchAndReplaceWidget = openedView.searchAndReplaceWidget;
searchAndReplaceWidget.toggleReplace(typeof args.replace === 'string');
let updatedText = false;
if (typeof args.query === 'string') {
openedView.setSearchParameters(args);
} else {
updatedText = openedView.updateTextFromSelection((typeof args.replace !== 'string'));
}
openedView.searchAndReplaceWidget.focus(undefined, updatedText, updatedText);
}
});
};
export class OpenSearchViewletAction extends FindOrReplaceInFilesAction {

View File

@@ -43,7 +43,7 @@ import { OpenFileFolderAction, OpenFolderAction } from 'vs/workbench/browser/act
import { ResourceLabels } from 'vs/workbench/browser/labels';
import { IEditor } from 'vs/workbench/common/editor';
import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget';
import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, RefreshAction } from 'vs/workbench/contrib/search/browser/searchActions';
import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, RefreshAction, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions';
import { FileMatchRenderer, FolderMatchRenderer, MatchRenderer, SearchAccessibilityProvider, SearchDelegate, SearchDND } from 'vs/workbench/contrib/search/browser/searchResultsView';
import { ISearchWidgetOptions, SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget';
import * as Constants from 'vs/workbench/contrib/search/common/constants';
@@ -452,8 +452,12 @@ export class SearchView extends ViewletPanel {
return;
}
const root = element instanceof SearchResult ? null : element;
this.tree.setChildren(root, this.createIterator(element, collapseResults));
if (element instanceof SearchResult) {
this.tree.setChildren(null, this.createIterator(element, collapseResults));
} else {
this.tree.setChildren(element, this.createIterator(element, collapseResults));
this.tree.rerender(element);
}
});
}
}
@@ -642,7 +646,7 @@ export class SearchView extends ViewletPanel {
}));
this._register(this.tree.onContextMenu(e => this.onContextMenu(e)));
const resourceNavigator = this._register(new TreeResourceNavigator2(this.tree, { openOnFocus: true }));
const resourceNavigator = this._register(new TreeResourceNavigator2(this.tree, { openOnFocus: true, openOnSelection: false }));
this._register(Event.debounce(resourceNavigator.onDidOpenResource, (last, event) => event, 75, true)(options => {
if (options.element instanceof Match) {
const selectedMatch: Match = options.element;
@@ -1043,6 +1047,37 @@ export class SearchView extends ViewletPanel {
this.onQueryChanged(true);
}
setSearchParameters(args: IFindInFilesArgs = {}): void {
if (typeof args.isCaseSensitive === 'boolean') {
this.searchWidget.searchInput.setCaseSensitive(args.isCaseSensitive);
}
if (typeof args.matchWholeWord === 'boolean') {
this.searchWidget.searchInput.setWholeWords(args.matchWholeWord);
}
if (typeof args.isRegex === 'boolean') {
this.searchWidget.searchInput.setRegex(args.isRegex);
}
if (typeof args.filesToInclude === 'string') {
this.searchIncludePattern.setValue(String(args.filesToInclude));
}
if (typeof args.filesToExclude === 'string') {
this.searchExcludePattern.setValue(String(args.filesToExclude));
}
if (typeof args.query === 'string') {
this.searchWidget.searchInput.setValue(args.query);
}
if (typeof args.replace === 'string') {
this.searchWidget.replaceInput.value = args.replace;
} else {
if (this.searchWidget.replaceInput.value !== '') {
this.searchWidget.replaceInput.value = '';
}
}
if (typeof args.triggerSearch === 'boolean' && args.triggerSearch) {
this.onQueryChanged(true);
}
}
toggleQueryDetails(moveFocus = true, show?: boolean, skipLayout?: boolean, reverse?: boolean): void {
const cls = 'more';
show = typeof show === 'undefined' ? !dom.hasClass(this.queryDetails, cls) : Boolean(show);

View File

@@ -20,6 +20,7 @@ import { SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFil
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IFileService } from 'vs/platform/files/common/files';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
const id = 'workbench.action.openSnippets';
@@ -120,7 +121,7 @@ async function computePicks(snippetService: ISnippetsService, envService: IEnvir
return { existing, future };
}
async function createSnippetFile(scope: string, defaultPath: URI, windowService: IWindowService, notificationService: INotificationService, fileService: IFileService, opener: IOpenerService) {
async function createSnippetFile(scope: string, defaultPath: URI, windowService: IWindowService, notificationService: INotificationService, fileService: IFileService, textFileService: ITextFileService, opener: IOpenerService) {
await fileService.createFolder(defaultPath);
await timeout(100); // ensure quick pick closes...
@@ -138,7 +139,7 @@ async function createSnippetFile(scope: string, defaultPath: URI, windowService:
return undefined;
}
await fileService.updateContent(resource, [
await textFileService.write(resource, [
'{',
'\t// Place your ' + scope + ' snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and ',
'\t// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope ',
@@ -163,7 +164,7 @@ async function createSnippetFile(scope: string, defaultPath: URI, windowService:
return undefined;
}
async function createLanguageSnippetFile(pick: ISnippetPick, fileService: IFileService) {
async function createLanguageSnippetFile(pick: ISnippetPick, fileService: IFileService, textFileService: ITextFileService) {
if (await fileService.exists(URI.file(pick.filepath))) {
return;
}
@@ -184,7 +185,7 @@ async function createLanguageSnippetFile(pick: ISnippetPick, fileService: IFileS
'\t// }',
'}'
].join('\n');
await fileService.updateContent(URI.file(pick.filepath), contents);
await textFileService.write(URI.file(pick.filepath), contents);
}
CommandsRegistry.registerCommand(id, async (accessor): Promise<any> => {
@@ -198,6 +199,7 @@ CommandsRegistry.registerCommand(id, async (accessor): Promise<any> => {
const notificationService = accessor.get(INotificationService);
const workspaceService = accessor.get(IWorkspaceContextService);
const fileService = accessor.get(IFileService);
const textFileService = accessor.get(ITextFileService);
const picks = await computePicks(snippetService, envService, modeService);
const existing: QuickPickInput[] = picks.existing;
@@ -231,12 +233,12 @@ CommandsRegistry.registerCommand(id, async (accessor): Promise<any> => {
});
if (globalSnippetPicks.indexOf(pick as SnippetPick) >= 0) {
return createSnippetFile((pick as SnippetPick).scope, (pick as SnippetPick).uri, windowService, notificationService, fileService, opener);
return createSnippetFile((pick as SnippetPick).scope, (pick as SnippetPick).uri, windowService, notificationService, fileService, textFileService, opener);
} else if (workspaceSnippetPicks.indexOf(pick as SnippetPick) >= 0) {
return createSnippetFile((pick as SnippetPick).scope, (pick as SnippetPick).uri, windowService, notificationService, fileService, opener);
return createSnippetFile((pick as SnippetPick).scope, (pick as SnippetPick).uri, windowService, notificationService, fileService, textFileService, opener);
} else if (ISnippetPick.is(pick)) {
if (pick.hint) {
await createLanguageSnippetFile(pick, fileService);
await createLanguageSnippetFile(pick, fileService, textFileService);
}
return opener.open(URI.file(pick.filepath));
}

View File

@@ -19,7 +19,7 @@ import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common
import * as themes from 'vs/workbench/common/theme';
import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { URI } from 'vs/base/common/uri';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -37,7 +37,7 @@ class PartsSplash {
constructor(
@IThemeService private readonly _themeService: IThemeService,
@IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService,
@IFileService private readonly _fileService: IFileService,
@ITextFileService private readonly _textFileService: ITextFileService,
@IEnvironmentService private readonly _envService: IEnvironmentService,
@IBroadcastService private readonly _broadcastService: IBroadcastService,
@ILifecycleService lifecycleService: ILifecycleService,
@@ -78,15 +78,14 @@ class PartsSplash {
sideBarWidth: getTotalWidth(this._layoutService.getContainer(Parts.SIDEBAR_PART)),
statusBarHeight: getTotalHeight(this._layoutService.getContainer(Parts.STATUSBAR_PART)),
};
this._fileService.updateContent(
this._textFileService.write(
URI.file(join(this._envService.userDataPath, 'rapid_render.json')),
JSON.stringify({
id: PartsSplash._splashElementId,
colorInfo,
layoutInfo,
baseTheme
}),
{ encoding: 'utf8' }
})
);
if (baseTheme !== this._lastBaseTheme || colorInfo.editorBackground !== this._lastBackground) {

View File

@@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri';
import { IFileService, IFileStat, IResolveFileResult, IContent } from 'vs/platform/files/common/files';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IWindowConfiguration, IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { endsWith } from 'vs/base/common/strings';
@@ -217,7 +217,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
@IFileService private readonly fileService: IFileService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IWindowService private readonly windowService: IWindowService,
@INotificationService private readonly notificationService: INotificationService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@@ -229,7 +229,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
private report(): void {
// Workspace Stats
this.resolveWorkspaceTags(this.windowService.getConfiguration(), rootFiles => this.handleWorkspaceFiles(rootFiles))
this.resolveWorkspaceTags(this.environmentService.configuration, rootFiles => this.handleWorkspaceFiles(rootFiles))
.then(tags => this.reportWorkspaceTags(tags), error => onUnexpectedError(error));
// Cloud Stats

View File

@@ -100,16 +100,27 @@ const presentation: IJSONSchema = {
default: false,
description: nls.localize('JsonSchema.tasks.presentation.focus', 'Controls whether the panel takes focus. Default is false. If set to true the panel is revealed as well.')
},
revealProblem: {
type: 'string',
enum: ['always', 'onProblem', 'never'],
enumDescriptions: [
nls.localize('JsonSchema.tasks.presentation.revealProblem.always', 'Always reveals the problems panel when this task is executed.'),
nls.localize('JsonSchema.tasks.presentation.revealProblem.onProblem', 'Only reveals the problems panel if a problem is found.'),
nls.localize('JsonSchema.tasks.presentation.revealProblem.never', 'Never reveals the problems panel when this task is executed.'),
],
default: 'never',
description: nls.localize('JsonSchema.tasks.presentation.revealProblem', 'Controls whether the problems panel is revealed when running this task or not. Takes precedence over option \"reveal\". Default is \"never\".')
},
reveal: {
type: 'string',
enum: ['always', 'silent', 'never'],
enumDescriptions: [
nls.localize('JsonSchema.tasks.presentation.reveal.always', 'Always reveals the terminal when this task is executed.'),
nls.localize('JsonSchema.tasks.presentation.reveal.silent', 'Only reveals the terminal if the task exits with an error.'),
nls.localize('JsonSchema.tasks.presentation.reveal.silent', 'Only reveals the terminal if the task exits with an error or the problem matcher finds an error.'),
nls.localize('JsonSchema.tasks.presentation.reveal.never', 'Never reveals the terminal when this task is executed.'),
],
default: 'always',
description: nls.localize('JsonSchema.tasks.presentation.reveals', 'Controls whether the panel running the task is revealed or not. Default is \"always\".')
description: nls.localize('JsonSchema.tasks.presentation.reveals', 'Controls whether the panel running the task is revealed or not. May be overridden by option \"revealProblem\". Default is \"always\".')
},
panel: {
type: 'string',

View File

@@ -43,6 +43,7 @@
.task-statusbar-item-label {
display: inline-block;
cursor: pointer;
padding: 0 5px 0 5px;
}
.task-statusbar-item-label > .task-statusbar-item-label-counter {

View File

@@ -91,6 +91,12 @@ export interface PresentationOptionsConfig {
*/
reveal?: string;
/**
* Controls whether the problems panel is revealed when running this task or not.
* Defaults to `RevealKind.Never`.
*/
revealProblem?: string;
/**
* Controls whether the executed command is printed to the output window or terminal as well.
*/
@@ -796,7 +802,7 @@ namespace CommandOptions {
namespace CommandConfiguration {
export namespace PresentationOptions {
const properties: MetaData<Tasks.PresentationOptions, void>[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'focus' }, { property: 'panel' }, { property: 'showReuseMessage' }, { property: 'clear' }, { property: 'group' }];
const properties: MetaData<Tasks.PresentationOptions, void>[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'revealProblem' }, { property: 'focus' }, { property: 'panel' }, { property: 'showReuseMessage' }, { property: 'clear' }, { property: 'group' }];
interface PresentationOptionsShape extends LegacyCommandProperties {
presentation?: PresentationOptionsConfig;
@@ -805,6 +811,7 @@ namespace CommandConfiguration {
export function from(this: void, config: PresentationOptionsShape, context: ParseContext): Tasks.PresentationOptions | undefined {
let echo: boolean;
let reveal: Tasks.RevealKind;
let revealProblem: Tasks.RevealProblemKind;
let focus: boolean;
let panel: Tasks.PanelKind;
let showReuseMessage: boolean;
@@ -827,6 +834,9 @@ namespace CommandConfiguration {
if (Types.isString(presentation.reveal)) {
reveal = Tasks.RevealKind.fromString(presentation.reveal);
}
if (Types.isString(presentation.revealProblem)) {
revealProblem = Tasks.RevealProblemKind.fromString(presentation.revealProblem);
}
if (Types.isBoolean(presentation.focus)) {
focus = presentation.focus;
}
@@ -847,7 +857,7 @@ namespace CommandConfiguration {
if (!hasProps) {
return undefined;
}
return { echo: echo!, reveal: reveal!, focus: focus!, panel: panel!, showReuseMessage: showReuseMessage!, clear: clear!, group };
return { echo: echo!, reveal: reveal!, revealProblem: revealProblem!, focus: focus!, panel: panel!, showReuseMessage: showReuseMessage!, clear: clear!, group };
}
export function assignProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions | undefined): Tasks.PresentationOptions | undefined {
@@ -860,7 +870,7 @@ namespace CommandConfiguration {
export function fillDefaults(value: Tasks.PresentationOptions, context: ParseContext): Tasks.PresentationOptions | undefined {
let defaultEcho = context.engine === Tasks.ExecutionEngine.Terminal ? true : false;
return _fillDefaults(value, { echo: defaultEcho, reveal: Tasks.RevealKind.Always, focus: false, panel: Tasks.PanelKind.Shared, showReuseMessage: true, clear: false }, properties, context);
return _fillDefaults(value, { echo: defaultEcho, reveal: Tasks.RevealKind.Always, revealProblem: Tasks.RevealProblemKind.Never, focus: false, panel: Tasks.PanelKind.Shared, showReuseMessage: true, clear: false }, properties, context);
}
export function freeze(value: Tasks.PresentationOptions): Readonly<Tasks.PresentationOptions> | undefined {

View File

@@ -12,8 +12,8 @@ import { UriComponents } from 'vs/base/common/uri';
import { ProblemMatcher } from 'vs/workbench/contrib/tasks/common/problemMatcher';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
export const TASK_RUNNING_STATE = new RawContextKey<boolean>('taskRunning', false);
@@ -123,7 +123,9 @@ export enum RevealKind {
/**
* Only brings the terminal to front if a problem is detected executing the task
* (e.g. the task couldn't be started because).
* e.g. the task couldn't be started,
* the task ended with an exit code other than zero,
* or the problem matcher found an error.
*/
Silent = 2,
@@ -148,6 +150,39 @@ export namespace RevealKind {
}
}
export enum RevealProblemKind {
/**
* Never reveals the problems panel when this task is executed.
*/
Never = 1,
/**
* Only reveals the problems panel if a problem is found.
*/
OnProblem = 2,
/**
* Never reveals the problems panel when this task is executed.
*/
Always = 3
}
export namespace RevealProblemKind {
export function fromString(this: void, value: string): RevealProblemKind {
switch (value.toLowerCase()) {
case 'always':
return RevealProblemKind.Always;
case 'never':
return RevealProblemKind.Never;
case 'onproblem':
return RevealProblemKind.OnProblem;
default:
return RevealProblemKind.OnProblem;
}
}
}
export enum PanelKind {
/**
@@ -189,6 +224,12 @@ export interface PresentationOptions {
*/
reveal: RevealKind;
/**
* Controls whether the problems pane is revealed when running this task or not.
* Defaults to `RevealProblemKind.Never`.
*/
revealProblem: RevealProblemKind;
/**
* Controls whether the command associated with the task is echoed
* in the user interface.
@@ -225,7 +266,7 @@ export interface PresentationOptions {
export namespace PresentationOptions {
export const defaults: PresentationOptions = {
echo: true, reveal: RevealKind.Always, focus: false, panel: PanelKind.Shared, showReuseMessage: true, clear: false
echo: true, reveal: RevealKind.Always, revealProblem: RevealProblemKind.Never, focus: false, panel: PanelKind.Shared, showReuseMessage: true, clear: false
};
}

View File

@@ -95,6 +95,8 @@ import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } fr
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { RunAutomaticTasks, AllowAutomaticTaskRunning, DisallowAutomaticTaskRunning } from 'vs/workbench/contrib/tasks/electron-browser/runAutomaticTasks';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
let tasksCategory = nls.localize('tasksCategory', "Tasks");
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
@@ -385,7 +387,7 @@ interface WorkspaceFolderConfigurationResult {
hasErrors: boolean;
}
interface TaskCustomizationTelementryEvent {
interface TaskCustomizationTelemetryEvent {
properties: string[];
}
@@ -467,6 +469,7 @@ class TaskService extends Disposable implements ITaskService {
@IConfigurationService private readonly configurationService: IConfigurationService,
@IMarkerService private readonly markerService: IMarkerService,
@IOutputService private readonly outputService: IOutputService,
@IPanelService private readonly panelService: IPanelService,
@IEditorService private readonly editorService: IEditorService,
@IFileService private readonly fileService: IFileService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@@ -485,6 +488,7 @@ class TaskService extends Disposable implements ITaskService {
@IDialogService private readonly dialogService: IDialogService,
@INotificationService private readonly notificationService: INotificationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService
) {
super();
@@ -1077,7 +1081,7 @@ class TaskService extends Disposable implements ITaskService {
if (editorConfig.editor.insertSpaces) {
content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize));
}
promise = this.fileService.createFile(workspaceFolder.toResource('.vscode/tasks.json'), content).then(() => { });
promise = this.textFileService.create(workspaceFolder.toResource('.vscode/tasks.json'), content).then(() => { });
} else {
// We have a global task configuration
if ((index === -1) && properties) {
@@ -1104,7 +1108,7 @@ class TaskService extends Disposable implements ITaskService {
return Promise.resolve(undefined);
}
return promise.then(() => {
let event: TaskCustomizationTelementryEvent = {
let event: TaskCustomizationTelemetryEvent = {
properties: properties ? Object.getOwnPropertyNames(properties) : []
};
/* __GDPR__
@@ -1352,9 +1356,9 @@ class TaskService extends Disposable implements ITaskService {
}
if (this.executionEngine === ExecutionEngine.Terminal) {
this._taskSystem = new TerminalTaskSystem(
this.terminalService, this.outputService, this.markerService,
this.terminalService, this.outputService, this.panelService, this.markerService,
this.modelService, this.configurationResolverService, this.telemetryService,
this.contextService, this._windowService,
this.contextService, this._environmentService,
TaskService.OutputChannelId,
(workspaceFolder: IWorkspaceFolder) => {
if (!workspaceFolder) {
@@ -1877,7 +1881,14 @@ class TaskService extends Disposable implements ITaskService {
private canRunCommand(): boolean {
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
this.notificationService.info(nls.localize('TaskService.noWorkspace', 'Tasks are only available on a workspace folder.'));
this.notificationService.prompt(
Severity.Info,
nls.localize('TaskService.noWorkspace', "Tasks are only available on a workspace folder."),
[{
label: nls.localize('TaskService.learnMore', "Learn More"),
run: () => window.open('https://code.visualstudio.com/docs/editor/tasks')
}]
);
return false;
}
return true;
@@ -2042,7 +2053,7 @@ class TaskService extends Disposable implements ITaskService {
this.showQuickPick(tasks ? tasks : this.tasks(),
nls.localize('TaskService.pickRunTask', 'Select the task to run'),
{
label: nls.localize('TaslService.noEntryToRun', 'No task to run found. Configure Tasks...'),
label: nls.localize('TaskService.noEntryToRun', 'No task to run found. Configure Tasks...'),
task: null
},
true).
@@ -2197,7 +2208,7 @@ class TaskService extends Disposable implements ITaskService {
}
let runQuickPick = (promise?: Promise<Task[]>) => {
this.showQuickPick(promise || this.getActiveTasks(),
nls.localize('TaskService.tastToTerminate', 'Select task to terminate'),
nls.localize('TaskService.taskToTerminate', 'Select task to terminate'),
{
label: nls.localize('TaskService.noTaskRunning', 'No task is currently running'),
task: null
@@ -2253,7 +2264,7 @@ class TaskService extends Disposable implements ITaskService {
}
let runQuickPick = (promise?: Promise<Task[]>) => {
this.showQuickPick(promise || this.getActiveTasks(),
nls.localize('TaskService.tastToRestart', 'Select the task to restart'),
nls.localize('TaskService.taskToRestart', 'Select the task to restart'),
{
label: nls.localize('TaskService.noTaskToRestart', 'No task to restart'),
task: null
@@ -2338,7 +2349,7 @@ class TaskService extends Disposable implements ITaskService {
"autoDetect" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
return this.fileService.createFile(resource, content).then((result): URI => {
return this.textFileService.create(resource, content).then((result): URI => {
this.telemetryService.publicLog(TaskService.TemplateTelemetryEventName, {
templateId: selection.id,
autoDetect: selection.autoDetect

View File

@@ -22,6 +22,7 @@ import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/marke
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ProblemMatcher, ProblemMatcherRegistry /*, ProblemPattern, getResource */ } from 'vs/workbench/contrib/tasks/common/problemMatcher';
import Constants from 'vs/workbench/contrib/markers/browser/constants';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -31,7 +32,7 @@ import { IOutputService } from 'vs/workbench/contrib/output/common/output';
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind } from 'vs/workbench/contrib/tasks/common/problemCollectors';
import {
Task, CustomTask, ContributedTask, RevealKind, CommandOptions, ShellConfiguration, RuntimeType, PanelKind,
TaskEvent, TaskEventKind, ShellQuotingOptions, ShellQuoting, CommandString, CommandConfiguration, ExtensionTaskSource, TaskScope
TaskEvent, TaskEventKind, ShellQuotingOptions, ShellQuoting, CommandString, CommandConfiguration, ExtensionTaskSource, TaskScope, RevealProblemKind
} from 'vs/workbench/contrib/tasks/common/tasks';
import {
ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, ITaskResolver,
@@ -39,9 +40,10 @@ import {
} from 'vs/workbench/contrib/tasks/common/taskSystem';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { URI } from 'vs/base/common/uri';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { Schemas } from 'vs/base/common/network';
import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
interface TerminalData {
terminal: ITerminalInstance;
@@ -159,14 +161,18 @@ export class TerminalTaskSystem implements ITaskSystem {
private readonly _onDidStateChange: Emitter<TaskEvent>;
constructor(private terminalService: ITerminalService, private outputService: IOutputService,
constructor(
private terminalService: ITerminalService,
private outputService: IOutputService,
private panelService: IPanelService,
private markerService: IMarkerService, private modelService: IModelService,
private configurationResolverService: IConfigurationResolverService,
private telemetryService: ITelemetryService,
private contextService: IWorkspaceContextService,
private windowService: IWindowService,
private environmentService: IWorkbenchEnvironmentService,
private outputChannelId: string,
taskSystemInfoResolver: TaskSystemInfoResovler) {
taskSystemInfoResolver: TaskSystemInfoResovler
) {
this.activeTasks = Object.create(null);
this.terminals = Object.create(null);
@@ -514,11 +520,16 @@ export class TerminalTaskSystem implements ITaskSystem {
eventCounter--;
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task));
if (eventCounter === 0) {
let reveal = task.command.presentation!.reveal;
if ((reveal === RevealKind.Silent) && (watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity &&
if ((watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity &&
(watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) {
this.terminalService.setActiveInstance(terminal!);
this.terminalService.showPanel(false);
let reveal = task.command.presentation!.reveal;
let revealProblem = task.command.presentation!.revealProblem;
if (revealProblem === RevealProblemKind.OnProblem) {
this.panelService.openPanel(Constants.MARKERS_PANEL_ID, true);
} else if (reveal === RevealKind.Silent) {
this.terminalService.setActiveInstance(terminal!);
this.terminalService.showPanel(false);
}
}
}
}
@@ -642,7 +653,11 @@ export class TerminalTaskSystem implements ITaskSystem {
}
}
let reveal = task.command.presentation!.reveal;
if (terminal && (reveal === RevealKind.Silent) && ((exitCode !== 0) || (startStopProblemMatcher.numberOfMatches > 0) && startStopProblemMatcher.maxMarkerSeverity &&
let revealProblem = task.command.presentation!.revealProblem;
let revealProblemPanel = terminal && (revealProblem === RevealProblemKind.OnProblem) && (startStopProblemMatcher.numberOfMatches > 0);
if (revealProblemPanel) {
this.panelService.openPanel(Constants.MARKERS_PANEL_ID);
} else if (terminal && (reveal === RevealKind.Silent) && ((exitCode !== 0) || (startStopProblemMatcher.numberOfMatches > 0) && startStopProblemMatcher.maxMarkerSeverity &&
(startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) {
this.terminalService.setActiveInstance(terminal);
this.terminalService.showPanel(false);
@@ -673,7 +688,10 @@ export class TerminalTaskSystem implements ITaskSystem {
if (!terminal) {
return Promise.reject(new Error(`Failed to create terminal for task ${task._label}`));
}
if (task.command.presentation && (task.command.presentation.reveal === RevealKind.Always)) {
let showProblemPanel = task.command.presentation && (task.command.presentation.revealProblem === RevealProblemKind.Always);
if (showProblemPanel) {
this.panelService.openPanel(Constants.MARKERS_PANEL_ID);
} else if (task.command.presentation && (task.command.presentation.reveal === RevealKind.Always)) {
this.terminalService.setActiveInstance(terminal);
this.terminalService.showPanel(task.command.presentation.focus);
}
@@ -853,7 +871,7 @@ export class TerminalTaskSystem implements ITaskSystem {
}
}
// This must be normalized to the OS
const authority = this.windowService.getConfiguration().remoteAuthority;
const authority = this.environmentService.configuration.remoteAuthority;
shellLaunchConfig.cwd = URI.from({ scheme: authority ? REMOTE_HOST_SCHEME : Schemas.file, authority: authority, path: cwd });
}
if (options.env) {

View File

@@ -83,7 +83,7 @@ class PresentationBuilder {
public result: Tasks.PresentationOptions;
constructor(public parent: CommandConfigurationBuilder) {
this.result = { echo: false, reveal: Tasks.RevealKind.Always, focus: false, panel: Tasks.PanelKind.Shared, showReuseMessage: true, clear: false };
this.result = { echo: false, reveal: Tasks.RevealKind.Always, revealProblem: Tasks.RevealProblemKind.Never, focus: false, panel: Tasks.PanelKind.Shared, showReuseMessage: true, clear: false };
}
public echo(value: boolean): PresentationBuilder {

View File

@@ -12,7 +12,7 @@ import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/a
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { language } from 'vs/base/common/platform';
import { Disposable } from 'vs/base/common/lifecycle';
import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
@@ -30,13 +30,13 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr
@IEditorService editorService: IEditorService,
@IKeybindingService keybindingsService: IKeybindingService,
@IWorkbenchThemeService themeService: IWorkbenchThemeService,
@IWindowService windowService: IWindowService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IConfigurationService configurationService: IConfigurationService,
@IViewletService viewletService: IViewletService
) {
super();
const { filesToOpen, filesToCreate, filesToDiff } = windowService.getConfiguration();
const { filesToOpen, filesToCreate, filesToDiff } = environmentService.configuration;
const activeViewlet = viewletService.getActiveViewlet();
/* __GDPR__

View File

@@ -264,6 +264,11 @@ configurationRegistry.registerConfiguration({
type: 'boolean',
default: true
},
'terminal.integrated.enableLatencyMitigation': {
description: nls.localize('terminal.integrated.enableLatencyMitigation', "Whether to enable the latency mitigation feature for high-latency terminals."),
type: 'boolean',
default: false
},
'terminal.integrated.experimentalRefreshOnResume': {
description: nls.localize('terminal.integrated.experimentalRefreshOnResume', "An experimental setting that will refresh the terminal renderer when the system is resumed."),
type: 'boolean',

View File

@@ -587,6 +587,19 @@ export class TerminalInstance implements ITerminalInstance {
if (this._processManager) {
this._widgetManager = new TerminalWidgetManager(this._wrapperElement);
this._processManager.onProcessReady(() => this._linkHandler.setWidgetManager(this._widgetManager));
this._processManager.onProcessReady(() => {
if (this._configHelper.config.enableLatencyMitigation) {
if (!this._processManager) {
return;
}
this._processManager.getLatency().then(latency => {
if (latency > 20 && (this._xterm as any).typeAheadInit) {
(this._xterm as any).typeAheadInit(this._processManager, this._themeService);
}
});
}
});
}
const computedStyle = window.getComputedStyle(this._container);

View File

@@ -6,7 +6,7 @@
import * as platform from 'vs/base/common/platform';
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal';
import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent } from 'vs/workbench/contrib/terminal/common/terminal';
import { ILogService } from 'vs/platform/log/common/log';
import { Emitter, Event } from 'vs/base/common/event';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
@@ -14,11 +14,10 @@ import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/commo
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { Schemas } from 'vs/base/common/network';
import { REMOTE_HOST_SCHEME, getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
import { sanitizeProcessEnvironment } from 'vs/base/common/processes';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IProductService } from 'vs/platform/product/common/product';
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -57,6 +56,8 @@ export class TerminalProcessManager implements ITerminalProcessManager {
private readonly _onProcessReady = new Emitter<void>();
public get onProcessReady(): Event<void> { return this._onProcessReady.event; }
private readonly _onBeforeProcessData = new Emitter<IBeforeProcessDataEvent>();
public get onBeforeProcessData(): Event<IBeforeProcessDataEvent> { return this._onBeforeProcessData.event; }
private readonly _onProcessData = new Emitter<string>();
public get onProcessData(): Event<string> { return this._onProcessData.event; }
private readonly _onProcessTitle = new Emitter<string>();
@@ -72,9 +73,8 @@ export class TerminalProcessManager implements ITerminalProcessManager {
@ILogService private readonly _logService: ILogService,
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
@IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService,
@IWindowService private readonly _windowService: IWindowService,
@IConfigurationService private readonly _workspaceConfigurationService: IConfigurationService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@IProductService private readonly _productService: IProductService,
@ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService,
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService
@@ -114,7 +114,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
if (shellLaunchConfig.cwd && typeof shellLaunchConfig.cwd === 'object') {
this.remoteAuthority = getRemoteAuthority(shellLaunchConfig.cwd);
} else {
this.remoteAuthority = this._windowService.getConfiguration().remoteAuthority;
this.remoteAuthority = this._environmentService.configuration.remoteAuthority;
}
const hasRemoteAuthority = !!this.remoteAuthority;
let launchRemotely = hasRemoteAuthority || forceExtHostProcess;
@@ -183,7 +183,11 @@ export class TerminalProcessManager implements ITerminalProcessManager {
const p = this._process!;
p.onProcessData(data => {
this._onProcessData.fire(data);
const beforeProcessDataEvent: IBeforeProcessDataEvent = { data };
this._onBeforeProcessData.fire(beforeProcessDataEvent);
if (beforeProcessDataEvent.data && beforeProcessDataEvent.data.length > 0) {
this._onProcessData.fire(beforeProcessDataEvent.data);
}
});
p.onProcessIdReady(pid => {

View File

@@ -17,7 +17,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance';
@@ -35,7 +35,7 @@ export abstract class TerminalService extends CommonTerminalService implements I
@INotificationService notificationService: INotificationService,
@IDialogService dialogService: IDialogService,
@IInstantiationService protected readonly _instantiationService: IInstantiationService,
@IWindowService private _windowService: IWindowService,
@IWorkbenchEnvironmentService private _environmentService: IWorkbenchEnvironmentService,
@IExtensionService extensionService: IExtensionService,
@IFileService fileService: IFileService,
) {
@@ -76,7 +76,7 @@ export abstract class TerminalService extends CommonTerminalService implements I
return;
}
if (this._windowService.getConfiguration().remoteAuthority) {
if (this._environmentService.configuration.remoteAuthority) {
// Don't suggest if the opened workspace is remote
return;
}
@@ -87,7 +87,7 @@ export abstract class TerminalService extends CommonTerminalService implements I
return;
}
if (this._windowService.getConfiguration().remoteAuthority) {
if (this._environmentService.configuration.remoteAuthority) {
// Don't suggest if the opened workspace is remote
return;
}

View File

@@ -0,0 +1,136 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Terminal as XTermTerminal } from 'vscode-xterm';
import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal';
import { IThemeService } from 'vs/platform/theme/common/themeService';
export interface ITerminalCore {
buffer: any;
}
export interface ITypeAheadAddonTerminal {
_core: ITerminalCore;
on: any;
write(data: string): void;
cols: number;
__typeAheadQueue: string[];
__typeAheadState: TypeAheadState;
__typeAheadCurrentYBase: number;
__typeAheadCurrentY: number;
}
enum TypeAheadState {
/**
* The normal state, ready to type if it starts.
*/
Normal,
/**
* Something happens such that we cannot make a good guess on what to print,
* wait until the cursor row changes before proceeding.
*/
AwaitingRowChange
}
function isCharPrintable(data: string): boolean {
const code = data.charCodeAt(0);
return data.length === 1 && code >= 32 && code <= 126;
}
function init(terminal: any, processManager: ITerminalProcessManager, themeService: IThemeService): void {
const t = terminal as ITypeAheadAddonTerminal;
t.__typeAheadQueue = [];
t.__typeAheadState = TypeAheadState.Normal;
t.__typeAheadCurrentYBase = 0;
t.__typeAheadCurrentY = 0;
function typeAhead(data: string): void {
for (let i = 0; i < data.length; i++) {
t.__typeAheadQueue.push(data[i]);
}
t.write(data);
}
t.on('cursormove', () => {
// Reset if the cursor row changed
if (t._core.buffer.ybase !== t.__typeAheadCurrentYBase || t._core.buffer.y !== t.__typeAheadCurrentY) {
t.__typeAheadCurrentYBase = t._core.buffer.ybase;
t.__typeAheadCurrentY = t._core.buffer.y;
t.__typeAheadState = TypeAheadState.Normal;
}
});
t.on('data', (data: string) => {
// Exit if we're waiting for a row change
if (t.__typeAheadState === TypeAheadState.AwaitingRowChange) {
return;
}
// Only enable in the normal buffer
if (!t._core.buffer._hasScrollback) {
return;
}
// // Handle enter
// if (data === '\r') {
// typeAhead('\r\n');
// return;
// }
// // Left arrow
// if (data === '\x1b[D') {
// // TODO: How to stop it from going beyond prompt?
// typeAhead(String.fromCharCode(8));
// }
// // Right arrow
// if (data === '\x1b[C') {
// // TODO: How to stop it from going beyond prompt?
// typeAhead('\x1b[C');
// }
// // Backspace (DEL)
// if (data.charCodeAt(0) === 127) {
// // TODO: This would require knowing the prompt length to be able to shift everything
// }
if (!isCharPrintable(data)) {
t.__typeAheadState = TypeAheadState.AwaitingRowChange;
return;
}
if (t._core.buffer.x === t.cols - 1) {
// TODO: Does the space get added on Windows/Linux too?
data += ' \r';
}
typeAhead(data);
});
processManager.onBeforeProcessData(event => {
let consumeCount = 0;
for (let i = 0; i < event.data.length; i++) {
if (t.__typeAheadQueue[0] === event.data[i]) {
t.__typeAheadQueue.shift();
consumeCount++;
} else {
t.__typeAheadQueue.length = 0;
break;
}
}
if (consumeCount === event.data.length) {
event.data = '';
} else if (consumeCount > 0) {
event.data = event.data.substr(consumeCount);
}
});
}
export function apply(terminalConstructor: typeof XTermTerminal) {
(<any>terminalConstructor.prototype).typeAheadInit = function (processManager: ITerminalProcessManager, themeService: IThemeService): void {
init(this, processManager, themeService);
};
}

View File

@@ -101,6 +101,7 @@ export interface ITerminalConfiguration {
experimentalBufferImpl: 'JsArray' | 'TypedArray';
splitCwd: 'workspaceRoot' | 'initial' | 'inherited';
windowsEnableConpty: boolean;
enableLatencyMitigation: boolean;
experimentalRefreshOnResume: boolean;
}
@@ -634,6 +635,14 @@ export interface ITerminalCommandTracker {
selectToNextLine(): void;
}
export interface IBeforeProcessDataEvent {
/**
* The data of the event, this can be modified by the event listener to change what gets sent
* to the terminal.
*/
data: string;
}
export interface ITerminalProcessManager extends IDisposable {
readonly processState: ProcessState;
readonly ptyProcessReady: Promise<void>;
@@ -643,6 +652,7 @@ export interface ITerminalProcessManager extends IDisposable {
readonly userHome: string | undefined;
readonly onProcessReady: Event<void>;
readonly onBeforeProcessData: Event<IBeforeProcessDataEvent>;
readonly onProcessData: Event<string>;
readonly onProcessTitle: Event<string>;
readonly onProcessExit: Event<number>;

View File

@@ -12,6 +12,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess';
import * as typeAheadAddon from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon';
let Terminal: typeof XTermTerminal;
@@ -35,6 +36,7 @@ export class TerminalInstanceService implements ITerminalInstanceService {
Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/search/search'));
Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/webLinks/webLinks'));
Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/winptyCompat/winptyCompat'));
Terminal.applyAddon(typeAheadAddon);
// Localize strings
Terminal.strings.blankLine = nls.localize('terminal.integrated.a11yBlankLine', 'Blank line');
Terminal.strings.promptLabel = nls.localize('terminal.integrated.a11yPromptLabel', 'Terminal input');

View File

@@ -19,7 +19,8 @@ import { getDefaultShell, linuxDistro, getWindowsBuildNumber } from 'vs/workbenc
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ipcRenderer as ipc } from 'electron';
import { IOpenFileRequest, IWindowService } from 'vs/platform/windows/common/windows';
import { IOpenFileRequest } from 'vs/platform/windows/common/windows';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput';
import { coalesce } from 'vs/base/common/arrays';
@@ -43,10 +44,10 @@ export class TerminalService extends BrowserTerminalService implements ITerminal
@INotificationService notificationService: INotificationService,
@IDialogService dialogService: IDialogService,
@IExtensionService extensionService: IExtensionService,
@IWindowService windowService: IWindowService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IFileService fileService: IFileService
) {
super(contextKeyService, panelService, layoutService, lifecycleService, storageService, notificationService, dialogService, instantiationService, windowService, extensionService, fileService);
super(contextKeyService, panelService, layoutService, lifecycleService, storageService, notificationService, dialogService, instantiationService, environmentService, extensionService, fileService);
this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, linuxDistro);
ipc.on('vscode:openFiles', (_event: any, request: IOpenFileRequest) => {

View File

@@ -23,7 +23,7 @@ import * as semver from 'semver';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { INotificationService, INotificationHandle, Severity } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ReleaseNotesManager } from './releaseNotesEditor';
import { isWindows } from 'vs/base/common/platform';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -300,7 +300,7 @@ export class UpdateContribution implements IGlobalActivity {
@IDialogService private readonly dialogService: IDialogService,
@IUpdateService private readonly updateService: IUpdateService,
@IActivityService private readonly activityService: IActivityService,
@IWindowService private readonly windowService: IWindowService
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) {
this.state = updateService.state;
@@ -330,7 +330,7 @@ export class UpdateContribution implements IGlobalActivity {
case StateType.Idle:
if (state.error) {
this.onError(state.error);
} else if (this.state.type === StateType.CheckingForUpdates && this.state.context && this.state.context.windowId === this.windowService.getCurrentWindowId()) {
} else if (this.state.type === StateType.CheckingForUpdates && this.state.context && this.state.context.windowId === this.environmentService.configuration.windowId) {
this.onUpdateNotAvailable();
}
break;
@@ -555,7 +555,7 @@ export class UpdateContribution implements IGlobalActivity {
return null;
case StateType.Idle:
const windowId = this.windowService.getCurrentWindowId();
const windowId = this.environmentService.configuration.windowId;
return new Action('update.check', nls.localize('checkForUpdates', "Check for Updates..."), undefined, true, () =>
this.updateService.checkForUpdates({ windowId }));

View File

@@ -17,14 +17,36 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import * as colorRegistry from 'vs/platform/theme/common/colorRegistry';
import { DARK, ITheme, IThemeService, LIGHT } from 'vs/platform/theme/common/themeService';
import { registerFileProtocol, WebviewProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols';
import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService';
import { WebviewFindWidget } from '../browser/webviewFindWidget';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService';
import { WebviewFindWidget } from '../browser/webviewFindWidget';
import { WebviewContentOptions, WebviewPortMapping, WebviewOptions, Webview } from 'vs/workbench/contrib/webview/common/webview';
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEditorOptions, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
export interface WebviewPortMapping {
readonly port: number;
readonly resolvedPort: number;
}
export interface WebviewOptions {
readonly allowSvgs?: boolean;
readonly extension?: {
readonly location: URI;
readonly id?: ExtensionIdentifier;
};
readonly enableFindWidget?: boolean;
}
export interface WebviewContentOptions {
readonly allowScripts?: boolean;
readonly svgWhiteList?: string[];
readonly localResourceRoots?: ReadonlyArray<URI>;
readonly portMappings?: ReadonlyArray<WebviewPortMapping>;
}
interface IKeydownEvent {
key: string;
@@ -126,11 +148,15 @@ class WebviewProtocolProvider extends Disposable {
class WebviewPortMappingProvider extends Disposable {
private readonly _tunnels = new Map<number, Promise<RemoteTunnel>>();
constructor(
session: WebviewSession,
extensionLocation: URI | undefined,
mappings: () => ReadonlyArray<WebviewPortMapping>,
private readonly tunnelService: ITunnelService,
extensionId: ExtensionIdentifier | undefined,
@ITelemetryService telemetryService: ITelemetryService,
@ITelemetryService telemetryService: ITelemetryService
) {
super();
@@ -148,7 +174,7 @@ class WebviewPortMappingProvider extends Disposable {
hasLogged = true;
/* __GDPR__
"webview.accessLocalhost" : {
"webview.accessLocalhost" : {
"extension" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
@@ -157,12 +183,25 @@ class WebviewPortMappingProvider extends Disposable {
const port = +localhostMatch[1];
for (const mapping of mappings()) {
if (mapping.port === port && mapping.port !== mapping.resolvedPort) {
return {
redirectURL: details.url.replace(
new RegExp(`^${uri.scheme}://localhost:${mapping.port}/`),
`${uri.scheme}://localhost:${mapping.resolvedPort}/`)
};
if (mapping.port === port) {
if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) {
const tunnel = await this.getOrCreateTunnel(mapping.resolvedPort);
if (tunnel) {
return {
redirectURL: details.url.replace(
new RegExp(`^${uri.scheme}://localhost:${mapping.port}/`),
`${uri.scheme}://localhost:${tunnel.tunnelLocalPort}/`)
};
}
}
if (mapping.port !== mapping.resolvedPort) {
return {
redirectURL: details.url.replace(
new RegExp(`^${uri.scheme}://localhost:${mapping.port}/`),
`${uri.scheme}://localhost:${mapping.resolvedPort}/`)
};
}
}
}
}
@@ -170,6 +209,27 @@ class WebviewPortMappingProvider extends Disposable {
return undefined;
});
}
dispose() {
super.dispose();
for (const tunnel of this._tunnels.values()) {
tunnel.then(tunnel => tunnel.dispose());
}
this._tunnels.clear();
}
private getOrCreateTunnel(remotePort: number): Promise<RemoteTunnel> | undefined {
const existing = this._tunnels.get(remotePort);
if (existing) {
return existing;
}
const tunnel = this.tunnelService.openTunnel(remotePort);
if (tunnel) {
this._tunnels.set(remotePort, tunnel);
}
return tunnel;
}
}
class SvgBlocker extends Disposable {
@@ -314,6 +374,7 @@ export class WebviewElement extends Disposable implements Webview {
@IThemeService themeService: IThemeService,
@IEnvironmentService environmentService: IEnvironmentService,
@IFileService fileService: IFileService,
@ITunnelService tunnelService: ITunnelService,
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
) {
@@ -354,10 +415,12 @@ export class WebviewElement extends Disposable implements Webview {
this._register(new WebviewPortMappingProvider(
session,
() => (this._contentOptions.portMappings || []),
_options.extension ? _options.extension.location : undefined,
() => (this._contentOptions.portMappings || [{ port: 3000, resolvedPort: 4000 }]),
tunnelService,
_options.extension ? _options.extension.id : undefined,
telemetryService));
telemetryService
));
if (!this._options.allowSvgs) {
const svgBlocker = this._register(new SvgBlocker(session, this._contentOptions));

View File

@@ -35,7 +35,7 @@ export class WalkThroughContentProvider implements ITextModelContentProvider, IW
reject(err);
}
});
}) : this.textFileService.resolveTextContent(URI.file(resource.fsPath)).then(content => content.value));
}) : this.textFileService.resolve(URI.file(resource.fsPath)).then(content => content.value));
return content.then(content => {
let codeEditorModel = this.modelService.getModel(resource);
if (!codeEditorModel) {
@@ -61,7 +61,7 @@ export class WalkThroughSnippetContentProvider implements ITextModelContentProvi
}
public provideTextContent(resource: URI): Promise<ITextModel> {
return this.textFileService.resolveTextContent(URI.file(resource.fsPath)).then(content => {
return this.textFileService.resolve(URI.file(resource.fsPath)).then(content => {
let codeEditorModel = this.modelService.getModel(resource);
if (!codeEditorModel) {
const j = parseInt(resource.fragment);