Merge VS Code 1.21 source code (#1067)
* Initial VS Code 1.21 file copy with patches * A few more merges * Post npm install * Fix batch of build breaks * Fix more build breaks * Fix more build errors * Fix more build breaks * Runtime fixes 1 * Get connection dialog working with some todos * Fix a few packaging issues * Copy several node_modules to package build to fix loader issues * Fix breaks from master * A few more fixes * Make tests pass * First pass of license header updates * Second pass of license header updates * Fix restore dialog issues * Remove add additional themes menu items * fix select box issues where the list doesn't show up * formatting * Fix editor dispose issue * Copy over node modules to correct location on all platforms
@@ -72,14 +72,14 @@ export class BackupModelTracker implements IWorkbenchContribution {
|
||||
// Do not backup when auto save after delay is configured
|
||||
if (!this.configuredAutoSaveAfterDelay) {
|
||||
const model = this.textFileService.models.get(event.resource);
|
||||
this.backupFileService.backupResource(model.getResource(), model.getValue(), model.getVersionId()).done(null, errors.onUnexpectedError);
|
||||
this.backupFileService.backupResource(model.getResource(), model.createSnapshot(), model.getVersionId()).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onUntitledModelChanged(resource: Uri): void {
|
||||
if (this.untitledEditorService.isDirty(resource)) {
|
||||
this.untitledEditorService.loadOrCreate({ resource }).then(model => this.backupFileService.backupResource(resource, model.getValue(), model.getVersionId())).done(null, errors.onUnexpectedError);
|
||||
this.untitledEditorService.loadOrCreate({ resource }).then(model => this.backupFileService.backupResource(resource, model.createSnapshot(), model.getVersionId())).done(null, errors.onUnexpectedError);
|
||||
} else {
|
||||
this.discardBackup(resource);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import errors = require('vs/base/common/errors');
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
@@ -17,6 +17,7 @@ import { Position, IResourceInput, IUntitledResourceInput } from 'vs/platform/ed
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
export class BackupRestorer implements IWorkbenchContribution {
|
||||
|
||||
@@ -30,7 +31,8 @@ export class BackupRestorer implements IWorkbenchContribution {
|
||||
@IBackupFileService private backupFileService: IBackupFileService,
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@IEditorGroupService private groupService: IEditorGroupService,
|
||||
@ILifecycleService private lifecycleService: ILifecycleService
|
||||
@ILifecycleService private lifecycleService: ILifecycleService,
|
||||
@IFileService private fileService: IFileService
|
||||
) {
|
||||
this.restoreBackups();
|
||||
}
|
||||
@@ -69,9 +71,9 @@ export class BackupRestorer implements IWorkbenchContribution {
|
||||
|
||||
backups.forEach(backup => {
|
||||
if (stacks.isOpen(backup)) {
|
||||
if (backup.scheme === Schemas.file) {
|
||||
if (this.fileService.canHandleResource(backup)) {
|
||||
restorePromises.push(this.textFileService.models.loadOrCreate(backup).then(null, () => unresolved.push(backup)));
|
||||
} else if (backup.scheme === UNTITLED_SCHEMA) {
|
||||
} else if (backup.scheme === Schemas.untitled) {
|
||||
restorePromises.push(this.untitledEditorService.loadOrCreate({ resource: backup }).then(null, () => unresolved.push(backup)));
|
||||
}
|
||||
} else {
|
||||
@@ -95,11 +97,9 @@ export class BackupRestorer implements IWorkbenchContribution {
|
||||
const options = { pinned: true, preserveFocus: true, inactive: index > 0 || hasOpenedEditors };
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
if (resource.scheme === UNTITLED_SCHEMA
|
||||
if (resource.scheme === Schemas.untitled
|
||||
&& !BackupRestorer.UNTITLED_REGEX.test(resource.fsPath)
|
||||
&& !BackupRestorer.SQLQUERY_REGEX.test(resource.fsPath)) {
|
||||
// TODO@Ben debt: instead of guessing if an untitled file has an associated file path or not
|
||||
// this information should be provided by the backup service and stored as meta data within
|
||||
return { filePath: resource.fsPath, options };
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ export class NodeCachedDataManager implements IWorkbenchContribution {
|
||||
if (err) {
|
||||
/* __GDPR__
|
||||
"cachedDataError" : {
|
||||
"errorCode" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
||||
"errorCode" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"path": { "classification": "CustomerContent", "purpose": "PerformanceAndHealth" }
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import * as path from 'path';
|
||||
import * as cp from 'child_process';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { nfcall } from 'vs/base/common/async';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import URI from 'vs/base/common/uri';
|
||||
@@ -14,29 +15,40 @@ import { Action } from 'vs/base/common/actions';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { IMessageService, Severity } from 'vs/platform/message/common/message';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IChoiceService, Choice } from 'vs/platform/dialogs/common/dialogs';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { ILogService } from '../../../../platform/log/common/log';
|
||||
|
||||
function ignore<T>(code: string, value: T = null): (err: any) => TPromise<T> {
|
||||
return err => err.code === code ? TPromise.as<T>(value) : TPromise.wrapError<T>(err);
|
||||
}
|
||||
|
||||
const root = URI.parse(require.toUrl('')).fsPath;
|
||||
const source = path.resolve(root, '..', 'bin', 'code');
|
||||
let _source: string = null;
|
||||
function getSource(): string {
|
||||
if (!_source) {
|
||||
const root = URI.parse(require.toUrl('')).fsPath;
|
||||
_source = path.resolve(root, '..', 'bin', 'code');
|
||||
}
|
||||
return _source;
|
||||
}
|
||||
|
||||
function isAvailable(): TPromise<boolean> {
|
||||
return pfs.exists(source);
|
||||
return pfs.exists(getSource());
|
||||
}
|
||||
|
||||
class InstallAction extends Action {
|
||||
|
||||
static ID = 'workbench.action.installCommandLine';
|
||||
static readonly ID = 'workbench.action.installCommandLine';
|
||||
static LABEL = nls.localize('install', "Install '{0}' command in PATH", product.applicationName);
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IMessageService private messageService: IMessageService
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IChoiceService private choiceService: IChoiceService,
|
||||
@ILogService private logService: ILogService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
@@ -49,7 +61,7 @@ class InstallAction extends Action {
|
||||
return isAvailable().then(isAvailable => {
|
||||
if (!isAvailable) {
|
||||
const message = nls.localize('not available', "This command is not available");
|
||||
this.messageService.show(Severity.Info, message);
|
||||
this.notificationService.info(message);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -61,7 +73,7 @@ class InstallAction extends Action {
|
||||
const createSymlink = () => {
|
||||
return pfs.unlink(this.target)
|
||||
.then(null, ignore('ENOENT'))
|
||||
.then(() => pfs.symlink(source, this.target));
|
||||
.then(() => pfs.symlink(getSource(), this.target));
|
||||
};
|
||||
|
||||
return createSymlink().then(null, err => {
|
||||
@@ -75,7 +87,8 @@ class InstallAction extends Action {
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
this.messageService.show(Severity.Info, nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", product.applicationName));
|
||||
this.logService.trace('cli#install', this.target);
|
||||
this.notificationService.info(nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", product.applicationName));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -84,40 +97,42 @@ class InstallAction extends Action {
|
||||
return pfs.lstat(this.target)
|
||||
.then(stat => stat.isSymbolicLink())
|
||||
.then(() => pfs.readlink(this.target))
|
||||
.then(link => link === source)
|
||||
.then(link => link === getSource())
|
||||
.then(null, ignore('ENOENT', false));
|
||||
}
|
||||
|
||||
private createBinFolder(): TPromise<void> {
|
||||
return new TPromise<void>((c, e) => {
|
||||
const message = nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command.");
|
||||
const actions = [
|
||||
new Action('ok', nls.localize('ok', "OK"), '', true, () => {
|
||||
const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && chown \\" & (do shell script (\\"whoami\\")) & \\" /usr/local/bin\\" with administrator privileges"';
|
||||
const choices: Choice[] = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")];
|
||||
|
||||
nfcall(cp.exec, command, {})
|
||||
.then(null, _ => TPromise.wrapError(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'."))))
|
||||
.done(c, e);
|
||||
this.choiceService.choose(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), choices, 1, true).then(choice => {
|
||||
switch (choice) {
|
||||
case 0 /* OK */:
|
||||
const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && chown \\" & (do shell script (\\"whoami\\")) & \\" /usr/local/bin\\" with administrator privileges"';
|
||||
|
||||
return null;
|
||||
}),
|
||||
new Action('cancel2', nls.localize('cancel2', "Cancel"), '', true, () => { e(new Error(nls.localize('aborted', "Aborted"))); return null; })
|
||||
];
|
||||
|
||||
this.messageService.show(Severity.Info, { message, actions });
|
||||
nfcall(cp.exec, command, {})
|
||||
.then(null, _ => TPromise.wrapError(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'."))))
|
||||
.done(c, e);
|
||||
break;
|
||||
case 1 /* Cancel */:
|
||||
e(new Error(nls.localize('aborted', "Aborted")));
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class UninstallAction extends Action {
|
||||
|
||||
static ID = 'workbench.action.uninstallCommandLine';
|
||||
static readonly ID = 'workbench.action.uninstallCommandLine';
|
||||
static LABEL = nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName);
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IMessageService private messageService: IMessageService
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@ILogService private logService: ILogService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
@@ -130,20 +145,21 @@ class UninstallAction extends Action {
|
||||
return isAvailable().then(isAvailable => {
|
||||
if (!isAvailable) {
|
||||
const message = nls.localize('not available', "This command is not available");
|
||||
this.messageService.show(Severity.Info, message);
|
||||
this.notificationService.info(message);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return pfs.unlink(this.target)
|
||||
.then(null, ignore('ENOENT'))
|
||||
.then(() => {
|
||||
this.messageService.show(Severity.Info, nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", product.applicationName));
|
||||
this.logService.trace('cli#uninstall', this.target);
|
||||
this.notificationService.info(nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", product.applicationName));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
if (platform.isMacintosh) {
|
||||
const category = nls.localize('shellCommand', "Shell Command");
|
||||
|
||||
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
|
||||
@@ -13,4 +13,10 @@ import './electron-browser/toggleMultiCursorModifier';
|
||||
import './electron-browser/toggleRenderControlCharacter';
|
||||
import './electron-browser/toggleRenderWhitespace';
|
||||
import './electron-browser/toggleWordWrap';
|
||||
import './electron-browser/wordWrapMigration';
|
||||
import { OPTIONS, TextBufferType } from 'vs/editor/common/model/textModel';
|
||||
|
||||
// Configure text buffer implementation
|
||||
if (process.env['VSCODE_PIECE_TREE']) {
|
||||
console.log(`Using TextBufferType.PieceTree (env variable VSCODE_PIECE_TREE)`);
|
||||
OPTIONS.TEXT_BUFFER_IMPLEMENTATION = TextBufferType.PieceTree;
|
||||
}
|
||||
|
||||
@@ -82,10 +82,10 @@ class AccessibilityHelpWidget extends Widget implements IOverlayWidget {
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@IContextKeyService private _contextKeyService: IContextKeyService,
|
||||
@IKeybindingService private _keybindingService: IKeybindingService,
|
||||
@IConfigurationService private _configurationService: IConfigurationService,
|
||||
@IOpenerService private _openerService: IOpenerService
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IOpenerService private readonly _openerService: IOpenerService
|
||||
) {
|
||||
super();
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ import { clipboard } from 'electron';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { EndOfLinePreference, IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLinePreference } from 'vs/editor/common/model';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
||||
@@ -11,23 +11,22 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IEditorContribution, IModel } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { registerEditorAction, registerEditorContribution, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditor, ContentWidgetPositionPreference, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IGrammar, StackElement, IToken } from 'vscode-textmate';
|
||||
import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding';
|
||||
import { TokenizationRegistry, LanguageIdentifier, FontStyle, StandardTokenType } from 'vs/editor/common/modes';
|
||||
import { TokenizationRegistry, LanguageIdentifier, FontStyle, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { findMatchingThemeRule } from 'vs/workbench/services/textMate/electron-browser/TMHelper';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
|
||||
import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
class InspectTMScopesController extends Disposable implements IEditorContribution {
|
||||
|
||||
@@ -41,7 +40,7 @@ class InspectTMScopesController extends Disposable implements IEditorContributio
|
||||
private _textMateService: ITextMateService;
|
||||
private _themeService: IWorkbenchThemeService;
|
||||
private _modeService: IModeService;
|
||||
private _messageService: IMessageService;
|
||||
private _notificationService: INotificationService;
|
||||
private _widget: InspectTMScopesWidget;
|
||||
|
||||
constructor(
|
||||
@@ -49,14 +48,14 @@ class InspectTMScopesController extends Disposable implements IEditorContributio
|
||||
@ITextMateService textMateService: ITextMateService,
|
||||
@IModeService modeService: IModeService,
|
||||
@IWorkbenchThemeService themeService: IWorkbenchThemeService,
|
||||
@IMessageService messageService: IMessageService,
|
||||
@INotificationService notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
this._editor = editor;
|
||||
this._textMateService = textMateService;
|
||||
this._themeService = themeService;
|
||||
this._modeService = modeService;
|
||||
this._messageService = messageService;
|
||||
this._notificationService = notificationService;
|
||||
this._widget = null;
|
||||
|
||||
this._register(this._editor.onDidChangeModel((e) => this.stop()));
|
||||
@@ -80,7 +79,7 @@ class InspectTMScopesController extends Disposable implements IEditorContributio
|
||||
if (!this._editor.getModel()) {
|
||||
return;
|
||||
}
|
||||
this._widget = new InspectTMScopesWidget(this._editor, this._textMateService, this._modeService, this._themeService, this._messageService);
|
||||
this._widget = new InspectTMScopesWidget(this._editor, this._textMateService, this._modeService, this._themeService, this._notificationService);
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
@@ -179,8 +178,8 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget {
|
||||
private readonly _editor: ICodeEditor;
|
||||
private readonly _modeService: IModeService;
|
||||
private readonly _themeService: IWorkbenchThemeService;
|
||||
private readonly _messageService: IMessageService;
|
||||
private readonly _model: IModel;
|
||||
private readonly _notificationService: INotificationService;
|
||||
private readonly _model: ITextModel;
|
||||
private readonly _domNode: HTMLElement;
|
||||
private readonly _grammar: TPromise<IGrammar>;
|
||||
|
||||
@@ -189,14 +188,14 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget {
|
||||
textMateService: ITextMateService,
|
||||
modeService: IModeService,
|
||||
themeService: IWorkbenchThemeService,
|
||||
messageService: IMessageService
|
||||
notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
this._isDisposed = false;
|
||||
this._editor = editor;
|
||||
this._modeService = modeService;
|
||||
this._themeService = themeService;
|
||||
this._messageService = messageService;
|
||||
this._notificationService = notificationService;
|
||||
this._model = this._editor.getModel();
|
||||
this._domNode = document.createElement('div');
|
||||
this._domNode.className = 'tm-inspect-widget';
|
||||
@@ -222,7 +221,7 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget {
|
||||
this._grammar.then(
|
||||
(grammar) => this._compute(grammar, position),
|
||||
(err) => {
|
||||
this._messageService.show(Severity.Warning, err);
|
||||
this._notificationService.warn(err);
|
||||
setTimeout(() => {
|
||||
InspectTMScopesController.get(this._editor).stop();
|
||||
});
|
||||
@@ -273,7 +272,7 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget {
|
||||
|
||||
let theme = this._themeService.getColorTheme();
|
||||
result += `<hr class="tm-metadata-separator"/>`;
|
||||
let matchingRule = findMatchingThemeRule(theme, data.tokens1[token1Index].scopes);
|
||||
let matchingRule = findMatchingThemeRule(theme, data.tokens1[token1Index].scopes, false);
|
||||
if (matchingRule) {
|
||||
result += `<code class="tm-theme-selector">${matchingRule.rawSelector}\n${JSON.stringify(matchingRule.settings, null, '\t')}</code>`;
|
||||
} else {
|
||||
|
||||
@@ -4,21 +4,20 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/codeEditor';
|
||||
import * as nls from 'vs/nls';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { IEditorContribution, IModel } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { InternalEditorOptions, EDITOR_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
const transientWordWrapState = 'transientWordWrapState';
|
||||
const isWordWrapMinifiedKey = 'isWordWrapMinified';
|
||||
@@ -42,18 +41,18 @@ interface IWordWrapState {
|
||||
/**
|
||||
* Store (in memory) the word wrap state for a particular model.
|
||||
*/
|
||||
function writeTransientState(model: IModel, state: IWordWrapTransientState, codeEditorService: ICodeEditorService): void {
|
||||
function writeTransientState(model: ITextModel, state: IWordWrapTransientState, codeEditorService: ICodeEditorService): void {
|
||||
codeEditorService.setTransientModelProperty(model, transientWordWrapState, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read (in memory) the word wrap state for a particular model.
|
||||
*/
|
||||
function readTransientState(model: IModel, codeEditorService: ICodeEditorService): IWordWrapTransientState {
|
||||
function readTransientState(model: ITextModel, codeEditorService: ICodeEditorService): IWordWrapTransientState {
|
||||
return codeEditorService.getTransientModelProperty(model, transientWordWrapState);
|
||||
}
|
||||
|
||||
function readWordWrapState(model: IModel, configurationService: ITextResourceConfigurationService, codeEditorService: ICodeEditorService): IWordWrapState {
|
||||
function readWordWrapState(model: ITextModel, configurationService: ITextResourceConfigurationService, codeEditorService: ICodeEditorService): IWordWrapState {
|
||||
const editorConfig = configurationService.getValue(model.uri, 'editor') as { wordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded'; wordWrapMinified: boolean };
|
||||
let _configuredWordWrap = editorConfig && (typeof editorConfig.wordWrap === 'string' || typeof editorConfig.wordWrap === 'boolean') ? editorConfig.wordWrap : void 0;
|
||||
|
||||
@@ -150,8 +149,8 @@ class ToggleWordWrapAction extends EditorAction {
|
||||
const editorConfiguration = editor.getConfiguration();
|
||||
if (editorConfiguration.wrappingInfo.inDiffEditor) {
|
||||
// Cannot change wrapping settings inside the diff editor
|
||||
const messageService = accessor.get(IMessageService);
|
||||
messageService.show(Severity.Info, nls.localize('wordWrap.notInDiffEditor', "Cannot toggle word wrap in a diff editor."));
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
notificationService.info(nls.localize('wordWrap.notInDiffEditor', "Cannot toggle word wrap in a diff editor."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -258,7 +257,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: 'editor.action.toggleWordWrap',
|
||||
title: nls.localize('unwrapMinified', "Disable wrapping for this file"),
|
||||
iconClass: 'toggle-word-wrap-action'
|
||||
iconPath: { dark: URI.parse(require.toUrl('vs/workbench/parts/codeEditor/electron-browser/media/WordWrap_16x.svg')).fsPath }
|
||||
},
|
||||
group: 'navigation',
|
||||
order: 1,
|
||||
@@ -272,7 +271,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: 'editor.action.toggleWordWrap',
|
||||
title: nls.localize('wrapMinified', "Enable wrapping for this file"),
|
||||
iconClass: 'toggle-word-wrap-action'
|
||||
iconPath: { dark: URI.parse(require.toUrl('vs/workbench/parts/codeEditor/electron-browser/media/WordWrap_16x.svg')).fsPath }
|
||||
},
|
||||
group: 'navigation',
|
||||
order: 1,
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
|
||||
interface IStorageData {
|
||||
dontShowPrompt: boolean;
|
||||
}
|
||||
|
||||
class WordWrapMigrationStorage {
|
||||
private static readonly KEY = 'wordWrapMigration';
|
||||
|
||||
private _storageService: IStorageService;
|
||||
private _value: IStorageData;
|
||||
|
||||
constructor(storageService: IStorageService) {
|
||||
this._storageService = storageService;
|
||||
this._value = this._read();
|
||||
}
|
||||
|
||||
private _read(): IStorageData {
|
||||
let jsonValue = this._storageService.get(WordWrapMigrationStorage.KEY, StorageScope.GLOBAL);
|
||||
if (!jsonValue) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(jsonValue);
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public get(): IStorageData {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
public set(data: IStorageData): void {
|
||||
this._value = data;
|
||||
this._storageService.store(WordWrapMigrationStorage.KEY, JSON.stringify(this._value), StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
class WordWrapMigrationController extends Disposable implements IEditorContribution {
|
||||
|
||||
private static readonly ID = 'editor.contrib.wordWrapMigrationController';
|
||||
private static _checked = false;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@IPreferencesService private preferencesService: IPreferencesService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._promptIfNecessary();
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return WordWrapMigrationController.ID;
|
||||
}
|
||||
|
||||
private _promptIfNecessary(): void {
|
||||
if (WordWrapMigrationController._checked) {
|
||||
// Already checked
|
||||
return;
|
||||
}
|
||||
WordWrapMigrationController._checked = true;
|
||||
|
||||
let result = this.configurationService.inspect('editor.wrappingColumn');
|
||||
if (typeof result.value === 'undefined') {
|
||||
// Setting is not used
|
||||
return;
|
||||
}
|
||||
|
||||
const storage = new WordWrapMigrationStorage(this.storageService);
|
||||
const storedData = storage.get();
|
||||
if (storedData && storedData.dontShowPrompt) {
|
||||
// Do not prompt stored
|
||||
return;
|
||||
}
|
||||
|
||||
let isUserSetting = (typeof result.user !== 'undefined');
|
||||
this._prompt(storage, isUserSetting);
|
||||
}
|
||||
|
||||
private _prompt(storage: WordWrapMigrationStorage, userSettings: boolean): void {
|
||||
const okAction = new Action(
|
||||
'wordWrapMigration.ok',
|
||||
nls.localize('wordWrapMigration.ok', "OK"),
|
||||
null,
|
||||
true,
|
||||
() => TPromise.as(true)
|
||||
);
|
||||
const dontShowAgainAction = new Action(
|
||||
'wordWrapMigration.dontShowAgain',
|
||||
nls.localize('wordWrapMigration.dontShowAgain', "Don't show again"),
|
||||
null,
|
||||
true,
|
||||
() => {
|
||||
storage.set({
|
||||
dontShowPrompt: true
|
||||
});
|
||||
return TPromise.as(true);
|
||||
}
|
||||
);
|
||||
const openSettings = new Action(
|
||||
'wordWrapMigration.openSettings',
|
||||
nls.localize('wordWrapMigration.openSettings', "Open Settings"),
|
||||
null,
|
||||
true,
|
||||
() => {
|
||||
if (userSettings) {
|
||||
this.preferencesService.openGlobalSettings();
|
||||
} else {
|
||||
this.preferencesService.openWorkspaceSettings();
|
||||
}
|
||||
return TPromise.as(true);
|
||||
}
|
||||
);
|
||||
this.messageService.show(Severity.Info, {
|
||||
message: nls.localize('wordWrapMigration.prompt', "The setting `editor.wrappingColumn` has been deprecated in favor of `editor.wordWrap`."),
|
||||
actions: [okAction, openSettings, dontShowAgainAction]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(WordWrapMigrationController);
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IExpression, IDebugService, IEnablement } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Expression, FunctionBreakpoint, Variable } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { Expression, Variable } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ITree, ContextMenuEvent, IActionProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
@@ -13,14 +13,15 @@ import { InputBox, IInputValidationOptions } from 'vs/base/browser/ui/inputbox/i
|
||||
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
|
||||
import { ClickBehavior, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IControllerOptions } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
|
||||
export const twistiePixels = 20;
|
||||
@@ -135,7 +136,6 @@ export function renderRenameBox(debugService: IDebugService, contextViewService:
|
||||
});
|
||||
const styler = attachInputBoxStyler(inputBox, themeService);
|
||||
|
||||
tree.setHighlight();
|
||||
inputBox.value = options.initialValue ? options.initialValue : '';
|
||||
inputBox.focus();
|
||||
inputBox.select();
|
||||
@@ -147,13 +147,11 @@ export function renderRenameBox(debugService: IDebugService, contextViewService:
|
||||
if (!disposed) {
|
||||
disposed = true;
|
||||
if (element instanceof Expression && renamed && inputBox.value) {
|
||||
debugService.renameWatchExpression(element.getId(), inputBox.value).done(null, onUnexpectedError);
|
||||
debugService.renameWatchExpression(element.getId(), inputBox.value);
|
||||
debugService.getViewModel().setSelectedExpression(undefined);
|
||||
} else if (element instanceof Expression && !element.name) {
|
||||
debugService.removeWatchExpressions(element.getId());
|
||||
} else if (element instanceof FunctionBreakpoint && inputBox.value) {
|
||||
debugService.renameFunctionBreakpoint(element.getId(), renamed ? inputBox.value : element.name).done(null, onUnexpectedError);
|
||||
} else if (element instanceof FunctionBreakpoint && !element.name) {
|
||||
debugService.removeFunctionBreakpoints(element.getId()).done(null, onUnexpectedError);
|
||||
debugService.getViewModel().setSelectedExpression(undefined);
|
||||
} else if (element instanceof Variable) {
|
||||
element.errorMessage = null;
|
||||
if (renamed && element.value !== inputBox.value) {
|
||||
@@ -161,12 +159,12 @@ export function renderRenameBox(debugService: IDebugService, contextViewService:
|
||||
// if everything went fine we need to refresh ui elements since the variable update can change watch and variables view
|
||||
.done(() => {
|
||||
tree.refresh(element, false);
|
||||
debugService.evaluateWatchExpressions();
|
||||
// Need to force watch expressions to update since a variable change can have an effect on watches
|
||||
debugService.focusStackFrame(debugService.getViewModel().focusedStackFrame);
|
||||
}, onUnexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
tree.clearHighlight();
|
||||
tree.DOMFocus();
|
||||
tree.setFocus(element);
|
||||
|
||||
@@ -190,20 +188,21 @@ export function renderRenameBox(debugService: IDebugService, contextViewService:
|
||||
}));
|
||||
}
|
||||
|
||||
export class BaseDebugController extends DefaultController {
|
||||
export class BaseDebugController extends WorkbenchTreeController {
|
||||
|
||||
private contributedContextMenu: IMenu;
|
||||
|
||||
constructor(
|
||||
private actionProvider: IActionProvider,
|
||||
menuId: MenuId,
|
||||
options: IControllerOptions,
|
||||
@IDebugService protected debugService: IDebugService,
|
||||
@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IMenuService menuService: IMenuService
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false });
|
||||
super(options, configurationService);
|
||||
|
||||
this.contributedContextMenu = menuService.createMenu(menuId, contextKeyService);
|
||||
}
|
||||
@@ -225,7 +224,7 @@ export class BaseDebugController extends DefaultController {
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.actionProvider.getSecondaryActions(tree, element).then(actions => {
|
||||
fillInActions(this.contributedContextMenu, { arg: this.getContext(element) }, actions);
|
||||
fillInActions(this.contributedContextMenu, { arg: this.getContext(element) }, actions, this.contextMenuService);
|
||||
return actions;
|
||||
}),
|
||||
onHide: (wasCancelled?: boolean) => {
|
||||
@@ -15,7 +15,7 @@ import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IDebugService, IBreakpoint, IRawBreakpoint } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IDebugService, IBreakpoint } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
@@ -71,7 +71,7 @@ export class BreakpointWidget extends ZoneWidget {
|
||||
|
||||
this.hitCountContext = breakpoint && breakpoint.hitCondition && !breakpoint.condition;
|
||||
const selected = this.hitCountContext ? 1 : 0;
|
||||
const selectBox = new SelectBox([nls.localize('expression', "Expression"), nls.localize('hitCount', "Hit Count")], selected);
|
||||
const selectBox = new SelectBox([nls.localize('expression', "Expression"), nls.localize('hitCount', "Hit Count")], selected, this.contextViewService);
|
||||
this.toDispose.push(attachSelectBoxStyler(selectBox, this.themeService));
|
||||
selectBox.render(dom.append(container, $('.breakpoint-select-container')));
|
||||
selectBox.onDidSelect(e => {
|
||||
@@ -109,31 +109,38 @@ export class BreakpointWidget extends ZoneWidget {
|
||||
const oldBreakpoint = this.debugService.getModel().getBreakpoints()
|
||||
.filter(bp => bp.lineNumber === this.lineNumber && bp.column === this.column && bp.uri.toString() === uri.toString()).pop();
|
||||
|
||||
const raw: IRawBreakpoint = {
|
||||
lineNumber: this.lineNumber,
|
||||
column: oldBreakpoint ? oldBreakpoint.column : undefined,
|
||||
enabled: true,
|
||||
condition: oldBreakpoint && oldBreakpoint.condition,
|
||||
hitCondition: oldBreakpoint && oldBreakpoint.hitCondition
|
||||
};
|
||||
let condition = oldBreakpoint && oldBreakpoint.condition;
|
||||
let hitCondition = oldBreakpoint && oldBreakpoint.hitCondition;
|
||||
|
||||
if (this.hitCountContext) {
|
||||
raw.hitCondition = this.inputBox.value;
|
||||
hitCondition = this.inputBox.value;
|
||||
if (this.conditionInput) {
|
||||
raw.condition = this.conditionInput;
|
||||
condition = this.conditionInput;
|
||||
}
|
||||
} else {
|
||||
raw.condition = this.inputBox.value;
|
||||
condition = this.inputBox.value;
|
||||
if (this.hitCountInput) {
|
||||
raw.hitCondition = this.hitCountInput;
|
||||
hitCondition = this.hitCountInput;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldBreakpoint) {
|
||||
this.debugService.removeBreakpoints(oldBreakpoint.getId()).done(null, errors.onUnexpectedError);
|
||||
this.debugService.updateBreakpoints(oldBreakpoint.uri, {
|
||||
[oldBreakpoint.getId()]: {
|
||||
condition,
|
||||
hitCondition,
|
||||
verified: oldBreakpoint.verified
|
||||
}
|
||||
}, false);
|
||||
} else {
|
||||
this.debugService.addBreakpoints(uri, [{
|
||||
lineNumber: this.lineNumber,
|
||||
column: oldBreakpoint ? oldBreakpoint.column : undefined,
|
||||
enabled: true,
|
||||
condition,
|
||||
hitCondition
|
||||
}]).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
this.debugService.addBreakpoints(uri, [raw]).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
this.dispose();
|
||||
|
||||
@@ -26,13 +26,14 @@ import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IDelegate, IListContextMenuEvent, IRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import { IEditorService, IEditor } from 'vs/platform/editor/common/editor';
|
||||
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { WorkbenchList, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchList } from 'vs/platform/list/browser/listService';
|
||||
import { ViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -50,13 +51,12 @@ export class BreakpointsView extends ViewsViewletPanel {
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IListService private listService: IListService,
|
||||
@IThemeService private themeService: IThemeService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IContextViewService private contextViewService: IContextViewService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super(options, keybindingService, contextMenuService);
|
||||
super(options, keybindingService, contextMenuService, configurationService);
|
||||
|
||||
this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize();
|
||||
this.settings = options.viewletSettings;
|
||||
@@ -67,42 +67,41 @@ export class BreakpointsView extends ViewsViewletPanel {
|
||||
dom.addClass(container, 'debug-breakpoints');
|
||||
const delegate = new BreakpointsDelegate(this.debugService);
|
||||
|
||||
this.list = new WorkbenchList<IEnablement>(container, delegate, [
|
||||
this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [
|
||||
this.instantiationService.createInstance(BreakpointsRenderer),
|
||||
new ExceptionBreakpointsRenderer(this.debugService),
|
||||
new FunctionBreakpointsRenderer(this.debugService),
|
||||
this.instantiationService.createInstance(FunctionBreakpointsRenderer),
|
||||
new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService)
|
||||
], {
|
||||
identityProvider: element => element.getId(),
|
||||
multipleSelectionSupport: false
|
||||
}, this.contextKeyService, this.listService, this.themeService);
|
||||
}) as WorkbenchList<IEnablement>;
|
||||
|
||||
CONTEXT_BREAKPOINTS_FOCUSED.bindTo(this.list.contextKeyService);
|
||||
|
||||
this.list.onContextMenu(this.onListContextMenu, this, this.disposables);
|
||||
|
||||
const handleBreakpointFocus = (preserveFocuse: boolean, sideBySide: boolean, selectFunctionBreakpoint: boolean) => {
|
||||
this.disposables.push(this.list.onOpen(e => {
|
||||
let isSingleClick = false;
|
||||
let isDoubleClick = false;
|
||||
let openToSide = false;
|
||||
|
||||
const browserEvent = e.browserEvent;
|
||||
if (browserEvent instanceof MouseEvent) {
|
||||
isSingleClick = browserEvent.detail === 1;
|
||||
isDoubleClick = browserEvent.detail === 2;
|
||||
openToSide = (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey);
|
||||
}
|
||||
|
||||
const focused = this.list.getFocusedElements();
|
||||
const element = focused.length ? focused[0] : undefined;
|
||||
if (element instanceof Breakpoint) {
|
||||
openBreakpointSource(element, sideBySide, preserveFocuse, this.debugService, this.editorService).done(undefined, onUnexpectedError);
|
||||
openBreakpointSource(element, openToSide, isSingleClick, this.debugService, this.editorService).done(undefined, onUnexpectedError);
|
||||
}
|
||||
if (selectFunctionBreakpoint && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) {
|
||||
if (isDoubleClick && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) {
|
||||
this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
|
||||
this.onBreakpointsChange();
|
||||
}
|
||||
};
|
||||
this.disposables.push(this.list.onKeyUp(e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
if (event.equals(KeyCode.Enter)) {
|
||||
handleBreakpointFocus(false, event && (event.ctrlKey || event.metaKey), false);
|
||||
}
|
||||
}));
|
||||
this.disposables.push(this.list.onMouseDblClick(e => {
|
||||
handleBreakpointFocus(false, false, true);
|
||||
}));
|
||||
this.disposables.push(this.list.onMouseClick(e => {
|
||||
handleBreakpointFocus(true, false, false);
|
||||
}));
|
||||
|
||||
this.list.splice(0, this.list.length, this.elements);
|
||||
@@ -118,14 +117,20 @@ export class BreakpointsView extends ViewsViewletPanel {
|
||||
const actions: IAction[] = [];
|
||||
const element = e.element;
|
||||
|
||||
if (element instanceof Breakpoint) {
|
||||
if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) {
|
||||
actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editConditionalBreakpoint', "Edit Breakpoint..."), undefined, true, () => {
|
||||
return openBreakpointSource(element, false, false, this.debugService, this.editorService).then(editor => {
|
||||
const codeEditor = editor.getControl();
|
||||
if (isCodeEditor(codeEditor)) {
|
||||
codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
|
||||
}
|
||||
});
|
||||
if (element instanceof Breakpoint) {
|
||||
return openBreakpointSource(element, false, false, this.debugService, this.editorService).then(editor => {
|
||||
const codeEditor = editor.getControl();
|
||||
if (isCodeEditor(codeEditor)) {
|
||||
codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
|
||||
this.onBreakpointsChange();
|
||||
return undefined;
|
||||
}));
|
||||
actions.push(new Separator());
|
||||
}
|
||||
@@ -245,7 +250,11 @@ interface IBaseBreakpointTemplateData {
|
||||
toDispose: IDisposable[];
|
||||
}
|
||||
|
||||
interface IBreakpointTemplateData extends IBaseBreakpointTemplateData {
|
||||
interface IBaseBreakpointWithIconTemplateData extends IBaseBreakpointTemplateData {
|
||||
icon: HTMLElement;
|
||||
}
|
||||
|
||||
interface IBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {
|
||||
lineNumber: HTMLElement;
|
||||
filePath: HTMLElement;
|
||||
}
|
||||
@@ -262,12 +271,13 @@ class BreakpointsRenderer implements IRenderer<IBreakpoint, IBreakpointTemplateD
|
||||
constructor(
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@ITextFileService private textFileService: ITextFileService
|
||||
) {
|
||||
// noop
|
||||
}
|
||||
|
||||
static ID = 'breakpoints';
|
||||
static readonly ID = 'breakpoints';
|
||||
|
||||
get templateId() {
|
||||
return BreakpointsRenderer.ID;
|
||||
@@ -277,6 +287,7 @@ class BreakpointsRenderer implements IRenderer<IBreakpoint, IBreakpointTemplateD
|
||||
const data: IBreakpointTemplateData = Object.create(null);
|
||||
data.breakpoint = dom.append(container, $('.breakpoint'));
|
||||
|
||||
data.icon = $('.icon');
|
||||
data.checkbox = <HTMLInputElement>$('input');
|
||||
data.checkbox.type = 'checkbox';
|
||||
data.toDispose = [];
|
||||
@@ -284,6 +295,7 @@ class BreakpointsRenderer implements IRenderer<IBreakpoint, IBreakpointTemplateD
|
||||
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
|
||||
}));
|
||||
|
||||
dom.append(data.breakpoint, data.icon);
|
||||
dom.append(data.breakpoint, data.checkbox);
|
||||
|
||||
data.name = dom.append(data.breakpoint, $('span.name'));
|
||||
@@ -307,6 +319,10 @@ class BreakpointsRenderer implements IRenderer<IBreakpoint, IBreakpointTemplateD
|
||||
data.filePath.textContent = getPathLabel(resources.dirname(breakpoint.uri), this.contextService, this.environmentService);
|
||||
data.checkbox.checked = breakpoint.enabled;
|
||||
|
||||
const { message, className } = getBreakpointMessageAndClassName(this.debugService, this.textFileService, breakpoint);
|
||||
data.icon.className = className + ' icon';
|
||||
data.icon.title = message ? message : '';
|
||||
|
||||
const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;
|
||||
if (debugActive && !breakpoint.verified) {
|
||||
dom.addClass(data.breakpoint, 'disabled');
|
||||
@@ -331,7 +347,7 @@ class ExceptionBreakpointsRenderer implements IRenderer<IExceptionBreakpoint, IB
|
||||
// noop
|
||||
}
|
||||
|
||||
static ID = 'exceptionbreakpoints';
|
||||
static readonly ID = 'exceptionbreakpoints';
|
||||
|
||||
get templateId() {
|
||||
return ExceptionBreakpointsRenderer.ID;
|
||||
@@ -368,24 +384,26 @@ class ExceptionBreakpointsRenderer implements IRenderer<IExceptionBreakpoint, IB
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionBreakpointsRenderer implements IRenderer<IFunctionBreakpoint, IBaseBreakpointTemplateData> {
|
||||
class FunctionBreakpointsRenderer implements IRenderer<FunctionBreakpoint, IBaseBreakpointWithIconTemplateData> {
|
||||
|
||||
constructor(
|
||||
private debugService: IDebugService
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@ITextFileService private textFileService: ITextFileService
|
||||
) {
|
||||
// noop
|
||||
}
|
||||
|
||||
static ID = 'functionbreakpoints';
|
||||
static readonly ID = 'functionbreakpoints';
|
||||
|
||||
get templateId() {
|
||||
return FunctionBreakpointsRenderer.ID;
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): IBaseBreakpointTemplateData {
|
||||
renderTemplate(container: HTMLElement): IBaseBreakpointWithIconTemplateData {
|
||||
const data: IBreakpointTemplateData = Object.create(null);
|
||||
data.breakpoint = dom.append(container, $('.breakpoint'));
|
||||
|
||||
data.icon = $('.icon');
|
||||
data.checkbox = <HTMLInputElement>$('input');
|
||||
data.checkbox.type = 'checkbox';
|
||||
data.toDispose = [];
|
||||
@@ -393,6 +411,7 @@ class FunctionBreakpointsRenderer implements IRenderer<IFunctionBreakpoint, IBas
|
||||
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
|
||||
}));
|
||||
|
||||
dom.append(data.breakpoint, data.icon);
|
||||
dom.append(data.breakpoint, data.checkbox);
|
||||
|
||||
data.name = dom.append(data.breakpoint, $('span.name'));
|
||||
@@ -400,9 +419,12 @@ class FunctionBreakpointsRenderer implements IRenderer<IFunctionBreakpoint, IBas
|
||||
return data;
|
||||
}
|
||||
|
||||
renderElement(functionBreakpoint: IFunctionBreakpoint, index: number, data: IBaseBreakpointTemplateData): void {
|
||||
renderElement(functionBreakpoint: FunctionBreakpoint, index: number, data: IBaseBreakpointWithIconTemplateData): void {
|
||||
data.context = functionBreakpoint;
|
||||
data.name.textContent = functionBreakpoint.name;
|
||||
const { className, message } = getBreakpointMessageAndClassName(this.debugService, this.textFileService, functionBreakpoint);
|
||||
data.icon.className = className + ' icon';
|
||||
data.icon.title = message ? message : '';
|
||||
data.checkbox.checked = functionBreakpoint.enabled;
|
||||
data.breakpoint.title = functionBreakpoint.name;
|
||||
|
||||
@@ -414,7 +436,7 @@ class FunctionBreakpointsRenderer implements IRenderer<IFunctionBreakpoint, IBas
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IBaseBreakpointTemplateData): void {
|
||||
disposeTemplate(templateData: IBaseBreakpointWithIconTemplateData): void {
|
||||
dispose(templateData.toDispose);
|
||||
}
|
||||
}
|
||||
@@ -429,7 +451,7 @@ class FunctionBreakpointInputRenderer implements IRenderer<IFunctionBreakpoint,
|
||||
// noop
|
||||
}
|
||||
|
||||
static ID = 'functionbreakpointinput';
|
||||
static readonly ID = 'functionbreakpointinput';
|
||||
|
||||
get templateId() {
|
||||
return FunctionBreakpointInputRenderer.ID;
|
||||
@@ -467,7 +489,9 @@ class FunctionBreakpointInputRenderer implements IRenderer<IFunctionBreakpoint,
|
||||
}
|
||||
}));
|
||||
toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
|
||||
wrapUp(true);
|
||||
if (!template.breakpoint.name) {
|
||||
wrapUp(true);
|
||||
}
|
||||
}));
|
||||
|
||||
template.inputBox = inputBox;
|
||||
@@ -488,7 +512,7 @@ class FunctionBreakpointInputRenderer implements IRenderer<IFunctionBreakpoint,
|
||||
}
|
||||
}
|
||||
|
||||
function openBreakpointSource(breakpoint: Breakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): TPromise<IEditor> {
|
||||
export function openBreakpointSource(breakpoint: Breakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): TPromise<IEditor> {
|
||||
if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
@@ -516,3 +540,78 @@ function openBreakpointSource(breakpoint: Breakpoint, sideBySide: boolean, prese
|
||||
}
|
||||
}, sideBySide);
|
||||
}
|
||||
|
||||
export function getBreakpointMessageAndClassName(debugService: IDebugService, textFileService: ITextFileService, breakpoint: IBreakpoint | FunctionBreakpoint): { message?: string, className: string } {
|
||||
const state = debugService.state;
|
||||
const debugActive = state === State.Running || state === State.Stopped;
|
||||
|
||||
if (!breakpoint.enabled || !debugService.getModel().areBreakpointsActivated()) {
|
||||
return {
|
||||
className: breakpoint instanceof FunctionBreakpoint ? 'debug-function-breakpoint-disabled' : 'debug-breakpoint-disabled',
|
||||
message: nls.localize('breakpointDisabledHover', "Disabled breakpoint"),
|
||||
};
|
||||
}
|
||||
|
||||
const appendMessage = (text: string): string => {
|
||||
return !(breakpoint instanceof FunctionBreakpoint) && breakpoint.message ? text.concat(', ' + breakpoint.message) : text;
|
||||
};
|
||||
if (debugActive && !breakpoint.verified) {
|
||||
return {
|
||||
className: breakpoint instanceof FunctionBreakpoint ? 'debug-function-breakpoint-unverified' : 'debug-breakpoint-unverified',
|
||||
message: appendMessage(nls.localize('breakpointUnverifieddHover', "Unverified breakpoint")),
|
||||
};
|
||||
}
|
||||
|
||||
const process = debugService.getViewModel().focusedProcess;
|
||||
if (breakpoint instanceof FunctionBreakpoint) {
|
||||
if (process && !process.session.capabilities.supportsFunctionBreakpoints) {
|
||||
return {
|
||||
className: 'debug-function-breakpoint-unverified',
|
||||
message: nls.localize('functionBreakpointUnsupported', "Function breakpoints not supported by this debug type"),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
className: 'debug-function-breakpoint',
|
||||
};
|
||||
}
|
||||
|
||||
if (debugActive && textFileService.isDirty(breakpoint.uri)) {
|
||||
return {
|
||||
className: 'debug-breakpoint-unverified',
|
||||
message: appendMessage(nls.localize('breakpointDirtydHover', "Unverified breakpoint. File is modified, please restart debug session.")),
|
||||
};
|
||||
}
|
||||
|
||||
if (breakpoint.condition || breakpoint.hitCondition) {
|
||||
if (process && breakpoint.condition && !process.session.capabilities.supportsConditionalBreakpoints) {
|
||||
return {
|
||||
className: 'debug-breakpoint-unsupported',
|
||||
message: nls.localize('conditionalBreakpointUnsupported', "Conditional breakpoints not supported by this debug type"),
|
||||
};
|
||||
}
|
||||
if (process && breakpoint.hitCondition && !process.session.capabilities.supportsHitConditionalBreakpoints) {
|
||||
return {
|
||||
className: 'debug-breakpoint-unsupported',
|
||||
message: nls.localize('hitBreakpointUnsupported', "Hit conditional breakpoints not supported by this debug type"),
|
||||
};
|
||||
}
|
||||
|
||||
if (breakpoint.condition && breakpoint.hitCondition) {
|
||||
return {
|
||||
className: 'debug-breakpoint-conditional',
|
||||
message: appendMessage(`Expression: ${breakpoint.condition}\nHitCount: ${breakpoint.hitCondition}`)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
className: 'debug-breakpoint-conditional',
|
||||
message: appendMessage(breakpoint.condition ? breakpoint.condition : breakpoint.hitCondition)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
className: 'debug-breakpoint',
|
||||
message: breakpoint.message
|
||||
};
|
||||
}
|
||||
@@ -19,6 +19,8 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { selectBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -40,10 +42,12 @@ export class StartDebugActionItem implements IActionItem {
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IThemeService private themeService: IThemeService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@ICommandService private commandService: ICommandService
|
||||
@ICommandService private commandService: ICommandService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IContextViewService contextViewService: IContextViewService,
|
||||
) {
|
||||
this.toDispose = [];
|
||||
this.selectBox = new SelectBox([], -1);
|
||||
this.selectBox = new SelectBox([], -1, contextViewService);
|
||||
this.toDispose.push(attachSelectBoxStyler(this.selectBox, themeService, {
|
||||
selectBackground: SIDE_BAR_BACKGROUND
|
||||
}));
|
||||
@@ -151,12 +155,13 @@ export class StartDebugActionItem implements IActionItem {
|
||||
this.options = [];
|
||||
const manager = this.debugService.getConfigurationManager();
|
||||
const launches = manager.getLaunches();
|
||||
manager.getLaunches().forEach(launch =>
|
||||
const inWorkspace = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE;
|
||||
launches.forEach(launch =>
|
||||
launch.getConfigurationNames().forEach(name => {
|
||||
if (name === manager.selectedName && launch === manager.selectedLaunch) {
|
||||
if (name === manager.selectedConfiguration.name && launch === manager.selectedConfiguration.launch) {
|
||||
this.selected = this.options.length;
|
||||
}
|
||||
const label = launches.length > 1 ? `${name} (${launch.workspace.name})` : name;
|
||||
const label = inWorkspace ? `${name} (${launch.name})` : name;
|
||||
this.options.push({ label, handler: () => { manager.selectConfiguration(launch, name); return true; } });
|
||||
}));
|
||||
|
||||
@@ -166,11 +171,11 @@ export class StartDebugActionItem implements IActionItem {
|
||||
this.options.push({ label: StartDebugActionItem.SEPARATOR, handler: undefined });
|
||||
|
||||
const disabledIdx = this.options.length - 1;
|
||||
launches.forEach(l => {
|
||||
const label = launches.length > 1 ? nls.localize("addConfigTo", "Add Config ({0})...", l.workspace.name) : nls.localize('addConfiguration', "Add Configuration...");
|
||||
launches.filter(l => !l.hidden).forEach(l => {
|
||||
const label = inWorkspace ? nls.localize("addConfigTo", "Add Config ({0})...", l.name) : nls.localize('addConfiguration', "Add Configuration...");
|
||||
this.options.push({
|
||||
label, handler: () => {
|
||||
this.commandService.executeCommand('debug.addConfiguration', l.workspace.uri.toString()).done(undefined, errors.onUnexpectedError);
|
||||
this.commandService.executeCommand('debug.addConfiguration', l.uri.toString()).done(undefined, errors.onUnexpectedError);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@@ -184,9 +189,10 @@ export class FocusProcessActionItem extends SelectActionItem {
|
||||
constructor(
|
||||
action: IAction,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextViewService contextViewService: IContextViewService
|
||||
) {
|
||||
super(null, action, [], -1);
|
||||
super(null, action, [], -1, contextViewService);
|
||||
|
||||
this.toDispose.push(attachSelectBoxStyler(this.selectBox, themeService));
|
||||
|
||||
|
||||
@@ -6,14 +6,12 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import * as lifecycle from 'vs/base/common/lifecycle';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IDebugService, State, IProcess, IThread, IEnablement, IBreakpoint, IStackFrame, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, IExpression, REPL_ID, ProcessState }
|
||||
from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Variable, Expression, Thread, Breakpoint, Process } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
@@ -22,6 +20,9 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { TogglePanelAction } from 'vs/workbench/browser/panel';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { CollapseAction } from 'vs/workbench/browser/viewlet';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
|
||||
export abstract class AbstractDebugAction extends Action {
|
||||
|
||||
@@ -71,13 +72,14 @@ export abstract class AbstractDebugAction extends Action {
|
||||
}
|
||||
|
||||
export class ConfigureAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.configure';
|
||||
static readonly ID = 'workbench.action.debug.configure';
|
||||
static LABEL = nls.localize('openLaunchJson', "Open {0}", 'launch.json');
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IDebugService debugService: IDebugService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IMessageService private messageService: IMessageService
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(id, label, 'debug-action configure', debugService, keybindingService);
|
||||
this.toDispose.push(debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateClass()));
|
||||
@@ -85,7 +87,7 @@ export class ConfigureAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
public get tooltip(): string {
|
||||
if (this.debugService.getConfigurationManager().selectedName) {
|
||||
if (this.debugService.getConfigurationManager().selectedConfiguration.name) {
|
||||
return ConfigureAction.LABEL;
|
||||
}
|
||||
|
||||
@@ -93,17 +95,17 @@ export class ConfigureAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
private updateClass(): void {
|
||||
this.class = this.debugService.getConfigurationManager().selectedName ? 'debug-action configure' : 'debug-action configure notification';
|
||||
this.class = this.debugService.getConfigurationManager().selectedConfiguration.name ? 'debug-action configure' : 'debug-action configure notification';
|
||||
}
|
||||
|
||||
public run(event?: any): TPromise<any> {
|
||||
if (!this.debugService.getConfigurationManager().selectedLaunch) {
|
||||
this.messageService.show(severity.Info, nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration."));
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
this.notificationService.info(nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration."));
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
const sideBySide = !!(event && (event.ctrlKey || event.metaKey));
|
||||
return this.debugService.getConfigurationManager().selectedLaunch.openConfigFile(sideBySide);
|
||||
return this.debugService.getConfigurationManager().selectedConfiguration.launch.openConfigFile(sideBySide);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +126,8 @@ export class StartAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
const launch = this.debugService.getConfigurationManager().selectedLaunch;
|
||||
return this.debugService.startDebugging(launch ? launch.workspace : undefined, undefined, this.isNoDebug());
|
||||
const launch = this.debugService.getConfigurationManager().selectedConfiguration.launch;
|
||||
return this.debugService.startDebugging(launch, undefined, this.isNoDebug());
|
||||
}
|
||||
|
||||
protected isNoDebug(): boolean {
|
||||
@@ -134,7 +136,7 @@ export class StartAction extends AbstractDebugAction {
|
||||
|
||||
public static isEnabled(debugService: IDebugService, contextService: IWorkspaceContextService, configName: string) {
|
||||
const processes = debugService.getModel().getProcesses();
|
||||
const launch = debugService.getConfigurationManager().selectedLaunch;
|
||||
const launch = debugService.getConfigurationManager().selectedConfiguration.launch;
|
||||
|
||||
if (debugService.state === State.Initializing) {
|
||||
return false;
|
||||
@@ -142,7 +144,7 @@ export class StartAction extends AbstractDebugAction {
|
||||
if (contextService && contextService.getWorkbenchState() === WorkbenchState.EMPTY && processes.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (processes.some(p => p.getName(false) === configName && (!launch || p.session.root.uri.toString() === launch.workspace.uri.toString()))) {
|
||||
if (processes.some(p => p.getName(false) === configName && (!launch || !launch.workspace || !p.session.root || p.session.root.uri.toString() === launch.workspace.uri.toString()))) {
|
||||
return false;
|
||||
}
|
||||
const compound = launch && launch.getCompound(configName);
|
||||
@@ -155,12 +157,12 @@ export class StartAction extends AbstractDebugAction {
|
||||
|
||||
// Disabled if the launch drop down shows the launch config that is already running.
|
||||
protected isEnabled(state: State): boolean {
|
||||
return StartAction.isEnabled(this.debugService, this.contextService, this.debugService.getConfigurationManager().selectedName);
|
||||
return StartAction.isEnabled(this.debugService, this.contextService, this.debugService.getConfigurationManager().selectedConfiguration.name);
|
||||
}
|
||||
}
|
||||
|
||||
export class RunAction extends StartAction {
|
||||
static ID = 'workbench.action.debug.run';
|
||||
static readonly ID = 'workbench.action.debug.run';
|
||||
static LABEL = nls.localize('startWithoutDebugging', "Start Without Debugging");
|
||||
|
||||
protected isNoDebug(): boolean {
|
||||
@@ -169,7 +171,7 @@ export class RunAction extends StartAction {
|
||||
}
|
||||
|
||||
export class SelectAndStartAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.selectandstart';
|
||||
static readonly ID = 'workbench.action.debug.selectandstart';
|
||||
static LABEL = nls.localize('selectAndStartDebugging', "Select and Start Debugging");
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@@ -190,7 +192,7 @@ export class SelectAndStartAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class RestartAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.restart';
|
||||
static readonly ID = 'workbench.action.debug.restart';
|
||||
static LABEL = nls.localize('restartDebug', "Restart");
|
||||
static RECONNECT_LABEL = nls.localize('reconnectDebug', "Reconnect");
|
||||
|
||||
@@ -224,7 +226,7 @@ export class RestartAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class StepOverAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.stepOver';
|
||||
static readonly ID = 'workbench.action.debug.stepOver';
|
||||
static LABEL = nls.localize('stepOverDebug', "Step Over");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -245,7 +247,7 @@ export class StepOverAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class StepIntoAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.stepInto';
|
||||
static readonly ID = 'workbench.action.debug.stepInto';
|
||||
static LABEL = nls.localize('stepIntoDebug', "Step Into");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -266,7 +268,7 @@ export class StepIntoAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class StepOutAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.stepOut';
|
||||
static readonly ID = 'workbench.action.debug.stepOut';
|
||||
static LABEL = nls.localize('stepOutDebug', "Step Out");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -287,7 +289,7 @@ export class StepOutAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class StopAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.stop';
|
||||
static readonly ID = 'workbench.action.debug.stop';
|
||||
static LABEL = nls.localize('stopDebug', "Stop");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -308,7 +310,7 @@ export class StopAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class DisconnectAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.disconnect';
|
||||
static readonly ID = 'workbench.action.debug.disconnect';
|
||||
static LABEL = nls.localize('disconnectDebug', "Disconnect");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -326,7 +328,7 @@ export class DisconnectAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class ContinueAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.continue';
|
||||
static readonly ID = 'workbench.action.debug.continue';
|
||||
static LABEL = nls.localize('continueDebug', "Continue");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -347,7 +349,7 @@ export class ContinueAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class PauseAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.pause';
|
||||
static readonly ID = 'workbench.action.debug.pause';
|
||||
static LABEL = nls.localize('pauseDebug', "Pause");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -368,7 +370,7 @@ export class PauseAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class RestartFrameAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.restartFrame';
|
||||
static readonly ID = 'workbench.action.debug.restartFrame';
|
||||
static LABEL = nls.localize('restartFrame', "Restart Frame");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -385,7 +387,7 @@ export class RestartFrameAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class RemoveBreakpointAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.removeBreakpoint';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.removeBreakpoint';
|
||||
static LABEL = nls.localize('removeBreakpoint', "Remove Breakpoint");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -399,7 +401,7 @@ export class RemoveBreakpointAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class RemoveAllBreakpointsAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.removeAllBreakpoints';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.removeAllBreakpoints';
|
||||
static LABEL = nls.localize('removeAllBreakpoints', "Remove All Breakpoints");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -418,7 +420,7 @@ export class RemoveAllBreakpointsAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class EnableBreakpointAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.enableBreakpoint';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.enableBreakpoint';
|
||||
static LABEL = nls.localize('enableBreakpoint', "Enable Breakpoint");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -431,7 +433,7 @@ export class EnableBreakpointAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class DisableBreakpointAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.disableBreakpoint';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.disableBreakpoint';
|
||||
static LABEL = nls.localize('disableBreakpoint', "Disable Breakpoint");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -444,7 +446,7 @@ export class DisableBreakpointAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class EnableAllBreakpointsAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.enableAllBreakpoints';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.enableAllBreakpoints';
|
||||
static LABEL = nls.localize('enableAllBreakpoints', "Enable All Breakpoints");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -463,7 +465,7 @@ export class EnableAllBreakpointsAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class DisableAllBreakpointsAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.disableAllBreakpoints';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.disableAllBreakpoints';
|
||||
static LABEL = nls.localize('disableAllBreakpoints', "Disable All Breakpoints");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -482,7 +484,7 @@ export class DisableAllBreakpointsAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class ToggleBreakpointsActivatedAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.toggleBreakpointsActivatedAction';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.toggleBreakpointsActivatedAction';
|
||||
static ACTIVATE_LABEL = nls.localize('activateBreakpoints', "Activate Breakpoints");
|
||||
static DEACTIVATE_LABEL = nls.localize('deactivateBreakpoints', "Deactivate Breakpoints");
|
||||
|
||||
@@ -506,7 +508,7 @@ export class ToggleBreakpointsActivatedAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class ReapplyBreakpointsAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.reapplyBreakpointsAction';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.reapplyBreakpointsAction';
|
||||
static LABEL = nls.localize('reapplyAllBreakpoints', "Reapply All Breakpoints");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -526,7 +528,7 @@ export class ReapplyBreakpointsAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class AddFunctionBreakpointAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.addFunctionBreakpointAction';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.addFunctionBreakpointAction';
|
||||
static LABEL = nls.localize('addFunctionBreakpoint', "Add Function Breakpoint");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -546,7 +548,7 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class AddConditionalBreakpointAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.addConditionalBreakpointAction';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.addConditionalBreakpointAction';
|
||||
static LABEL = nls.localize('addConditionalBreakpoint', "Add Conditional Breakpoint...");
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@@ -565,7 +567,7 @@ export class AddConditionalBreakpointAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class EditConditionalBreakpointAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.editConditionalBreakpointAction';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.editConditionalBreakpointAction';
|
||||
static LABEL = nls.localize('editConditionalBreakpoint', "Edit Breakpoint...");
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@@ -584,7 +586,7 @@ export class EditConditionalBreakpointAction extends AbstractDebugAction {
|
||||
|
||||
|
||||
export class SetValueAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.setValue';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.setValue';
|
||||
static LABEL = nls.localize('setValue', "Set Value");
|
||||
|
||||
constructor(id: string, label: string, private variable: Variable, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -607,7 +609,7 @@ export class SetValueAction extends AbstractDebugAction {
|
||||
|
||||
|
||||
export class AddWatchExpressionAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.addWatchExpression';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.addWatchExpression';
|
||||
static LABEL = nls.localize('addWatchExpression', "Add Expression");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -616,7 +618,8 @@ export class AddWatchExpressionAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
return this.debugService.addWatchExpression();
|
||||
this.debugService.addWatchExpression();
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
|
||||
protected isEnabled(state: State): boolean {
|
||||
@@ -625,7 +628,7 @@ export class AddWatchExpressionAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class EditWatchExpressionAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.editWatchExpression';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.editWatchExpression';
|
||||
static LABEL = nls.localize('editWatchExpression', "Edit Expression");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -639,7 +642,7 @@ export class EditWatchExpressionAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class AddToWatchExpressionsAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.addToWatchExpressions';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.addToWatchExpressions';
|
||||
static LABEL = nls.localize('addToWatchExpressions', "Add to Watch");
|
||||
|
||||
constructor(id: string, label: string, private expression: IExpression, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -648,12 +651,14 @@ export class AddToWatchExpressionsAction extends AbstractDebugAction {
|
||||
|
||||
public run(): TPromise<any> {
|
||||
const name = this.expression instanceof Variable ? this.expression.evaluateName : this.expression.name;
|
||||
return this.debugService.addWatchExpression(name);
|
||||
this.debugService.addWatchExpression(name);
|
||||
return TPromise.as(undefined);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoveWatchExpressionAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.removeWatchExpression';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.removeWatchExpression';
|
||||
static LABEL = nls.localize('removeWatchExpression', "Remove Expression");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -667,7 +672,7 @@ export class RemoveWatchExpressionAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class RemoveAllWatchExpressionsAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.removeAllWatchExpressions';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.removeAllWatchExpressions';
|
||||
static LABEL = nls.localize('removeAllWatchExpressions', "Remove All Expressions");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -686,7 +691,7 @@ export class RemoveAllWatchExpressionsAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class ClearReplAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.panel.action.clearReplAction';
|
||||
static readonly ID = 'workbench.debug.panel.action.clearReplAction';
|
||||
static LABEL = nls.localize('clearRepl', "Clear Console");
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@@ -706,7 +711,7 @@ export class ClearReplAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class ToggleReplAction extends TogglePanelAction {
|
||||
static ID = 'workbench.debug.action.toggleRepl';
|
||||
static readonly ID = 'workbench.debug.action.toggleRepl';
|
||||
static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugConsoleAction' }, 'Debug Console');
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
|
||||
@@ -748,7 +753,7 @@ export class ToggleReplAction extends TogglePanelAction {
|
||||
|
||||
export class FocusReplAction extends Action {
|
||||
|
||||
static ID = 'workbench.debug.action.focusRepl';
|
||||
static readonly ID = 'workbench.debug.action.focusRepl';
|
||||
static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusConsole' }, 'Focus Debug Console');
|
||||
|
||||
|
||||
@@ -764,7 +769,7 @@ export class FocusReplAction extends Action {
|
||||
}
|
||||
|
||||
export class FocusProcessAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.focusProcess';
|
||||
static readonly ID = 'workbench.action.debug.focusProcess';
|
||||
static LABEL = nls.localize('focusProcess', "Focus Process");
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@@ -778,19 +783,19 @@ export class FocusProcessAction extends AbstractDebugAction {
|
||||
public run(processName: string): TPromise<any> {
|
||||
const isMultiRoot = this.debugService.getConfigurationManager().getLaunches().length > 1;
|
||||
const process = this.debugService.getModel().getProcesses().filter(p => p.getName(isMultiRoot) === processName).pop();
|
||||
return this.debugService.focusStackFrameAndEvaluate(null, process, true).then(() => {
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
if (stackFrame) {
|
||||
return stackFrame.openInEditor(this.editorService, true);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
this.debugService.focusStackFrame(undefined, undefined, process, true);
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
if (stackFrame) {
|
||||
return stackFrame.openInEditor(this.editorService, true);
|
||||
}
|
||||
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
// Actions used by the chakra debugger
|
||||
export class StepBackAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.stepBack';
|
||||
static readonly ID = 'workbench.action.debug.stepBack';
|
||||
static LABEL = nls.localize('stepBackDebug', "Step Back");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -813,7 +818,7 @@ export class StepBackAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
export class ReverseContinueAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.action.debug.reverseContinue';
|
||||
static readonly ID = 'workbench.action.debug.reverseContinue';
|
||||
static LABEL = nls.localize('reverseContinue', "Reverse");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
@@ -834,3 +839,15 @@ export class ReverseContinueAction extends AbstractDebugAction {
|
||||
process && process.session.capabilities.supportsStepBack;
|
||||
}
|
||||
}
|
||||
|
||||
export class ReplCollapseAllAction extends CollapseAction {
|
||||
constructor(viewer: ITree, private toFocus: { focus(): void; }) {
|
||||
super(viewer, true, undefined);
|
||||
}
|
||||
|
||||
public run(event?: any): TPromise<any> {
|
||||
return super.run(event).then(() => {
|
||||
this.toFocus.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!vs/workbench/parts/debug/browser/media/debugActionsWidget';
|
||||
import 'vs/css!./media/debugActionsWidget';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import * as builder from 'vs/base/browser/builder';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
@@ -21,7 +20,6 @@ import { AbstractDebugAction, PauseAction, ContinueAction, StepBackAction, Rever
|
||||
import { FocusProcessActionItem } from 'vs/workbench/parts/debug/browser/debugActionItems';
|
||||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Themable } from 'vs/workbench/common/theme';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
@@ -29,6 +27,8 @@ import { registerColor, contrastBorder, widgetShadow } from 'vs/platform/theme/c
|
||||
import { localize } from 'vs/nls';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
const $ = builder.$;
|
||||
const DEBUG_ACTIONS_WIDGET_POSITION_KEY = 'debug.actionswidgetposition';
|
||||
@@ -38,6 +38,11 @@ export const debugToolBarBackground = registerColor('debugToolBar.background', {
|
||||
light: '#F3F3F3',
|
||||
hc: '#000000'
|
||||
}, localize('debugToolBarBackground', "Debug toolbar background color."));
|
||||
export const debugToolBarBorder = registerColor('debugToolBar.border', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: null
|
||||
}, localize('debugToolBarBorder', "Debug toolbar border color."));
|
||||
|
||||
export class DebugActionsWidget extends Themable implements IWorkbenchContribution {
|
||||
|
||||
@@ -51,7 +56,7 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
|
||||
private isBuilt: boolean;
|
||||
|
||||
constructor(
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IPartService private partService: IPartService,
|
||||
@@ -59,7 +64,8 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IContextViewService contextViewService: IContextViewService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
@@ -75,7 +81,7 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
|
||||
orientation: ActionsOrientation.HORIZONTAL,
|
||||
actionItemProvider: (action: IAction) => {
|
||||
if (action.id === FocusProcessAction.ID) {
|
||||
return new FocusProcessActionItem(action, this.debugService, this.themeService);
|
||||
return new FocusProcessActionItem(action, this.debugService, this.themeService, contextViewService);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -97,7 +103,7 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
|
||||
this.toUnbind.push(this.actionBar.actionRunner.onDidRun((e: IRunEvent) => {
|
||||
// check for error
|
||||
if (e.error && !errors.isPromiseCanceledError(e.error)) {
|
||||
this.messageService.show(severity.Error, e.error);
|
||||
this.notificationService.error(e.error);
|
||||
}
|
||||
|
||||
// log in telemetry
|
||||
@@ -159,9 +165,16 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
|
||||
this.$el.style('box-shadow', widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : null);
|
||||
|
||||
const contrastBorderColor = this.getColor(contrastBorder);
|
||||
this.$el.style('border-style', contrastBorderColor ? 'solid' : null);
|
||||
this.$el.style('border-width', contrastBorderColor ? '1px' : null);
|
||||
this.$el.style('border-color', contrastBorderColor);
|
||||
const borderColor = this.getColor(debugToolBarBorder);
|
||||
|
||||
if (contrastBorderColor) {
|
||||
this.$el.style('border', `1px solid ${contrastBorderColor}`);
|
||||
} else {
|
||||
this.$el.style({
|
||||
'border': borderColor ? `solid ${borderColor}` : 'none',
|
||||
'border-width': '1px 0'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,4 +295,4 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
|
||||
delete this.$el;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,40 +6,30 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Expression, Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { openBreakpointSource } from 'vs/workbench/parts/debug/browser/breakpointsView';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { InputFocusedContext } from 'vs/platform/workbench/common/contextkeys';
|
||||
|
||||
export function registerCommands(): void {
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.logToDebugConsole',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
handler: (accessor: ServicesAccessor, value: string) => {
|
||||
if (typeof value === 'string') {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
// Use warning as severity to get the orange color for messages coming from the debug extension
|
||||
debugService.logToRepl(value, severity.Warning);
|
||||
}
|
||||
},
|
||||
when: undefined,
|
||||
primary: undefined
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.toggleBreakpoint',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(5),
|
||||
when: CONTEXT_BREAKPOINTS_FOCUSED,
|
||||
when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, InputFocusedContext.toNegated()),
|
||||
primary: KeyCode.Space,
|
||||
handler: (accessor) => {
|
||||
const listService = accessor.get(IListService);
|
||||
@@ -100,7 +90,7 @@ export function registerCommands(): void {
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.removeWatchExpression',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: CONTEXT_WATCH_EXPRESSIONS_FOCUSED,
|
||||
when: ContextKeyExpr.and(CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_EXPRESSION_SELECTED.toNegated()),
|
||||
primary: KeyCode.Delete,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
|
||||
handler: (accessor) => {
|
||||
@@ -121,7 +111,7 @@ export function registerCommands(): void {
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.removeBreakpoint',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: CONTEXT_BREAKPOINTS_FOCUSED,
|
||||
when: ContextKeyExpr.and(CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_BREAKPOINT_SELECTED.toNegated()),
|
||||
primary: KeyCode.Delete,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
|
||||
handler: (accessor) => {
|
||||
@@ -162,13 +152,13 @@ export function registerCommands(): void {
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: undefined,
|
||||
primary: undefined,
|
||||
handler: (accessor, workspaceUri: string) => {
|
||||
handler: (accessor, launchUri: string) => {
|
||||
const manager = accessor.get(IDebugService).getConfigurationManager();
|
||||
if (accessor.get(IWorkspaceContextService).getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
accessor.get(IMessageService).show(severity.Info, nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration."));
|
||||
accessor.get(INotificationService).info(nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration."));
|
||||
return TPromise.as(null);
|
||||
}
|
||||
const launch = manager.getLaunches().filter(l => l.workspace.uri.toString() === workspaceUri).pop() || manager.selectedLaunch;
|
||||
const launch = manager.getLaunches().filter(l => l.uri.toString() === launchUri).pop() || manager.selectedConfiguration.launch;
|
||||
|
||||
return launch.openConfigFile(false).done(editor => {
|
||||
if (editor) {
|
||||
@@ -182,4 +172,70 @@ export function registerCommands(): void {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const COLUMN_BREAKPOINT_COMMAND_ID = 'editor.debug.action.toggleColumnBreakpoint';
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
primary: KeyMod.Shift | KeyCode.F9,
|
||||
when: EditorContextKeys.textFocus,
|
||||
id: COLUMN_BREAKPOINT_COMMAND_ID,
|
||||
handler: (accessor) => {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const editor = editorService.getActiveEditor();
|
||||
const control = editor && <ICodeEditor>editor.getControl();
|
||||
if (control) {
|
||||
const position = control.getPosition();
|
||||
const modelUri = control.getModel().uri;
|
||||
const bp = debugService.getModel().getBreakpoints()
|
||||
.filter(bp => bp.lineNumber === position.lineNumber && bp.column === position.column && bp.uri.toString() === modelUri.toString()).pop();
|
||||
|
||||
if (bp) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
if (debugService.getConfigurationManager().canSetBreakpointsIn(control.getModel())) {
|
||||
return debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber, column: position.column }]);
|
||||
}
|
||||
}
|
||||
|
||||
return TPromise.as(null);
|
||||
}
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: COLUMN_BREAKPOINT_COMMAND_ID,
|
||||
title: nls.localize('columnBreakpoint', "Column Breakpoint"),
|
||||
category: nls.localize('debug', "Debug")
|
||||
}
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorContext, {
|
||||
command: {
|
||||
id: COLUMN_BREAKPOINT_COMMAND_ID,
|
||||
title: nls.localize('addColumnBreakpoint', "Add Column Breakpoint")
|
||||
},
|
||||
when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, EditorContextKeys.writable),
|
||||
group: 'debug',
|
||||
order: 1
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.openBreakpointToSide',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: CONTEXT_BREAKPOINTS_FOCUSED,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Enter,
|
||||
secondary: [KeyMod.Alt | KeyCode.Enter],
|
||||
handler: (accessor) => {
|
||||
const listService = accessor.get(IListService);
|
||||
const list = listService.lastFocusedList;
|
||||
if (list instanceof List) {
|
||||
const focus = list.getFocusedElements();
|
||||
if (focus.length && focus[0] instanceof Breakpoint) {
|
||||
return openBreakpointSource(focus[0], true, false, accessor.get(IDebugService), accessor.get(IWorkbenchEditorService));
|
||||
}
|
||||
}
|
||||
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import uri from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { guessMimeTypes, MIME_TEXT } from 'vs/base/common/mime';
|
||||
import { IModel } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService';
|
||||
@@ -39,7 +39,7 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC
|
||||
textModelResolverService.registerTextModelContentProvider(DEBUG_SCHEME, this);
|
||||
}
|
||||
|
||||
public provideTextContent(resource: uri): TPromise<IModel> {
|
||||
public provideTextContent(resource: uri): TPromise<ITextModel> {
|
||||
|
||||
let process: IProcess;
|
||||
let sourceRef: number;
|
||||
@@ -56,7 +56,7 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC
|
||||
}
|
||||
|
||||
if (!process) {
|
||||
return TPromise.wrapError<IModel>(new Error(localize('unable', "Unable to resolve the resource without a debug session")));
|
||||
return TPromise.wrapError<ITextModel>(new Error(localize('unable', "Unable to resolve the resource without a debug session")));
|
||||
}
|
||||
const source = process.sources.get(resource.toString());
|
||||
let rawSource: DebugProtocol.Source;
|
||||
@@ -73,20 +73,24 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC
|
||||
};
|
||||
}
|
||||
|
||||
const createErrModel = (message: string) => {
|
||||
this.debugService.sourceIsNotAvailable(resource);
|
||||
const modePromise = this.modeService.getOrCreateMode(MIME_TEXT);
|
||||
const model = this.modelService.createModel(message, modePromise, resource);
|
||||
|
||||
return model;
|
||||
};
|
||||
|
||||
return process.session.source({ sourceReference: sourceRef, source: rawSource }).then(response => {
|
||||
if (!response) {
|
||||
return createErrModel(localize('canNotResolveSource', "Could not resolve resource {0}, no response from debug extension.", resource.toString()));
|
||||
}
|
||||
|
||||
const mime = response.body.mimeType || guessMimeTypes(resource.path)[0];
|
||||
const modePromise = this.modeService.getOrCreateMode(mime);
|
||||
const model = this.modelService.createModel(response.body.content, modePromise, resource);
|
||||
|
||||
return model;
|
||||
}, (err: DebugProtocol.ErrorResponse) => {
|
||||
|
||||
this.debugService.sourceIsNotAvailable(resource);
|
||||
const modePromise = this.modeService.getOrCreateMode(MIME_TEXT);
|
||||
const model = this.modelService.createModel(err.message, modePromise, resource);
|
||||
|
||||
return model;
|
||||
});
|
||||
}, (err: DebugProtocol.ErrorResponse) => createErrModel(err.message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,63 +49,6 @@ class ToggleBreakpointAction extends EditorAction {
|
||||
}
|
||||
}
|
||||
|
||||
function addColumnBreakpoint(accessor: ServicesAccessor, editor: ICodeEditor, remove: boolean): TPromise<any> {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
|
||||
const position = editor.getPosition();
|
||||
const modelUri = editor.getModel().uri;
|
||||
const bp = debugService.getModel().getBreakpoints()
|
||||
.filter(bp => bp.lineNumber === position.lineNumber && bp.column === position.column && bp.uri.toString() === modelUri.toString()).pop();
|
||||
|
||||
if (bp) {
|
||||
return remove ? debugService.removeBreakpoints(bp.getId()) : TPromise.as(null);
|
||||
}
|
||||
if (debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) {
|
||||
return debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber, column: position.column }]);
|
||||
}
|
||||
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
class ToggleColumnBreakpointAction extends EditorAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.debug.action.toggleColumnBreakpoint',
|
||||
label: nls.localize('columnBreakpointAction', "Debug: Column Breakpoint"),
|
||||
alias: 'Debug: Column Breakpoint',
|
||||
precondition: null,
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.textFocus,
|
||||
primary: KeyMod.Shift | KeyCode.F9
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<any> {
|
||||
return addColumnBreakpoint(accessor, editor, true);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO@Isidor merge two column breakpoints actions together
|
||||
class ToggleColumnBreakpointContextMenuAction extends EditorAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.debug.action.toggleColumnBreakpointContextMenu',
|
||||
label: nls.localize('columnBreakpoint', "Add Column Breakpoint"),
|
||||
alias: 'Toggle Column Breakpoint',
|
||||
precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, EditorContextKeys.writable),
|
||||
menuOpts: {
|
||||
group: 'debug',
|
||||
order: 1
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<any> {
|
||||
return addColumnBreakpoint(accessor, editor, false);
|
||||
}
|
||||
}
|
||||
|
||||
class ConditionalBreakpointAction extends EditorAction {
|
||||
|
||||
constructor() {
|
||||
@@ -268,12 +211,9 @@ class CloseBreakpointWidgetCommand extends EditorCommand {
|
||||
}
|
||||
|
||||
registerEditorAction(ToggleBreakpointAction);
|
||||
registerEditorAction(ToggleColumnBreakpointAction);
|
||||
registerEditorAction(ToggleColumnBreakpointContextMenuAction);
|
||||
registerEditorAction(ConditionalBreakpointAction);
|
||||
registerEditorAction(RunToCursorAction);
|
||||
registerEditorAction(SelectionToReplAction);
|
||||
registerEditorAction(SelectionToWatchExpressionsAction);
|
||||
registerEditorAction(ShowDebugHoverAction);
|
||||
|
||||
registerEditorCommand(new CloseBreakpointWidgetCommand());
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as lifecycle from 'vs/base/common/lifecycle';
|
||||
import { Constants } from 'vs/editor/common/core/uint';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IModel, TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IDebugService, IBreakpoint, State } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { getBreakpointMessageAndClassName } from 'vs/workbench/parts/debug/browser/breakpointsView';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
||||
interface IBreakpointDecoration {
|
||||
decorationId: string;
|
||||
@@ -21,26 +21,24 @@ interface IBreakpointDecoration {
|
||||
}
|
||||
|
||||
interface IDebugEditorModelData {
|
||||
model: IModel;
|
||||
model: ITextModel;
|
||||
toDispose: lifecycle.IDisposable[];
|
||||
breakpointDecorations: IBreakpointDecoration[];
|
||||
currentStackDecorations: string[];
|
||||
dirty: boolean;
|
||||
topStackFrameRange: Range;
|
||||
}
|
||||
|
||||
const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
|
||||
|
||||
export class DebugEditorModelManager implements IWorkbenchContribution {
|
||||
static ID = 'breakpointManager';
|
||||
|
||||
static readonly ID = 'breakpointManager';
|
||||
static readonly STICKINESS = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
|
||||
private modelDataMap: Map<string, IDebugEditorModelData>;
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
private ignoreDecorationsChangedEvent: boolean;
|
||||
|
||||
constructor(
|
||||
@IModelService private modelService: IModelService,
|
||||
@IDebugService private debugService: IDebugService
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@ITextFileService private textFileService: ITextFileService
|
||||
) {
|
||||
this.modelDataMap = new Map<string, IDebugEditorModelData>();
|
||||
this.toDispose = [];
|
||||
@@ -68,14 +66,13 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
|
||||
this.toDispose.push(this.debugService.onDidChangeState(state => {
|
||||
if (state === State.Inactive) {
|
||||
this.modelDataMap.forEach(modelData => {
|
||||
modelData.dirty = false;
|
||||
modelData.topStackFrameRange = undefined;
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private onModelAdded(model: IModel): void {
|
||||
private onModelAdded(model: ITextModel): void {
|
||||
const modelUrlStr = model.uri.toString();
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints().filter(bp => bp.uri.toString() === modelUrlStr);
|
||||
|
||||
@@ -89,12 +86,11 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
|
||||
toDispose: toDispose,
|
||||
breakpointDecorations: breakpointDecorationIds.map((decorationId, index) => ({ decorationId, modelId: breakpoints[index].getId(), range: desiredDecorations[index].range })),
|
||||
currentStackDecorations: currentStackDecorations,
|
||||
dirty: false,
|
||||
topStackFrameRange: undefined
|
||||
});
|
||||
}
|
||||
|
||||
private onModelRemoved(model: IModel): void {
|
||||
private onModelRemoved(model: ITextModel): void {
|
||||
const modelUriStr = model.uri.toString();
|
||||
if (this.modelDataMap.has(modelUriStr)) {
|
||||
lifecycle.dispose(this.modelDataMap.get(modelUriStr).toDispose);
|
||||
@@ -220,9 +216,8 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
|
||||
}
|
||||
}
|
||||
}
|
||||
modelData.dirty = this.debugService.state !== State.Inactive;
|
||||
|
||||
this.debugService.updateBreakpoints(modelUri, data);
|
||||
this.debugService.updateBreakpoints(modelUri, data, true);
|
||||
}
|
||||
|
||||
private onBreakpointsChange(): void {
|
||||
@@ -262,121 +257,73 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
|
||||
({ decorationId, modelId: newBreakpoints[index].getId(), range: desiredDecorations[index].range }));
|
||||
}
|
||||
|
||||
private createBreakpointDecorations(model: IModel, breakpoints: IBreakpoint[]): { range: Range; options: IModelDecorationOptions; }[] {
|
||||
return breakpoints.map((breakpoint) => {
|
||||
const column = model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber);
|
||||
const range = model.validateRange(
|
||||
breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1)
|
||||
: new Range(breakpoint.lineNumber, column, breakpoint.lineNumber, column + 1) // Decoration has to have a width #20688
|
||||
);
|
||||
return {
|
||||
options: this.getBreakpointDecorationOptions(breakpoint),
|
||||
range
|
||||
};
|
||||
private createBreakpointDecorations(model: ITextModel, breakpoints: IBreakpoint[]): { range: Range; options: IModelDecorationOptions; }[] {
|
||||
const result: { range: Range; options: IModelDecorationOptions; }[] = [];
|
||||
breakpoints.forEach((breakpoint) => {
|
||||
if (breakpoint.lineNumber <= model.getLineCount()) {
|
||||
const column = model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber);
|
||||
const range = model.validateRange(
|
||||
breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1)
|
||||
: new Range(breakpoint.lineNumber, column, breakpoint.lineNumber, column + 1) // Decoration has to have a width #20688
|
||||
);
|
||||
|
||||
result.push({
|
||||
options: this.getBreakpointDecorationOptions(breakpoint),
|
||||
range
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private getBreakpointDecorationOptions(breakpoint: IBreakpoint): IModelDecorationOptions {
|
||||
const activated = this.debugService.getModel().areBreakpointsActivated();
|
||||
const state = this.debugService.state;
|
||||
const debugActive = state === State.Running || state === State.Stopped;
|
||||
const modelData = this.modelDataMap.get(breakpoint.uri.toString());
|
||||
const { className, message } = getBreakpointMessageAndClassName(this.debugService, this.textFileService, breakpoint);
|
||||
let glyphMarginHoverMessage: MarkdownString;
|
||||
|
||||
let result = (!breakpoint.enabled || !activated) ? DebugEditorModelManager.BREAKPOINT_DISABLED_DECORATION :
|
||||
debugActive && modelData && modelData.dirty && !breakpoint.verified ? DebugEditorModelManager.BREAKPOINT_DIRTY_DECORATION :
|
||||
debugActive && !breakpoint.verified ? DebugEditorModelManager.BREAKPOINT_UNVERIFIED_DECORATION :
|
||||
!breakpoint.condition && !breakpoint.hitCondition ? DebugEditorModelManager.BREAKPOINT_DECORATION : null;
|
||||
|
||||
if (result) {
|
||||
result = objects.deepClone(result);
|
||||
if (breakpoint.message) {
|
||||
result.glyphMarginHoverMessage = new MarkdownString().appendText(breakpoint.message);
|
||||
if (message) {
|
||||
if (breakpoint.condition || breakpoint.hitCondition) {
|
||||
const modelData = this.modelDataMap.get(breakpoint.uri.toString());
|
||||
const modeId = modelData ? modelData.model.getLanguageIdentifier().language : '';
|
||||
glyphMarginHoverMessage = new MarkdownString().appendCodeblock(modeId, message);
|
||||
} else {
|
||||
glyphMarginHoverMessage = new MarkdownString().appendText(message);
|
||||
}
|
||||
if (breakpoint.column) {
|
||||
result.beforeContentClassName = `debug-breakpoint-column ${result.glyphMarginClassName}-column`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const process = this.debugService.getViewModel().focusedProcess;
|
||||
if (process && !process.session.capabilities.supportsConditionalBreakpoints) {
|
||||
return DebugEditorModelManager.BREAKPOINT_UNSUPPORTED_DECORATION;
|
||||
}
|
||||
|
||||
const modeId = modelData ? modelData.model.getLanguageIdentifier().language : '';
|
||||
let condition: string;
|
||||
if (breakpoint.condition && breakpoint.hitCondition) {
|
||||
condition = `Expression: ${breakpoint.condition}\nHitCount: ${breakpoint.hitCondition}`;
|
||||
} else {
|
||||
condition = breakpoint.condition ? breakpoint.condition : breakpoint.hitCondition;
|
||||
}
|
||||
const glyphMarginHoverMessage = new MarkdownString().appendCodeblock(modeId, condition);
|
||||
const glyphMarginClassName = 'debug-breakpoint-conditional-glyph';
|
||||
const beforeContentClassName = breakpoint.column ? `debug-breakpoint-column ${glyphMarginClassName}-column` : undefined;
|
||||
|
||||
return {
|
||||
glyphMarginClassName,
|
||||
glyphMarginClassName: className,
|
||||
glyphMarginHoverMessage,
|
||||
stickiness,
|
||||
beforeContentClassName
|
||||
stickiness: DebugEditorModelManager.STICKINESS,
|
||||
beforeContentClassName: breakpoint.column ? `debug-breakpoint-column ${className}-column` : undefined
|
||||
};
|
||||
}
|
||||
|
||||
// editor decorations
|
||||
|
||||
private static BREAKPOINT_DECORATION: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-glyph',
|
||||
stickiness
|
||||
};
|
||||
|
||||
private static BREAKPOINT_DISABLED_DECORATION: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-disabled-glyph',
|
||||
glyphMarginHoverMessage: new MarkdownString().appendText(nls.localize('breakpointDisabledHover', "Disabled Breakpoint")),
|
||||
stickiness
|
||||
};
|
||||
|
||||
private static BREAKPOINT_UNVERIFIED_DECORATION: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-unverified-glyph',
|
||||
glyphMarginHoverMessage: new MarkdownString().appendText(nls.localize('breakpointUnverifieddHover', "Unverified Breakpoint")),
|
||||
stickiness
|
||||
};
|
||||
|
||||
private static BREAKPOINT_DIRTY_DECORATION: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-unverified-glyph',
|
||||
glyphMarginHoverMessage: new MarkdownString().appendText(nls.localize('breakpointDirtydHover', "Unverified breakpoint. File is modified, please restart debug session.")),
|
||||
stickiness
|
||||
};
|
||||
|
||||
private static BREAKPOINT_UNSUPPORTED_DECORATION: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-unsupported-glyph',
|
||||
glyphMarginHoverMessage: new MarkdownString().appendText(nls.localize('breakpointUnsupported', "Conditional breakpoints not supported by this debug type")),
|
||||
stickiness
|
||||
};
|
||||
|
||||
// we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement.
|
||||
private static TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-top-stack-frame-glyph',
|
||||
stickiness
|
||||
glyphMarginClassName: 'debug-top-stack-frame',
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
|
||||
private static FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-focused-stack-frame-glyph',
|
||||
stickiness
|
||||
glyphMarginClassName: 'debug-focused-stack-frame',
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
|
||||
private static TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = {
|
||||
isWholeLine: true,
|
||||
inlineClassName: 'debug-remove-token-colors',
|
||||
className: 'debug-top-stack-frame-line',
|
||||
stickiness
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
|
||||
private static TOP_STACK_FRAME_EXCEPTION_DECORATION: IModelDecorationOptions = {
|
||||
isWholeLine: true,
|
||||
inlineClassName: 'debug-remove-token-colors',
|
||||
className: 'debug-top-stack-frame-exception-line',
|
||||
stickiness
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
|
||||
private static TOP_STACK_FRAME_INLINE_DECORATION: IModelDecorationOptions = {
|
||||
@@ -387,6 +334,6 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
|
||||
isWholeLine: true,
|
||||
inlineClassName: 'debug-remove-token-colors',
|
||||
className: 'debug-focused-stack-frame-line',
|
||||
stickiness
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ import * as errors from 'vs/base/common/errors';
|
||||
import { QuickOpenEntry, QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { StartAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { Severity } from 'vs/workbench/services/message/browser/messageList';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
class AddConfigEntry extends Model.QuickOpenEntry {
|
||||
|
||||
@@ -29,7 +28,7 @@ class AddConfigEntry extends Model.QuickOpenEntry {
|
||||
}
|
||||
|
||||
public getDescription(): string {
|
||||
return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.launch.workspace.name : '';
|
||||
return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.launch.name : '';
|
||||
}
|
||||
|
||||
public getAriaLabel(): string {
|
||||
@@ -40,7 +39,7 @@ class AddConfigEntry extends Model.QuickOpenEntry {
|
||||
if (mode === QuickOpen.Mode.PREVIEW) {
|
||||
return false;
|
||||
}
|
||||
this.commandService.executeCommand('debug.addConfiguration', this.launch.workspace.uri.toString()).done(undefined, errors.onUnexpectedError);
|
||||
this.commandService.executeCommand('debug.addConfiguration', this.launch.uri.toString()).done(undefined, errors.onUnexpectedError);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -48,7 +47,7 @@ class AddConfigEntry extends Model.QuickOpenEntry {
|
||||
|
||||
class StartDebugEntry extends Model.QuickOpenEntry {
|
||||
|
||||
constructor(private debugService: IDebugService, private contextService: IWorkspaceContextService, private messageService: IMessageService, private launch: ILaunch, private configurationName: string, highlights: Model.IHighlight[] = []) {
|
||||
constructor(private debugService: IDebugService, private contextService: IWorkspaceContextService, private notificationService: INotificationService, private launch: ILaunch, private configurationName: string, highlights: Model.IHighlight[] = []) {
|
||||
super(highlights);
|
||||
}
|
||||
|
||||
@@ -57,7 +56,7 @@ class StartDebugEntry extends Model.QuickOpenEntry {
|
||||
}
|
||||
|
||||
public getDescription(): string {
|
||||
return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.launch.workspace.name : '';
|
||||
return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.launch.name : '';
|
||||
}
|
||||
|
||||
public getAriaLabel(): string {
|
||||
@@ -70,7 +69,7 @@ class StartDebugEntry extends Model.QuickOpenEntry {
|
||||
}
|
||||
// Run selected debug configuration
|
||||
this.debugService.getConfigurationManager().selectConfiguration(this.launch, this.configurationName);
|
||||
this.debugService.startDebugging(this.launch.workspace).done(undefined, e => this.messageService.show(Severity.Error, e));
|
||||
this.debugService.startDebugging(this.launch).done(undefined, e => this.notificationService.error(e));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -85,7 +84,7 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler {
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@ICommandService private commandService: ICommandService,
|
||||
@IMessageService private messageService: IMessageService
|
||||
@INotificationService private notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -103,14 +102,15 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler {
|
||||
launch.getConfigurationNames().map(config => ({ config: config, highlights: Filters.matchesContiguousSubString(input, config) }))
|
||||
.filter(({ highlights }) => !!highlights)
|
||||
.forEach(({ config, highlights }) => {
|
||||
if (launch === configManager.selectedLaunch && config === configManager.selectedName) {
|
||||
if (launch === configManager.selectedConfiguration.launch && config === configManager.selectedConfiguration.name) {
|
||||
this.autoFocusIndex = configurations.length;
|
||||
}
|
||||
configurations.push(new StartDebugEntry(this.debugService, this.contextService, this.messageService, launch, config, highlights));
|
||||
configurations.push(new StartDebugEntry(this.debugService, this.contextService, this.notificationService, launch, config, highlights));
|
||||
});
|
||||
}
|
||||
launches.forEach((l, index) => {
|
||||
const label = launches.length > 1 ? nls.localize("addConfigTo", "Add Config ({0})...", l.workspace.name) : nls.localize('addConfiguration', "Add Configuration...");
|
||||
launches.filter(l => !l.hidden).forEach((l, index) => {
|
||||
|
||||
const label = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? nls.localize("addConfigTo", "Add Config ({0})...", l.name) : nls.localize('addConfiguration', "Add Configuration...");
|
||||
const entry = new AddConfigEntry(label, l, this.commandService, this.contextService, Filters.matchesContiguousSubString(input, label));
|
||||
if (index === 0) {
|
||||
configurations.push(new QuickOpenEntryGroup(entry, undefined, true));
|
||||
|
||||
@@ -92,8 +92,13 @@ export class DebugStatus extends Themable implements IStatusbarItem {
|
||||
private setLabel(): void {
|
||||
if (this.label && this.statusBarItem) {
|
||||
const manager = this.debugService.getConfigurationManager();
|
||||
const name = manager.selectedName || '';
|
||||
this.label.textContent = manager.getLaunches().length > 1 ? `${name} (${manager.selectedLaunch.workspace.name})` : name;
|
||||
const name = manager.selectedConfiguration.name;
|
||||
if (name) {
|
||||
this.statusBarItem.style.display = 'block';
|
||||
this.label.textContent = manager.getLaunches().length > 1 ? `${name} (${manager.selectedConfiguration.launch.name})` : name;
|
||||
} else {
|
||||
this.statusBarItem.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,17 +15,18 @@ import { IDebugService, VIEWLET_ID, State, VARIABLES_VIEW_ID, WATCH_VIEW_ID, CAL
|
||||
import { StartAction, ToggleReplAction, ConfigureAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { StartDebugActionItem } from 'vs/workbench/parts/debug/browser/debugActionItems';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { ViewLocation } from 'vs/workbench/browser/parts/views/viewsRegistry';
|
||||
import { ViewLocation } from 'vs/workbench/common/views';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
|
||||
export class DebugViewlet extends PersistentViewsViewlet {
|
||||
|
||||
@@ -35,6 +36,7 @@ export class DebugViewlet extends PersistentViewsViewlet {
|
||||
private panelListeners = new Map<string, IDisposable>();
|
||||
|
||||
constructor(
|
||||
@IPartService partService: IPartService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IProgressService private progressService: IProgressService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@@ -46,7 +48,7 @@ export class DebugViewlet extends PersistentViewsViewlet {
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IExtensionService extensionService: IExtensionService
|
||||
) {
|
||||
super(VIEWLET_ID, ViewLocation.Debug, `${VIEWLET_ID}.state`, false, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService);
|
||||
super(VIEWLET_ID, ViewLocation.Debug, `${VIEWLET_ID}.state`, false, partService, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService);
|
||||
|
||||
this.progressRunner = null;
|
||||
|
||||
@@ -82,7 +84,7 @@ export class DebugViewlet extends PersistentViewsViewlet {
|
||||
}
|
||||
|
||||
public getActionItem(action: IAction): IActionItem {
|
||||
if (action.id === StartAction.ID && !!this.debugService.getConfigurationManager().selectedLaunch) {
|
||||
if (action.id === StartAction.ID) {
|
||||
this.startDebugActionItem = this.instantiationService.createInstance(StartDebugActionItem, null, action);
|
||||
return this.startDebugActionItem;
|
||||
}
|
||||
@@ -138,7 +140,7 @@ export class DebugViewlet extends PersistentViewsViewlet {
|
||||
|
||||
export class FocusVariablesViewAction extends Action {
|
||||
|
||||
static ID = 'workbench.debug.action.focusVariablesView';
|
||||
static readonly ID = 'workbench.debug.action.focusVariablesView';
|
||||
static LABEL = nls.localize('debugFocusVariablesView', 'Focus Variables');
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@@ -156,7 +158,7 @@ export class FocusVariablesViewAction extends Action {
|
||||
|
||||
export class FocusWatchViewAction extends Action {
|
||||
|
||||
static ID = 'workbench.debug.action.focusWatchView';
|
||||
static readonly ID = 'workbench.debug.action.focusWatchView';
|
||||
static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusWatchView' }, 'Focus Watch');
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@@ -174,7 +176,7 @@ export class FocusWatchViewAction extends Action {
|
||||
|
||||
export class FocusCallStackViewAction extends Action {
|
||||
|
||||
static ID = 'workbench.debug.action.focusCallStackView';
|
||||
static readonly ID = 'workbench.debug.action.focusCallStackView';
|
||||
static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusCallStackView' }, 'Focus CallStack');
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@@ -192,7 +194,7 @@ export class FocusCallStackViewAction extends Action {
|
||||
|
||||
export class FocusBreakpointsViewAction extends Action {
|
||||
|
||||
static ID = 'workbench.debug.action.focusBreakpointsView';
|
||||
static readonly ID = 'workbench.debug.action.focusBreakpointsView';
|
||||
static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusBreakpointsView' }, 'Focus Breakpoints');
|
||||
|
||||
constructor(id: string, label: string,
|
||||
|
||||
@@ -11,6 +11,7 @@ import * as nls from 'vs/nls';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
export class LinkDetector {
|
||||
private static readonly MAX_LENGTH = 500;
|
||||
private static FILE_LOCATION_PATTERNS: RegExp[] = [
|
||||
// group 0: full path with line and column
|
||||
// group 1: full path without line and column, matched by `*.*` in the end to work only on paths with extensions in the end (s.t. node:10352 would not match)
|
||||
@@ -34,8 +35,11 @@ export class LinkDetector {
|
||||
* If no links were detected, returns the original string.
|
||||
*/
|
||||
public handleLinks(text: string): HTMLElement | string {
|
||||
let linkContainer: HTMLElement;
|
||||
if (text.length > LinkDetector.MAX_LENGTH) {
|
||||
return text;
|
||||
}
|
||||
|
||||
let linkContainer: HTMLElement;
|
||||
for (let pattern of LinkDetector.FILE_LOCATION_PATTERNS) {
|
||||
pattern.lastIndex = 0; // the holy grail of software development
|
||||
let lastMatchIndex = 0;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-red{fill:#c10;}</style></defs><title>BreakpointFunction_16xMD</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.7,14H1.3L8,1.941Z"/></g><g id="iconBg"><path class="icon-vs-red" d="M13,13H3L8,4Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 482 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-disabled-grey{fill:#848484;}</style></defs><title>BreakpointFunction_disabled_16xMD</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.7,14H1.3L8,1.941Z"/></g><g id="iconBg"><path class="icon-disabled-grey" d="M13,13H3L8,4Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 508 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-disabled-grey{fill:#848484;}</style></defs><title>BreakpointFunction_disabled_16xMD</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.7,14H1.3L8,1.941Z"/></g><g id="iconBg"><path class="icon-disabled-grey" d="M13,13H3L8,4Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 508 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-red{fill:#848484;}</style></defs><title>BreakpointFunctionUnverified_16xMD</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.7,14H1.3L8,1.941Z"/></g><g id="iconBg"><path class="icon-vs-red" d="M8,4,3,13H13ZM8,6.059,11.3,12H4.7Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 516 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-red{fill:#848484;}</style></defs><title>BreakpointFunctionUnverified_16xMD</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.7,14H1.3L8,1.941Z"/></g><g id="iconBg"><path class="icon-vs-red" d="M8,4,3,13H13ZM8,6.059,11.3,12H4.7Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 516 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-red{fill:#e51400;}</style></defs><title>BreakpointFunction_16xMD</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.7,14H1.3L8,1.941Z"/></g><g id="iconBg"><path class="icon-vs-red" d="M13,13H3L8,4Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 484 B |
@@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame-range {
|
||||
background: #ffeca0;
|
||||
background: rgba(255, 255, 102, 0.6);
|
||||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame-column::before {
|
||||
@@ -29,30 +29,30 @@
|
||||
background: rgba(206, 231, 206, 1);
|
||||
}
|
||||
|
||||
.monaco-editor .debug-breakpoint-hint-glyph {
|
||||
.debug-breakpoint-hint {
|
||||
background: url('breakpoint-hint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-breakpoint-disabled-glyph,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-disabled-glyph-column::before {
|
||||
.debug-breakpoint-disabled,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-disabled-column::before {
|
||||
background: url('breakpoint-disabled.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-breakpoint-unverified-glyph,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-unverified-glyph-column::before {
|
||||
.debug-breakpoint-unverified,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-unverified-column::before {
|
||||
background: url('breakpoint-unverified.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame-glyph {
|
||||
.monaco-editor .debug-top-stack-frame {
|
||||
background: url('current-arrow.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-focused-stack-frame-glyph {
|
||||
.monaco-editor .debug-focused-stack-frame {
|
||||
background: url('stackframe-arrow.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-breakpoint-glyph,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-glyph-column::before {
|
||||
.debug-breakpoint,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-column::before {
|
||||
background: url('breakpoint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
@@ -68,27 +68,40 @@
|
||||
background-position: initial !important;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-breakpoint-conditional-glyph,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-conditional-glyph-column::before {
|
||||
.debug-function-breakpoint {
|
||||
background: url('breakpoint-function.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.debug-function-breakpoint-unverified {
|
||||
background: url('breakpoint-function-unverified.svg') center center no-repeat;
|
||||
|
||||
}
|
||||
|
||||
.debug-function-breakpoint-disabled {
|
||||
background: url('breakpoint-function-disabled.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.debug-breakpoint-conditional,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-conditional-column::before {
|
||||
background: url('breakpoint-conditional.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-breakpoint-unsupported-glyph,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-unsupported-glyph-column::before {
|
||||
.debug-breakpoint-unsupported,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-unsupported-column::before {
|
||||
background: url('breakpoint-unsupported.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame-glyph.debug-breakpoint-glyph,
|
||||
.monaco-editor .debug-top-stack-frame-glyph.debug-breakpoint-conditional-glyph,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-glyph-column.debug-top-stack-frame-column::before,
|
||||
.monaco-editor.vs-dark .debug-top-stack-frame-glyph.debug-breakpoint-glyph,
|
||||
.monaco-editor.vs-dark .debug-top-stack-frame-glyph.debug-breakpoint-conditional-glyph,
|
||||
.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-glyph-column.debug-top-stack-frame-column::before {
|
||||
.monaco-editor .debug-top-stack-frame.debug-breakpoint,
|
||||
.monaco-editor .debug-top-stack-frame.debug-breakpoint-conditional,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-column.debug-top-stack-frame-column::before,
|
||||
.monaco-editor.vs-dark .debug-top-stack-frame.debug-breakpoint,
|
||||
.monaco-editor.vs-dark .debug-top-stack-frame.debug-breakpoint-conditional,
|
||||
.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-column.debug-top-stack-frame-column::before {
|
||||
background: url('current-and-breakpoint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-focused-stack-frame-glyph.debug-breakpoint-glyph,
|
||||
.monaco-editor .debug-focused-stack-frame-glyph.debug-breakpoint-conditional-glyph {
|
||||
.monaco-editor .debug-focused-stack-frame.debug-breakpoint,
|
||||
.monaco-editor .debug-focused-stack-frame.debug-breakpoint-conditional {
|
||||
background: url('stackframe-and-breakpoint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
@@ -233,40 +246,61 @@
|
||||
background-color: rgba(255, 255, 0, 0.3)
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .debug-breakpoint-glyph,
|
||||
.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-glyph-column::before {
|
||||
.vs-dark .debug-breakpoint,
|
||||
.hc-black .debug-breakpoint,
|
||||
.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-column::before {
|
||||
background: url('breakpoint-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .debug-breakpoint-conditional-glyph,
|
||||
.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-conditional-glyph-column::before {
|
||||
.vs-dark .debug-breakpoint-conditional,
|
||||
.hc-black .debug-breakpoint-conditional,
|
||||
.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-conditional-column::before {
|
||||
background: url('breakpoint-conditional-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .debug-breakpoint-unsupported-glyph,
|
||||
.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-unsupported-glyph-column::before {
|
||||
.vs-dark .debug-breakpoint-unsupported,
|
||||
.hc-black .debug-breakpoint-unsupported,
|
||||
.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-unsupported-column::before {
|
||||
background: url('breakpoint-unsupported-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .debug-breakpoint-disabled-glyph,
|
||||
.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-disabled-glyph-column::before {
|
||||
.vs-dark .debug-breakpoint-disabled,
|
||||
.hc-black .debug-breakpoint-disabled,
|
||||
.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-disabled-column::before {
|
||||
background: url('breakpoint-disabled-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .debug-breakpoint-unverified-glyph,
|
||||
.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-unverified-glyph-column::before {
|
||||
.vs-dark .debug-breakpoint-unverified,
|
||||
.hc-black .debug-breakpoint-unverified,
|
||||
.monaco-editor.vs-dark .debug-breakpoint-column.debug-breakpoint-unverified-column::before {
|
||||
background: url('breakpoint-unverified-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .debug-focused-stack-frame-glyph {
|
||||
.monaco-editor.vs-dark .debug-focused-stack-frame {
|
||||
background: url('stackframe-arrow-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .debug-focused-stack-frame-glyph.debug-breakpoint-glyph,
|
||||
.monaco-editor.vs-dark .debug-focused-stack-frame-glyph.debug-breakpoint-conditional-glyph {
|
||||
.monaco-editor.vs-dark .debug-focused-stack-frame.debug-breakpoint,
|
||||
.monaco-editor.vs-dark .debug-focused-stack-frame.debug-breakpoint-conditional {
|
||||
background: url('stackframe-and-breakpoint-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .debug-function-breakpoint,
|
||||
.hc-black .debug-function-breakpoint {
|
||||
background: url('breakpoint-function-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .debug-function-breakpoint-unverified,
|
||||
.hc-black .debug-function-breakpoint-unverified {
|
||||
background: url('breakpoint-function-unverified.svg') center center no-repeat;
|
||||
|
||||
}
|
||||
|
||||
.vs-dark .debug-function-breakpoint-disabled,
|
||||
.hc-black .debug-function-breakpoint-disabled {
|
||||
background: url('breakpoint-function-disabled.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
/* High Contrast Theming */
|
||||
|
||||
.monaco-editor.hc-black .debug-focused-stack-frame-line {
|
||||
|
||||
@@ -348,7 +348,11 @@
|
||||
|
||||
/* Breakpoints */
|
||||
|
||||
.debug-viewlet .debug-breakpoints .monaco-list-row {
|
||||
.debug-viewlet .debug-breakpoints .monaco-list-row .breakpoint {
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
.debug-viewlet .debug-breakpoints .breakpoint.exception {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
@@ -363,6 +367,11 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.debug-viewlet .debug-breakpoints .breakpoint > .icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.debug-viewlet .debug-breakpoints .breakpoint > .file-path {
|
||||
opacity: 0.7;
|
||||
font-size: 0.9em;
|
||||
|
||||
@@ -11,7 +11,7 @@ import { IPartService, Parts } from 'vs/workbench/services/part/common/partServi
|
||||
import { IDebugService, State } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_BACKGROUND, Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_BORDER } from 'vs/workbench/common/theme';
|
||||
import { addClass, removeClass } from 'vs/base/browser/dom';
|
||||
import { addClass, removeClass, createStyleSheet } from 'vs/base/browser/dom';
|
||||
|
||||
// colors for theming
|
||||
|
||||
@@ -34,6 +34,7 @@ export const STATUS_BAR_DEBUGGING_BORDER = registerColor('statusBar.debuggingBor
|
||||
}, localize('statusBarDebuggingBorder', "Status bar border color separating to the sidebar and editor when a program is being debugged. The status bar is shown in the bottom of the window"));
|
||||
|
||||
export class StatusBarColorProvider extends Themable implements IWorkbenchContribution {
|
||||
private styleElement: HTMLStyleElement;
|
||||
|
||||
constructor(
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -61,13 +62,23 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri
|
||||
removeClass(container, 'debugging');
|
||||
}
|
||||
|
||||
container.style.backgroundColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_DEBUGGING_BACKGROUND, STATUS_BAR_BACKGROUND));
|
||||
// Container Colors
|
||||
const backgroundColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_DEBUGGING_BACKGROUND, STATUS_BAR_BACKGROUND));
|
||||
container.style.backgroundColor = backgroundColor;
|
||||
container.style.color = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_DEBUGGING_FOREGROUND, STATUS_BAR_FOREGROUND));
|
||||
|
||||
// Border Color
|
||||
const borderColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_DEBUGGING_BORDER, STATUS_BAR_BORDER)) || this.getColor(contrastBorder);
|
||||
container.style.borderTopWidth = borderColor ? '1px' : null;
|
||||
container.style.borderTopStyle = borderColor ? 'solid' : null;
|
||||
container.style.borderTopColor = borderColor;
|
||||
|
||||
// Notification Beak
|
||||
if (!this.styleElement) {
|
||||
this.styleElement = createStyleSheet(container);
|
||||
}
|
||||
|
||||
this.styleElement.innerHTML = `.monaco-workbench > .part.statusbar > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor} !important; }`;
|
||||
}
|
||||
|
||||
private getColorKey(noFolderColor: string, debuggingColor: string, normalColor: string): string {
|
||||
@@ -10,7 +10,8 @@ import severity from 'vs/base/common/severity';
|
||||
import Event from 'vs/base/common/event';
|
||||
import { IJSONSchemaSnippet } from 'vs/base/common/jsonSchema';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IModel as EditorIModel, IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel as EditorIModel } from 'vs/editor/common/model';
|
||||
import { IEditor } from 'vs/platform/editor/common/editor';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { ISuggestion } from 'vs/editor/common/modes';
|
||||
@@ -39,6 +40,8 @@ export const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = new RawContextKey<boolean>('bre
|
||||
export const CONTEXT_BREAKPOINTS_FOCUSED = new RawContextKey<boolean>('breakpointsFocused', true);
|
||||
export const CONTEXT_WATCH_EXPRESSIONS_FOCUSED = new RawContextKey<boolean>('watchExpressionsFocused', true);
|
||||
export const CONTEXT_VARIABLES_FOCUSED = new RawContextKey<boolean>('variablesFocused', true);
|
||||
export const CONTEXT_EXPRESSION_SELECTED = new RawContextKey<boolean>('expressionSelected', false);
|
||||
export const CONTEXT_BREAKPOINT_SELECTED = new RawContextKey<boolean>('breakpointSelected', false);
|
||||
|
||||
export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug';
|
||||
export const DEBUG_SCHEME = 'debug';
|
||||
@@ -221,7 +224,8 @@ export interface IEnablement extends ITreeElement {
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export interface IRawBreakpoint {
|
||||
export interface IBreakpointData {
|
||||
id?: string;
|
||||
lineNumber: number;
|
||||
column?: number;
|
||||
enabled?: boolean;
|
||||
@@ -229,6 +233,11 @@ export interface IRawBreakpoint {
|
||||
hitCondition?: string;
|
||||
}
|
||||
|
||||
export interface IBreakpointUpdateData extends DebugProtocol.Breakpoint {
|
||||
condition?: string;
|
||||
hitCondition?: string;
|
||||
}
|
||||
|
||||
export interface IBreakpoint extends IEnablement {
|
||||
uri: uri;
|
||||
lineNumber: number;
|
||||
@@ -327,12 +336,12 @@ export enum State {
|
||||
|
||||
export interface IDebugConfiguration {
|
||||
allowBreakpointsEverywhere: boolean;
|
||||
openDebug: string;
|
||||
openDebug: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart';
|
||||
openExplorerOnEnd: boolean;
|
||||
inlineValues: boolean;
|
||||
hideActionBar: boolean;
|
||||
showInStatusBar: string;
|
||||
internalConsoleOptions: string;
|
||||
showInStatusBar: 'never' | 'always' | 'onFirstSessionStart';
|
||||
internalConsoleOptions: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart';
|
||||
}
|
||||
|
||||
export interface IGlobalConfig {
|
||||
@@ -345,7 +354,7 @@ export interface IEnvConfig {
|
||||
name?: string;
|
||||
type: string;
|
||||
request: string;
|
||||
internalConsoleOptions?: string;
|
||||
internalConsoleOptions?: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart';
|
||||
preLaunchTask?: string;
|
||||
__restart?: any;
|
||||
__sessionId?: string;
|
||||
@@ -362,7 +371,7 @@ export interface IConfig extends IEnvConfig {
|
||||
|
||||
export interface ICompound {
|
||||
name: string;
|
||||
configurations: string[];
|
||||
configurations: (string | { name: string, folder: string })[];
|
||||
}
|
||||
|
||||
export interface IAdapterExecutable {
|
||||
@@ -400,6 +409,7 @@ export interface IDebugConfigurationProvider {
|
||||
handle: number;
|
||||
resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig): TPromise<IConfig>;
|
||||
provideDebugConfigurations?(folderUri: uri | undefined): TPromise<IConfig[]>;
|
||||
debugAdapterExecutable(folderUri: uri | undefined): TPromise<IAdapterExecutable>;
|
||||
}
|
||||
|
||||
export interface IConfigurationManager {
|
||||
@@ -409,16 +419,19 @@ export interface IConfigurationManager {
|
||||
canSetBreakpointsIn(model: EditorIModel): boolean;
|
||||
|
||||
/**
|
||||
* Returns null for no folder workspace. Otherwise returns a launch object corresponding to the selected debug configuration.
|
||||
* Returns an object containing the selected launch configuration and the selected configuration name. Both these fields can be null (no folder workspace).
|
||||
*/
|
||||
selectedLaunch: ILaunch;
|
||||
|
||||
selectedName: string;
|
||||
selectedConfiguration: {
|
||||
launch: ILaunch;
|
||||
name: string;
|
||||
};
|
||||
|
||||
selectConfiguration(launch: ILaunch, name?: string, debugStarted?: boolean): void;
|
||||
|
||||
getLaunches(): ILaunch[];
|
||||
|
||||
getLaunch(workspaceUri: uri): ILaunch | undefined;
|
||||
|
||||
/**
|
||||
* Allows to register on change of selected debug configuration.
|
||||
*/
|
||||
@@ -426,7 +439,9 @@ export interface IConfigurationManager {
|
||||
|
||||
registerDebugConfigurationProvider(handle: number, debugConfigurationProvider: IDebugConfigurationProvider): void;
|
||||
unregisterDebugConfigurationProvider(handle: number): void;
|
||||
|
||||
resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any): TPromise<any>;
|
||||
debugAdapterExecutable(folderUri: uri | undefined, type: string): TPromise<IAdapterExecutable | undefined>;
|
||||
}
|
||||
|
||||
export interface ILaunch {
|
||||
@@ -436,8 +451,21 @@ export interface ILaunch {
|
||||
*/
|
||||
uri: uri;
|
||||
|
||||
/**
|
||||
* Name of the launch.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Workspace of the launch. Can be null.
|
||||
*/
|
||||
workspace: IWorkspaceFolder;
|
||||
|
||||
/**
|
||||
* Should this launch be shown in the debug dropdown.
|
||||
*/
|
||||
hidden: boolean;
|
||||
|
||||
/**
|
||||
* Returns a configuration with the specified name.
|
||||
* Returns null if there is no configuration with the specified name.
|
||||
@@ -454,7 +482,7 @@ export interface ILaunch {
|
||||
* Returns the names of all configurations and compounds.
|
||||
* Ignores configurations which are invalid.
|
||||
*/
|
||||
getConfigurationNames(): string[];
|
||||
getConfigurationNames(includeCompounds?: boolean): string[];
|
||||
|
||||
/**
|
||||
* Returns the resolved configuration.
|
||||
@@ -512,17 +540,17 @@ export interface IDebugService {
|
||||
/**
|
||||
* Sets the focused stack frame and evaluates all expressions against the newly focused stack frame,
|
||||
*/
|
||||
focusStackFrameAndEvaluate(focusedStackFrame: IStackFrame, process?: IProcess, explicit?: boolean): TPromise<void>;
|
||||
focusStackFrame(focusedStackFrame: IStackFrame, thread?: IThread, process?: IProcess, explicit?: boolean): void;
|
||||
|
||||
/**
|
||||
* Adds new breakpoints to the model for the file specified with the uri. Notifies debug adapter of breakpoint changes.
|
||||
*/
|
||||
addBreakpoints(uri: uri, rawBreakpoints: IRawBreakpoint[]): TPromise<void>;
|
||||
addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): TPromise<void>;
|
||||
|
||||
/**
|
||||
* Updates the breakpoints and notifies the debug adapter of breakpoint changes.
|
||||
* Updates the breakpoints.
|
||||
*/
|
||||
updateBreakpoints(uri: uri, data: { [id: string]: DebugProtocol.Breakpoint }): TPromise<void>;
|
||||
updateBreakpoints(uri: uri, data: { [id: string]: IBreakpointUpdateData }, sendOnResourceSaved: boolean): void;
|
||||
|
||||
/**
|
||||
* Enables or disables all breakpoints. If breakpoint is passed only enables or disables the passed breakpoint.
|
||||
@@ -543,9 +571,9 @@ export interface IDebugService {
|
||||
removeBreakpoints(id?: string): TPromise<any>;
|
||||
|
||||
/**
|
||||
* Adds a new no name function breakpoint. The function breakpoint should be renamed once user enters the name.
|
||||
* Adds a new function breakpoint for the given name.
|
||||
*/
|
||||
addFunctionBreakpoint(): void;
|
||||
addFunctionBreakpoint(name?: string, id?: string): void;
|
||||
|
||||
/**
|
||||
* Renames an already existing function breakpoint.
|
||||
@@ -577,12 +605,12 @@ export interface IDebugService {
|
||||
/**
|
||||
* Adds a new watch expression and evaluates it against the debug adapter.
|
||||
*/
|
||||
addWatchExpression(name?: string): TPromise<void>;
|
||||
addWatchExpression(name?: string): void;
|
||||
|
||||
/**
|
||||
* Renames a watch expression and evaluates it against the debug adapter.
|
||||
*/
|
||||
renameWatchExpression(id: string, newName: string): TPromise<void>;
|
||||
renameWatchExpression(id: string, newName: string): void;
|
||||
|
||||
/**
|
||||
* Moves a watch expression to a new possition. Used for reordering watch expressions.
|
||||
@@ -594,17 +622,12 @@ export interface IDebugService {
|
||||
*/
|
||||
removeWatchExpressions(id?: string): void;
|
||||
|
||||
/**
|
||||
* Evaluates all watch expression.
|
||||
*/
|
||||
evaluateWatchExpressions(): TPromise<void>;
|
||||
|
||||
/**
|
||||
* Starts debugging. If the configOrName is not passed uses the selected configuration in the debug dropdown.
|
||||
* Also saves all files, manages if compounds are present in the configuration
|
||||
* and resolveds configurations via DebugConfigurationProviders.
|
||||
*/
|
||||
startDebugging(root: IWorkspaceFolder, configOrName?: IConfig | string, noDebug?: boolean): TPromise<any>;
|
||||
startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug?: boolean): TPromise<any>;
|
||||
|
||||
/**
|
||||
* Restarts a process or creates a new one if there is no active session.
|
||||
|
||||
@@ -20,10 +20,11 @@ import { ISuggestion } from 'vs/editor/common/modes';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import {
|
||||
ITreeElement, IExpression, IExpressionContainer, IProcess, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IModel, IReplElementSource,
|
||||
IConfig, ISession, IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IRawBreakpoint, IExceptionInfo, IReplElement, ProcessState, IBreakpointsChangeEvent
|
||||
IConfig, ISession, IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IReplElement, ProcessState, IBreakpointsChangeEvent, IBreakpointUpdateData
|
||||
} from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
const MAX_REPL_LENGTH = 10000;
|
||||
|
||||
@@ -380,9 +381,9 @@ export class StackFrame implements IStackFrame {
|
||||
return `${this.name} (${this.source.inMemory ? this.source.name : this.source.uri.fsPath}:${this.range.startLineNumber})`;
|
||||
}
|
||||
|
||||
public openInEditor(editorService: IWorkbenchEditorService, preserveFocus?: boolean, sideBySide?: boolean): TPromise<any> {
|
||||
public openInEditor(editorService: IWorkbenchEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise<any> {
|
||||
return !this.source.available ? TPromise.as(null) :
|
||||
this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide);
|
||||
this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide, pinned);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,7 +546,7 @@ export class Process implements IProcess {
|
||||
}
|
||||
|
||||
public getName(includeRoot: boolean): string {
|
||||
return includeRoot ? `${this.configuration.name} (${resources.basenameOrAuthority(this.session.root.uri)})` : this.configuration.name;
|
||||
return includeRoot && this.session.root ? `${this.configuration.name} (${resources.basenameOrAuthority(this.session.root.uri)})` : this.configuration.name;
|
||||
}
|
||||
|
||||
public get state(): ProcessState {
|
||||
@@ -560,6 +561,11 @@ export class Process implements IProcess {
|
||||
let source = new Source(raw, this.getId());
|
||||
if (this.sources.has(source.uri.toString())) {
|
||||
source = this.sources.get(source.uri.toString());
|
||||
source.raw = mixin(source.raw, raw);
|
||||
if (source.raw && raw) {
|
||||
// Always take the latest presentation hint from adapter #42139
|
||||
source.raw.presentationHint = raw.presentationHint;
|
||||
}
|
||||
} else {
|
||||
this.sources.set(source.uri.toString(), source);
|
||||
}
|
||||
@@ -674,7 +680,6 @@ export class Breakpoint implements IBreakpoint {
|
||||
public message: string;
|
||||
public endLineNumber: number;
|
||||
public endColumn: number;
|
||||
private id: string;
|
||||
|
||||
constructor(
|
||||
public uri: uri,
|
||||
@@ -683,13 +688,13 @@ export class Breakpoint implements IBreakpoint {
|
||||
public enabled: boolean,
|
||||
public condition: string,
|
||||
public hitCondition: string,
|
||||
public adapterData: any
|
||||
public adapterData: any,
|
||||
private id = generateUuid()
|
||||
) {
|
||||
if (enabled === undefined) {
|
||||
this.enabled = true;
|
||||
}
|
||||
this.verified = false;
|
||||
this.id = generateUuid();
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
@@ -699,13 +704,11 @@ export class Breakpoint implements IBreakpoint {
|
||||
|
||||
export class FunctionBreakpoint implements IFunctionBreakpoint {
|
||||
|
||||
private id: string;
|
||||
public verified: boolean;
|
||||
public idFromAdapter: number;
|
||||
|
||||
constructor(public name: string, public enabled: boolean, public hitCondition: string) {
|
||||
constructor(public name: string, public enabled: boolean, public hitCondition: string, private id = generateUuid()) {
|
||||
this.verified = false;
|
||||
this.id = generateUuid();
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
@@ -852,6 +855,7 @@ export class Model implements IModel {
|
||||
const ebp = this.exceptionBreakpoints.filter(ebp => ebp.filter === d.filter).pop();
|
||||
return new ExceptionBreakpoint(d.filter, d.label, ebp ? ebp.enabled : d.default);
|
||||
});
|
||||
this._onDidChangeBreakpoints.fire();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -864,8 +868,8 @@ export class Model implements IModel {
|
||||
this._onDidChangeBreakpoints.fire();
|
||||
}
|
||||
|
||||
public addBreakpoints(uri: uri, rawData: IRawBreakpoint[], fireEvent = true): Breakpoint[] {
|
||||
const newBreakpoints = rawData.map(rawBp => new Breakpoint(uri, rawBp.lineNumber, rawBp.column, rawBp.enabled, rawBp.condition, rawBp.hitCondition, undefined));
|
||||
public addBreakpoints(uri: uri, rawData: IBreakpointData[], fireEvent = true): Breakpoint[] {
|
||||
const newBreakpoints = rawData.map(rawBp => new Breakpoint(uri, rawBp.lineNumber, rawBp.column, rawBp.enabled, rawBp.condition, rawBp.hitCondition, undefined, rawBp.id));
|
||||
this.breakpoints = this.breakpoints.concat(newBreakpoints);
|
||||
this.breakpointsActivated = true;
|
||||
this.sortAndDeDup();
|
||||
@@ -882,7 +886,7 @@ export class Model implements IModel {
|
||||
this._onDidChangeBreakpoints.fire({ removed: toRemove });
|
||||
}
|
||||
|
||||
public updateBreakpoints(data: { [id: string]: DebugProtocol.Breakpoint }): void {
|
||||
public updateBreakpoints(data: { [id: string]: IBreakpointUpdateData }): void {
|
||||
const updated: IBreakpoint[] = [];
|
||||
this.breakpoints.forEach(bp => {
|
||||
const bpData = data[bp.getId()];
|
||||
@@ -895,6 +899,8 @@ export class Model implements IModel {
|
||||
bp.idFromAdapter = bpData.id;
|
||||
bp.message = bpData.message;
|
||||
bp.adapterData = bpData.source ? bpData.source.adapterData : bp.adapterData;
|
||||
bp.condition = bpData.condition || bp.condition;
|
||||
bp.hitCondition = bpData.hitCondition || bp.hitCondition;
|
||||
updated.push(bp);
|
||||
}
|
||||
});
|
||||
@@ -955,8 +961,8 @@ export class Model implements IModel {
|
||||
this._onDidChangeBreakpoints.fire({ changed: changed });
|
||||
}
|
||||
|
||||
public addFunctionBreakpoint(functionName: string): FunctionBreakpoint {
|
||||
const newFunctionBreakpoint = new FunctionBreakpoint(functionName, true, null);
|
||||
public addFunctionBreakpoint(functionName: string, id: string): FunctionBreakpoint {
|
||||
const newFunctionBreakpoint = new FunctionBreakpoint(functionName, true, null, id);
|
||||
this.functionBreakpoints.push(newFunctionBreakpoint);
|
||||
this._onDidChangeBreakpoints.fire({ added: [newFunctionBreakpoint] });
|
||||
|
||||
@@ -1046,45 +1052,20 @@ export class Model implements IModel {
|
||||
return this.watchExpressions;
|
||||
}
|
||||
|
||||
public addWatchExpression(process: IProcess, stackFrame: IStackFrame, name: string): TPromise<void> {
|
||||
public addWatchExpression(process: IProcess, stackFrame: IStackFrame, name: string): IExpression {
|
||||
const we = new Expression(name);
|
||||
this.watchExpressions.push(we);
|
||||
if (!name) {
|
||||
this._onDidChangeWatchExpressions.fire(we);
|
||||
return TPromise.as(null);
|
||||
}
|
||||
this._onDidChangeWatchExpressions.fire(we);
|
||||
|
||||
return this.evaluateWatchExpressions(process, stackFrame, we.getId());
|
||||
return we;
|
||||
}
|
||||
|
||||
public renameWatchExpression(process: IProcess, stackFrame: IStackFrame, id: string, newName: string): TPromise<void> {
|
||||
public renameWatchExpression(process: IProcess, stackFrame: IStackFrame, id: string, newName: string): void {
|
||||
const filtered = this.watchExpressions.filter(we => we.getId() === id);
|
||||
if (filtered.length === 1) {
|
||||
filtered[0].name = newName;
|
||||
// Evaluate all watch expressions again since the new watch expression might have changed some.
|
||||
return this.evaluateWatchExpressions(process, stackFrame).then(() => {
|
||||
this._onDidChangeWatchExpressions.fire(filtered[0]);
|
||||
});
|
||||
this._onDidChangeWatchExpressions.fire(filtered[0]);
|
||||
}
|
||||
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public evaluateWatchExpressions(process: IProcess, stackFrame: IStackFrame, id: string = null): TPromise<void> {
|
||||
if (id) {
|
||||
const filtered = this.watchExpressions.filter(we => we.getId() === id);
|
||||
if (filtered.length !== 1) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
return filtered[0].evaluate(process, stackFrame, 'watch').then(() => {
|
||||
this._onDidChangeWatchExpressions.fire(filtered[0]);
|
||||
});
|
||||
}
|
||||
|
||||
return TPromise.join(this.watchExpressions.map(we => we.evaluate(process, stackFrame, 'watch'))).then(() => {
|
||||
this._onDidChangeWatchExpressions.fire();
|
||||
});
|
||||
}
|
||||
|
||||
public removeWatchExpressions(id: string = null): void {
|
||||
|
||||
@@ -233,6 +233,20 @@ declare module DebugProtocol {
|
||||
};
|
||||
}
|
||||
|
||||
/** Event message for 'capabilities' event type.
|
||||
The event indicates that one or more capabilities have changed.
|
||||
Since the capabilities are dependent on the frontend and its UI, it might not be possible to change that at random times (or too late).
|
||||
Consequently this event has a hint characteristic: a frontend can only be expected to make a 'best effort' in honouring individual capabilities but there are no guarantees.
|
||||
Only changed capabilities need to be included, all other capabilities keep their values.
|
||||
*/
|
||||
export interface CapabilitiesEvent extends Event {
|
||||
// event: 'capabilities';
|
||||
body: {
|
||||
/** The set of updated capabilities. */
|
||||
capabilities: Capabilities;
|
||||
};
|
||||
}
|
||||
|
||||
/** runInTerminal request; value of command field is 'runInTerminal'.
|
||||
With this request a debug adapter can run a command in a terminal.
|
||||
*/
|
||||
@@ -1073,6 +1087,8 @@ declare module DebugProtocol {
|
||||
supportsDelayedStackTraceLoading?: boolean;
|
||||
/** The debug adapter supports the 'loadedSources' request. */
|
||||
supportsLoadedSourcesRequest?: boolean;
|
||||
/** The debug adapter supports logpoints by interpreting the 'logMessage' attribute of the SourceBreakpoint. */
|
||||
supportsLogPoints?: boolean;
|
||||
}
|
||||
|
||||
/** An ExceptionBreakpointsFilter is shown in the UI as an option for configuring how exceptions are dealt with. */
|
||||
@@ -1307,9 +1323,9 @@ declare module DebugProtocol {
|
||||
visibility?: string;
|
||||
}
|
||||
|
||||
/** Properties of a breakpoint passed to the setBreakpoints request. */
|
||||
/** Properties of a breakpoint or logpoint passed to the setBreakpoints request. */
|
||||
export interface SourceBreakpoint {
|
||||
/** The source line of the breakpoint. */
|
||||
/** The source line of the breakpoint or logpoint. */
|
||||
line: number;
|
||||
/** An optional source column of the breakpoint. */
|
||||
column?: number;
|
||||
@@ -1317,6 +1333,8 @@ declare module DebugProtocol {
|
||||
condition?: string;
|
||||
/** An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. */
|
||||
hitCondition?: string;
|
||||
/** If this attribute exists and is non-empty, the backend must not 'break' (stop) but log the message instead. Expressions within {} are interpolated. */
|
||||
logMessage?: string;
|
||||
}
|
||||
|
||||
/** Properties of a breakpoint passed to the setFunctionBreakpoints request. */
|
||||
|
||||
@@ -11,6 +11,7 @@ import * as resources from 'vs/base/common/resources';
|
||||
import { DEBUG_SCHEME } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source");
|
||||
|
||||
@@ -19,7 +20,7 @@ export class Source {
|
||||
public readonly uri: uri;
|
||||
public available: boolean;
|
||||
|
||||
constructor(public readonly raw: DebugProtocol.Source, sessionId: string) {
|
||||
constructor(public raw: DebugProtocol.Source, sessionId: string) {
|
||||
if (!raw) {
|
||||
this.raw = { name: UNKNOWN_SOURCE_LABEL };
|
||||
}
|
||||
@@ -56,7 +57,7 @@ export class Source {
|
||||
return this.uri.scheme === DEBUG_SCHEME;
|
||||
}
|
||||
|
||||
public openInEditor(editorService: IWorkbenchEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean): TPromise<any> {
|
||||
public openInEditor(editorService: IWorkbenchEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise<any> {
|
||||
return !this.available ? TPromise.as(null) : editorService.openEditor({
|
||||
resource: this.uri,
|
||||
description: this.origin,
|
||||
@@ -65,7 +66,7 @@ export class Source {
|
||||
selection,
|
||||
revealIfVisible: true,
|
||||
revealInCenterIfOutsideViewport: true,
|
||||
pinned: !preserveFocus && !this.inMemory
|
||||
pinned: pinned || (!preserveFocus && !this.inMemory)
|
||||
}
|
||||
}, sideBySide);
|
||||
}
|
||||
@@ -76,7 +77,7 @@ export class Source {
|
||||
let processId: string;
|
||||
|
||||
switch (modelUri.scheme) {
|
||||
case 'file':
|
||||
case Schemas.file:
|
||||
path = paths.normalize(modelUri.fsPath, true);
|
||||
break;
|
||||
case DEBUG_SCHEME:
|
||||
@@ -110,4 +111,4 @@ export class Source {
|
||||
processId
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,78 +4,92 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import * as debug from 'vs/workbench/parts/debug/common/debug';
|
||||
import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IProcess, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
export class ViewModel implements debug.IViewModel {
|
||||
export class ViewModel implements IViewModel {
|
||||
|
||||
private _focusedStackFrame: debug.IStackFrame;
|
||||
private _focusedProcess: debug.IProcess;
|
||||
private selectedExpression: debug.IExpression;
|
||||
private selectedFunctionBreakpoint: debug.IFunctionBreakpoint;
|
||||
private _onDidFocusProcess: Emitter<debug.IProcess | undefined>;
|
||||
private _onDidFocusStackFrame: Emitter<{ stackFrame: debug.IStackFrame, explicit: boolean }>;
|
||||
private _onDidSelectExpression: Emitter<debug.IExpression>;
|
||||
private _focusedStackFrame: IStackFrame;
|
||||
private _focusedProcess: IProcess;
|
||||
private _focusedThread: IThread;
|
||||
private selectedExpression: IExpression;
|
||||
private selectedFunctionBreakpoint: IFunctionBreakpoint;
|
||||
private _onDidFocusProcess: Emitter<IProcess | undefined>;
|
||||
private _onDidFocusStackFrame: Emitter<{ stackFrame: IStackFrame, explicit: boolean }>;
|
||||
private _onDidSelectExpression: Emitter<IExpression>;
|
||||
private multiProcessView: boolean;
|
||||
private expressionSelectedContextKey: IContextKey<boolean>;
|
||||
private breakpointSelectedContextKey: IContextKey<boolean>;
|
||||
|
||||
constructor() {
|
||||
this._onDidFocusProcess = new Emitter<debug.IProcess | undefined>();
|
||||
this._onDidFocusStackFrame = new Emitter<{ stackFrame: debug.IStackFrame, explicit: boolean }>();
|
||||
this._onDidSelectExpression = new Emitter<debug.IExpression>();
|
||||
constructor(contextKeyService: IContextKeyService) {
|
||||
this._onDidFocusProcess = new Emitter<IProcess | undefined>();
|
||||
this._onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame, explicit: boolean }>();
|
||||
this._onDidSelectExpression = new Emitter<IExpression>();
|
||||
this.multiProcessView = false;
|
||||
this.expressionSelectedContextKey = CONTEXT_EXPRESSION_SELECTED.bindTo(contextKeyService);
|
||||
this.breakpointSelectedContextKey = CONTEXT_BREAKPOINT_SELECTED.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return 'root';
|
||||
}
|
||||
|
||||
public get focusedProcess(): debug.IProcess {
|
||||
public get focusedProcess(): IProcess {
|
||||
return this._focusedProcess;
|
||||
}
|
||||
|
||||
public get focusedThread(): debug.IThread {
|
||||
public get focusedThread(): IThread {
|
||||
return this._focusedStackFrame ? this._focusedStackFrame.thread : (this._focusedProcess ? this._focusedProcess.getAllThreads().pop() : null);
|
||||
}
|
||||
|
||||
public get focusedStackFrame(): debug.IStackFrame {
|
||||
public get focusedStackFrame(): IStackFrame {
|
||||
return this._focusedStackFrame;
|
||||
}
|
||||
|
||||
public setFocusedStackFrame(stackFrame: debug.IStackFrame, process: debug.IProcess, explicit: boolean): void {
|
||||
this._focusedStackFrame = stackFrame;
|
||||
if (process !== this._focusedProcess) {
|
||||
public setFocus(stackFrame: IStackFrame, thread: IThread, process: IProcess, explicit: boolean): void {
|
||||
let shouldEmit = this._focusedProcess !== process || this._focusedThread !== thread || this._focusedStackFrame !== stackFrame;
|
||||
|
||||
if (this._focusedProcess !== process) {
|
||||
this._focusedProcess = process;
|
||||
this._onDidFocusProcess.fire(process);
|
||||
}
|
||||
this._onDidFocusStackFrame.fire({ stackFrame, explicit });
|
||||
this._focusedThread = thread;
|
||||
this._focusedStackFrame = stackFrame;
|
||||
|
||||
if (shouldEmit) {
|
||||
this._onDidFocusStackFrame.fire({ stackFrame, explicit });
|
||||
}
|
||||
}
|
||||
|
||||
public get onDidFocusProcess(): Event<debug.IProcess> {
|
||||
public get onDidFocusProcess(): Event<IProcess> {
|
||||
return this._onDidFocusProcess.event;
|
||||
}
|
||||
|
||||
public get onDidFocusStackFrame(): Event<{ stackFrame: debug.IStackFrame, explicit: boolean }> {
|
||||
public get onDidFocusStackFrame(): Event<{ stackFrame: IStackFrame, explicit: boolean }> {
|
||||
return this._onDidFocusStackFrame.event;
|
||||
}
|
||||
|
||||
public getSelectedExpression(): debug.IExpression {
|
||||
public getSelectedExpression(): IExpression {
|
||||
return this.selectedExpression;
|
||||
}
|
||||
|
||||
public setSelectedExpression(expression: debug.IExpression) {
|
||||
public setSelectedExpression(expression: IExpression) {
|
||||
this.selectedExpression = expression;
|
||||
this.expressionSelectedContextKey.set(!!expression);
|
||||
this._onDidSelectExpression.fire(expression);
|
||||
}
|
||||
|
||||
public get onDidSelectExpression(): Event<debug.IExpression> {
|
||||
public get onDidSelectExpression(): Event<IExpression> {
|
||||
return this._onDidSelectExpression.event;
|
||||
}
|
||||
|
||||
public getSelectedFunctionBreakpoint(): debug.IFunctionBreakpoint {
|
||||
public getSelectedFunctionBreakpoint(): IFunctionBreakpoint {
|
||||
return this.selectedFunctionBreakpoint;
|
||||
}
|
||||
|
||||
public setSelectedFunctionBreakpoint(functionBreakpoint: debug.IFunctionBreakpoint): void {
|
||||
public setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint): void {
|
||||
this.selectedFunctionBreakpoint = functionBreakpoint;
|
||||
this.breakpointSelectedContextKey.set(!!functionBreakpoint);
|
||||
}
|
||||
|
||||
public isMultiProcessView(): boolean {
|
||||
|
||||
@@ -15,10 +15,8 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { BaseDebugController, twistiePixels, renderViewTree } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { BaseDebugController, twistiePixels, renderViewTree } from 'vs/workbench/parts/debug/browser/baseDebugView';
|
||||
import { ITree, IActionProvider, IDataSource, IRenderer, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IAction, IActionItem } from 'vs/base/common/actions';
|
||||
import { RestartAction, StopAction, ContinueAction, StepOverAction, StepIntoAction, StepOutAction, PauseAction, RestartFrameAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { CopyStackTraceAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
|
||||
@@ -26,8 +24,9 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
|
||||
import { basenameOrAuthority } from 'vs/base/common/resources';
|
||||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { TreeResourceNavigator, WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -39,18 +38,19 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
private onCallStackChangeScheduler: RunOnceScheduler;
|
||||
private settings: any;
|
||||
private needsRefresh: boolean;
|
||||
private ignoreSelectionChangedEvent: boolean;
|
||||
private treeContainer: HTMLElement;
|
||||
|
||||
constructor(
|
||||
private options: IViewletViewOptions,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService private themeService: IThemeService,
|
||||
@IListService private listService: IListService
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService, configurationService);
|
||||
this.settings = options.viewletSettings;
|
||||
|
||||
// Create scheduler to prevent unnecessary flashing of tree when reacting to changes
|
||||
@@ -95,26 +95,42 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
dom.addClass(container, 'debug-call-stack');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
const actionProvider = new CallStackActionProvider(this.debugService, this.keybindingService);
|
||||
const controller = this.instantiationService.createInstance(CallStackController, actionProvider, MenuId.DebugCallStackContext);
|
||||
const controller = this.instantiationService.createInstance(CallStackController, actionProvider, MenuId.DebugCallStackContext, {});
|
||||
|
||||
this.tree = new WorkbenchTree(this.treeContainer, {
|
||||
this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
|
||||
dataSource: new CallStackDataSource(),
|
||||
renderer: this.instantiationService.createInstance(CallStackRenderer),
|
||||
accessibilityProvider: this.instantiationService.createInstance(CallstackAccessibilityProvider),
|
||||
controller
|
||||
}, {
|
||||
ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'callStackAriaLabel' }, "Debug Call Stack"),
|
||||
twistiePixels,
|
||||
keyboardSupport: false
|
||||
}, this.contextKeyService, this.listService, this.themeService);
|
||||
twistiePixels
|
||||
});
|
||||
|
||||
this.disposables.push(this.tree.onDidChangeSelection(event => {
|
||||
if (event && event.payload && event.payload.origin === 'keyboard') {
|
||||
const element = this.tree.getFocus();
|
||||
if (element instanceof ThreadAndProcessIds) {
|
||||
controller.showMoreStackFrames(this.tree, element);
|
||||
} else if (element instanceof StackFrame) {
|
||||
controller.focusStackFrame(element, event, false);
|
||||
const callstackNavigator = new TreeResourceNavigator(this.tree);
|
||||
this.disposables.push(callstackNavigator);
|
||||
this.disposables.push(callstackNavigator.openResource(e => {
|
||||
if (this.ignoreSelectionChangedEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const element = e.element;
|
||||
if (element instanceof StackFrame) {
|
||||
this.debugService.focusStackFrame(element, element.thread, element.thread.process, true);
|
||||
element.openInEditor(this.editorService, e.editorOptions.preserveFocus, e.sideBySide, e.editorOptions.pinned).done(undefined, errors.onUnexpectedError);
|
||||
}
|
||||
if (element instanceof Thread) {
|
||||
this.debugService.focusStackFrame(undefined, element, element.process, true);
|
||||
}
|
||||
if (element instanceof Process) {
|
||||
this.debugService.focusStackFrame(undefined, undefined, element, true);
|
||||
}
|
||||
if (element instanceof ThreadAndProcessIds) {
|
||||
const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === element.processId).pop();
|
||||
const thread = process && process.getThread(element.threadId);
|
||||
if (thread) {
|
||||
(<Thread>thread).fetchCallStack()
|
||||
.done(() => this.tree.refresh(), errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -129,8 +145,14 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
this.onCallStackChangeScheduler.schedule();
|
||||
}
|
||||
}));
|
||||
this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(() =>
|
||||
this.updateTreeSelection().done(undefined, errors.onUnexpectedError)));
|
||||
this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(() => {
|
||||
if (!this.isVisible) {
|
||||
this.needsRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateTreeSelection().done(undefined, errors.onUnexpectedError);
|
||||
}));
|
||||
|
||||
// Schedule the update of the call stack tree if the viewlet is opened after a session started #14684
|
||||
if (this.debugService.state === State.Stopped) {
|
||||
@@ -138,6 +160,13 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
}
|
||||
}
|
||||
|
||||
layoutBody(size: number): void {
|
||||
if (this.treeContainer) {
|
||||
this.treeContainer.style.height = size + 'px';
|
||||
}
|
||||
super.layoutBody(size);
|
||||
}
|
||||
|
||||
private updateTreeSelection(): TPromise<void> {
|
||||
if (!this.tree.getInput()) {
|
||||
// Tree not initialized yet
|
||||
@@ -147,13 +176,22 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
const thread = this.debugService.getViewModel().focusedThread;
|
||||
const process = this.debugService.getViewModel().focusedProcess;
|
||||
const updateSelection = (element: IStackFrame | IProcess) => {
|
||||
this.ignoreSelectionChangedEvent = true;
|
||||
try {
|
||||
this.tree.setSelection([element]);
|
||||
} finally {
|
||||
this.ignoreSelectionChangedEvent = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (!thread) {
|
||||
if (!process) {
|
||||
this.tree.clearSelection();
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
this.tree.setSelection([process]);
|
||||
updateSelection(process);
|
||||
return this.tree.reveal(process);
|
||||
}
|
||||
|
||||
@@ -162,7 +200,7 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
this.tree.setSelection([stackFrame]);
|
||||
updateSelection(stackFrame);
|
||||
return this.tree.reveal(stackFrame);
|
||||
});
|
||||
}
|
||||
@@ -182,20 +220,6 @@ export class CallStackView extends TreeViewsViewletPanel {
|
||||
}
|
||||
|
||||
class CallStackController extends BaseDebugController {
|
||||
|
||||
protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
|
||||
if (element instanceof ThreadAndProcessIds) {
|
||||
return this.showMoreStackFrames(tree, element);
|
||||
}
|
||||
if (element instanceof StackFrame) {
|
||||
super.onLeftClick(tree, element, event);
|
||||
this.focusStackFrame(element, event, event.detail !== 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onLeftClick(tree, element, event);
|
||||
}
|
||||
|
||||
protected getContext(element: any): any {
|
||||
if (element instanceof StackFrame) {
|
||||
if (element.source.inMemory) {
|
||||
@@ -208,25 +232,6 @@ class CallStackController extends BaseDebugController {
|
||||
return element.threadId;
|
||||
}
|
||||
}
|
||||
|
||||
// user clicked / pressed on 'Load More Stack Frames', get those stack frames and refresh the tree.
|
||||
public showMoreStackFrames(tree: ITree, threadAndProcessIds: ThreadAndProcessIds): boolean {
|
||||
const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === threadAndProcessIds.processId).pop();
|
||||
const thread = process && process.getThread(threadAndProcessIds.threadId);
|
||||
if (thread) {
|
||||
(<Thread>thread).fetchCallStack()
|
||||
.done(() => tree.refresh(), errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public focusStackFrame(stackFrame: IStackFrame, event: any, preserveFocus: boolean): void {
|
||||
this.debugService.focusStackFrameAndEvaluate(stackFrame, undefined, true).then(() => {
|
||||
const sideBySide = (event && (event.ctrlKey || event.metaKey));
|
||||
return stackFrame.openInEditor(this.editorService, preserveFocus, sideBySide);
|
||||
}, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ToggleViewletAction, Extensions as ViewletExtensions, ViewletRegistry,
|
||||
import { TogglePanelAction, Extensions as PanelExtensions, PanelRegistry, PanelDescriptor } from 'vs/workbench/browser/panel';
|
||||
import { StatusbarItemDescriptor, StatusbarAlignment, IStatusbarRegistry, Extensions as StatusExtensions } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { VariablesView } from 'vs/workbench/parts/debug/electron-browser/variablesView';
|
||||
import { BreakpointsView } from 'vs/workbench/parts/debug/electron-browser/breakpointsView';
|
||||
import { BreakpointsView } from 'vs/workbench/parts/debug/browser/breakpointsView';
|
||||
import { WatchExpressionsView } from 'vs/workbench/parts/debug/electron-browser/watchExpressionsView';
|
||||
import { CallStackView } from 'vs/workbench/parts/debug/electron-browser/callStackView';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
@@ -38,10 +38,10 @@ import { DebugContentProvider } from 'vs/workbench/parts/debug/browser/debugCont
|
||||
import 'vs/workbench/parts/debug/electron-browser/debugEditorContribution';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import * as debugCommands from 'vs/workbench/parts/debug/electron-browser/debugCommands';
|
||||
import { registerCommands } from 'vs/workbench/parts/debug/browser/debugCommands';
|
||||
import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
|
||||
import { StatusBarColorProvider } from 'vs/workbench/parts/debug/electron-browser/statusbarColorProvider';
|
||||
import { ViewLocation, ViewsRegistry } from 'vs/workbench/browser/parts/views/viewsRegistry';
|
||||
import { StatusBarColorProvider } from 'vs/workbench/parts/debug/browser/statusbarColorProvider';
|
||||
import { ViewLocation, ViewsRegistry } from 'vs/workbench/common/views';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import URI from 'vs/base/common/uri';
|
||||
@@ -50,6 +50,7 @@ import { Repl } from 'vs/workbench/parts/debug/electron-browser/repl';
|
||||
import { DebugQuickOpenHandler } from 'vs/workbench/parts/debug/browser/debugQuickOpen';
|
||||
import { DebugStatus } from 'vs/workbench/parts/debug/browser/debugStatus';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration';
|
||||
|
||||
class OpenDebugViewletAction extends ToggleViewletAction {
|
||||
public static readonly ID = VIEWLET_ID;
|
||||
@@ -107,10 +108,10 @@ Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(new PanelDescri
|
||||
Registry.as<PanelRegistry>(PanelExtensions.Panels).setDefaultPanelId(REPL_ID);
|
||||
|
||||
// Register default debug views
|
||||
ViewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctor: VariablesView, order: 10, size: 40, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctor: WatchExpressionsView, order: 20, size: 10, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctor: CallStackView, order: 30, size: 30, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctor: BreakpointsView, order: 40, size: 20, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctor: VariablesView, order: 10, weight: 40, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctor: WatchExpressionsView, order: 20, weight: 10, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctor: CallStackView, order: 30, weight: 30, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctor: BreakpointsView, order: 40, weight: 20, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
|
||||
// register action to open viewlet
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionRegistryExtensions.WorkbenchActions);
|
||||
@@ -201,17 +202,18 @@ configurationRegistry.registerConfiguration({
|
||||
'debug.openDebug': {
|
||||
enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'],
|
||||
default: 'openOnFirstSessionStart',
|
||||
description: nls.localize('openDebug', "Controls whether debug viewlet should be open on debugging session start.")
|
||||
description: nls.localize('openDebug', "Controls whether debug view should be open on debugging session start.")
|
||||
},
|
||||
'launch': {
|
||||
type: 'object',
|
||||
description: nls.localize({ comment: ['This is the description for a setting'], key: 'launch' }, "Global debug launch configuration. Should be used as an alternative to 'launch.json' that is shared across workspaces"),
|
||||
default: {}
|
||||
default: { configurations: [], compounds: [] },
|
||||
$ref: launchSchemaId
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
debugCommands.registerCommands();
|
||||
registerCommands();
|
||||
|
||||
// Register Debug Status
|
||||
const statusBar = Registry.as<IStatusbarRegistry>(StatusExtensions.Statusbar);
|
||||
@@ -223,7 +225,7 @@ if (isMacintosh) {
|
||||
const registerTouchBarEntry = (id: string, title: string, order, when: ContextKeyExpr, icon: string) => {
|
||||
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
|
||||
command: {
|
||||
id, title, iconPath: URI.parse(require.toUrl(`vs/workbench/parts/debug/electron-browser/media/${icon}`)).fsPath
|
||||
id, title, iconPath: { dark: URI.parse(require.toUrl(`vs/workbench/parts/debug/electron-browser/media/${icon}`)).fsPath }
|
||||
},
|
||||
when,
|
||||
group: '9_debug',
|
||||
@@ -239,4 +241,4 @@ if (isMacintosh) {
|
||||
registerTouchBarEntry(StepOutAction.ID, StepOutAction.LABEL, 4, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepout-tb.png');
|
||||
registerTouchBarEntry(RestartAction.ID, RestartAction.LABEL, 5, CONTEXT_IN_DEBUG_MODE, 'restart-tb.png');
|
||||
registerTouchBarEntry(StopAction.ID, StopAction.LABEL, 6, CONTEXT_IN_DEBUG_MODE, 'stop-tb.png');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,25 +14,27 @@ import * as objects from 'vs/base/common/objects';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { IModel } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IEditor } from 'vs/platform/editor/common/editor';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import * as extensionsRegistry from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
import * as extensionsRegistry from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IDebugConfigurationProvider, IRawAdapter, ICompound, IDebugConfiguration, IConfig, IEnvConfig, IGlobalConfig, IConfigurationManager, ILaunch } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IDebugConfigurationProvider, IRawAdapter, ICompound, IDebugConfiguration, IConfig, IEnvConfig, IGlobalConfig, IConfigurationManager, ILaunch, IAdapterExecutable } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences';
|
||||
|
||||
// debuggers extension point
|
||||
export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<IRawAdapter[]>('debuggers', [], {
|
||||
@@ -102,11 +104,11 @@ export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerE
|
||||
}
|
||||
},
|
||||
osx: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.osx', "OS X specific settings."),
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.osx', "macOS specific settings."),
|
||||
type: 'object',
|
||||
properties: {
|
||||
runtime: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.osx.runtime', "Runtime used for OSX."),
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.osx.runtime', "Runtime used for macOS."),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
@@ -147,14 +149,12 @@ const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtens
|
||||
});
|
||||
|
||||
// debug general schema
|
||||
|
||||
export const schemaId = 'vscode://schemas/launch';
|
||||
const defaultCompound: ICompound = { name: 'Compound', configurations: [] };
|
||||
const schema: IJSONSchema = {
|
||||
id: schemaId,
|
||||
id: launchSchemaId,
|
||||
type: 'object',
|
||||
title: nls.localize('app.launch.json.title', "Launch"),
|
||||
required: ['version', 'configurations'],
|
||||
required: [],
|
||||
default: { version: '0.2.0', configurations: [], compounds: [] },
|
||||
properties: {
|
||||
version: {
|
||||
@@ -186,7 +186,23 @@ const schema: IJSONSchema = {
|
||||
type: 'array',
|
||||
default: [],
|
||||
items: {
|
||||
type: 'string'
|
||||
oneOf: [{
|
||||
enum: [],
|
||||
description: nls.localize('useUniqueNames', "Please use unique configuration names.")
|
||||
}, {
|
||||
type: 'object',
|
||||
required: ['name'],
|
||||
properties: {
|
||||
name: {
|
||||
enum: [],
|
||||
description: nls.localize('app.launch.json.compound.name', "Name of compound. Appears in the launch configuration drop down menu.")
|
||||
},
|
||||
folder: {
|
||||
enum: [],
|
||||
description: nls.localize('app.launch.json.compound.folder', "Name of folder in which the compound is located.")
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
description: nls.localize('app.launch.json.compounds.configurations', "Names of configurations that will be started as part of this compound.")
|
||||
}
|
||||
@@ -201,7 +217,7 @@ const schema: IJSONSchema = {
|
||||
};
|
||||
|
||||
const jsonRegistry = <IJSONContributionRegistry>Registry.as(JSONExtensions.JSONContribution);
|
||||
jsonRegistry.registerSchema(schemaId, schema);
|
||||
jsonRegistry.registerSchema(launchSchemaId, schema);
|
||||
const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname';
|
||||
const DEBUG_SELECTED_ROOT = 'debug.selectedroot';
|
||||
|
||||
@@ -209,8 +225,8 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
private adapters: Adapter[];
|
||||
private breakpointModeIdsSet = new Set<string>();
|
||||
private launches: ILaunch[];
|
||||
private _selectedName: string;
|
||||
private _selectedLaunch: ILaunch;
|
||||
private selectedName: string;
|
||||
private selectedLaunch: ILaunch;
|
||||
private toDispose: IDisposable[];
|
||||
private _onDidSelectConfigurationName = new Emitter<void>();
|
||||
private providers: IDebugConfigurationProvider[];
|
||||
@@ -232,7 +248,7 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
this.registerListeners(lifecycleService);
|
||||
this.initLaunches();
|
||||
const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE);
|
||||
const filtered = this.launches.filter(l => l.workspace.uri.toString() === previousSelectedRoot);
|
||||
const filtered = this.launches.filter(l => l.uri.toString() === previousSelectedRoot);
|
||||
this.selectConfiguration(filtered.length ? filtered[0] : undefined, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE));
|
||||
}
|
||||
|
||||
@@ -276,6 +292,14 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
.then(results => results.reduce((first, second) => first.concat(second), []));
|
||||
}
|
||||
|
||||
public debugAdapterExecutable(folderUri: uri | undefined, type: string): TPromise<IAdapterExecutable | undefined> {
|
||||
const providers = this.providers.filter(p => p.type === type && p.debugAdapterExecutable);
|
||||
if (providers.length === 1) {
|
||||
return providers[0].debugAdapterExecutable(folderUri);
|
||||
}
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
|
||||
private registerListeners(lifecycleService: ILifecycleService): void {
|
||||
debuggersExtPoint.setHandler((extensions) => {
|
||||
extensions.forEach(extension => {
|
||||
@@ -293,7 +317,7 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
if (duplicate) {
|
||||
duplicate.merge(rawAdapter, extension.description);
|
||||
} else {
|
||||
this.adapters.push(new Adapter(rawAdapter, extension.description, this.configurationService, this.commandService));
|
||||
this.adapters.push(new Adapter(this, rawAdapter, extension.description, this.configurationService, this.commandService));
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -310,6 +334,8 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
items.defaultSnippets.push(...configurationSnippets);
|
||||
}
|
||||
});
|
||||
|
||||
this.setCompoundSchemaValues();
|
||||
});
|
||||
|
||||
breakpointsExtPoint.setHandler(extensions => {
|
||||
@@ -323,10 +349,12 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
this.toDispose.push(this.contextService.onDidChangeWorkspaceFolders(() => {
|
||||
this.initLaunches();
|
||||
this.selectConfiguration();
|
||||
this.setCompoundSchemaValues();
|
||||
}));
|
||||
this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('launch')) {
|
||||
this.selectConfiguration();
|
||||
this.setCompoundSchemaValues();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -335,42 +363,75 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
|
||||
private initLaunches(): void {
|
||||
this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, folder));
|
||||
if (this.launches.indexOf(this._selectedLaunch) === -1) {
|
||||
this._selectedLaunch = undefined;
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||
this.launches.push(this.instantiationService.createInstance(WorkspaceLaunch, this));
|
||||
}
|
||||
this.launches.push(this.instantiationService.createInstance(UserLaunch, this));
|
||||
|
||||
if (this.launches.indexOf(this.selectedLaunch) === -1) {
|
||||
this.selectedLaunch = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private setCompoundSchemaValues(): void {
|
||||
const compoundConfigurationsSchema = (<IJSONSchema>schema.properties['compounds'].items).properties['configurations'];
|
||||
const launchNames = this.launches.map(l =>
|
||||
l.getConfigurationNames(false)).reduce((first, second) => first.concat(second), []);
|
||||
(<IJSONSchema>compoundConfigurationsSchema.items).oneOf[0].enum = launchNames;
|
||||
(<IJSONSchema>compoundConfigurationsSchema.items).oneOf[1].properties.name.enum = launchNames;
|
||||
|
||||
const folderNames = this.contextService.getWorkspace().folders.map(f => f.name);
|
||||
(<IJSONSchema>compoundConfigurationsSchema.items).oneOf[1].properties.folder.enum = folderNames;
|
||||
|
||||
jsonRegistry.registerSchema(launchSchemaId, schema);
|
||||
}
|
||||
|
||||
public getLaunches(): ILaunch[] {
|
||||
return this.launches;
|
||||
}
|
||||
|
||||
public get selectedLaunch(): ILaunch {
|
||||
return this._selectedLaunch;
|
||||
public getLaunch(workspaceUri: uri): ILaunch {
|
||||
if (!uri.isUri(workspaceUri)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.launches.filter(l => l.workspace && l.workspace.uri.toString() === workspaceUri.toString()).pop();
|
||||
}
|
||||
|
||||
public get selectedName(): string {
|
||||
return this._selectedName;
|
||||
public get selectedConfiguration(): { launch: ILaunch, name: string } {
|
||||
return {
|
||||
launch: this.selectedLaunch,
|
||||
name: this.selectedName
|
||||
};
|
||||
}
|
||||
|
||||
public get onDidSelectConfiguration(): Event<void> {
|
||||
return this._onDidSelectConfigurationName.event;
|
||||
}
|
||||
|
||||
public getWorkspaceLaunch(): ILaunch {
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||
return this.launches[this.launches.length - 1];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public selectConfiguration(launch?: ILaunch, name?: string, debugStarted?: boolean): void {
|
||||
const previousLaunch = this._selectedLaunch;
|
||||
const previousName = this._selectedName;
|
||||
const previousLaunch = this.selectedLaunch;
|
||||
const previousName = this.selectedName;
|
||||
|
||||
if (!launch) {
|
||||
launch = this.selectedLaunch && this.selectedLaunch.getConfigurationNames().length ? this.selectedLaunch : first(this.launches, l => !!l.getConfigurationNames().length, this.launches.length ? this.launches[0] : undefined);
|
||||
}
|
||||
|
||||
this._selectedLaunch = launch;
|
||||
this.selectedLaunch = launch;
|
||||
const names = launch ? launch.getConfigurationNames() : [];
|
||||
if (name && names.indexOf(name) >= 0) {
|
||||
this._selectedName = name;
|
||||
this.selectedName = name;
|
||||
}
|
||||
if (names.indexOf(this.selectedName) === -1) {
|
||||
this._selectedName = names.length ? names[0] : undefined;
|
||||
this.selectedName = names.length ? names[0] : undefined;
|
||||
}
|
||||
|
||||
if (this.selectedLaunch !== previousLaunch || this.selectedName !== previousName) {
|
||||
@@ -378,10 +439,10 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
}
|
||||
}
|
||||
|
||||
public canSetBreakpointsIn(model: IModel): boolean {
|
||||
public canSetBreakpointsIn(model: ITextModel): boolean {
|
||||
const modeId = model ? model.getLanguageIdentifier().language : null;
|
||||
if (!modeId || modeId === 'jsonc') {
|
||||
// do not allow breakpoints in our settings files
|
||||
if (!modeId || modeId === 'jsonc' || modeId === 'log') {
|
||||
// do not allow breakpoints in our settings files and output
|
||||
return false;
|
||||
}
|
||||
if (this.configurationService.getValue<IDebugConfiguration>('debug').allowBreakpointsEverywhere) {
|
||||
@@ -403,6 +464,7 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
}
|
||||
|
||||
const editor = this.editorService.getActiveEditor();
|
||||
let candidates: Adapter[];
|
||||
if (editor) {
|
||||
const codeEditor = editor.getControl();
|
||||
if (isCodeEditor(codeEditor)) {
|
||||
@@ -412,10 +474,16 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
if (adapters.length === 1) {
|
||||
return TPromise.as(adapters[0]);
|
||||
}
|
||||
if (adapters.length > 1) {
|
||||
candidates = adapters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.quickOpenService.pick([...this.adapters.filter(a => a.hasInitialConfiguration() || a.hasConfigurationProvider), { label: 'More...', separator: { border: true } }], { placeHolder: nls.localize('selectDebug', "Select Environment") })
|
||||
if (!candidates) {
|
||||
candidates = this.adapters.filter(a => a.hasInitialConfiguration() || a.hasConfigurationProvider);
|
||||
}
|
||||
return this.quickOpenService.pick([...candidates, { label: 'More...', separator: { border: true } }], { placeHolder: nls.localize('selectDebug', "Select Environment") })
|
||||
.then(picked => {
|
||||
if (picked instanceof Adapter) {
|
||||
return picked;
|
||||
@@ -430,8 +498,8 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
|
||||
private store(): void {
|
||||
this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE);
|
||||
if (this._selectedLaunch) {
|
||||
this.storageService.store(DEBUG_SELECTED_ROOT, this._selectedLaunch.workspace.uri.toString(), StorageScope.WORKSPACE);
|
||||
if (this.selectedLaunch) {
|
||||
this.storageService.store(DEBUG_SELECTED_ROOT, this.selectedLaunch.uri.toString(), StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,15 +514,32 @@ class Launch implements ILaunch {
|
||||
private configurationManager: ConfigurationManager,
|
||||
public workspace: IWorkspaceFolder,
|
||||
@IFileService private fileService: IFileService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IConfigurationResolverService private configurationResolverService: IConfigurationResolverService
|
||||
@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
|
||||
@IConfigurationService protected configurationService: IConfigurationService,
|
||||
@IConfigurationResolverService private configurationResolverService: IConfigurationResolverService,
|
||||
@IWorkspaceContextService protected contextService: IWorkspaceContextService
|
||||
) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public get uri(): uri {
|
||||
return this.workspace.uri.with({ path: paths.join(this.workspace.uri.path, '/.vscode/launch.json') });
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return this.workspace.name;
|
||||
}
|
||||
|
||||
public get hidden(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected getConfig(): IGlobalConfig {
|
||||
return this.configurationService.inspect<IGlobalConfig>('launch', { resource: this.workspace.uri }).workspaceFolder;
|
||||
}
|
||||
|
||||
public getCompound(name: string): ICompound {
|
||||
const config = this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri });
|
||||
const config = this.getConfig();
|
||||
if (!config || !config.compounds) {
|
||||
return null;
|
||||
}
|
||||
@@ -462,13 +547,13 @@ class Launch implements ILaunch {
|
||||
return config.compounds.filter(compound => compound.name === name).pop();
|
||||
}
|
||||
|
||||
public getConfigurationNames(): string[] {
|
||||
const config = this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri });
|
||||
public getConfigurationNames(includeCompounds = true): string[] {
|
||||
const config = this.getConfig();
|
||||
if (!config || !config.configurations || !Array.isArray(config.configurations)) {
|
||||
return [];
|
||||
} else {
|
||||
const names = config.configurations.filter(cfg => cfg && typeof cfg.name === 'string').map(cfg => cfg.name);
|
||||
if (names.length > 0 && config.compounds) {
|
||||
if (includeCompounds && config.compounds) {
|
||||
if (config.compounds) {
|
||||
names.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length)
|
||||
.map(compound => compound.name));
|
||||
@@ -480,7 +565,8 @@ class Launch implements ILaunch {
|
||||
}
|
||||
|
||||
public getConfiguration(name: string): IConfig {
|
||||
const config = objects.deepClone(this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri }));
|
||||
// We need to clone the configuration in order to be able to make changes to it #42198
|
||||
const config = objects.deepClone(this.getConfig());
|
||||
if (!config || !config.configurations) {
|
||||
return null;
|
||||
}
|
||||
@@ -488,6 +574,18 @@ class Launch implements ILaunch {
|
||||
return config.configurations.filter(config => config && config.name === name).shift();
|
||||
}
|
||||
|
||||
protected getWorkspaceForResolving(): IWorkspaceFolder {
|
||||
if (this.workspace) {
|
||||
return this.workspace;
|
||||
}
|
||||
|
||||
if (this.contextService.getWorkspace().folders.length === 1) {
|
||||
return this.contextService.getWorkspace().folders[0];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public resolveConfiguration(config: IConfig): TPromise<IConfig> {
|
||||
const result = objects.deepClone(config) as IConfig;
|
||||
// Set operating system specific properties #1873
|
||||
@@ -504,22 +602,18 @@ class Launch implements ILaunch {
|
||||
|
||||
// massage configuration attributes - append workspace path to relatvie paths, substitute variables in paths.
|
||||
Object.keys(result).forEach(key => {
|
||||
result[key] = this.configurationResolverService.resolveAny(this.workspace, result[key]);
|
||||
result[key] = this.configurationResolverService.resolveAny(this.getWorkspaceForResolving(), result[key]);
|
||||
});
|
||||
|
||||
const adapter = this.configurationManager.getAdapter(result.type);
|
||||
return this.configurationResolverService.resolveInteractiveVariables(result, adapter ? adapter.variables : null);
|
||||
}
|
||||
|
||||
public get uri(): uri {
|
||||
return this.workspace.uri.with({ path: paths.join(this.workspace.uri.path, '/.vscode/launch.json') });
|
||||
}
|
||||
|
||||
public openConfigFile(sideBySide: boolean, type?: string): TPromise<IEditor> {
|
||||
const resource = this.uri;
|
||||
let configFileCreated = false;
|
||||
let pinned = false;
|
||||
|
||||
return this.fileService.resolveContent(resource).then(content => content, err => {
|
||||
return this.fileService.resolveContent(resource).then(content => content.value, err => {
|
||||
|
||||
// launch.json not found: create one by collecting launch configs from debugConfigProviders
|
||||
|
||||
@@ -537,20 +631,20 @@ class Launch implements ILaunch {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
configFileCreated = true;
|
||||
pinned = true; // pin only if config file is created #8727
|
||||
return this.fileService.updateContent(resource, content).then(() => {
|
||||
// convert string into IContent; see #32135
|
||||
return { value: content };
|
||||
return content;
|
||||
});
|
||||
});
|
||||
}).then(content => {
|
||||
if (!content) {
|
||||
return undefined;
|
||||
}
|
||||
const index = content.value.indexOf(`"${this.configurationManager.selectedName}"`);
|
||||
const index = content.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`);
|
||||
let startLineNumber = 1;
|
||||
for (let i = 0; i < index; i++) {
|
||||
if (content.value.charAt(i) === '\n') {
|
||||
if (content.charAt(i) === '\n') {
|
||||
startLineNumber++;
|
||||
}
|
||||
}
|
||||
@@ -561,7 +655,7 @@ class Launch implements ILaunch {
|
||||
options: {
|
||||
forceOpen: true,
|
||||
selection,
|
||||
pinned: configFileCreated, // pin only if config file is created #8727
|
||||
pinned,
|
||||
revealIfVisible: true
|
||||
},
|
||||
}, sideBySide);
|
||||
@@ -570,3 +664,68 @@ class Launch implements ILaunch {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class WorkspaceLaunch extends Launch implements ILaunch {
|
||||
|
||||
constructor(
|
||||
configurationManager: ConfigurationManager,
|
||||
@IFileService fileService: IFileService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IConfigurationResolverService configurationResolverService: IConfigurationResolverService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(configurationManager, undefined, fileService, editorService, configurationService, configurationResolverService, contextService);
|
||||
}
|
||||
|
||||
get uri(): uri {
|
||||
return this.contextService.getWorkspace().configuration;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return nls.localize('workspace', "workspace");
|
||||
}
|
||||
|
||||
protected getConfig(): IGlobalConfig {
|
||||
return this.configurationService.inspect<IGlobalConfig>('launch').workspace;
|
||||
}
|
||||
|
||||
openConfigFile(sideBySide: boolean, type?: string): TPromise<IEditor> {
|
||||
return this.editorService.openEditor({ resource: this.contextService.getWorkspace().configuration });
|
||||
}
|
||||
}
|
||||
|
||||
class UserLaunch extends Launch implements ILaunch {
|
||||
|
||||
constructor(
|
||||
configurationManager: ConfigurationManager,
|
||||
@IFileService fileService: IFileService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IConfigurationResolverService configurationResolverService: IConfigurationResolverService,
|
||||
@IPreferencesService private preferencesService: IPreferencesService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(configurationManager, undefined, fileService, editorService, configurationService, configurationResolverService, contextService);
|
||||
}
|
||||
|
||||
get uri(): uri {
|
||||
return this.preferencesService.userSettingsResource;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return nls.localize('user settings', "user settings");
|
||||
}
|
||||
|
||||
public get hidden(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected getConfig(): IGlobalConfig {
|
||||
return this.configurationService.inspect<IGlobalConfig>('launch').user;
|
||||
}
|
||||
|
||||
openConfigFile(sideBySide: boolean, type?: string): TPromise<IEditor> {
|
||||
return this.preferencesService.openGlobalSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ import { StandardTokenType } from 'vs/editor/common/modes';
|
||||
import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper';
|
||||
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { IDecorationOptions, IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/editorCommon';
|
||||
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
|
||||
import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -27,7 +28,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { DebugHoverWidget } from 'vs/workbench/parts/debug/electron-browser/debugHover';
|
||||
import { RemoveBreakpointAction, EditConditionalBreakpointAction, EnableBreakpointAction, DisableBreakpointAction, AddConditionalBreakpointAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { IDebugEditorContribution, IDebugService, State, IBreakpoint, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo } from 'vs/workbench/parts/debug/common/debug';
|
||||
@@ -40,7 +41,7 @@ import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
|
||||
import { first } from 'vs/base/common/arrays';
|
||||
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { ContextSubMenu } from 'vs/base/browser/contextmenu';
|
||||
|
||||
const HOVER_DELAY = 300;
|
||||
const LAUNCH_JSON_REGEX = /launch\.json$/;
|
||||
@@ -77,13 +78,12 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
@ICommandService private commandService: ICommandService,
|
||||
@ICodeEditorService private codeEditorService: ICodeEditorService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IListService listService: IListService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService
|
||||
) {
|
||||
this.breakpointHintDecoration = [];
|
||||
this.hoverWidget = new DebugHoverWidget(this.editor, this.debugService, this.instantiationService, themeService, contextKeyService, listService);
|
||||
this.hoverWidget = new DebugHoverWidget(this.editor, this.debugService, this.instantiationService, themeService);
|
||||
this.toDispose = [];
|
||||
this.showHoverScheduler = new RunOnceScheduler(() => this.showHover(this.hoverRange, false), HOVER_DELAY);
|
||||
this.hideHoverScheduler = new RunOnceScheduler(() => this.hoverWidget.hide(), HOVER_DELAY);
|
||||
@@ -409,12 +409,13 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
// configuration widget
|
||||
private updateConfigurationWidgetVisibility(): void {
|
||||
const model = this.editor.getModel();
|
||||
if (this.configurationWidget) {
|
||||
this.configurationWidget.dispose();
|
||||
}
|
||||
if (model && LAUNCH_JSON_REGEX.test(model.uri.toString())) {
|
||||
this.configurationWidget = this.instantiationService.createInstance(FloatingClickWidget, this.editor, nls.localize('addConfiguration', "Add Configuration..."), null);
|
||||
this.configurationWidget.render();
|
||||
this.toDispose.push(this.configurationWidget.onClick(() => this.addLaunchConfiguration().done(undefined, errors.onUnexpectedError)));
|
||||
} else if (this.configurationWidget) {
|
||||
this.configurationWidget.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,7 +469,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
}
|
||||
|
||||
private static BREAKPOINT_HELPER_DECORATION: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-hint-glyph',
|
||||
glyphMarginClassName: 'debug-breakpoint-hint',
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
|
||||
};
|
||||
|
||||
@@ -581,6 +582,10 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
if (!this.wordToLineNumbersMap) {
|
||||
this.wordToLineNumbersMap = new Map<string, Position[]>();
|
||||
const model = this.editor.getModel();
|
||||
if (!model) {
|
||||
return this.wordToLineNumbersMap;
|
||||
}
|
||||
|
||||
// For every word in every line, map its ranges for fast lookup
|
||||
for (let lineNumber = 1, len = model.getLineCount(); lineNumber <= len; ++lineNumber) {
|
||||
const lineContent = model.getLineContent(lineNumber);
|
||||
@@ -592,11 +597,14 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
|
||||
model.forceTokenization(lineNumber);
|
||||
const lineTokens = model.getLineTokens(lineNumber);
|
||||
for (let token = lineTokens.firstToken(); !!token; token = token.next()) {
|
||||
const tokenStr = lineContent.substring(token.startOffset, token.endOffset);
|
||||
for (let tokenIndex = 0, tokenCount = lineTokens.getCount(); tokenIndex < tokenCount; tokenIndex++) {
|
||||
const tokenStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
const tokenEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
const tokenType = lineTokens.getStandardTokenType(tokenIndex);
|
||||
const tokenStr = lineContent.substring(tokenStartOffset, tokenEndOffset);
|
||||
|
||||
// Token is a word and not a comment
|
||||
if (token.tokenType === StandardTokenType.Other) {
|
||||
if (tokenType === StandardTokenType.Other) {
|
||||
DEFAULT_WORD_REGEXP.lastIndex = 0; // We assume tokens will usually map 1:1 to words if they match
|
||||
const wordMatch = DEFAULT_WORD_REGEXP.exec(tokenStr);
|
||||
|
||||
@@ -606,7 +614,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
this.wordToLineNumbersMap.set(word, []);
|
||||
}
|
||||
|
||||
this.wordToLineNumbersMap.get(word).push(new Position(lineNumber, token.startOffset));
|
||||
this.wordToLineNumbersMap.get(word).push(new Position(lineNumber, tokenStartOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { DefaultController, ICancelableEvent, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { ICancelableEvent, OpenMode } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
@@ -19,14 +19,14 @@ import { IContentWidget, ICodeEditor, IContentWidgetPosition, ContentWidgetPosit
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDebugService, IExpression, IExpressionContainer } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Expression } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { renderExpressionValue } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { renderExpressionValue } from 'vs/workbench/parts/debug/browser/baseDebugView';
|
||||
import { VariablesDataSource, VariablesRenderer } from 'vs/workbench/parts/debug/electron-browser/variablesView';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
const $ = dom.$;
|
||||
const MAX_ELEMENTS_SHOWN = 18;
|
||||
@@ -54,9 +54,7 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
private editor: ICodeEditor,
|
||||
private debugService: IDebugService,
|
||||
private instantiationService: IInstantiationService,
|
||||
private themeService: IThemeService,
|
||||
private contextKeyService: IContextKeyService,
|
||||
private listService: IListService
|
||||
private themeService: IThemeService
|
||||
) {
|
||||
this.toDispose = [];
|
||||
|
||||
@@ -71,16 +69,15 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
this.complexValueTitle = dom.append(this.complexValueContainer, $('.title'));
|
||||
this.treeContainer = dom.append(this.complexValueContainer, $('.debug-hover-tree'));
|
||||
this.treeContainer.setAttribute('role', 'tree');
|
||||
this.tree = new WorkbenchTree(this.treeContainer, {
|
||||
this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
|
||||
dataSource: new VariablesDataSource(),
|
||||
renderer: this.instantiationService.createInstance(VariablesHoverRenderer),
|
||||
controller: new DebugHoverController(this.editor)
|
||||
controller: this.instantiationService.createInstance(DebugHoverController, this.editor)
|
||||
}, {
|
||||
indentPixels: 6,
|
||||
twistiePixels: 15,
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Debug Hover"),
|
||||
keyboardSupport: false
|
||||
}, this.contextKeyService, this.listService, this.themeService);
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Debug Hover")
|
||||
});
|
||||
|
||||
this.valueContainer = $('.value');
|
||||
this.valueContainer.tabIndex = 0;
|
||||
@@ -337,10 +334,13 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class DebugHoverController extends DefaultController {
|
||||
class DebugHoverController extends WorkbenchTreeController {
|
||||
|
||||
constructor(private editor: ICodeEditor) {
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false });
|
||||
constructor(
|
||||
private editor: ICodeEditor,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ openMode: OpenMode.SINGLE_CLICK }, configurationService);
|
||||
}
|
||||
|
||||
protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin = 'mouse'): boolean {
|
||||
|
||||
@@ -11,7 +11,6 @@ import * as strings from 'vs/base/common/strings';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { first, distinct } from 'vs/base/common/arrays';
|
||||
import { isObject, isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
@@ -22,10 +21,9 @@ import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMarkerService } from 'vs/platform/markers/common/markers';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
|
||||
@@ -37,7 +35,7 @@ import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression,
|
||||
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
|
||||
import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager';
|
||||
import { ToggleMarkersPanelAction } from 'vs/workbench/parts/markers/browser/markersPanelActions';
|
||||
import Constants from 'vs/workbench/parts/markers/common/constants';
|
||||
import { ITaskService, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService';
|
||||
import { TaskError } from 'vs/workbench/parts/tasks/common/taskSystem';
|
||||
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
|
||||
@@ -53,6 +51,10 @@ import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-br
|
||||
import { IRemoteConsoleLog, parse, getFirstFrame } from 'vs/base/node/console';
|
||||
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
|
||||
import { TaskEvent, TaskEventKind } from 'vs/workbench/parts/tasks/common/tasks';
|
||||
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { normalizeDriveLetter } from 'vs/base/common/labels';
|
||||
|
||||
const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
|
||||
const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated';
|
||||
@@ -88,7 +90,8 @@ export class DebugService implements debug.IDebugService {
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@IViewletService private viewletService: IViewletService,
|
||||
@IPanelService private panelService: IPanelService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IChoiceService private choiceService: IChoiceService,
|
||||
@IPartService private partService: IPartService,
|
||||
@IWindowService private windowService: IWindowService,
|
||||
@IBroadcastService private broadcastService: IBroadcastService,
|
||||
@@ -122,7 +125,7 @@ export class DebugService implements debug.IDebugService {
|
||||
this.model = new Model(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(),
|
||||
this.loadExceptionBreakpoints(), this.loadWatchExpressions());
|
||||
this.toDispose.push(this.model);
|
||||
this.viewModel = new ViewModel();
|
||||
this.viewModel = new ViewModel(contextKeyService);
|
||||
this.firstSessionStart = true;
|
||||
|
||||
this.registerListeners();
|
||||
@@ -252,7 +255,7 @@ export class DebugService implements debug.IDebugService {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
this.focusStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError);
|
||||
this.focusStackFrame(stackFrameToFocus);
|
||||
if (thread.stoppedDetails) {
|
||||
this.windowService.focusWindow();
|
||||
aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.range.startLineNumber));
|
||||
@@ -274,7 +277,7 @@ export class DebugService implements debug.IDebugService {
|
||||
if (session) {
|
||||
session.disconnect().done(null, errors.onUnexpectedError);
|
||||
}
|
||||
this.messageService.show(severity.Error, e.message);
|
||||
this.notificationService.error(e.message);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -309,7 +312,7 @@ export class DebugService implements debug.IDebugService {
|
||||
aria.status(nls.localize('debuggingStopped', "Debugging stopped."));
|
||||
if (session && session.getId() === event.sessionId) {
|
||||
if (event.body && event.body.restart && process) {
|
||||
this.restartProcess(process, event.body.restart).done(null, err => this.messageService.show(severity.Error, err.message));
|
||||
this.restartProcess(process, event.body.restart).done(null, err => this.notificationService.error(err.message));
|
||||
} else {
|
||||
session.disconnect().done(null, errors.onUnexpectedError);
|
||||
}
|
||||
@@ -320,7 +323,7 @@ export class DebugService implements debug.IDebugService {
|
||||
const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId;
|
||||
this.model.clearThreads(session.getId(), false, threadId);
|
||||
if (this.viewModel.focusedProcess.getId() === session.getId()) {
|
||||
this.focusStackFrameAndEvaluate(null, this.viewModel.focusedProcess).done(null, errors.onUnexpectedError);
|
||||
this.focusStackFrame(undefined, this.viewModel.focusedThread, this.viewModel.focusedProcess);
|
||||
}
|
||||
this.updateStateAndEmit(session.getId(), debug.State.Running);
|
||||
}));
|
||||
@@ -534,21 +537,34 @@ export class DebugService implements debug.IDebugService {
|
||||
}
|
||||
}
|
||||
|
||||
public focusStackFrameAndEvaluate(stackFrame: debug.IStackFrame, process?: debug.IProcess, explicit?: boolean): TPromise<void> {
|
||||
public focusStackFrame(stackFrame: debug.IStackFrame, thread?: debug.IThread, process?: debug.IProcess, explicit?: boolean): void {
|
||||
if (!process) {
|
||||
const processes = this.model.getProcesses();
|
||||
process = stackFrame ? stackFrame.thread.process : processes.length ? processes[0] : null;
|
||||
if (stackFrame || thread) {
|
||||
process = stackFrame ? stackFrame.thread.process : thread.process;
|
||||
} else {
|
||||
const processes = this.model.getProcesses();
|
||||
process = processes.length ? processes[0] : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (!thread) {
|
||||
if (stackFrame) {
|
||||
thread = stackFrame.thread;
|
||||
} else {
|
||||
const threads = process ? process.getAllThreads() : undefined;
|
||||
thread = threads && threads.length ? threads[0] : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stackFrame) {
|
||||
const threads = process ? process.getAllThreads() : null;
|
||||
const callStack = threads && threads.length === 1 ? threads[0].getCallStack() : null;
|
||||
stackFrame = callStack && callStack.length ? callStack[0] : null;
|
||||
if (thread) {
|
||||
const callStack = thread.getCallStack();
|
||||
stackFrame = callStack && callStack.length ? callStack[0] : null;
|
||||
}
|
||||
}
|
||||
|
||||
this.viewModel.setFocusedStackFrame(stackFrame, process, explicit);
|
||||
this.viewModel.setFocus(stackFrame, thread, process, explicit);
|
||||
this.updateStateAndEmit();
|
||||
|
||||
return this.model.evaluateWatchExpressions(process, stackFrame);
|
||||
}
|
||||
|
||||
public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> {
|
||||
@@ -567,16 +583,20 @@ export class DebugService implements debug.IDebugService {
|
||||
return this.sendAllBreakpoints();
|
||||
}
|
||||
|
||||
public addBreakpoints(uri: uri, rawBreakpoints: debug.IRawBreakpoint[]): TPromise<void> {
|
||||
public addBreakpoints(uri: uri, rawBreakpoints: debug.IBreakpointData[]): TPromise<void> {
|
||||
this.model.addBreakpoints(uri, rawBreakpoints);
|
||||
rawBreakpoints.forEach(rbp => aria.status(nls.localize('breakpointAdded', "Added breakpoint, line {0}, file {1}", rbp.lineNumber, uri.fsPath)));
|
||||
|
||||
return this.sendBreakpoints(uri);
|
||||
}
|
||||
|
||||
public updateBreakpoints(uri: uri, data: { [id: string]: DebugProtocol.Breakpoint }): TPromise<void> {
|
||||
public updateBreakpoints(uri: uri, data: { [id: string]: DebugProtocol.Breakpoint }, sendOnResourceSaved: boolean): void {
|
||||
this.model.updateBreakpoints(data);
|
||||
return this.sendBreakpoints(uri);
|
||||
if (sendOnResourceSaved) {
|
||||
this.breakpointsToSendOnResourceSaved.add(uri.toString());
|
||||
} else {
|
||||
this.sendBreakpoints(uri);
|
||||
}
|
||||
}
|
||||
|
||||
public removeBreakpoints(id?: string): TPromise<any> {
|
||||
@@ -594,8 +614,8 @@ export class DebugService implements debug.IDebugService {
|
||||
return this.sendAllBreakpoints();
|
||||
}
|
||||
|
||||
public addFunctionBreakpoint(): void {
|
||||
const newFunctionBreakpoint = this.model.addFunctionBreakpoint('');
|
||||
public addFunctionBreakpoint(name?: string, id?: string): void {
|
||||
const newFunctionBreakpoint = this.model.addFunctionBreakpoint(name || '', id);
|
||||
this.viewModel.setSelectedFunctionBreakpoint(newFunctionBreakpoint);
|
||||
}
|
||||
|
||||
@@ -612,7 +632,7 @@ export class DebugService implements debug.IDebugService {
|
||||
public addReplExpression(name: string): TPromise<void> {
|
||||
return this.model.addReplExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name)
|
||||
// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
|
||||
.then(() => this.focusStackFrameAndEvaluate(this.viewModel.focusedStackFrame, this.viewModel.focusedProcess));
|
||||
.then(() => this.focusStackFrame(this.viewModel.focusedStackFrame, this.viewModel.focusedThread, this.viewModel.focusedProcess));
|
||||
}
|
||||
|
||||
public removeReplExpressions(): void {
|
||||
@@ -628,11 +648,12 @@ export class DebugService implements debug.IDebugService {
|
||||
}
|
||||
}
|
||||
|
||||
public addWatchExpression(name: string): TPromise<void> {
|
||||
return this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
|
||||
public addWatchExpression(name: string): void {
|
||||
const we = this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
|
||||
this.viewModel.setSelectedExpression(we);
|
||||
}
|
||||
|
||||
public renameWatchExpression(id: string, newName: string): TPromise<void> {
|
||||
public renameWatchExpression(id: string, newName: string): void {
|
||||
return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName);
|
||||
}
|
||||
|
||||
@@ -644,14 +665,10 @@ export class DebugService implements debug.IDebugService {
|
||||
this.model.removeWatchExpressions(id);
|
||||
}
|
||||
|
||||
public evaluateWatchExpressions(): TPromise<void> {
|
||||
return this.model.evaluateWatchExpressions(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame);
|
||||
}
|
||||
|
||||
public startDebugging(root: IWorkspaceFolder, configOrName?: debug.IConfig | string, noDebug = false, topCompoundName?: string): TPromise<any> {
|
||||
public startDebugging(launch: debug.ILaunch, configOrName?: debug.IConfig | string, noDebug = false): TPromise<any> {
|
||||
|
||||
// make sure to save all files and that the configuration is up to date
|
||||
return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(root).then(() =>
|
||||
return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined).then(() =>
|
||||
this.extensionService.whenInstalledExtensionsRegistered().then(() => {
|
||||
if (this.model.getProcesses().length === 0) {
|
||||
this.removeReplExpressions();
|
||||
@@ -659,12 +676,10 @@ export class DebugService implements debug.IDebugService {
|
||||
this.model.getBreakpoints().forEach(bp => bp.verified = false);
|
||||
}
|
||||
this.launchJsonChanged = false;
|
||||
const manager = this.getConfigurationManager();
|
||||
const launch = root ? manager.getLaunches().filter(l => l.workspace.uri.toString() === root.uri.toString()).pop() : undefined;
|
||||
|
||||
let config: debug.IConfig, compound: debug.ICompound;
|
||||
if (!configOrName) {
|
||||
configOrName = this.configurationManager.selectedName;
|
||||
configOrName = this.configurationManager.selectedConfiguration.name;
|
||||
}
|
||||
if (typeof configOrName === 'string' && launch) {
|
||||
config = launch.getConfiguration(configOrName);
|
||||
@@ -672,10 +687,6 @@ export class DebugService implements debug.IDebugService {
|
||||
} else if (typeof configOrName !== 'string') {
|
||||
config = configOrName;
|
||||
}
|
||||
if (launch) {
|
||||
// in the drop down the name of the top most compound takes precedence over the launch config name
|
||||
manager.selectConfiguration(launch, topCompoundName || (typeof configOrName === 'string' ? configOrName : undefined), true);
|
||||
}
|
||||
|
||||
if (compound) {
|
||||
if (!compound.configurations) {
|
||||
@@ -683,7 +694,35 @@ export class DebugService implements debug.IDebugService {
|
||||
"Compound must have \"configurations\" attribute set in order to start multiple configurations.")));
|
||||
}
|
||||
|
||||
return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(root, name, noDebug, topCompoundName || compound.name) : TPromise.as(null)));
|
||||
return TPromise.join(compound.configurations.map(configData => {
|
||||
const name = typeof configData === 'string' ? configData : configData.name;
|
||||
if (name === compound.name) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
let launchForName: debug.ILaunch;
|
||||
if (typeof configData === 'string') {
|
||||
const launchesContainingName = this.configurationManager.getLaunches().filter(l => !!l.getConfiguration(name));
|
||||
if (launchesContainingName.length === 1) {
|
||||
launchForName = launchesContainingName[0];
|
||||
} else if (launchesContainingName.length > 1 && launchesContainingName.indexOf(launch) >= 0) {
|
||||
// If there are multiple launches containing the configuration give priority to the configuration in the current launch
|
||||
launchForName = launch;
|
||||
} else {
|
||||
return TPromise.wrapError(new Error(launchesContainingName.length === 0 ? nls.localize('noConfigurationNameInWorkspace', "Could not find launch configuration '{0}' in the workspace.", name)
|
||||
: nls.localize('multipleConfigurationNamesInWorkspace', "There are multiple launch configurations '{0}' in the workspace. Use folder name to qualify the configuration.", name)));
|
||||
}
|
||||
} else if (configData.folder) {
|
||||
const launchesMatchingConfigData = this.configurationManager.getLaunches().filter(l => l.workspace && l.workspace.name === configData.folder && !!l.getConfiguration(configData.name));
|
||||
if (launchesMatchingConfigData.length === 1) {
|
||||
launchForName = launchesMatchingConfigData[0];
|
||||
} else {
|
||||
return TPromise.wrapError(new Error(nls.localize('noFolderWithName', "Can not find folder with name '{0}' for configuration '{1}' in compound '{2}'.", configData.folder, configData.name, compound.name)));
|
||||
}
|
||||
}
|
||||
|
||||
return this.startDebugging(launchForName, name, noDebug);
|
||||
}));
|
||||
}
|
||||
if (configOrName && !config) {
|
||||
const message = !!launch ? nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName) :
|
||||
@@ -714,16 +753,15 @@ export class DebugService implements debug.IDebugService {
|
||||
|
||||
return (type ? TPromise.as(null) : this.configurationManager.guessAdapter().then(a => type = a && a.type)).then(() =>
|
||||
(type ? this.extensionService.activateByEvent(`onDebugResolve:${type}`) : TPromise.as(null)).then(() =>
|
||||
this.configurationManager.resolveConfigurationByProviders(launch ? launch.workspace.uri : undefined, type, config).then(config => {
|
||||
this.configurationManager.resolveConfigurationByProviders(launch && launch.workspace ? launch.workspace.uri : undefined, type, config).then(config => {
|
||||
// a falsy config indicates an aborted launch
|
||||
if (config && config.type) {
|
||||
return this.createProcess(root, config, sessionId);
|
||||
}
|
||||
if (launch) {
|
||||
return launch.openConfigFile(false, type).then(editor => undefined);
|
||||
return this.createProcess(launch, config, sessionId);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
if (launch) {
|
||||
return launch.openConfigFile(false, type).done(undefined, errors.onUnexpectedError);
|
||||
}
|
||||
})
|
||||
).then(() => wrapUpState(), err => {
|
||||
wrapUpState();
|
||||
@@ -733,9 +771,9 @@ export class DebugService implements debug.IDebugService {
|
||||
)));
|
||||
}
|
||||
|
||||
private createProcess(root: IWorkspaceFolder, config: debug.IConfig, sessionId: string): TPromise<void> {
|
||||
private createProcess(launch: debug.ILaunch, config: debug.IConfig, sessionId: string): TPromise<void> {
|
||||
return this.textFileService.saveAll().then(() =>
|
||||
(this.configurationManager.selectedLaunch ? this.configurationManager.selectedLaunch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => {
|
||||
(launch ? launch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => {
|
||||
if (!resolvedConfig) {
|
||||
// User canceled resolving of interactive variables, silently return
|
||||
return undefined;
|
||||
@@ -744,65 +782,50 @@ export class DebugService implements debug.IDebugService {
|
||||
if (!this.configurationManager.getAdapter(resolvedConfig.type) || (config.request !== 'attach' && config.request !== 'launch')) {
|
||||
let message: string;
|
||||
if (config.request !== 'attach' && config.request !== 'launch') {
|
||||
message = config.request ? nls.localize('debugRequestNotSupported', "Attribute `{0}` has an unsupported value '{1}' in the chosen debug configuration.", 'request', config.request)
|
||||
message = config.request ? nls.localize('debugRequestNotSupported', "Attribute '{0}' has an unsupported value '{1}' in the chosen debug configuration.", 'request', config.request)
|
||||
: nls.localize('debugRequesMissing', "Attribute '{0}' is missing from the chosen debug configuration.", 'request');
|
||||
|
||||
} else {
|
||||
message = resolvedConfig.type ? nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", resolvedConfig.type) :
|
||||
nls.localize('debugTypeMissing', "Missing property `type` for the chosen launch configuration.");
|
||||
nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration.");
|
||||
}
|
||||
|
||||
return TPromise.wrapError(errors.create(message, { actions: [this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL), CloseAction] }));
|
||||
return this.showError(message);
|
||||
}
|
||||
|
||||
this.toDisposeOnSessionEnd.set(sessionId, []);
|
||||
const debugAnywayAction = new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => {
|
||||
this.messageService.hideAll();
|
||||
return this.doCreateProcess(root, resolvedConfig, sessionId);
|
||||
|
||||
const workspace = launch ? launch.workspace : undefined;
|
||||
const debugAnywayAction = new Action('debug.debugAnyway', nls.localize('debugAnyway', "Debug Anyway"), undefined, true, () => {
|
||||
return this.doCreateProcess(workspace, resolvedConfig, sessionId);
|
||||
});
|
||||
|
||||
return this.runPreLaunchTask(sessionId, root, resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => {
|
||||
return this.runPreLaunchTask(sessionId, workspace, resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => {
|
||||
const errorCount = resolvedConfig.preLaunchTask ? this.markerService.getStatistics().errors : 0;
|
||||
const successExitCode = taskSummary && taskSummary.exitCode === 0;
|
||||
const failureExitCode = taskSummary && taskSummary.exitCode !== undefined && taskSummary.exitCode !== 0;
|
||||
if (successExitCode || (errorCount === 0 && !failureExitCode)) {
|
||||
return this.doCreateProcess(root, resolvedConfig, sessionId);
|
||||
return this.doCreateProcess(workspace, resolvedConfig, sessionId);
|
||||
}
|
||||
|
||||
this.messageService.show(severity.Error, {
|
||||
message: errorCount > 1 ? nls.localize('preLaunchTaskErrors', "Build errors have been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
|
||||
errorCount === 1 ? nls.localize('preLaunchTaskError', "Build error has been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
|
||||
nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", resolvedConfig.preLaunchTask, taskSummary.exitCode),
|
||||
actions: [
|
||||
debugAnywayAction,
|
||||
this.instantiationService.createInstance(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL),
|
||||
CloseAction
|
||||
]
|
||||
const message = errorCount > 1 ? nls.localize('preLaunchTaskErrors', "Build errors have been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
|
||||
errorCount === 1 ? nls.localize('preLaunchTaskError', "Build error has been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
|
||||
nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", resolvedConfig.preLaunchTask, taskSummary.exitCode);
|
||||
|
||||
const showErrorsAction = new Action('debug.showErrors', nls.localize('showErrors', "Show Errors"), undefined, true, () => {
|
||||
return this.panelService.openPanel(Constants.MARKERS_PANEL_ID).then(() => undefined);
|
||||
});
|
||||
return undefined;
|
||||
|
||||
return this.showError(message, [debugAnywayAction, showErrorsAction]);
|
||||
}, (err: TaskError) => {
|
||||
this.messageService.show(err.severity, {
|
||||
message: err.message,
|
||||
actions: [
|
||||
debugAnywayAction,
|
||||
this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
|
||||
this.taskService.configureAction(),
|
||||
CloseAction
|
||||
]
|
||||
});
|
||||
return this.showError(err.message, [debugAnywayAction, this.taskService.configureAction()]);
|
||||
});
|
||||
}, err => {
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
this.messageService.show(severity.Error, nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type."));
|
||||
return undefined;
|
||||
return this.showError(nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type."));
|
||||
}
|
||||
|
||||
return this.configurationManager.selectedLaunch.openConfigFile(false).then(openend => {
|
||||
if (openend) {
|
||||
this.messageService.show(severity.Info, nls.localize('NewLaunchConfig', "Please set up the launch configuration file for your application. {0}", err.message));
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
return launch && launch.openConfigFile(false).then(editor => void 0);
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -870,8 +893,8 @@ export class DebugService implements debug.IDebugService {
|
||||
if (session.disconnected) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
this.focusStackFrame(undefined, undefined, process);
|
||||
this._onDidNewProcess.fire(process);
|
||||
this.focusStackFrameAndEvaluate(null, process);
|
||||
|
||||
const internalConsoleOptions = configuration.internalConsoleOptions || this.configurationService.getValue<debug.IDebugConfiguration>('debug').internalConsoleOptions;
|
||||
if (internalConsoleOptions === 'openOnSessionStart' || (this.firstSessionStart && internalConsoleOptions === 'openOnFirstSessionStart')) {
|
||||
@@ -911,8 +934,8 @@ export class DebugService implements debug.IDebugService {
|
||||
isBuiltin: adapter.extensionDescription.isBuiltin,
|
||||
launchJsonExists: root && !!this.configurationService.getValue<debug.IGlobalConfig>('launch', { resource: root.uri })
|
||||
});
|
||||
}).then(() => process, (error: any) => {
|
||||
if (error instanceof Error && error.message === 'Canceled') {
|
||||
}).then(() => process, (error: Error | string) => {
|
||||
if (errors.isPromiseCanceledError(error)) {
|
||||
// Do not show 'canceled' error messages to the user #7906
|
||||
return TPromise.as(null);
|
||||
}
|
||||
@@ -921,7 +944,7 @@ export class DebugService implements debug.IDebugService {
|
||||
/* __GDPR__
|
||||
"debugMisconfiguration" : {
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"error": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
"error": { "classification": "CallstackOrException", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined, error: errorMessage });
|
||||
@@ -940,14 +963,24 @@ export class DebugService implements debug.IDebugService {
|
||||
this.inDebugMode.reset();
|
||||
}
|
||||
|
||||
const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL);
|
||||
const actions = (error.actions && error.actions.length) ? error.actions.concat([configureAction]) : [CloseAction, configureAction];
|
||||
this.messageService.show(severity.Error, { message: errorMessage, actions });
|
||||
this.showError(errorMessage, errors.isErrorWithActions(error) ? error.actions : []);
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private showError(message: string, actions: IAction[] = []): TPromise<any> {
|
||||
const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL);
|
||||
actions.push(configureAction);
|
||||
return this.choiceService.choose(severity.Error, message, actions.map(a => a.label).concat(nls.localize('cancel', "Cancel")), actions.length, true).then(choice => {
|
||||
if (choice < actions.length) {
|
||||
return actions[choice].run();
|
||||
}
|
||||
|
||||
return TPromise.as(null);
|
||||
});
|
||||
}
|
||||
|
||||
private runPreLaunchTask(sessionId: string, root: IWorkspaceFolder, taskName: string): TPromise<ITaskSummary> {
|
||||
if (!taskName) {
|
||||
return TPromise.as(null);
|
||||
@@ -1020,7 +1053,7 @@ export class DebugService implements debug.IDebugService {
|
||||
const preserveFocus = focusedProcess && process.getId() === focusedProcess.getId();
|
||||
|
||||
return process.session.disconnect(true).then(() => {
|
||||
if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost')) {
|
||||
if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost') && process.session.root) {
|
||||
return this.broadcastService.broadcast({
|
||||
channel: EXTENSION_RELOAD_BROADCAST_CHANNEL,
|
||||
payload: [process.session.root.uri.fsPath]
|
||||
@@ -1031,15 +1064,17 @@ export class DebugService implements debug.IDebugService {
|
||||
setTimeout(() => {
|
||||
// Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration
|
||||
let config = process.configuration;
|
||||
if (this.launchJsonChanged && this.configurationManager.selectedLaunch) {
|
||||
|
||||
const launch = process.session.root ? this.configurationManager.getLaunch(process.session.root.uri) : undefined;
|
||||
if (this.launchJsonChanged && launch) {
|
||||
this.launchJsonChanged = false;
|
||||
config = this.configurationManager.selectedLaunch.getConfiguration(process.configuration.name) || config;
|
||||
config = launch.getConfiguration(process.configuration.name) || config;
|
||||
// Take the type from the process since the debug extension might overwrite it #21316
|
||||
config.type = process.configuration.type;
|
||||
config.noDebug = process.configuration.noDebug;
|
||||
}
|
||||
config.__restart = restartData;
|
||||
this.createProcess(process.session.root, config, process.getId()).then(() => c(null), err => e(err));
|
||||
this.startDebugging(launch, config).then(() => c(null), err => e(err));
|
||||
}, 300);
|
||||
});
|
||||
}).then(() => {
|
||||
@@ -1047,7 +1082,7 @@ export class DebugService implements debug.IDebugService {
|
||||
// Restart should preserve the focused process
|
||||
const restartedProcess = this.model.getProcesses().filter(p => p.configuration.name === process.configuration.name).pop();
|
||||
if (restartedProcess && restartedProcess !== this.viewModel.focusedProcess) {
|
||||
this.focusStackFrameAndEvaluate(null, restartedProcess);
|
||||
this.focusStackFrame(undefined, undefined, restartedProcess);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1090,7 +1125,7 @@ export class DebugService implements debug.IDebugService {
|
||||
});
|
||||
|
||||
this.model.removeProcess(session.getId());
|
||||
if (process && process.state !== debug.ProcessState.INACTIVE) {
|
||||
if (process) {
|
||||
process.inactive = true;
|
||||
this._onDidEndProcess.fire(process);
|
||||
}
|
||||
@@ -1098,7 +1133,7 @@ export class DebugService implements debug.IDebugService {
|
||||
this.toDisposeOnSessionEnd.set(session.getId(), lifecycle.dispose(this.toDisposeOnSessionEnd.get(session.getId())));
|
||||
const focusedProcess = this.viewModel.focusedProcess;
|
||||
if (focusedProcess && focusedProcess.getId() === session.getId()) {
|
||||
this.focusStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
|
||||
this.focusStackFrame(null);
|
||||
}
|
||||
this.updateStateAndEmit(session.getId(), debug.State.Inactive);
|
||||
|
||||
@@ -1146,11 +1181,6 @@ export class DebugService implements debug.IDebugService {
|
||||
if (!session.readyForBreakpoints) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
if (this.textFileService.isDirty(modelUri)) {
|
||||
// Only send breakpoints for a file once it is not dirty #8077
|
||||
this.breakpointsToSendOnResourceSaved.add(modelUri.toString());
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
const breakpointsToSend = this.model.getBreakpoints().filter(bp => this.model.areBreakpointsActivated() && bp.enabled && bp.uri.toString() === modelUri.toString());
|
||||
|
||||
@@ -1166,6 +1196,8 @@ export class DebugService implements debug.IDebugService {
|
||||
if (breakpointsToSend.length && !rawSource.adapterData) {
|
||||
rawSource.adapterData = breakpointsToSend[0].adapterData;
|
||||
}
|
||||
// Normalize all drive letters going out from vscode to debug adapters so we are consistent with our resolving #43959
|
||||
rawSource.path = normalizeDriveLetter(rawSource.path);
|
||||
|
||||
return session.setBreakpoints({
|
||||
source: rawSource,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IDebugService, IStackFrame } from 'vs/workbench/parts/debug/common/debu
|
||||
import { clipboard } from 'electron';
|
||||
|
||||
export class CopyValueAction extends Action {
|
||||
static ID = 'workbench.debug.viewlet.action.copyValue';
|
||||
static readonly ID = 'workbench.debug.viewlet.action.copyValue';
|
||||
static LABEL = nls.localize('copyValue', "Copy Value");
|
||||
|
||||
constructor(id: string, label: string, private value: any, @IDebugService private debugService: IDebugService) {
|
||||
@@ -34,8 +34,25 @@ export class CopyValueAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class CopyEvaluatePathAction extends Action {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.copyEvaluatePath';
|
||||
static LABEL = nls.localize('copyAsExpression', "Copy as Expression");
|
||||
|
||||
constructor(id: string, label: string, private value: any) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
if (this.value instanceof Variable) {
|
||||
clipboard.writeText(this.value.evaluateName);
|
||||
}
|
||||
|
||||
return TPromise.as(null);
|
||||
}
|
||||
}
|
||||
|
||||
export class CopyAction extends Action {
|
||||
static ID = 'workbench.debug.action.copy';
|
||||
static readonly ID = 'workbench.debug.action.copy';
|
||||
static LABEL = nls.localize('copy', "Copy");
|
||||
|
||||
public run(): TPromise<any> {
|
||||
@@ -45,7 +62,7 @@ export class CopyAction extends Action {
|
||||
}
|
||||
|
||||
export class CopyAllAction extends Action {
|
||||
static ID = 'workbench.debug.action.copyAll';
|
||||
static readonly ID = 'workbench.debug.action.copyAll';
|
||||
static LABEL = nls.localize('copyAll', "Copy All");
|
||||
|
||||
constructor(id: string, label: string, private tree: ITree) {
|
||||
@@ -69,7 +86,7 @@ export class CopyAllAction extends Action {
|
||||
}
|
||||
|
||||
export class CopyStackTraceAction extends Action {
|
||||
static ID = 'workbench.action.debug.copyStackTrace';
|
||||
static readonly ID = 'workbench.action.debug.copyStackTrace';
|
||||
static LABEL = nls.localize('copyStackTrace', "Copy Call Stack");
|
||||
|
||||
public run(frame: IStackFrame): TPromise<any> {
|
||||
|
||||
@@ -12,9 +12,7 @@ import objects = require('vs/base/common/objects');
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import errors = require('vs/base/common/errors');
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import stdfork = require('vs/base/node/stdFork');
|
||||
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { ITerminalService as IExternalTerminalService } from 'vs/workbench/parts/execution/common/execution';
|
||||
@@ -26,6 +24,7 @@ import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { TerminalSupport } from 'vs/workbench/parts/debug/electron-browser/terminalSupport';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
export interface SessionExitedEvent extends debug.DebugEvent {
|
||||
body: {
|
||||
@@ -72,7 +71,7 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
private adapter: Adapter,
|
||||
public customTelemetryService: ITelemetryService,
|
||||
public root: IWorkspaceFolder,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IOutputService private outputService: IOutputService,
|
||||
@ITerminalService private terminalService: ITerminalService,
|
||||
@@ -166,7 +165,7 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
if (error && error.sendTelemetry) {
|
||||
/* __GDPR__
|
||||
"debugProtocolErrorResponse" : {
|
||||
"error" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
"error" : { "classification": "CallstackOrException", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('debugProtocolErrorResponse', { error: telemetryMessage });
|
||||
@@ -183,7 +182,7 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
if (error && error.url) {
|
||||
const label = error.urlLabel ? error.urlLabel : nls.localize('moreInfo', "More Info");
|
||||
return TPromise.wrapError<R>(errors.create(userMessage, {
|
||||
actions: [CloseAction, new Action('debug.moreInfo', label, null, true, () => {
|
||||
actions: [new Action('debug.moreInfo', label, null, true, () => {
|
||||
window.open(error.url);
|
||||
return TPromise.as(null);
|
||||
})]
|
||||
@@ -206,6 +205,9 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
if (event.event === 'initialized') {
|
||||
this.readyForBreakpoints = true;
|
||||
this._onDidInitialize.fire(event);
|
||||
} else if (event.event === 'capabilities' && event.body) {
|
||||
const capabilites = (<DebugProtocol.CapabilitiesEvent>event).body.capabilities;
|
||||
this._capabilities = objects.mixin(this._capabilities, capabilites);
|
||||
} else if (event.event === 'stopped') {
|
||||
this.emittedStopped = true;
|
||||
this._onDidStop.fire(<DebugProtocol.StoppedEvent>event);
|
||||
@@ -524,7 +526,7 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
}
|
||||
|
||||
protected onServerError(err: Error): void {
|
||||
this.messageService.show(severity.Error, nls.localize('stoppingDebugAdapter', "{0}. Stopping the debug adapter.", err.message));
|
||||
this.notificationService.error(nls.localize('stoppingDebugAdapter', "{0}. Stopping the debug adapter.", err.message));
|
||||
this.stopServer().done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
@@ -532,7 +534,7 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
this.serverProcess = null;
|
||||
this.cachedInitServer = null;
|
||||
if (!this.disconnected) {
|
||||
this.messageService.show(severity.Error, nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly"));
|
||||
this.notificationService.error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly"));
|
||||
}
|
||||
this.onEvent({ event: 'exit', type: 'event', seq: 0 });
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { ITree, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
|
||||
import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest';
|
||||
import { SuggestController } from 'vs/editor/contrib/suggest/suggestController';
|
||||
import { IReadOnlyModel } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
@@ -41,15 +41,16 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { clipboard } from 'electron';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { OpenMode, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
const replTreeOptions: ITreeOptions = {
|
||||
twistiePixels: 20,
|
||||
ariaLabel: nls.localize('replAriaLabel', "Read Eval Print Loop Panel"),
|
||||
keyboardSupport: false
|
||||
ariaLabel: nls.localize('replAriaLabel', "Read Eval Print Loop Panel")
|
||||
};
|
||||
|
||||
const HISTORY_STORAGE_KEY = 'debug.repl.history';
|
||||
@@ -82,6 +83,7 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
private actions: IAction[];
|
||||
private dimension: Dimension;
|
||||
private replInputHeight: number;
|
||||
private model: ITextModel;
|
||||
|
||||
constructor(
|
||||
@debug.IDebugService private debugService: debug.IDebugService,
|
||||
@@ -91,8 +93,7 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
@IPanelService private panelService: IPanelService,
|
||||
@IThemeService protected themeService: IThemeService,
|
||||
@IModelService private modelService: IModelService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IListService private listService: IListService
|
||||
@IContextKeyService private contextKeyService: IContextKeyService
|
||||
) {
|
||||
super(debug.REPL_ID, telemetryService, themeService);
|
||||
|
||||
@@ -118,7 +119,7 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
this.refreshTimeoutHandle = null;
|
||||
const previousScrollPosition = this.tree.getScrollPosition();
|
||||
this.tree.refresh().then(() => {
|
||||
if (previousScrollPosition === 1 || previousScrollPosition === 0) {
|
||||
if (previousScrollPosition === 1) {
|
||||
// Only scroll if we were scrolled all the way down before tree refreshed #10486
|
||||
this.tree.setScrollPosition(1);
|
||||
}
|
||||
@@ -134,15 +135,15 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
this.createReplInput(this.container);
|
||||
|
||||
this.renderer = this.instantiationService.createInstance(ReplExpressionsRenderer);
|
||||
const controller = this.instantiationService.createInstance(ReplExpressionsController, new ReplExpressionsActionProvider(this.instantiationService), MenuId.DebugConsoleContext);
|
||||
const controller = this.instantiationService.createInstance(ReplExpressionsController, new ReplExpressionsActionProvider(this.instantiationService, this.replInput), MenuId.DebugConsoleContext, { openMode: OpenMode.SINGLE_CLICK, clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change, to preserve focus behaviour in input field */ });
|
||||
controller.toFocusOnClick = this.replInput;
|
||||
|
||||
this.tree = new WorkbenchTree(this.treeContainer, {
|
||||
this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
|
||||
dataSource: new ReplExpressionsDataSource(),
|
||||
renderer: this.renderer,
|
||||
accessibilityProvider: new ReplExpressionsAccessibilityProvider(),
|
||||
controller
|
||||
}, replTreeOptions, this.contextKeyService, this.listService, this.themeService);
|
||||
}, replTreeOptions);
|
||||
|
||||
if (!Repl.HISTORY) {
|
||||
Repl.HISTORY = new ReplHistory(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')));
|
||||
@@ -151,6 +152,17 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
return this.tree.setInput(this.debugService.getModel());
|
||||
}
|
||||
|
||||
public setVisible(visible: boolean): TPromise<void> {
|
||||
if (!visible) {
|
||||
dispose(this.model);
|
||||
} else {
|
||||
this.model = this.modelService.createModel('', null, uri.parse(`${debug.DEBUG_SCHEME}:input`));
|
||||
this.replInput.setModel(this.model);
|
||||
}
|
||||
|
||||
return super.setVisible(visible);
|
||||
}
|
||||
|
||||
private createReplInput(container: HTMLElement): void {
|
||||
this.replInputContainer = dom.append(container, $('.repl-input-wrapper'));
|
||||
|
||||
@@ -165,12 +177,10 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection(
|
||||
[IContextKeyService, scopedContextKeyService], [IPrivateReplService, this]));
|
||||
this.replInput = scopedInstantiationService.createInstance(ReplInputEditor, this.replInputContainer, this.getReplInputOptions());
|
||||
const model = this.modelService.createModel('', null, uri.parse(`${debug.DEBUG_SCHEME}:input`));
|
||||
this.replInput.setModel(model);
|
||||
|
||||
modes.SuggestRegistry.register({ scheme: debug.DEBUG_SCHEME }, {
|
||||
triggerCharacters: ['.'],
|
||||
provideCompletionItems: (model: IReadOnlyModel, position: Position, _context: modes.SuggestContext, token: CancellationToken): Thenable<modes.ISuggestResult> => {
|
||||
provideCompletionItems: (model: ITextModel, position: Position, _context: modes.SuggestContext, token: CancellationToken): Thenable<modes.ISuggestResult> => {
|
||||
const word = this.replInput.getModel().getWordAtPosition(position);
|
||||
const overwriteBefore = word ? word.word.length : 0;
|
||||
const text = this.replInput.getModel().getLineContent(position.lineNumber);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { IAction } from 'vs/base/common/actions';
|
||||
import * as lifecycle from 'vs/base/common/lifecycle';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { isFullWidthCharacter, removeAnsiEscapeCodes, endsWith } from 'vs/base/common/strings';
|
||||
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
@@ -17,8 +17,8 @@ import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer
|
||||
import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IExpressionContainer, IExpression, IReplElementSource } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Model, RawObjectReplElement, Expression, SimpleReplElement, Variable } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/browser/baseDebugView';
|
||||
import { ClearReplAction, ReplCollapseAllAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { CopyAction, CopyAllAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -417,7 +417,7 @@ export class ReplExpressionsAccessibilityProvider implements IAccessibilityProvi
|
||||
|
||||
export class ReplExpressionsActionProvider implements IActionProvider {
|
||||
|
||||
constructor(private instantiationService: IInstantiationService) {
|
||||
constructor(private instantiationService: IInstantiationService, private toFocus: { focus(): void }) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@@ -437,6 +437,8 @@ export class ReplExpressionsActionProvider implements IActionProvider {
|
||||
const actions: IAction[] = [];
|
||||
actions.push(new CopyAction(CopyAction.ID, CopyAction.LABEL));
|
||||
actions.push(new CopyAllAction(CopyAllAction.ID, CopyAllAction.LABEL, tree));
|
||||
actions.push(new ReplCollapseAllAction(tree, this.toFocus));
|
||||
actions.push(new Separator());
|
||||
actions.push(this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL));
|
||||
|
||||
return TPromise.as(actions);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import cp = require('child_process');
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ITerminalService, ITerminalInstance, ITerminalConfiguration } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
@@ -24,11 +25,6 @@ export class TerminalSupport {
|
||||
return nativeTerminalService.runInTerminal(args.title, args.cwd, args.args, args.env || {});
|
||||
}
|
||||
|
||||
let delay = 0;
|
||||
if (!TerminalSupport.integratedTerminalInstance) {
|
||||
TerminalSupport.integratedTerminalInstance = terminalService.createInstance({ name: args.title || nls.localize('debug.terminal.title', "debuggee") });
|
||||
delay = 2000; // delay the first sendText so that the newly created terminal is ready.
|
||||
}
|
||||
if (!TerminalSupport.terminalDisposedListener) {
|
||||
// React on terminal disposed and check if that is the debug terminal #12956
|
||||
TerminalSupport.terminalDisposedListener = terminalService.onInstanceDisposed(terminal => {
|
||||
@@ -37,22 +33,49 @@ export class TerminalSupport {
|
||||
}
|
||||
});
|
||||
}
|
||||
terminalService.setActiveInstance(TerminalSupport.integratedTerminalInstance);
|
||||
|
||||
let t = TerminalSupport.integratedTerminalInstance;
|
||||
if ((t && this.isBusy(t)) || !t) {
|
||||
t = terminalService.createInstance({ name: args.title || nls.localize('debug.terminal.title', "debuggee") });
|
||||
TerminalSupport.integratedTerminalInstance = t;
|
||||
}
|
||||
terminalService.setActiveInstance(t);
|
||||
terminalService.showPanel(true);
|
||||
|
||||
return new TPromise<void>((c, e) => {
|
||||
const command = this.prepareCommand(args, configurationService);
|
||||
t.sendText(command, true);
|
||||
|
||||
setTimeout(() => {
|
||||
if (TerminalSupport.integratedTerminalInstance) {
|
||||
const command = this.prepareCommand(args, configurationService);
|
||||
TerminalSupport.integratedTerminalInstance.sendText(command, true);
|
||||
c(void 0);
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
|
||||
private static isBusy(t: ITerminalInstance): boolean {
|
||||
if (t.processId) {
|
||||
try {
|
||||
// if shell has at least one child process, assume that shell is busy
|
||||
if (platform.isWindows) {
|
||||
const result = cp.spawnSync('wmic', ['process', 'get', 'ParentProcessId']);
|
||||
if (result.stdout) {
|
||||
const pids = result.stdout.toString().split('\r\n');
|
||||
if (!pids.some(p => parseInt(p) === t.processId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
e(new Error(nls.localize('debug.terminal.not.available.error', "Integrated terminal not available")));
|
||||
const result = cp.spawnSync('/usr/bin/pgrep', ['-lP', String(t.processId)]);
|
||||
if (result.stdout) {
|
||||
const r = result.stdout.toString().trim();
|
||||
if (r.length === 0 || r.indexOf(' tmux') >= 0) { // ignore 'tmux'; see #43683
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, delay);
|
||||
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
// silently ignore
|
||||
}
|
||||
}
|
||||
// fall back to safe side
|
||||
return true;
|
||||
}
|
||||
|
||||
private static prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments, configurationService: IConfigurationService): string {
|
||||
@@ -94,7 +117,8 @@ export class TerminalSupport {
|
||||
|
||||
quote = (s: string) => {
|
||||
s = s.replace(/\'/g, '\'\'');
|
||||
return s.indexOf(' ') >= 0 || s.indexOf('\'') >= 0 || s.indexOf('"') >= 0 ? `'${s}'` : s;
|
||||
return `'${s}'`;
|
||||
//return s.indexOf(' ') >= 0 || s.indexOf('\'') >= 0 || s.indexOf('"') >= 0 ? `'${s}'` : s;
|
||||
};
|
||||
|
||||
if (args.cwd) {
|
||||
|
||||
@@ -7,8 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import { RunOnceScheduler, sequence } from 'vs/base/common/async';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { prepareActions } from 'vs/workbench/browser/actions';
|
||||
import { IHighlightEvent, IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { CollapseAction } from 'vs/workbench/browser/viewlet';
|
||||
import { TreeViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IDebugService, State, CONTEXT_VARIABLES_FOCUSED, IExpression } from 'vs/workbench/parts/debug/common/debug';
|
||||
@@ -17,19 +16,19 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { twistiePixels, renderViewTree, IVariableTemplateData, BaseDebugController, renderRenameBox, renderVariable } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { twistiePixels, renderViewTree, IVariableTemplateData, BaseDebugController, renderRenameBox, renderVariable } from 'vs/workbench/parts/debug/browser/baseDebugView';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IAction, IActionItem } from 'vs/base/common/actions';
|
||||
import { SetValueAction, AddToWatchExpressionsAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { CopyValueAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
|
||||
import { CopyValueAction, CopyEvaluatePathAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { OpenMode } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -40,6 +39,7 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
private settings: any;
|
||||
private expandedElements: any[];
|
||||
private needsRefresh: boolean;
|
||||
private treeContainer: HTMLElement;
|
||||
|
||||
constructor(
|
||||
options: IViewletViewOptions,
|
||||
@@ -47,11 +47,9 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IListService private listService: IListService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService);
|
||||
|
||||
this.settings = options.viewletSettings;
|
||||
this.expandedElements = [];
|
||||
@@ -63,8 +61,6 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
this.expandedElements = expanded;
|
||||
}
|
||||
|
||||
// Always clear tree highlight to avoid ending up in a broken state #12203
|
||||
this.tree.clearHighlight();
|
||||
this.needsRefresh = false;
|
||||
this.tree.refresh().then(() => {
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
@@ -88,16 +84,15 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
dom.addClass(container, 'debug-variables');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
|
||||
this.tree = new WorkbenchTree(this.treeContainer, {
|
||||
this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
|
||||
dataSource: new VariablesDataSource(),
|
||||
renderer: this.instantiationService.createInstance(VariablesRenderer),
|
||||
accessibilityProvider: new VariablesAccessibilityProvider(),
|
||||
controller: this.instantiationService.createInstance(VariablesController, new VariablesActionProvider(this.debugService, this.keybindingService), MenuId.DebugVariablesContext)
|
||||
controller: this.instantiationService.createInstance(VariablesController, new VariablesActionProvider(this.debugService, this.keybindingService), MenuId.DebugVariablesContext, { openMode: OpenMode.SINGLE_CLICK })
|
||||
}, {
|
||||
ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"),
|
||||
twistiePixels,
|
||||
keyboardSupport: false
|
||||
}, this.contextKeyService, this.listService, this.themeService);
|
||||
twistiePixels
|
||||
});
|
||||
|
||||
CONTEXT_VARIABLES_FOCUSED.bindTo(this.tree.contextKeyService);
|
||||
|
||||
@@ -106,7 +101,7 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
this.tree.setInput(viewModel);
|
||||
|
||||
const collapseAction = new CollapseAction(this.tree, false, 'explorer-action collapse-explorer');
|
||||
this.toolbar.setActions(prepareActions([collapseAction]))();
|
||||
this.toolbar.setActions([collapseAction])();
|
||||
|
||||
this.disposables.push(viewModel.onDidFocusStackFrame(sf => {
|
||||
if (!this.isVisible() || !this.isExpanded()) {
|
||||
@@ -127,21 +122,19 @@ export class VariablesView extends TreeViewsViewletPanel {
|
||||
}));
|
||||
|
||||
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
|
||||
if (!expression || !(expression instanceof Variable)) {
|
||||
return;
|
||||
if (expression instanceof Variable) {
|
||||
this.tree.refresh(expression, false).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
this.tree.refresh(expression, false).then(() => {
|
||||
this.tree.setHighlight(expression);
|
||||
once(this.tree.onDidChangeHighlight)((e: IHighlightEvent) => {
|
||||
if (!e.highlight) {
|
||||
this.debugService.getViewModel().setSelectedExpression(null);
|
||||
}
|
||||
});
|
||||
}).done(null, errors.onUnexpectedError);
|
||||
}));
|
||||
}
|
||||
|
||||
layoutBody(size: number): void {
|
||||
if (this.treeContainer) {
|
||||
this.treeContainer.style.height = size + 'px';
|
||||
}
|
||||
super.layoutBody(size);
|
||||
}
|
||||
|
||||
public setExpanded(expanded: boolean): void {
|
||||
super.setExpanded(expanded);
|
||||
if (expanded && this.needsRefresh) {
|
||||
@@ -187,6 +180,7 @@ class VariablesActionProvider implements IActionProvider {
|
||||
const variable = <Variable>element;
|
||||
actions.push(new SetValueAction(SetValueAction.ID, SetValueAction.LABEL, variable, this.debugService, this.keybindingService));
|
||||
actions.push(new CopyValueAction(CopyValueAction.ID, CopyValueAction.LABEL, variable, this.debugService));
|
||||
actions.push(new CopyEvaluatePathAction(CopyEvaluatePathAction.ID, CopyEvaluatePathAction.LABEL, variable));
|
||||
actions.push(new Separator());
|
||||
actions.push(new AddToWatchExpressionsAction(AddToWatchExpressionsAction.ID, AddToWatchExpressionsAction.LABEL, variable, this.debugService, this.keybindingService));
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ import * as dom from 'vs/base/browser/dom';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { prepareActions } from 'vs/workbench/browser/actions';
|
||||
import { IHighlightEvent, IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider, IDragAndDropData, IDragOverReaction, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider, IDragAndDropData, IDragOverReaction, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree';
|
||||
import { CollapseAction } from 'vs/workbench/browser/viewlet';
|
||||
import { TreeViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IDebugService, IExpression, CONTEXT_WATCH_EXPRESSIONS_FOCUSED } from 'vs/workbench/parts/debug/common/debug';
|
||||
@@ -20,17 +19,16 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { IAction, IActionItem } from 'vs/base/common/actions';
|
||||
import { CopyValueAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { IMouseEvent, DragMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IVariableTemplateData, renderVariable, renderRenameBox, renderExpressionValue, BaseDebugController, twistiePixels, renderViewTree } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
|
||||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { DefaultDragAndDrop, OpenMode, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IVariableTemplateData, renderVariable, renderRenameBox, renderExpressionValue, BaseDebugController, twistiePixels, renderViewTree } from 'vs/workbench/parts/debug/browser/baseDebugView';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
const $ = dom.$;
|
||||
const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
|
||||
@@ -39,7 +37,7 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
||||
|
||||
private static readonly MEMENTO = 'watchexpressionsview.memento';
|
||||
private onWatchExpressionsUpdatedScheduler: RunOnceScheduler;
|
||||
private toReveal: IExpression;
|
||||
private treeContainer: HTMLElement;
|
||||
private settings: any;
|
||||
private needsRefresh: boolean;
|
||||
|
||||
@@ -48,26 +46,15 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IListService private listService: IListService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('expressionsSection', "Expressions Section") }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('expressionsSection', "Expressions Section") }, keybindingService, contextMenuService, configurationService);
|
||||
this.settings = options.viewletSettings;
|
||||
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
|
||||
// only expand when a new watch expression is added.
|
||||
if (we instanceof Expression) {
|
||||
this.setExpanded(true);
|
||||
}
|
||||
}));
|
||||
|
||||
this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => {
|
||||
this.needsRefresh = false;
|
||||
this.tree.refresh().done(() => {
|
||||
return this.toReveal instanceof Expression ? this.tree.reveal(this.toReveal) : TPromise.as(true);
|
||||
}, errors.onUnexpectedError);
|
||||
this.tree.refresh().done(undefined, errors.onUnexpectedError);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
@@ -76,17 +63,16 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
||||
this.treeContainer = renderViewTree(container);
|
||||
|
||||
const actionProvider = new WatchExpressionsActionProvider(this.debugService, this.keybindingService);
|
||||
this.tree = new WorkbenchTree(this.treeContainer, {
|
||||
dataSource: new WatchExpressionsDataSource(),
|
||||
this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
|
||||
dataSource: new WatchExpressionsDataSource(this.debugService),
|
||||
renderer: this.instantiationService.createInstance(WatchExpressionsRenderer),
|
||||
accessibilityProvider: new WatchExpressionsAccessibilityProvider(),
|
||||
controller: this.instantiationService.createInstance(WatchExpressionsController, actionProvider, MenuId.DebugWatchContext),
|
||||
controller: this.instantiationService.createInstance(WatchExpressionsController, actionProvider, MenuId.DebugWatchContext, { clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */, openMode: OpenMode.SINGLE_CLICK }),
|
||||
dnd: new WatchExpressionsDragAndDrop(this.debugService)
|
||||
}, {
|
||||
ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"),
|
||||
twistiePixels,
|
||||
keyboardSupport: false
|
||||
}, this.contextKeyService, this.listService, this.themeService);
|
||||
twistiePixels
|
||||
});
|
||||
|
||||
CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(this.tree.contextKeyService);
|
||||
|
||||
@@ -95,7 +81,7 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
||||
const addWatchExpressionAction = new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService);
|
||||
const collapseAction = new CollapseAction(this.tree, true, 'explorer-action collapse-explorer');
|
||||
const removeAllWatchExpressionsAction = new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService);
|
||||
this.toolbar.setActions(prepareActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction]))();
|
||||
this.toolbar.setActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction])();
|
||||
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
|
||||
if (!this.isExpanded() || !this.isVisible()) {
|
||||
@@ -103,28 +89,35 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
||||
return;
|
||||
}
|
||||
|
||||
this.tree.refresh().done(() => {
|
||||
return we instanceof Expression ? this.tree.reveal(we) : TPromise.as(true);
|
||||
}, errors.onUnexpectedError);
|
||||
}));
|
||||
this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(() => {
|
||||
if (!this.isExpanded() || !this.isVisible()) {
|
||||
this.needsRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.onWatchExpressionsUpdatedScheduler.isScheduled()) {
|
||||
this.onWatchExpressionsUpdatedScheduler.schedule();
|
||||
}
|
||||
this.toReveal = we;
|
||||
}));
|
||||
|
||||
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
|
||||
if (!expression || !(expression instanceof Expression)) {
|
||||
return;
|
||||
if (expression instanceof Expression) {
|
||||
this.tree.refresh(expression, false).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
this.tree.refresh(expression, false).then(() => {
|
||||
this.tree.setHighlight(expression);
|
||||
once(this.tree.onDidChangeHighlight)((e: IHighlightEvent) => {
|
||||
if (!e.highlight) {
|
||||
this.debugService.getViewModel().setSelectedExpression(null);
|
||||
}
|
||||
});
|
||||
}).done(null, errors.onUnexpectedError);
|
||||
}));
|
||||
}
|
||||
|
||||
layoutBody(size: number): void {
|
||||
if (this.treeContainer) {
|
||||
this.treeContainer.style.height = size + 'px';
|
||||
}
|
||||
super.layoutBody(size);
|
||||
}
|
||||
|
||||
public setExpanded(expanded: boolean): void {
|
||||
super.setExpanded(expanded);
|
||||
if (expanded && this.needsRefresh) {
|
||||
@@ -200,6 +193,10 @@ class WatchExpressionsActionProvider implements IActionProvider {
|
||||
|
||||
class WatchExpressionsDataSource implements IDataSource {
|
||||
|
||||
constructor(private debugService: IDebugService) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public getId(tree: ITree, element: any): string {
|
||||
return element.getId();
|
||||
}
|
||||
@@ -215,7 +212,9 @@ class WatchExpressionsDataSource implements IDataSource {
|
||||
|
||||
public getChildren(tree: ITree, element: any): TPromise<any> {
|
||||
if (element instanceof Model) {
|
||||
return TPromise.as((<Model>element).getWatchExpressions());
|
||||
const viewModel = this.debugService.getViewModel();
|
||||
return TPromise.join(element.getWatchExpressions().map(we =>
|
||||
we.name ? we.evaluate(viewModel.focusedProcess, viewModel.focusedStackFrame, 'watch').then(() => we) : TPromise.as(we)));
|
||||
}
|
||||
|
||||
let expression = <Expression>element;
|
||||
@@ -291,7 +290,7 @@ class WatchExpressionsRenderer implements IRenderer {
|
||||
|
||||
private renderWatchExpression(tree: ITree, watchExpression: IExpression, data: IWatchExpressionTemplateData): void {
|
||||
let selectedExpression = this.debugService.getViewModel().getSelectedExpression();
|
||||
if ((selectedExpression instanceof Expression && selectedExpression.getId() === watchExpression.getId()) || (watchExpression instanceof Expression && !watchExpression.name)) {
|
||||
if ((selectedExpression instanceof Expression && selectedExpression.getId() === watchExpression.getId())) {
|
||||
renderRenameBox(this.debugService, this.contextViewService, this.themeService, tree, watchExpression, data.expression, {
|
||||
initialValue: watchExpression.name,
|
||||
placeholder: nls.localize('watchExpressionPlaceholder', "Expression to watch"),
|
||||
@@ -344,6 +343,10 @@ class WatchExpressionsController extends BaseDebugController {
|
||||
const expression = <IExpression>element;
|
||||
this.debugService.getViewModel().setSelectedExpression(expression);
|
||||
return true;
|
||||
} else if (element instanceof Model && event.detail === 2) {
|
||||
// Double click in watch panel triggers to add a new watch expression
|
||||
this.debugService.addWatchExpression();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onLeftClick(tree, element, event);
|
||||
|
||||
@@ -13,14 +13,14 @@ import * as paths from 'vs/base/common/paths';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IConfig, IRawAdapter, IAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { IConfig, IRawAdapter, IAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
|
||||
export class Adapter {
|
||||
|
||||
constructor(private rawAdapter: IRawAdapter, public extensionDescription: IExtensionDescription,
|
||||
constructor(private configurationManager: IConfigurationManager, private rawAdapter: IRawAdapter, public extensionDescription: IExtensionDescription,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@ICommandService private commandService: ICommandService
|
||||
) {
|
||||
@@ -33,23 +33,32 @@ export class Adapter {
|
||||
|
||||
public getAdapterExecutable(root: IWorkspaceFolder, verifyAgainstFS = true): TPromise<IAdapterExecutable> {
|
||||
|
||||
if (this.rawAdapter.adapterExecutableCommand && root) {
|
||||
return this.commandService.executeCommand<IAdapterExecutable>(this.rawAdapter.adapterExecutableCommand, root.uri.toString()).then(ad => {
|
||||
return this.verifyAdapterDetails(ad, verifyAgainstFS);
|
||||
});
|
||||
}
|
||||
return this.configurationManager.debugAdapterExecutable(root ? root.uri : undefined, this.rawAdapter.type).then(adapterExecutable => {
|
||||
|
||||
const adapterExecutable = <IAdapterExecutable>{
|
||||
command: this.getProgram(),
|
||||
args: this.getAttributeBasedOnPlatform('args')
|
||||
};
|
||||
const runtime = this.getRuntime();
|
||||
if (runtime) {
|
||||
const runtimeArgs = this.getAttributeBasedOnPlatform('runtimeArgs');
|
||||
adapterExecutable.args = (runtimeArgs || []).concat([adapterExecutable.command]).concat(adapterExecutable.args || []);
|
||||
adapterExecutable.command = runtime;
|
||||
}
|
||||
return this.verifyAdapterDetails(adapterExecutable, verifyAgainstFS);
|
||||
if (adapterExecutable) {
|
||||
return this.verifyAdapterDetails(adapterExecutable, verifyAgainstFS);
|
||||
}
|
||||
|
||||
// try deprecated command based extension API
|
||||
if (this.rawAdapter.adapterExecutableCommand && root) {
|
||||
return this.commandService.executeCommand<IAdapterExecutable>(this.rawAdapter.adapterExecutableCommand, root.uri.toString()).then(ad => {
|
||||
return this.verifyAdapterDetails(ad, verifyAgainstFS);
|
||||
});
|
||||
}
|
||||
|
||||
// fallback: executable contribution specified in package.json
|
||||
adapterExecutable = <IAdapterExecutable>{
|
||||
command: this.getProgram(),
|
||||
args: this.getAttributeBasedOnPlatform('args')
|
||||
};
|
||||
const runtime = this.getRuntime();
|
||||
if (runtime) {
|
||||
const runtimeArgs = this.getAttributeBasedOnPlatform('runtimeArgs');
|
||||
adapterExecutable.args = (runtimeArgs || []).concat([adapterExecutable.command]).concat(adapterExecutable.args || []);
|
||||
adapterExecutable.command = runtime;
|
||||
}
|
||||
return this.verifyAdapterDetails(adapterExecutable, verifyAgainstFS);
|
||||
});
|
||||
}
|
||||
|
||||
private verifyAdapterDetails(details: IAdapterExecutable, verifyAgainstFS: boolean): TPromise<IAdapterExecutable> {
|
||||
@@ -79,7 +88,7 @@ export class Adapter {
|
||||
}
|
||||
|
||||
return TPromise.wrapError(new Error(nls.localize({ key: 'debugAdapterCannotDetermineExecutable', comment: ['Adapter executable file not found'] },
|
||||
"Cannot determine executable for debug adapter '{0}'.", details.command)));
|
||||
"Cannot determine executable for debug adapter '{0}'.", this.type)));
|
||||
}
|
||||
|
||||
private getRuntime(): string {
|
||||
|
||||
@@ -21,7 +21,7 @@ export abstract class V8Protocol {
|
||||
this.sequence = 1;
|
||||
this.contentLength = -1;
|
||||
this.pendingRequests = new Map<number, (e: DebugProtocol.Response) => void>();
|
||||
this.rawData = new Buffer(0);
|
||||
this.rawData = Buffer.allocUnsafe(0);
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
|
||||
@@ -7,12 +7,13 @@ import * as assert from 'assert';
|
||||
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
|
||||
import { StackFrame, Expression, Thread, Process } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { MockSession } from 'vs/workbench/parts/debug/test/common/mockDebug';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
|
||||
suite('Debug - View Model', () => {
|
||||
let model: ViewModel;
|
||||
|
||||
setup(() => {
|
||||
model = new ViewModel();
|
||||
model = new ViewModel(new MockContextKeyService());
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
@@ -26,7 +27,7 @@ suite('Debug - View Model', () => {
|
||||
const process = new Process({ name: 'mockProcess', type: 'node', request: 'launch' }, mockSession);
|
||||
const thread = new Thread(process, 'myThread', 1);
|
||||
const frame = new StackFrame(thread, 1, null, 'app.js', 'normal', { startColumn: 1, startLineNumber: 1, endColumn: undefined, endLineNumber: undefined }, 0);
|
||||
model.setFocusedStackFrame(frame, process, false);
|
||||
model.setFocus(frame, thread, process, false);
|
||||
|
||||
assert.equal(model.focusedStackFrame.getId(), frame.getId());
|
||||
assert.equal(model.focusedThread.threadId, 1);
|
||||
|
||||
@@ -6,47 +6,44 @@
|
||||
import uri from 'vs/base/common/uri';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as debug from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ILaunch, IDebugService, State, DebugEvent, IProcess, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IModel, IViewModel, ISession } from 'vs/workbench/parts/debug/common/debug';
|
||||
|
||||
export class MockDebugService implements debug.IDebugService {
|
||||
export class MockDebugService implements IDebugService {
|
||||
public _serviceBrand: any;
|
||||
|
||||
public get state(): debug.State {
|
||||
public get state(): State {
|
||||
return null;
|
||||
}
|
||||
|
||||
public get onDidCustomEvent(): Event<debug.DebugEvent> {
|
||||
public get onDidCustomEvent(): Event<DebugEvent> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public get onDidNewProcess(): Event<debug.IProcess> {
|
||||
public get onDidNewProcess(): Event<IProcess> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public get onDidEndProcess(): Event<debug.IProcess> {
|
||||
public get onDidEndProcess(): Event<IProcess> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public get onDidChangeState(): Event<debug.State> {
|
||||
public get onDidChangeState(): Event<State> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public getConfigurationManager(): debug.IConfigurationManager {
|
||||
public getConfigurationManager(): IConfigurationManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
public focusStackFrameAndEvaluate(focusedStackFrame: debug.IStackFrame): TPromise<void> {
|
||||
public focusStackFrame(focusedStackFrame: IStackFrame): void {
|
||||
}
|
||||
|
||||
public addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public addBreakpoints(uri: uri, rawBreakpoints: debug.IRawBreakpoint[]): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public updateBreakpoints(uri: uri, data: { [id: string]: DebugProtocol.Breakpoint }): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
public updateBreakpoints(uri: uri, data: { [id: string]: IBreakpointUpdateData }, sendOnResourceSaved: boolean): void { }
|
||||
|
||||
public enableOrDisableBreakpoints(enabled: boolean): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
@@ -88,11 +85,7 @@ export class MockDebugService implements debug.IDebugService {
|
||||
|
||||
public removeWatchExpressions(id?: string): void { }
|
||||
|
||||
public evaluateWatchExpressions(): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public startDebugging(root: IWorkspaceFolder, configOrName?: debug.IConfig | string, noDebug?: boolean): TPromise<any> {
|
||||
public startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug?: boolean): TPromise<any> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
@@ -104,11 +97,11 @@ export class MockDebugService implements debug.IDebugService {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public getModel(): debug.IModel {
|
||||
public getModel(): IModel {
|
||||
return null;
|
||||
}
|
||||
|
||||
public getViewModel(): debug.IViewModel {
|
||||
public getViewModel(): IViewModel {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -117,7 +110,7 @@ export class MockDebugService implements debug.IDebugService {
|
||||
public sourceIsNotAvailable(uri: uri): void { }
|
||||
}
|
||||
|
||||
export class MockSession implements debug.ISession {
|
||||
export class MockSession implements ISession {
|
||||
public readyForBreakpoints = true;
|
||||
public emittedStopped = true;
|
||||
|
||||
@@ -173,7 +166,7 @@ export class MockSession implements debug.ISession {
|
||||
return {};
|
||||
}
|
||||
|
||||
public get onDidEvent(): Event<debug.DebugEvent> {
|
||||
public get onDidEvent(): Event<DebugEvent> {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -182,8 +175,8 @@ export class MockSession implements debug.ISession {
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
public get onDidExitAdapter(): Event<debug.DebugEvent> {
|
||||
const emitter = new Emitter<debug.DebugEvent>();
|
||||
public get onDidExitAdapter(): Event<DebugEvent> {
|
||||
const emitter = new Emitter<DebugEvent>();
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
import * as assert from 'assert';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { IRawAdapter } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IRawAdapter, IAdapterExecutable, IConfigurationManager } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
|
||||
suite('Debug - Adapter', () => {
|
||||
let adapter: Adapter;
|
||||
@@ -41,9 +44,14 @@ suite('Debug - Adapter', () => {
|
||||
}
|
||||
]
|
||||
};
|
||||
const configurationManager = {
|
||||
debugAdapterExecutable(folderUri: uri | undefined, type: string): TPromise<IAdapterExecutable | undefined> {
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
setup(() => {
|
||||
adapter = new Adapter(rawAdapter, { extensionFolderPath, id: 'adapter', name: 'myAdapter', version: '1.0.0', publisher: 'vscode', isBuiltin: false, engines: null },
|
||||
adapter = new Adapter(<IConfigurationManager>configurationManager, rawAdapter, { extensionFolderPath, id: 'adapter', name: 'myAdapter', version: '1.0.0', publisher: 'vscode', isBuiltin: false, engines: null },
|
||||
new TestConfigurationService(), null);
|
||||
});
|
||||
|
||||
|
||||
@@ -305,16 +305,15 @@ suite('Debug - Model', () => {
|
||||
const process = new Process({ name: 'mockProcess', type: 'node', request: 'launch' }, rawSession);
|
||||
const thread = new Thread(process, 'mockthread', 1);
|
||||
const stackFrame = new StackFrame(thread, 1, null, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: undefined, endColumn: undefined }, 0);
|
||||
model.addWatchExpression(process, stackFrame, 'console').done();
|
||||
model.addWatchExpression(process, stackFrame, 'console').done();
|
||||
model.addWatchExpression(process, stackFrame, 'console');
|
||||
model.addWatchExpression(process, stackFrame, 'console');
|
||||
let watchExpressions = model.getWatchExpressions();
|
||||
assertWatchExpressions(watchExpressions, 'console');
|
||||
|
||||
model.renameWatchExpression(process, stackFrame, watchExpressions[0].getId(), 'new_name').done();
|
||||
model.renameWatchExpression(process, stackFrame, watchExpressions[1].getId(), 'new_name').done();
|
||||
model.renameWatchExpression(process, stackFrame, watchExpressions[0].getId(), 'new_name');
|
||||
model.renameWatchExpression(process, stackFrame, watchExpressions[1].getId(), 'new_name');
|
||||
assertWatchExpressions(model.getWatchExpressions(), 'new_name');
|
||||
|
||||
model.evaluateWatchExpressions(process, null);
|
||||
assertWatchExpressions(model.getWatchExpressions(), 'new_name');
|
||||
|
||||
model.addWatchExpression(process, stackFrame, 'mockExpression');
|
||||
|
||||
@@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EditorAction, ServicesAccessor, IActionOptions } from 'vs/editor/browser/editorExtensions';
|
||||
import { grammarsExtPoint, ITMSyntaxExtensionPoint } from 'vs/workbench/services/textMate/electron-browser/TMGrammars';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IExtensionService, ExtensionPointContribution } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionService, ExtensionPointContribution } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
||||
@@ -6,29 +6,28 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import resources = require('vs/base/common/resources');
|
||||
import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions, ActionBarContributor } from 'vs/workbench/browser/actions';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import { explorerItemToFileResource } from 'vs/workbench/parts/files/common/files';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { ITerminalService } from 'vs/workbench/parts/execution/common/execution';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { toResource } from 'vs/workbench/common/editor';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { ITerminalService as IIntegratedTerminalService, KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { DEFAULT_TERMINAL_WINDOWS, DEFAULT_TERMINAL_LINUX_READY, DEFAULT_TERMINAL_OSX, ITerminalConfiguration } from 'vs/workbench/parts/execution/electron-browser/terminal';
|
||||
import { getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX, ITerminalConfiguration } from 'vs/workbench/parts/execution/electron-browser/terminal';
|
||||
import { WinTerminalService, MacTerminalService, LinuxTerminalService } from 'vs/workbench/parts/execution/electron-browser/terminalService';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { ResourceContextKey } from 'vs/workbench/common/resources';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { getMultiSelectedResources } from 'vs/workbench/parts/files/browser/files';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
|
||||
if (env.isWindows) {
|
||||
registerSingleton(ITerminalService, WinTerminalService);
|
||||
@@ -38,7 +37,7 @@ if (env.isWindows) {
|
||||
registerSingleton(ITerminalService, LinuxTerminalService);
|
||||
}
|
||||
|
||||
DEFAULT_TERMINAL_LINUX_READY.then(defaultTerminalLinux => {
|
||||
getDefaultTerminalLinuxReady().then(defaultTerminalLinux => {
|
||||
let configurationRegistry = <IConfigurationRegistry>Registry.as(Extensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': 'externalTerminal',
|
||||
@@ -59,7 +58,7 @@ DEFAULT_TERMINAL_LINUX_READY.then(defaultTerminalLinux => {
|
||||
'terminal.external.windowsExec': {
|
||||
'type': 'string',
|
||||
'description': nls.localize('terminal.external.windowsExec', "Customizes which terminal to run on Windows."),
|
||||
'default': DEFAULT_TERMINAL_WINDOWS,
|
||||
'default': getDefaultTerminalWindows(),
|
||||
'isExecutable': true
|
||||
},
|
||||
'terminal.external.osxExec': {
|
||||
@@ -78,152 +77,73 @@ DEFAULT_TERMINAL_LINUX_READY.then(defaultTerminalLinux => {
|
||||
});
|
||||
});
|
||||
|
||||
const OPEN_IN_TERMINAL_COMMAND_ID = 'openInTerminal';
|
||||
CommandsRegistry.registerCommand({
|
||||
id: OPEN_IN_TERMINAL_COMMAND_ID,
|
||||
handler: (accessor, resource: uri) => {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const fileService = accessor.get(IFileService);
|
||||
const integratedTerminalService = accessor.get(IIntegratedTerminalService);
|
||||
const terminalService = accessor.get(ITerminalService);
|
||||
const resources = getMultiSelectedResources(resource, accessor.get(IListService), editorService);
|
||||
|
||||
export abstract class AbstractOpenInTerminalAction extends Action {
|
||||
private resource: uri;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
|
||||
@IWorkspaceContextService protected contextService: IWorkspaceContextService,
|
||||
@IHistoryService protected historyService: IHistoryService
|
||||
) {
|
||||
super(id, label);
|
||||
|
||||
this.order = 49; // Allow other actions to position before or after
|
||||
return fileService.resolveFiles(resources.map(r => ({ resource: r }))).then(stats => {
|
||||
const directoriesToOpen = distinct(stats.map(({ stat }) => stat.isDirectory ? stat.resource.fsPath : paths.dirname(stat.resource.fsPath)));
|
||||
return directoriesToOpen.map(dir => {
|
||||
if (configurationService.getValue<ITerminalConfiguration>().terminal.explorerKind === 'integrated') {
|
||||
const instance = integratedTerminalService.createInstance({ cwd: dir }, true);
|
||||
if (instance && (resources.length === 1 || !resource || dir === resource.fsPath || dir === paths.dirname(resource.fsPath))) {
|
||||
integratedTerminalService.setActiveInstance(instance);
|
||||
integratedTerminalService.showPanel(true);
|
||||
}
|
||||
} else {
|
||||
terminalService.openTerminal(dir);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
public setResource(resource: uri): void {
|
||||
this.resource = resource;
|
||||
this.enabled = !paths.isUNC(this.resource.fsPath);
|
||||
}
|
||||
|
||||
public getPathToOpen(): string {
|
||||
let pathToOpen: string;
|
||||
|
||||
// Try workspace path first
|
||||
const root = this.historyService.getLastActiveWorkspaceRoot('file');
|
||||
pathToOpen = this.resource ? this.resource.fsPath : (root && root.fsPath);
|
||||
|
||||
// Otherwise check if we have an active file open
|
||||
if (!pathToOpen) {
|
||||
const file = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' });
|
||||
if (file) {
|
||||
pathToOpen = paths.dirname(file.fsPath); // take parent folder of file
|
||||
}
|
||||
}
|
||||
|
||||
return pathToOpen;
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenConsoleAction extends AbstractOpenInTerminalAction {
|
||||
|
||||
public static readonly ID = 'workbench.action.terminal.openNativeConsole';
|
||||
public static readonly Label = env.isWindows ? nls.localize('globalConsoleActionWin', "Open New Command Prompt") :
|
||||
nls.localize('globalConsoleActionMacLinux', "Open New Terminal");
|
||||
public static readonly ScopedLabel = env.isWindows ? nls.localize('scopedConsoleActionWin', "Open in Command Prompt") :
|
||||
nls.localize('scopedConsoleActionMacLinux', "Open in Terminal");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@ITerminalService private terminalService: ITerminalService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
||||
@IHistoryService historyService: IHistoryService
|
||||
) {
|
||||
super(id, label, editorService, contextService, historyService);
|
||||
}
|
||||
|
||||
public run(event?: any): TPromise<any> {
|
||||
let pathToOpen = this.getPathToOpen();
|
||||
this.terminalService.openTerminal(pathToOpen);
|
||||
|
||||
return TPromise.as(null);
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenIntegratedTerminalAction extends AbstractOpenInTerminalAction {
|
||||
|
||||
public static readonly ID = 'workbench.action.terminal.openFolderInIntegratedTerminal';
|
||||
public static readonly Label = nls.localize('openFolderInIntegratedTerminal', "Open in Terminal");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IIntegratedTerminalService private integratedTerminalService: IIntegratedTerminalService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
||||
@IHistoryService historyService: IHistoryService
|
||||
) {
|
||||
super(id, label, editorService, contextService, historyService);
|
||||
}
|
||||
|
||||
public run(event?: any): TPromise<any> {
|
||||
let pathToOpen = this.getPathToOpen();
|
||||
|
||||
const instance = this.integratedTerminalService.createInstance({ cwd: pathToOpen }, true);
|
||||
if (instance) {
|
||||
this.integratedTerminalService.setActiveInstance(instance);
|
||||
this.integratedTerminalService.showPanel(true);
|
||||
}
|
||||
return TPromise.as(null);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExplorerViewerActionContributor extends ActionBarContributor {
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IConfigurationService private configurationService: IConfigurationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public hasSecondaryActions(context: any): boolean {
|
||||
const fileResource = explorerItemToFileResource(context.element);
|
||||
return fileResource && fileResource.resource.scheme === 'file';
|
||||
}
|
||||
|
||||
public getSecondaryActions(context: any): IAction[] {
|
||||
let fileResource = explorerItemToFileResource(context.element);
|
||||
let resource = fileResource.resource;
|
||||
|
||||
// We want the parent unless this resource is a directory
|
||||
if (!fileResource.isDirectory) {
|
||||
resource = resources.dirname(resource);
|
||||
}
|
||||
|
||||
const configuration = this.configurationService.getValue<ITerminalConfiguration>();
|
||||
const explorerKind = configuration.terminal.explorerKind;
|
||||
|
||||
if (explorerKind === 'integrated') {
|
||||
let action = this.instantiationService.createInstance(OpenIntegratedTerminalAction, OpenIntegratedTerminalAction.ID, OpenIntegratedTerminalAction.Label);
|
||||
action.setResource(resource);
|
||||
|
||||
return [action];
|
||||
} else {
|
||||
let action = this.instantiationService.createInstance(OpenConsoleAction, OpenConsoleAction.ID, OpenConsoleAction.ScopedLabel);
|
||||
action.setResource(resource);
|
||||
|
||||
return [action];
|
||||
const OPEN_NATIVE_CONSOLE_COMMAND_ID = 'workbench.action.terminal.openNativeConsole';
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: OPEN_NATIVE_CONSOLE_COMMAND_ID,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C,
|
||||
when: KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
handler: (accessor) => {
|
||||
const historyService = accessor.get(IHistoryService);
|
||||
const terminalService = accessor.get(ITerminalService);
|
||||
const root = historyService.getLastActiveWorkspaceRoot(Schemas.file);
|
||||
if (root) {
|
||||
terminalService.openTerminal(root.fsPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const actionBarRegistry = Registry.as<IActionBarRegistry>(ActionBarExtensions.Actionbar);
|
||||
actionBarRegistry.registerActionBarContributor(Scope.VIEWER, ExplorerViewerActionContributor);
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: OPEN_NATIVE_CONSOLE_COMMAND_ID,
|
||||
title: env.isWindows ? nls.localize('globalConsoleActionWin', "Open New Command Prompt") :
|
||||
nls.localize('globalConsoleActionMacLinux', "Open New Terminal")
|
||||
}
|
||||
});
|
||||
|
||||
// Register Global Action to Open Console
|
||||
Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions).registerWorkbenchAction(
|
||||
new SyncActionDescriptor(
|
||||
OpenConsoleAction,
|
||||
OpenConsoleAction.ID,
|
||||
OpenConsoleAction.Label,
|
||||
{ primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C },
|
||||
KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED
|
||||
),
|
||||
env.isWindows ? 'Open New Command Prompt' : 'Open New Terminal'
|
||||
);
|
||||
const openConsoleCommand = {
|
||||
id: OPEN_IN_TERMINAL_COMMAND_ID,
|
||||
title: env.isWindows ? nls.localize('scopedConsoleActionWin', "Open in Command Prompt") :
|
||||
nls.localize('scopedConsoleActionMacLinux', "Open in Terminal")
|
||||
};
|
||||
MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
|
||||
group: 'navigation',
|
||||
order: 30,
|
||||
command: openConsoleCommand,
|
||||
when: ResourceContextKey.Scheme.isEqualTo(Schemas.file)
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
group: 'navigation',
|
||||
order: 30,
|
||||
command: openConsoleCommand,
|
||||
when: ResourceContextKey.Scheme.isEqualTo(Schemas.file)
|
||||
});
|
||||
|
||||
@@ -8,32 +8,44 @@ import env = require('vs/base/common/platform');
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
export const DEFAULT_TERMINAL_LINUX_READY = new TPromise<string>(c => {
|
||||
if (env.isLinux) {
|
||||
TPromise.join([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => {
|
||||
if (isDebian) {
|
||||
c('x-terminal-emulator');
|
||||
} else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') {
|
||||
c('gnome-terminal');
|
||||
} else if (process.env.DESKTOP_SESSION === 'kde-plasma') {
|
||||
c('konsole');
|
||||
} else if (process.env.COLORTERM) {
|
||||
c(process.env.COLORTERM);
|
||||
} else if (process.env.TERM) {
|
||||
c(process.env.TERM);
|
||||
} else {
|
||||
c('xterm');
|
||||
let _DEFAULT_TERMINAL_LINUX_READY: TPromise<string> = null;
|
||||
export function getDefaultTerminalLinuxReady(): TPromise<string> {
|
||||
if (!_DEFAULT_TERMINAL_LINUX_READY) {
|
||||
_DEFAULT_TERMINAL_LINUX_READY = new TPromise<string>(c => {
|
||||
if (env.isLinux) {
|
||||
TPromise.join([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => {
|
||||
if (isDebian) {
|
||||
c('x-terminal-emulator');
|
||||
} else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') {
|
||||
c('gnome-terminal');
|
||||
} else if (process.env.DESKTOP_SESSION === 'kde-plasma') {
|
||||
c('konsole');
|
||||
} else if (process.env.COLORTERM) {
|
||||
c(process.env.COLORTERM);
|
||||
} else if (process.env.TERM) {
|
||||
c(process.env.TERM);
|
||||
} else {
|
||||
c('xterm');
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
c('xterm');
|
||||
});
|
||||
c('xterm');
|
||||
});
|
||||
}
|
||||
return _DEFAULT_TERMINAL_LINUX_READY;
|
||||
}
|
||||
|
||||
export const DEFAULT_TERMINAL_OSX = 'Terminal.app';
|
||||
|
||||
export const DEFAULT_TERMINAL_WINDOWS = `${process.env.windir}\\${process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') ? 'Sysnative' : 'System32'}\\cmd.exe`;
|
||||
let _DEFAULT_TERMINAL_WINDOWS: string = null;
|
||||
export function getDefaultTerminalWindows(): string {
|
||||
if (!_DEFAULT_TERMINAL_WINDOWS) {
|
||||
_DEFAULT_TERMINAL_WINDOWS = `${process.env.windir}\\${process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') ? 'Sysnative' : 'System32'}\\cmd.exe`;
|
||||
}
|
||||
return _DEFAULT_TERMINAL_WINDOWS;
|
||||
}
|
||||
|
||||
export interface ITerminalConfiguration {
|
||||
terminal: {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { assign } from 'vs/base/common/objects';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ITerminalService } from 'vs/workbench/parts/execution/common/execution';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ITerminalConfiguration, DEFAULT_TERMINAL_WINDOWS, DEFAULT_TERMINAL_LINUX_READY, DEFAULT_TERMINAL_OSX } from 'vs/workbench/parts/execution/electron-browser/terminal';
|
||||
import { ITerminalConfiguration, getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX } from 'vs/workbench/parts/execution/electron-browser/terminal';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
|
||||
@@ -31,7 +31,7 @@ export class WinTerminalService implements ITerminalService {
|
||||
private static readonly CMD = 'cmd.exe';
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private _configurationService: IConfigurationService
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export class WinTerminalService implements ITerminalService {
|
||||
|
||||
const configuration = this._configurationService.getValue<ITerminalConfiguration>();
|
||||
const terminalConfig = configuration.terminal.external;
|
||||
const exec = terminalConfig.windowsExec || DEFAULT_TERMINAL_WINDOWS;
|
||||
const exec = terminalConfig.windowsExec || getDefaultTerminalWindows();
|
||||
|
||||
return new TPromise<void>((c, e) => {
|
||||
|
||||
@@ -78,7 +78,7 @@ export class WinTerminalService implements ITerminalService {
|
||||
|
||||
private spawnTerminal(spawner, configuration: ITerminalConfiguration, command: string, cwd?: string): TPromise<void> {
|
||||
const terminalConfig = configuration.terminal.external;
|
||||
const exec = terminalConfig.windowsExec || DEFAULT_TERMINAL_WINDOWS;
|
||||
const exec = terminalConfig.windowsExec || getDefaultTerminalWindows();
|
||||
const spawnType = this.getSpawnType(exec);
|
||||
|
||||
// Make the drive letter uppercase on Windows (see #9448)
|
||||
@@ -120,7 +120,7 @@ export class MacTerminalService implements ITerminalService {
|
||||
private static readonly OSASCRIPT = '/usr/bin/osascript'; // osascript is the AppleScript interpreter on OS X
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private _configurationService: IConfigurationService
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) { }
|
||||
|
||||
public openTerminal(cwd?: string): void {
|
||||
@@ -211,7 +211,7 @@ export class LinuxTerminalService implements ITerminalService {
|
||||
private static readonly WAIT_MESSAGE = nls.localize('press.any.key', "Press any key to continue...");
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private _configurationService: IConfigurationService
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) { }
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ export class LinuxTerminalService implements ITerminalService {
|
||||
|
||||
const configuration = this._configurationService.getValue<ITerminalConfiguration>();
|
||||
const terminalConfig = configuration.terminal.external;
|
||||
const execPromise = terminalConfig.linuxExec ? TPromise.as(terminalConfig.linuxExec) : DEFAULT_TERMINAL_LINUX_READY;
|
||||
const execPromise = terminalConfig.linuxExec ? TPromise.as(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady();
|
||||
|
||||
return new TPromise<void>((c, e) => {
|
||||
|
||||
@@ -280,7 +280,7 @@ export class LinuxTerminalService implements ITerminalService {
|
||||
|
||||
private spawnTerminal(spawner, configuration: ITerminalConfiguration, cwd?: string): TPromise<void> {
|
||||
const terminalConfig = configuration.terminal.external;
|
||||
const execPromise = terminalConfig.linuxExec ? TPromise.as(terminalConfig.linuxExec) : DEFAULT_TERMINAL_LINUX_READY;
|
||||
const execPromise = terminalConfig.linuxExec ? TPromise.as(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady();
|
||||
const env = cwd ? { cwd: cwd } : void 0;
|
||||
|
||||
return new TPromise<void>((c, e) => {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { deepEqual, equal } from 'assert';
|
||||
import { WinTerminalService, LinuxTerminalService, MacTerminalService } from 'vs/workbench/parts/execution/electron-browser/terminalService';
|
||||
import { DEFAULT_TERMINAL_WINDOWS, DEFAULT_TERMINAL_LINUX_READY, DEFAULT_TERMINAL_OSX } from 'vs/workbench/parts/execution/electron-browser/terminal';
|
||||
import { getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX } from 'vs/workbench/parts/execution/electron-browser/terminal';
|
||||
|
||||
suite('Execution - TerminalService', () => {
|
||||
let mockOnExit: Function;
|
||||
@@ -61,7 +61,7 @@ suite('Execution - TerminalService', () => {
|
||||
let mockSpawner = {
|
||||
spawn: (command: any, args: any, opts: any) => {
|
||||
// assert
|
||||
equal(args[args.length - 1], DEFAULT_TERMINAL_WINDOWS, 'terminal should equal expected');
|
||||
equal(args[args.length - 1], getDefaultTerminalWindows(), 'terminal should equal expected');
|
||||
done();
|
||||
return {
|
||||
on: (evt: any) => evt
|
||||
@@ -197,7 +197,7 @@ suite('Execution - TerminalService', () => {
|
||||
});
|
||||
|
||||
test(`LinuxTerminalService - uses default terminal when configuration.terminal.external.linuxExec is undefined`, done => {
|
||||
DEFAULT_TERMINAL_LINUX_READY.then(defaultTerminalLinux => {
|
||||
getDefaultTerminalLinuxReady().then(defaultTerminalLinux => {
|
||||
let testCwd = 'path/to/workspace';
|
||||
let mockSpawner = {
|
||||
spawn: (command: any, args: any, opts: any) => {
|
||||
@@ -220,4 +220,4 @@ suite('Execution - TerminalService', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,13 +9,14 @@ import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { TPromise, Promise } from 'vs/base/common/winjs.base';
|
||||
import { IDataSource, ITree, IRenderer } from 'vs/base/parts/tree/browser/tree';
|
||||
import { DefaultController, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IExtensionDependencies, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export interface IExtensionTemplateData {
|
||||
icon: HTMLImageElement;
|
||||
@@ -156,21 +157,24 @@ export class Renderer implements IRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
export class Controller extends DefaultController {
|
||||
export class Controller extends WorkbenchTreeController {
|
||||
|
||||
constructor( @IExtensionsWorkbenchService private extensionsWorkdbenchService: IExtensionsWorkbenchService) {
|
||||
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false });
|
||||
constructor(
|
||||
@IExtensionsWorkbenchService private extensionsWorkdbenchService: IExtensionsWorkbenchService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({}, configurationService);
|
||||
|
||||
// TODO@Sandeep this should be a command
|
||||
this.downKeyBindingDispatcher.set(KeyMod.CtrlCmd | KeyCode.Enter, (tree: ITree, event: any) => this.openExtension(tree, true));
|
||||
}
|
||||
|
||||
protected onLeftClick(tree: ITree, element: IExtensionDependencies, event: IMouseEvent): boolean {
|
||||
let currentFoucssed = tree.getFocus();
|
||||
let currentFocused = tree.getFocus();
|
||||
if (super.onLeftClick(tree, element, event)) {
|
||||
if (element.dependent === null) {
|
||||
if (currentFoucssed) {
|
||||
tree.setFocus(currentFoucssed);
|
||||
if (currentFocused) {
|
||||
tree.setFocus(currentFocused);
|
||||
} else {
|
||||
tree.focusFirst();
|
||||
}
|
||||
|
||||
@@ -16,29 +16,27 @@ import Event, { Emitter, once, chain } from 'vs/base/common/event';
|
||||
import Cache from 'vs/base/common/cache';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Builder } from 'vs/base/browser/builder';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { append, $, addClass, removeClass, finalHandler, join } from 'vs/base/browser/dom';
|
||||
import { append, $, addClass, removeClass, finalHandler, join, toggleClass } from 'vs/base/browser/dom';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionManifest, IKeyBinding, IView, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionManifest, IKeyBinding, IView, IExtensionTipsService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
|
||||
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { Renderer, DataSource, Controller } from 'vs/workbench/parts/extensions/browser/dependenciesViewer';
|
||||
import { RatingsWidget, InstallWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets';
|
||||
import { RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets';
|
||||
import { EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, BuiltinStatusLabelAction, ReloadAction, MaliciousStatusLabelAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
|
||||
import WebView from 'vs/workbench/parts/html/browser/webview';
|
||||
import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, ReloadAction, MaliciousStatusLabelAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
|
||||
import { Webview } from 'vs/workbench/parts/html/browser/webview';
|
||||
import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { Position } from 'vs/platform/editor/common/editor';
|
||||
@@ -46,31 +44,34 @@ import { IPartService, Parts } from 'vs/workbench/services/part/common/partServi
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IContextKeyService, RawContextKey, ContextKeyExpr, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Command, ICommandOptions } from 'vs/editor/browser/editorExtensions';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
/** A context key that is set when an extension editor webview has focus. */
|
||||
export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS = new RawContextKey<boolean>('extensionEditorWebviewFocus', undefined);
|
||||
/** A context key that is set when an extension editor webview not have focus. */
|
||||
export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS.toNegated();
|
||||
/** A context key that is set when the find widget find input in extension editor webview is focused. */
|
||||
export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED = new RawContextKey<boolean>('extensionEditorFindWidgetInputFocused', false);
|
||||
/** A context key that is set when the find widget find input in extension editor webview is not focused. */
|
||||
export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED.toNegated();
|
||||
|
||||
function renderBody(body: string): string {
|
||||
const styleSheetPath = require.toUrl('./media/markdown.css').replace('file://', 'vscode-core-resource://');
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src https:; media-src https:; script-src 'none'; style-src file:; child-src 'none'; frame-src 'none';">
|
||||
<link rel="stylesheet" type="text/css" href="${require.toUrl('./media/markdown.css')}">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src https: data:; media-src https:; script-src 'none'; style-src vscode-core-resource:; child-src 'none'; frame-src 'none';">
|
||||
<link rel="stylesheet" type="text/css" href="${styleSheetPath}">
|
||||
</head>
|
||||
<body>${body}</body>
|
||||
<body>
|
||||
<a id="scroll-to-top" role="button" aria-label="scroll to top" href="#"><span class="icon"></span></a>
|
||||
${body}
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
@@ -147,12 +148,13 @@ interface ILayoutParticipant {
|
||||
|
||||
export class ExtensionEditor extends BaseEditor {
|
||||
|
||||
static ID: string = 'workbench.editor.extension';
|
||||
static readonly ID: string = 'workbench.editor.extension';
|
||||
|
||||
private icon: HTMLImageElement;
|
||||
private name: HTMLElement;
|
||||
private identifier: HTMLElement;
|
||||
private preview: HTMLElement;
|
||||
private builtin: HTMLElement;
|
||||
private license: HTMLElement;
|
||||
private publisher: HTMLElement;
|
||||
private installCount: HTMLElement;
|
||||
@@ -176,22 +178,23 @@ export class ExtensionEditor extends BaseEditor {
|
||||
private contentDisposables: IDisposable[] = [];
|
||||
private transientDisposables: IDisposable[] = [];
|
||||
private disposables: IDisposable[];
|
||||
private activeWebview: WebView;
|
||||
private activeWebview: Webview;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IViewletService private viewletService: IViewletService,
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IThemeService protected themeService: IThemeService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@IOpenerService private openerService: IOpenerService,
|
||||
@IListService private listService: IListService,
|
||||
@IPartService private partService: IPartService,
|
||||
@IContextViewService private contextViewService: IContextViewService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IExtensionTipsService private extensionTipsService: IExtensionTipsService
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IContextViewService private readonly contextViewService: IContextViewService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService
|
||||
|
||||
) {
|
||||
super(ExtensionEditor.ID, telemetryService, themeService);
|
||||
this.disposables = [];
|
||||
@@ -215,7 +218,12 @@ export class ExtensionEditor extends BaseEditor {
|
||||
const title = append(details, $('.title'));
|
||||
this.name = append(title, $('span.name.clickable', { title: localize('name', "Extension name") }));
|
||||
this.identifier = append(title, $('span.identifier', { title: localize('extension id', "Extension identifier") }));
|
||||
|
||||
this.preview = append(title, $('span.preview', { title: localize('preview', "Preview") }));
|
||||
this.preview.textContent = localize('preview', "Preview");
|
||||
|
||||
this.builtin = append(title, $('span.builtin'));
|
||||
this.builtin.textContent = localize('builtin', "Built-in");
|
||||
|
||||
const subtitle = append(details, $('.subtitle'));
|
||||
this.publisher = append(subtitle, $('span.publisher.clickable', { title: localize('publisher', "Publisher name") }));
|
||||
@@ -267,15 +275,6 @@ export class ExtensionEditor extends BaseEditor {
|
||||
|
||||
this.transientDisposables = dispose(this.transientDisposables);
|
||||
|
||||
/* __GDPR__
|
||||
"extensionGallery:openExtension" : {
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionGallery:openExtension', extension.telemetryData);
|
||||
|
||||
this.extensionReadme = new Cache(() => extension.getReadme());
|
||||
this.extensionChangelog = new Cache(() => extension.getChangelog());
|
||||
this.extensionManifest = new Cache(() => extension.getManifest());
|
||||
@@ -287,23 +286,36 @@ export class ExtensionEditor extends BaseEditor {
|
||||
|
||||
this.name.textContent = extension.displayName;
|
||||
this.identifier.textContent = extension.id;
|
||||
if (extension.preview) {
|
||||
this.preview.textContent = localize('preview', "Preview");
|
||||
} else {
|
||||
this.preview.textContent = null;
|
||||
}
|
||||
this.preview.style.display = extension.preview ? 'inherit' : 'none';
|
||||
this.builtin.style.display = extension.type === LocalExtensionType.System ? 'inherit' : 'none';
|
||||
|
||||
this.publisher.textContent = extension.publisherDisplayName;
|
||||
this.description.textContent = extension.description;
|
||||
|
||||
const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason();
|
||||
this.recommendation.textContent = extRecommendations[extension.id.toLowerCase()];
|
||||
let recommendationsData = {};
|
||||
if (extRecommendations[extension.id.toLowerCase()]) {
|
||||
addClass(this.header, 'recommended');
|
||||
this.recommendation.textContent = extRecommendations[extension.id.toLowerCase()].reasonText;
|
||||
recommendationsData = { recommendationReason: extRecommendations[extension.id.toLowerCase()].reasonId };
|
||||
} else {
|
||||
removeClass(this.header, 'recommended');
|
||||
this.recommendation.textContent = '';
|
||||
}
|
||||
|
||||
/* __GDPR__
|
||||
"extensionGallery:openExtension" : {
|
||||
"recommendationReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionGallery:openExtension', assign(extension.telemetryData, recommendationsData));
|
||||
|
||||
toggleClass(this.name, 'clickable', !!extension.url);
|
||||
toggleClass(this.publisher, 'clickable', !!extension.url);
|
||||
toggleClass(this.rating, 'clickable', !!extension.url);
|
||||
if (extension.url) {
|
||||
this.name.onclick = finalHandler(() => window.open(extension.url));
|
||||
this.rating.onclick = finalHandler(() => window.open(`${extension.url}#review-details`));
|
||||
@@ -320,6 +332,10 @@ export class ExtensionEditor extends BaseEditor {
|
||||
this.license.onclick = null;
|
||||
this.license.style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
this.name.onclick = null;
|
||||
this.rating.onclick = null;
|
||||
this.publisher.onclick = null;
|
||||
}
|
||||
|
||||
if (extension.repository) {
|
||||
@@ -338,7 +354,6 @@ export class ExtensionEditor extends BaseEditor {
|
||||
//const ratings = this.instantiationService.createInstance(RatingsWidget, this.rating, { extension });
|
||||
//this.transientDisposables.push(ratings);
|
||||
|
||||
const builtinStatusAction = this.instantiationService.createInstance(BuiltinStatusLabelAction);
|
||||
const maliciousStatusAction = this.instantiationService.createInstance(MaliciousStatusLabelAction, true);
|
||||
const installAction = this.instantiationService.createInstance(CombinedInstallAction);
|
||||
const updateAction = this.instantiationService.createInstance(UpdateAction);
|
||||
@@ -347,7 +362,6 @@ export class ExtensionEditor extends BaseEditor {
|
||||
const reloadAction = this.instantiationService.createInstance(ReloadAction);
|
||||
|
||||
installAction.extension = extension;
|
||||
builtinStatusAction.extension = extension;
|
||||
maliciousStatusAction.extension = extension;
|
||||
updateAction.extension = extension;
|
||||
enableAction.extension = extension;
|
||||
@@ -355,8 +369,10 @@ export class ExtensionEditor extends BaseEditor {
|
||||
reloadAction.extension = extension;
|
||||
|
||||
this.extensionActionBar.clear();
|
||||
this.extensionActionBar.push([reloadAction, updateAction, enableAction, disableAction, installAction, builtinStatusAction, maliciousStatusAction], { icon: true, label: true });
|
||||
this.transientDisposables.push(enableAction, updateAction, reloadAction, disableAction, installAction, builtinStatusAction, maliciousStatusAction);
|
||||
this.extensionActionBar.push([reloadAction, updateAction, enableAction, disableAction, installAction, maliciousStatusAction], { icon: true, label: true });
|
||||
this.transientDisposables.push(enableAction, updateAction, reloadAction, disableAction, installAction, maliciousStatusAction);
|
||||
|
||||
this.content.innerHTML = ''; // Clear content before setting navbar actions.
|
||||
|
||||
this.navbar.clear();
|
||||
this.navbar.onChange(this.onNavbarChange.bind(this, extension), this, this.transientDisposables);
|
||||
@@ -365,8 +381,6 @@ export class ExtensionEditor extends BaseEditor {
|
||||
this.navbar.push(NavbarSection.Changelog, localize('changelog', "Changelog"));
|
||||
this.navbar.push(NavbarSection.Dependencies, localize('dependencies', "Dependencies"));
|
||||
|
||||
this.content.innerHTML = '';
|
||||
|
||||
return super.setInput(input, options);
|
||||
}
|
||||
|
||||
@@ -412,13 +426,11 @@ export class ExtensionEditor extends BaseEditor {
|
||||
.then(removeEmbeddedSVGs)
|
||||
.then<void>(body => {
|
||||
const allowedBadgeProviders = this.extensionsWorkbenchService.allowedBadgeProviders;
|
||||
const webViewOptions = allowedBadgeProviders.length > 0 ? { allowScripts: false, allowSvgs: false, svgWhiteList: allowedBadgeProviders } : undefined;
|
||||
this.activeWebview = new WebView(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions);
|
||||
const webViewOptions = allowedBadgeProviders.length > 0 ? { allowScripts: false, allowSvgs: false, svgWhiteList: allowedBadgeProviders } : {};
|
||||
this.activeWebview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this.environmentService, this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions);
|
||||
const removeLayoutParticipant = arrays.insert(this.layoutParticipants, this.activeWebview);
|
||||
this.contentDisposables.push(toDisposable(removeLayoutParticipant));
|
||||
|
||||
this.activeWebview.style(this.themeService.getTheme());
|
||||
this.activeWebview.contents = [body];
|
||||
this.activeWebview.contents = body;
|
||||
|
||||
this.activeWebview.onDidClickLink(link => {
|
||||
// Whitelist supported schemes for links
|
||||
@@ -426,7 +438,6 @@ export class ExtensionEditor extends BaseEditor {
|
||||
this.openerService.open(link);
|
||||
}
|
||||
}, null, this.contentDisposables);
|
||||
this.themeService.onThemeChange(theme => this.activeWebview.style(theme), null, this.contentDisposables);
|
||||
this.contentDisposables.push(this.activeWebview);
|
||||
})
|
||||
.then(null, () => {
|
||||
@@ -462,7 +473,8 @@ export class ExtensionEditor extends BaseEditor {
|
||||
this.renderColors(content, manifest, layout),
|
||||
this.renderJSONValidation(content, manifest, layout),
|
||||
this.renderDebuggers(content, manifest, layout),
|
||||
this.renderViews(content, manifest, layout)
|
||||
this.renderViews(content, manifest, layout),
|
||||
this.renderLocalizations(content, manifest, layout)
|
||||
];
|
||||
|
||||
const isEmpty = !renders.reduce((v, r) => r || v, false);
|
||||
@@ -506,7 +518,7 @@ export class ExtensionEditor extends BaseEditor {
|
||||
scrollableContent.scanDomNode();
|
||||
}, error => {
|
||||
append(this.content, $('p.nocontent')).textContent = error;
|
||||
this.messageService.show(Severity.Error, error);
|
||||
this.notificationService.error(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -514,15 +526,14 @@ export class ExtensionEditor extends BaseEditor {
|
||||
private renderDependencies(container: HTMLElement, extensionDependencies: IExtensionDependencies): Tree {
|
||||
const renderer = this.instantiationService.createInstance(Renderer);
|
||||
const controller = this.instantiationService.createInstance(Controller);
|
||||
const tree = new WorkbenchTree(container, {
|
||||
const tree = this.instantiationService.createInstance(WorkbenchTree, container, {
|
||||
dataSource: new DataSource(),
|
||||
renderer,
|
||||
controller
|
||||
}, {
|
||||
indentPixels: 40,
|
||||
twistiePixels: 20,
|
||||
keyboardSupport: false
|
||||
}, this.contextKeyService, this.listService, this.themeService);
|
||||
twistiePixels: 20
|
||||
});
|
||||
|
||||
tree.setInput(extensionDependencies);
|
||||
|
||||
@@ -618,6 +629,26 @@ export class ExtensionEditor extends BaseEditor {
|
||||
return true;
|
||||
}
|
||||
|
||||
private renderLocalizations(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
|
||||
const contributes = manifest.contributes;
|
||||
const localizations = contributes && contributes.localizations || [];
|
||||
|
||||
if (!localizations.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const details = $('details', { open: true, ontoggle: onDetailsToggle },
|
||||
$('summary', null, localize('localizations', "Localizations ({0})", localizations.length)),
|
||||
$('table', null,
|
||||
$('tr', null, $('th', null, localize('localizations language id', "Language Id")), $('th', null, localize('localizations language name', "Langauge Name")), $('th', null, localize('localizations localized language name', "Langauge Name (Localized)"))),
|
||||
...localizations.map(localization => $('tr', null, $('td', null, localization.languageId), $('td', null, localization.languageName), $('td', null, localization.languageNameLocalized)))
|
||||
)
|
||||
);
|
||||
|
||||
append(container, details);
|
||||
return true;
|
||||
}
|
||||
|
||||
private renderColorThemes(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
|
||||
const contributes = manifest.contributes;
|
||||
const contrib = contributes && contributes.themes || [];
|
||||
@@ -707,8 +738,15 @@ export class ExtensionEditor extends BaseEditor {
|
||||
|
||||
const details = $('details', { open: true, ontoggle: onDetailsToggle },
|
||||
$('summary', null, localize('JSON Validation', "JSON Validation ({0})", contrib.length)),
|
||||
$('ul', null, ...contrib.map(v => $('li', null, v.fileMatch)))
|
||||
);
|
||||
$('table', null,
|
||||
$('tr', null,
|
||||
$('th', null, localize('fileMatch', "File Match")),
|
||||
$('th', null, localize('schema', "Schema"))
|
||||
),
|
||||
...contrib.map(v => $('tr', null,
|
||||
$('td', null, $('code', null, v.fileMatch)),
|
||||
$('td', null, v.url)
|
||||
))));
|
||||
|
||||
append(container, details);
|
||||
return true;
|
||||
@@ -898,7 +936,7 @@ export class ExtensionEditor extends BaseEditor {
|
||||
return;
|
||||
}
|
||||
|
||||
this.messageService.show(Severity.Error, err);
|
||||
this.notificationService.error(err);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
@@ -17,18 +17,17 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
|
||||
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate';
|
||||
import { LocalExtensionType, IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { LocalExtensionType, IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { ToggleViewletAction } from 'vs/workbench/browser/viewlet';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { Query } from 'vs/workbench/parts/extensions/common/extensionQuery';
|
||||
import { IFileService, IContent } from 'vs/platform/files/common/files';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IExtensionService, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IExtensionService, IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -38,12 +37,43 @@ import { Color } from 'vs/base/common/color';
|
||||
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
|
||||
import { ITextEditorSelection } from 'vs/platform/editor/common/editor';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { PICK_WORKSPACE_FOLDER_COMMAND } from 'vs/workbench/browser/actions/workspaceActions';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { PagedModel } from 'vs/base/common/paging';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
|
||||
class DownloadExtensionAction extends Action {
|
||||
|
||||
constructor(
|
||||
private extension: IExtension,
|
||||
@IOpenerService private openerService: IOpenerService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
super('extensions.download', localize('download', "Download Manually"), '', true);
|
||||
}
|
||||
|
||||
run(): TPromise<void> {
|
||||
return this.openerService.open(URI.parse(this.extension.downloadUrl)).then(() => {
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Info,
|
||||
message: localize('install vsix', 'Once downloaded, please manually install the downloaded VSIX of \'{0}\'.', this.extension.id),
|
||||
actions: {
|
||||
primary: [
|
||||
this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL)
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class InstallAction extends Action {
|
||||
|
||||
@@ -59,7 +89,9 @@ export class InstallAction extends Action {
|
||||
set extension(extension: IExtension) { this._extension = extension; this.update(); }
|
||||
|
||||
constructor(
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@INotificationService private notificationService: INotificationService
|
||||
) {
|
||||
super('extensions.install', InstallAction.InstallLabel, InstallAction.Class, false);
|
||||
|
||||
@@ -90,7 +122,27 @@ export class InstallAction extends Action {
|
||||
|
||||
run(): TPromise<any> {
|
||||
this.extensionsWorkbenchService.open(this.extension);
|
||||
return this.extensionsWorkbenchService.install(this.extension);
|
||||
|
||||
return this.install(this.extension);
|
||||
}
|
||||
|
||||
private install(extension: IExtension): TPromise<void> {
|
||||
return this.extensionsWorkbenchService.install(extension).then(null, err => {
|
||||
if (!extension.downloadUrl) {
|
||||
return this.notificationService.error(err);
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: localize('failedToInstall', "Failed to install \'{0}\'.", extension.id),
|
||||
actions: {
|
||||
primary: [
|
||||
this.instantiationService.createInstance(DownloadExtensionAction, extension)
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -252,7 +304,9 @@ export class UpdateAction extends Action {
|
||||
set extension(extension: IExtension) { this._extension = extension; this.update(); }
|
||||
|
||||
constructor(
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@INotificationService private notificationService: INotificationService
|
||||
) {
|
||||
super('extensions.update', UpdateAction.Label, UpdateAction.DisabledClass, false);
|
||||
|
||||
@@ -284,7 +338,26 @@ export class UpdateAction extends Action {
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
return this.extensionsWorkbenchService.install(this.extension);
|
||||
return this.install(this.extension);
|
||||
}
|
||||
|
||||
private install(extension: IExtension): TPromise<void> {
|
||||
return this.extensionsWorkbenchService.install(extension).then(null, err => {
|
||||
if (!extension.downloadUrl) {
|
||||
return this.notificationService.error(err);
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: localize('failedToUpdate', "Failed to update \'{0}\'.", extension.id),
|
||||
actions: {
|
||||
primary: [
|
||||
this.instantiationService.createInstance(DownloadExtensionAction, extension)
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -350,7 +423,7 @@ export class DropDownMenuActionItem extends ActionItem {
|
||||
|
||||
export class ManageExtensionAction extends Action {
|
||||
|
||||
static ID = 'extensions.manage';
|
||||
static readonly ID = 'extensions.manage';
|
||||
|
||||
private static readonly Class = 'extension-action manage';
|
||||
private static readonly HideManageExtensionClass = `${ManageExtensionAction.Class} hide`;
|
||||
@@ -371,12 +444,12 @@ export class ManageExtensionAction extends Action {
|
||||
|
||||
this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [
|
||||
[
|
||||
instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL),
|
||||
instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL)
|
||||
instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL),
|
||||
instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL)
|
||||
],
|
||||
[
|
||||
instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL),
|
||||
instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL)
|
||||
instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL),
|
||||
instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL)
|
||||
],
|
||||
[
|
||||
instantiationService.createInstance(UninstallAction)
|
||||
@@ -392,7 +465,7 @@ export class ManageExtensionAction extends Action {
|
||||
this.class = ManageExtensionAction.HideManageExtensionClass;
|
||||
this.tooltip = '';
|
||||
this.enabled = false;
|
||||
if (this.extension && this.extension.type !== LocalExtensionType.System) {
|
||||
if (this.extension) {
|
||||
const state = this.extension.state;
|
||||
this.enabled = state === ExtensionState.Installed;
|
||||
this.class = this.enabled || state === ExtensionState.Uninstalling ? ManageExtensionAction.Class : ManageExtensionAction.HideManageExtensionClass;
|
||||
@@ -413,7 +486,7 @@ export class ManageExtensionAction extends Action {
|
||||
|
||||
export class EnableForWorkspaceAction extends Action implements IExtensionAction {
|
||||
|
||||
static ID = 'extensions.enableForWorkspace';
|
||||
static readonly ID = 'extensions.enableForWorkspace';
|
||||
static LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
@@ -437,7 +510,7 @@ export class EnableForWorkspaceAction extends Action implements IExtensionAction
|
||||
private update(): void {
|
||||
this.enabled = false;
|
||||
if (this.extension) {
|
||||
this.enabled = (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && this.extensionEnablementService.canChangeEnablement();
|
||||
this.enabled = (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,7 +526,7 @@ export class EnableForWorkspaceAction extends Action implements IExtensionAction
|
||||
|
||||
export class EnableGloballyAction extends Action implements IExtensionAction {
|
||||
|
||||
static ID = 'extensions.enableGlobally';
|
||||
static readonly ID = 'extensions.enableGlobally';
|
||||
static LABEL = localize('enableGloballyAction', "Enable");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
@@ -475,7 +548,7 @@ export class EnableGloballyAction extends Action implements IExtensionAction {
|
||||
private update(): void {
|
||||
this.enabled = false;
|
||||
if (this.extension) {
|
||||
this.enabled = (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && this.extensionEnablementService.canChangeEnablement();
|
||||
this.enabled = (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +564,7 @@ export class EnableGloballyAction extends Action implements IExtensionAction {
|
||||
|
||||
export class EnableAction extends Action {
|
||||
|
||||
static ID = 'extensions.enable';
|
||||
static readonly ID = 'extensions.enable';
|
||||
private static readonly EnabledClass = 'extension-action prominent enable';
|
||||
private static readonly DisabledClass = `${EnableAction.EnabledClass} disabled`;
|
||||
|
||||
@@ -514,8 +587,8 @@ export class EnableAction extends Action {
|
||||
super(EnableAction.ID, localize('enableAction', "Enable"), EnableAction.DisabledClass, false);
|
||||
|
||||
this._enableActions = [
|
||||
instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL),
|
||||
instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL)
|
||||
instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL),
|
||||
instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL)
|
||||
];
|
||||
this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [this._enableActions]);
|
||||
this.disposables.push(this._actionItem);
|
||||
@@ -549,7 +622,7 @@ export class EnableAction extends Action {
|
||||
|
||||
export class DisableForWorkspaceAction extends Action implements IExtensionAction {
|
||||
|
||||
static ID = 'extensions.disableForWorkspace';
|
||||
static readonly ID = 'extensions.disableForWorkspace';
|
||||
static LABEL = localize('disableForWorkspaceAction', "Disable (Workspace)");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
@@ -560,7 +633,8 @@ export class DisableForWorkspaceAction extends Action implements IExtensionActio
|
||||
|
||||
constructor(label: string,
|
||||
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService
|
||||
) {
|
||||
super(DisableForWorkspaceAction.ID, label);
|
||||
|
||||
@@ -572,7 +646,7 @@ export class DisableForWorkspaceAction extends Action implements IExtensionActio
|
||||
private update(): void {
|
||||
this.enabled = false;
|
||||
if (this.extension && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) {
|
||||
this.enabled = this.extension.type !== LocalExtensionType.System && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled);
|
||||
this.enabled = (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,7 +662,7 @@ export class DisableForWorkspaceAction extends Action implements IExtensionActio
|
||||
|
||||
export class DisableGloballyAction extends Action implements IExtensionAction {
|
||||
|
||||
static ID = 'extensions.disableGlobally';
|
||||
static readonly ID = 'extensions.disableGlobally';
|
||||
static LABEL = localize('disableGloballyAction', "Disable");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
@@ -598,7 +672,8 @@ export class DisableGloballyAction extends Action implements IExtensionAction {
|
||||
set extension(extension: IExtension) { this._extension = extension; this.update(); }
|
||||
|
||||
constructor(label: string,
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService
|
||||
) {
|
||||
super(DisableGloballyAction.ID, label);
|
||||
|
||||
@@ -609,7 +684,7 @@ export class DisableGloballyAction extends Action implements IExtensionAction {
|
||||
private update(): void {
|
||||
this.enabled = false;
|
||||
if (this.extension) {
|
||||
this.enabled = this.extension.type !== LocalExtensionType.System && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled);
|
||||
this.enabled = (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,7 +700,7 @@ export class DisableGloballyAction extends Action implements IExtensionAction {
|
||||
|
||||
export class DisableAction extends Action {
|
||||
|
||||
static ID = 'extensions.disable';
|
||||
static readonly ID = 'extensions.disable';
|
||||
|
||||
private static readonly EnabledClass = 'extension-action disable';
|
||||
private static readonly DisabledClass = `${DisableAction.EnabledClass} disabled`;
|
||||
@@ -646,8 +721,8 @@ export class DisableAction extends Action {
|
||||
) {
|
||||
super(DisableAction.ID, localize('disableAction', "Disable"), DisableAction.DisabledClass, false);
|
||||
this._disableActions = [
|
||||
instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL),
|
||||
instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL)
|
||||
instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL),
|
||||
instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL)
|
||||
];
|
||||
this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [this._disableActions]);
|
||||
this.disposables.push(this._actionItem);
|
||||
@@ -663,7 +738,7 @@ export class DisableAction extends Action {
|
||||
return;
|
||||
}
|
||||
|
||||
this.enabled = this.extension.state === ExtensionState.Installed && this.extension.type !== LocalExtensionType.System && this._disableActions.some(a => a.enabled);
|
||||
this.enabled = this.extension.state === ExtensionState.Installed && this._disableActions.some(a => a.enabled);
|
||||
this.class = this.enabled ? DisableAction.EnabledClass : DisableAction.DisabledClass;
|
||||
}
|
||||
|
||||
@@ -680,7 +755,7 @@ export class DisableAction extends Action {
|
||||
|
||||
export class CheckForUpdatesAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.checkForUpdates';
|
||||
static readonly ID = 'workbench.extensions.action.checkForUpdates';
|
||||
static LABEL = localize('checkForUpdates', "Check for Updates");
|
||||
|
||||
constructor(
|
||||
@@ -720,7 +795,7 @@ export class ToggleAutoUpdateAction extends Action {
|
||||
|
||||
export class EnableAutoUpdateAction extends ToggleAutoUpdateAction {
|
||||
|
||||
static ID = 'workbench.extensions.action.enableAutoUpdate';
|
||||
static readonly ID = 'workbench.extensions.action.enableAutoUpdate';
|
||||
static LABEL = localize('enableAutoUpdate', "Enable Auto Updating Extensions");
|
||||
|
||||
constructor(
|
||||
@@ -734,7 +809,7 @@ export class EnableAutoUpdateAction extends ToggleAutoUpdateAction {
|
||||
|
||||
export class DisableAutoUpdateAction extends ToggleAutoUpdateAction {
|
||||
|
||||
static ID = 'workbench.extensions.action.disableAutoUpdate';
|
||||
static readonly ID = 'workbench.extensions.action.disableAutoUpdate';
|
||||
static LABEL = localize('disableAutoUpdate', "Disable Auto Updating Extensions");
|
||||
|
||||
constructor(
|
||||
@@ -748,7 +823,7 @@ export class DisableAutoUpdateAction extends ToggleAutoUpdateAction {
|
||||
|
||||
export class UpdateAllAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.updateAllExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.updateAllExtensions';
|
||||
static LABEL = localize('updateAll', "Update All Extensions");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
@@ -756,7 +831,9 @@ export class UpdateAllAction extends Action {
|
||||
constructor(
|
||||
id = UpdateAllAction.ID,
|
||||
label = UpdateAllAction.LABEL,
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
super(id, label, '', false);
|
||||
|
||||
@@ -773,7 +850,26 @@ export class UpdateAllAction extends Action {
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
return TPromise.join(this.outdated.map(e => this.extensionsWorkbenchService.install(e)));
|
||||
return TPromise.join(this.outdated.map(e => this.install(e)));
|
||||
}
|
||||
|
||||
private install(extension: IExtension): TPromise<void> {
|
||||
return this.extensionsWorkbenchService.install(extension).then(null, err => {
|
||||
if (!extension.downloadUrl) {
|
||||
return this.notificationService.error(err);
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: localize('failedToUpdate', "Failed to update \'{0}\'.", extension.id),
|
||||
actions: {
|
||||
primary: [
|
||||
this.instantiationService.createInstance(DownloadExtensionAction, extension)
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -820,17 +916,17 @@ export class ReloadAction extends Action {
|
||||
if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) {
|
||||
return TPromise.wrap<void>(null);
|
||||
}
|
||||
return this.extensionService.getExtensions()
|
||||
.then(runningExtensions => this.computeReloadState(runningExtensions));
|
||||
return TPromise.join([this.extensionService.getExtensions(), this.extensionEnablementService.getDisabledExtensions()])
|
||||
.then(([runningExtensions, disabledExtensions]) => this.computeReloadState(runningExtensions, disabledExtensions));
|
||||
}).done(() => {
|
||||
this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass;
|
||||
});
|
||||
}
|
||||
|
||||
private computeReloadState(runningExtensions: IExtensionDescription[]): void {
|
||||
private computeReloadState(runningExtensions: IExtensionDescription[], disabledExtensions: IExtensionIdentifier[]): void {
|
||||
const isInstalled = this.extensionsWorkbenchService.local.some(e => e.id === this.extension.id);
|
||||
const isUninstalled = this.extension.state === ExtensionState.Uninstalled;
|
||||
const isDisabled = !this.extensionEnablementService.isEnabled({ id: this.extension.id, uuid: this.extension.uuid });
|
||||
const isDisabled = this.isDisabled(disabledExtensions);
|
||||
|
||||
const filteredExtensions = runningExtensions.filter(e => areSameExtensions(e, this.extension));
|
||||
const isExtensionRunning = filteredExtensions.length > 0;
|
||||
@@ -872,6 +968,14 @@ export class ReloadAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
private isDisabled(disabledExtensions: IExtensionIdentifier[]): boolean {
|
||||
const identifier = { id: this.extension.id, uuid: this.extension.uuid };
|
||||
if (this.extension.type === LocalExtensionType.System) {
|
||||
return disabledExtensions.some(d => areSameExtensions(d, identifier));
|
||||
}
|
||||
return !this.extensionEnablementService.isEnabled({ id: this.extension.id, uuid: this.extension.uuid });
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
return this.windowService.reloadWindow();
|
||||
}
|
||||
@@ -899,7 +1003,7 @@ export class InstallExtensionsAction extends OpenExtensionsViewletAction {
|
||||
|
||||
export class ShowEnabledExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.showEnabledExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.showEnabledExtensions';
|
||||
static LABEL = localize('showEnabledExtensions', 'Show Enabled Extensions');
|
||||
|
||||
constructor(
|
||||
@@ -922,7 +1026,7 @@ export class ShowEnabledExtensionsAction extends Action {
|
||||
|
||||
export class ShowInstalledExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.showInstalledExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.showInstalledExtensions';
|
||||
static LABEL = localize('showInstalledExtensions', "Show Installed Extensions");
|
||||
|
||||
constructor(
|
||||
@@ -945,7 +1049,7 @@ export class ShowInstalledExtensionsAction extends Action {
|
||||
|
||||
export class ShowDisabledExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.showDisabledExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.showDisabledExtensions';
|
||||
static LABEL = localize('showDisabledExtensions', "Show Disabled Extensions");
|
||||
|
||||
constructor(
|
||||
@@ -968,7 +1072,7 @@ export class ShowDisabledExtensionsAction extends Action {
|
||||
|
||||
export class ClearExtensionsInputAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.clearExtensionsInput';
|
||||
static readonly ID = 'workbench.extensions.action.clearExtensionsInput';
|
||||
static LABEL = localize('clearExtensionsInput', "Clear Extensions Input");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
@@ -1002,9 +1106,32 @@ export class ClearExtensionsInputAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class ShowBuiltInExtensionsAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.extensions.action.listBuiltInExtensions';
|
||||
static LABEL = localize('showBuiltInExtensions', "Show Built-in Extensions");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IViewletService private viewletService: IViewletService
|
||||
) {
|
||||
super(id, label, null, true);
|
||||
}
|
||||
|
||||
run(): TPromise<void> {
|
||||
return this.viewletService.openViewlet(VIEWLET_ID, true)
|
||||
.then(viewlet => viewlet as IExtensionsViewlet)
|
||||
.then(viewlet => {
|
||||
viewlet.search('@builtin ');
|
||||
viewlet.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ShowOutdatedExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.listOutdatedExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.listOutdatedExtensions';
|
||||
static LABEL = localize('showOutdatedExtensions', "Show Outdated Extensions");
|
||||
|
||||
constructor(
|
||||
@@ -1027,7 +1154,7 @@ export class ShowOutdatedExtensionsAction extends Action {
|
||||
|
||||
export class ShowPopularExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.showPopularExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.showPopularExtensions';
|
||||
static LABEL = localize('showPopularExtensions', "Show Popular Extensions");
|
||||
|
||||
constructor(
|
||||
@@ -1050,7 +1177,7 @@ export class ShowPopularExtensionsAction extends Action {
|
||||
|
||||
export class ShowRecommendedExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.showRecommendedExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.showRecommendedExtensions';
|
||||
static LABEL = localize('showRecommendedExtensions', "Show Recommended Extensions");
|
||||
|
||||
constructor(
|
||||
@@ -1073,7 +1200,7 @@ export class ShowRecommendedExtensionsAction extends Action {
|
||||
|
||||
export class InstallWorkspaceRecommendedExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.installWorkspaceRecommendedExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.installWorkspaceRecommendedExtensions';
|
||||
static LABEL = localize('installWorkspaceRecommendedExtensions', "Install All Workspace Recommended Extensions");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
@@ -1085,7 +1212,8 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action {
|
||||
@IViewletService private viewletService: IViewletService,
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IExtensionTipsService private extensionTipsService: IExtensionTipsService,
|
||||
@IMessageService private messageService: IMessageService
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
super(id, label, 'extension-action');
|
||||
this.extensionsWorkbenchService.onChange(() => this.update(), this, this.disposables);
|
||||
@@ -1112,7 +1240,7 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action {
|
||||
.then(viewlet => {
|
||||
if (!toInstall.length) {
|
||||
this.enabled = false;
|
||||
this.messageService.show(Severity.Info, localize('allExtensionsInstalled', "All extensions recommended for this workspace have already been installed"));
|
||||
this.notificationService.info(localize('allExtensionsInstalled', "All extensions recommended for this workspace have already been installed"));
|
||||
viewlet.focus();
|
||||
return TPromise.as(null);
|
||||
}
|
||||
@@ -1130,18 +1258,37 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action {
|
||||
extensionsWithDependencies.push(e);
|
||||
return TPromise.as(null);
|
||||
} else {
|
||||
return this.extensionsWorkbenchService.install(e);
|
||||
return this.install(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
return TPromise.join(installPromises).then(() => {
|
||||
return TPromise.join(extensionsWithDependencies.map(e => this.extensionsWorkbenchService.install(e)));
|
||||
return TPromise.join(extensionsWithDependencies.map(e => this.install(e)));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private install(extension: IExtension): TPromise<void> {
|
||||
return this.extensionsWorkbenchService.install(extension).then(null, err => {
|
||||
if (!extension.downloadUrl) {
|
||||
return this.notificationService.error(err);
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: localize('failedToInstall', "Failed to install \'{0}\'.", extension.id),
|
||||
actions: {
|
||||
primary: [
|
||||
this.instantiationService.createInstance(DownloadExtensionAction, extension)
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
super.dispose();
|
||||
@@ -1150,7 +1297,7 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action {
|
||||
|
||||
export class InstallRecommendedExtensionAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.installRecommendedExtension';
|
||||
static readonly ID = 'workbench.extensions.action.installRecommendedExtension';
|
||||
static LABEL = localize('installRecommendedExtension', "Install Recommended Extension");
|
||||
|
||||
private extensionId: string;
|
||||
@@ -1160,7 +1307,8 @@ export class InstallRecommendedExtensionAction extends Action {
|
||||
extensionId: string,
|
||||
@IViewletService private viewletService: IViewletService,
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IMessageService private messageService: IMessageService
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
super(InstallRecommendedExtensionAction.ID, InstallRecommendedExtensionAction.LABEL, null);
|
||||
this.extensionId = extensionId;
|
||||
@@ -1177,7 +1325,7 @@ export class InstallRecommendedExtensionAction extends Action {
|
||||
.then(viewlet => {
|
||||
if (this.extensionsWorkbenchService.local.some(x => x.id.toLowerCase() === this.extensionId.toLowerCase())) {
|
||||
this.enabled = false;
|
||||
this.messageService.show(Severity.Info, localize('extensionInstalled', "The recommended extension has already been installed"));
|
||||
this.notificationService.info(localize('extensionInstalled', "The recommended extension has already been installed"));
|
||||
viewlet.focus();
|
||||
return TPromise.as(null);
|
||||
}
|
||||
@@ -1186,11 +1334,30 @@ export class InstallRecommendedExtensionAction extends Action {
|
||||
viewlet.focus();
|
||||
|
||||
return this.extensionsWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation' }).then(pager => {
|
||||
return (pager && pager.firstPage && pager.firstPage.length) ? this.extensionsWorkbenchService.install(pager.firstPage[0]) : TPromise.as(null);
|
||||
return (pager && pager.firstPage && pager.firstPage.length) ? this.install(pager.firstPage[0]) : TPromise.as(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private install(extension: IExtension): TPromise<void> {
|
||||
return this.extensionsWorkbenchService.install(extension).then(null, err => {
|
||||
if (!extension.downloadUrl) {
|
||||
return this.notificationService.error(err);
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: localize('failedToInstall', "Failed to install \'{0}\'.", extension.id),
|
||||
actions: {
|
||||
primary: [
|
||||
this.instantiationService.createInstance(DownloadExtensionAction, extension)
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
super.dispose();
|
||||
@@ -1200,7 +1367,7 @@ export class InstallRecommendedExtensionAction extends Action {
|
||||
|
||||
export class ShowRecommendedKeymapExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.showRecommendedKeymapExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.showRecommendedKeymapExtensions';
|
||||
static SHORT_LABEL = localize('showRecommendedKeymapExtensionsShort', "Keymaps");
|
||||
|
||||
constructor(
|
||||
@@ -1223,7 +1390,7 @@ export class ShowRecommendedKeymapExtensionsAction extends Action {
|
||||
|
||||
export class ShowLanguageExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.showLanguageExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.showLanguageExtensions';
|
||||
static SHORT_LABEL = localize('showLanguageExtensionsShort', "Language Extensions");
|
||||
|
||||
constructor(
|
||||
@@ -1246,7 +1413,7 @@ export class ShowLanguageExtensionsAction extends Action {
|
||||
|
||||
export class ShowAzureExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.showAzureExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.showAzureExtensions';
|
||||
static SHORT_LABEL = localize('showAzureExtensionsShort', "Azure Extensions");
|
||||
|
||||
constructor(
|
||||
@@ -1445,7 +1612,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
|
||||
|
||||
export class ConfigureWorkspaceRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction {
|
||||
|
||||
static ID = 'workbench.extensions.action.configureWorkspaceRecommendedExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.configureWorkspaceRecommendedExtensions';
|
||||
static LABEL = localize('configureWorkspaceRecommendedExtensions', "Configure Recommended Extensions (Workspace)");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
@@ -1487,7 +1654,7 @@ export class ConfigureWorkspaceRecommendedExtensionsAction extends AbstractConfi
|
||||
|
||||
export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction {
|
||||
|
||||
static ID = 'workbench.extensions.action.configureWorkspaceFolderRecommendedExtensions';
|
||||
static readonly ID = 'workbench.extensions.action.configureWorkspaceFolderRecommendedExtensions';
|
||||
static LABEL = localize('configureWorkspaceFolderRecommendedExtensions', "Configure Recommended Extensions (Workspace Folder)");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
@@ -1513,7 +1680,7 @@ export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends Abstrac
|
||||
|
||||
public run(): TPromise<any> {
|
||||
const folderCount = this.contextService.getWorkspace().folders.length;
|
||||
const pickFolderPromise = folderCount === 1 ? TPromise.as(this.contextService.getWorkspace().folders[0]) : this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND);
|
||||
const pickFolderPromise = folderCount === 1 ? TPromise.as(this.contextService.getWorkspace().folders[0]) : this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND_ID);
|
||||
return pickFolderPromise
|
||||
.then(workspaceFolder => {
|
||||
if (workspaceFolder) {
|
||||
@@ -1530,31 +1697,6 @@ export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends Abstrac
|
||||
}
|
||||
}
|
||||
|
||||
export class BuiltinStatusLabelAction extends Action {
|
||||
|
||||
private static readonly Class = 'built-in-status';
|
||||
|
||||
private _extension: IExtension;
|
||||
get extension(): IExtension { return this._extension; }
|
||||
set extension(extension: IExtension) { this._extension = extension; this.update(); }
|
||||
|
||||
constructor() {
|
||||
super('extensions.install', localize('builtin', "Built-in"), '', false);
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
if (this.extension && this.extension.type === LocalExtensionType.System) {
|
||||
this.class = `${BuiltinStatusLabelAction.Class} system`;
|
||||
} else {
|
||||
this.class = `${BuiltinStatusLabelAction.Class} user`;
|
||||
}
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
}
|
||||
|
||||
export class MaliciousStatusLabelAction extends Action {
|
||||
|
||||
private static readonly Class = 'malicious-status';
|
||||
@@ -1564,10 +1706,10 @@ export class MaliciousStatusLabelAction extends Action {
|
||||
set extension(extension: IExtension) { this._extension = extension; this.update(); }
|
||||
|
||||
constructor(long: boolean) {
|
||||
const tooltip = localize('malicious tooltip', "This extension was reported to be malicious.");
|
||||
const tooltip = localize('malicious tooltip', "This extension was reported to be problematic.");
|
||||
const label = long ? tooltip : localize('malicious', "Malicious");
|
||||
super('extensions.install', label, '', false);
|
||||
this.tooltip = localize('malicious tooltip', "This extension was reported to be malicious.");
|
||||
this.tooltip = localize('malicious tooltip', "This extension was reported to be problematic.");
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
@@ -1585,7 +1727,7 @@ export class MaliciousStatusLabelAction extends Action {
|
||||
|
||||
export class DisableAllAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.disableAll';
|
||||
static readonly ID = 'workbench.extensions.action.disableAll';
|
||||
static LABEL = localize('disableAll', "Disable All Installed Extensions");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
@@ -1604,7 +1746,7 @@ export class DisableAllAction extends Action {
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.Disabled)));
|
||||
return TPromise.join(this.extensionsWorkbenchService.local.filter(e => e.type === LocalExtensionType.User).map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.Disabled)));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -1615,7 +1757,7 @@ export class DisableAllAction extends Action {
|
||||
|
||||
export class DisableAllWorkpsaceAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.disableAllWorkspace';
|
||||
static readonly ID = 'workbench.extensions.action.disableAllWorkspace';
|
||||
static LABEL = localize('disableAllWorkspace', "Disable All Installed Extensions for this Workspace");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
@@ -1636,7 +1778,7 @@ export class DisableAllWorkpsaceAction extends Action {
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.WorkspaceDisabled)));
|
||||
return TPromise.join(this.extensionsWorkbenchService.local.filter(e => e.type === LocalExtensionType.User).map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.WorkspaceDisabled)));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -1647,8 +1789,8 @@ export class DisableAllWorkpsaceAction extends Action {
|
||||
|
||||
export class EnableAllAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.enableAll';
|
||||
static LABEL = localize('enableAll', "Enable All Installed Extensions");
|
||||
static readonly ID = 'workbench.extensions.action.enableAll';
|
||||
static LABEL = localize('enableAll', "Enable All Extensions");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
@@ -1663,7 +1805,7 @@ export class EnableAllAction extends Action {
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
this.enabled = this.extensionsWorkbenchService.local.some(e => this.extensionEnablementService.canChangeEnablement() && (e.enablementState === EnablementState.Disabled || e.enablementState === EnablementState.WorkspaceDisabled));
|
||||
this.enabled = this.extensionsWorkbenchService.local.some(e => e.local && this.extensionEnablementService.canChangeEnablement(e.local) && (e.enablementState === EnablementState.Disabled || e.enablementState === EnablementState.WorkspaceDisabled));
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
@@ -1678,8 +1820,8 @@ export class EnableAllAction extends Action {
|
||||
|
||||
export class EnableAllWorkpsaceAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.enableAllWorkspace';
|
||||
static LABEL = localize('enableAllWorkspace', "Enable All Installed Extensions for this Workspace");
|
||||
static readonly ID = 'workbench.extensions.action.enableAllWorkspace';
|
||||
static LABEL = localize('enableAllWorkspace', "Enable All Extensions for this Workspace");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
@@ -1696,7 +1838,7 @@ export class EnableAllWorkpsaceAction extends Action {
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => this.extensionEnablementService.canChangeEnablement() && (e.enablementState === EnablementState.Disabled || e.enablementState === EnablementState.WorkspaceDisabled));
|
||||
this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => e.local && this.extensionEnablementService.canChangeEnablement(e.local) && (e.enablementState === EnablementState.Disabled || e.enablementState === EnablementState.WorkspaceDisabled));
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
@@ -1709,6 +1851,137 @@ export class EnableAllWorkpsaceAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenExtensionsFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.extensions.action.openExtensionsFolder';
|
||||
static LABEL = localize('openExtensionsFolder', "Open Extensions Folder");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWindowsService private windowsService: IWindowsService,
|
||||
@IFileService private fileService: IFileService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService
|
||||
) {
|
||||
super(id, label, null, true);
|
||||
}
|
||||
|
||||
run(): TPromise<void> {
|
||||
const extensionsHome = this.environmentService.extensionsPath;
|
||||
|
||||
return this.fileService.resolveFile(URI.file(extensionsHome)).then(file => {
|
||||
let itemToShow: string;
|
||||
if (file.children && file.children.length > 0) {
|
||||
itemToShow = file.children[0].resource.fsPath;
|
||||
} else {
|
||||
itemToShow = paths.normalize(extensionsHome, true);
|
||||
}
|
||||
|
||||
return this.windowsService.showItemInFolder(itemToShow);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class InstallVSIXAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.extensions.action.installVSIX';
|
||||
static LABEL = localize('installVSIX', "Install from VSIX...");
|
||||
|
||||
constructor(
|
||||
id = InstallVSIXAction.ID,
|
||||
label = InstallVSIXAction.LABEL,
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IChoiceService private choiceService: IChoiceService,
|
||||
@IWindowService private windowsService: IWindowService
|
||||
) {
|
||||
super(id, label, 'extension-action install-vsix', true);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
return this.windowsService.showOpenDialog({
|
||||
title: localize('installFromVSIX', "Install from VSIX"),
|
||||
filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }],
|
||||
properties: ['openFile'],
|
||||
buttonLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install"))
|
||||
}).then(result => {
|
||||
if (!result) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
return TPromise.join(result.map(vsix => this.extensionsWorkbenchService.install(vsix))).then(() => {
|
||||
return this.choiceService.choose(Severity.Info, localize('InstallVSIXAction.success', "Successfully installed the extension. Reload to enable it."), [localize('InstallVSIXAction.reloadNow', "Reload Now")]).then(choice => {
|
||||
if (choice === 0) {
|
||||
return this.windowsService.reloadWindow();
|
||||
}
|
||||
|
||||
return TPromise.as(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ReinstallAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.extensions.action.reinstall';
|
||||
static LABEL = localize('reinstall', "Reinstall Extension...");
|
||||
|
||||
constructor(
|
||||
id: string = ReinstallAction.ID, label: string = ReinstallAction.LABEL,
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IWindowService private windowService: IWindowService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
get enabled(): boolean {
|
||||
return this.extensionsWorkbenchService.local.filter(l => l.type === LocalExtensionType.User && l.local).length > 0;
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
return this.quickOpenService.pick(this.getEntries(), { placeHolder: localize('selectExtension', "Select Extension to Reinstall") });
|
||||
}
|
||||
|
||||
private getEntries(): TPromise<IPickOpenEntry[]> {
|
||||
return this.extensionsWorkbenchService.queryLocal()
|
||||
.then(local => {
|
||||
const entries: IPickOpenEntry[] = local
|
||||
.filter(extension => extension.type === LocalExtensionType.User)
|
||||
.map(extension => {
|
||||
return <IPickOpenEntry>{
|
||||
id: extension.id,
|
||||
label: extension.displayName,
|
||||
description: extension.id,
|
||||
run: () => this.reinstallExtension(extension),
|
||||
};
|
||||
});
|
||||
return entries;
|
||||
});
|
||||
}
|
||||
|
||||
private reinstallExtension(extension: IExtension): TPromise<void> {
|
||||
return this.extensionsWorkbenchService.reinstall(extension)
|
||||
.then(() => {
|
||||
this.notificationService.notify({
|
||||
message: localize('ReinstallAction.success', "Successfully reinstalled the extension."),
|
||||
severity: Severity.Info,
|
||||
actions: {
|
||||
primary: [<IAction>{
|
||||
id: 'reload',
|
||||
label: localize('ReinstallAction.reloadNow', "Reload Now"),
|
||||
enabled: true,
|
||||
run: () => this.windowService.reloadWindow(),
|
||||
dispose: () => null
|
||||
}]
|
||||
}
|
||||
});
|
||||
}, error => this.notificationService.error(error));
|
||||
}
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForLanguage', function (accessor: ServicesAccessor, fileExtension: string) {
|
||||
const viewletService = accessor.get(IViewletService);
|
||||
|
||||
@@ -1720,6 +1993,17 @@ CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForL
|
||||
});
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsWithId', function (accessor: ServicesAccessor, extensionId: string) {
|
||||
const viewletService = accessor.get(IViewletService);
|
||||
|
||||
return viewletService.openViewlet(VIEWLET_ID, true)
|
||||
.then(viewlet => viewlet as IExtensionsViewlet)
|
||||
.then(viewlet => {
|
||||
viewlet.search(`@id:${extensionId}`);
|
||||
viewlet.focus();
|
||||
});
|
||||
});
|
||||
|
||||
export const extensionButtonProminentBackground = registerColor('extensionButton.prominentBackground', {
|
||||
dark: '#327e36',
|
||||
light: '#327e36',
|
||||
|
||||
@@ -10,18 +10,18 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IMessageService, Severity } from 'vs/platform/message/common/message';
|
||||
import { IDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { InstallAction, UpdateAction, BuiltinStatusLabelAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, MaliciousStatusLabelAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
|
||||
import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, extensionButtonProminentForeground, MaliciousStatusLabelAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { Label, RatingsWidget, InstallWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { Label, RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
export interface ITemplateData {
|
||||
root: HTMLElement;
|
||||
@@ -48,7 +48,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IExtensionService private extensionService: IExtensionService,
|
||||
@IExtensionTipsService private extensionTipsService: IExtensionTipsService,
|
||||
@@ -61,8 +61,10 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
|
||||
const bookmark = append(root, $('span.bookmark'));
|
||||
append(bookmark, $('span.octicon.octicon-star'));
|
||||
const applyBookmarkStyle = (theme) => {
|
||||
const borderColor = theme.getColor(extensionButtonProminentBackground);
|
||||
bookmark.style.borderTopColor = borderColor ? borderColor.toString() : 'transparent';
|
||||
const bgColor = theme.getColor(extensionButtonProminentBackground);
|
||||
const fgColor = theme.getColor(extensionButtonProminentForeground);
|
||||
bookmark.style.borderTopColor = bgColor ? bgColor.toString() : 'transparent';
|
||||
bookmark.style.color = fgColor ? fgColor.toString() : 'white';
|
||||
};
|
||||
applyBookmarkStyle(this.themeService.getTheme());
|
||||
const bookmarkStyler = this.themeService.onThemeChange(applyBookmarkStyle.bind(this));
|
||||
@@ -88,21 +90,20 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
actionbar.onDidRun(({ error }) => error && this.messageService.show(Severity.Error, error));
|
||||
actionbar.onDidRun(({ error }) => error && this.notificationService.error(error));
|
||||
|
||||
const versionWidget = this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version);
|
||||
const installCountWidget = this.instantiationService.createInstance(InstallWidget, installCount, { small: true });
|
||||
const installCountWidget = this.instantiationService.createInstance(InstallCountWidget, installCount, { small: true });
|
||||
const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, ratings, { small: true });
|
||||
|
||||
const builtinStatusAction = this.instantiationService.createInstance(BuiltinStatusLabelAction);
|
||||
const maliciousStatusAction = this.instantiationService.createInstance(MaliciousStatusLabelAction, false);
|
||||
const installAction = this.instantiationService.createInstance(InstallAction);
|
||||
const updateAction = this.instantiationService.createInstance(UpdateAction);
|
||||
const reloadAction = this.instantiationService.createInstance(ReloadAction);
|
||||
const manageAction = this.instantiationService.createInstance(ManageExtensionAction);
|
||||
|
||||
actionbar.push([reloadAction, updateAction, installAction, builtinStatusAction, maliciousStatusAction, manageAction], actionOptions);
|
||||
const disposables = [versionWidget, installCountWidget, ratingsWidget, builtinStatusAction, maliciousStatusAction, updateAction, reloadAction, manageAction, actionbar, bookmarkStyler];
|
||||
actionbar.push([reloadAction, updateAction, installAction, maliciousStatusAction, manageAction], actionOptions);
|
||||
const disposables = [versionWidget, installCountWidget, ratingsWidget, maliciousStatusAction, updateAction, reloadAction, manageAction, actionbar, bookmarkStyler];
|
||||
|
||||
return {
|
||||
root, element, icon, name, installCount, ratings, author, description, disposables,
|
||||
@@ -111,7 +112,6 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
|
||||
versionWidget.extension = extension;
|
||||
installCountWidget.extension = extension;
|
||||
ratingsWidget.extension = extension;
|
||||
builtinStatusAction.extension = extension;
|
||||
maliciousStatusAction.extension = extension;
|
||||
installAction.extension = extension;
|
||||
updateAction.extension = extension;
|
||||
@@ -165,7 +165,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
|
||||
if (extRecommendations[extension.id.toLowerCase()]) {
|
||||
data.root.setAttribute('aria-label', extension.displayName + '. ' + extRecommendations[extension.id]);
|
||||
addClass(data.root, 'recommended');
|
||||
data.root.title = extRecommendations[extension.id.toLowerCase()];
|
||||
data.root.title = extRecommendations[extension.id.toLowerCase()].reasonText;
|
||||
}
|
||||
|
||||
data.name.textContent = extension.displayName;
|
||||
|
||||
@@ -11,7 +11,7 @@ import { QuickOpenHandler } from 'vs/workbench/browser/quickopen';
|
||||
import { IExtensionsViewlet, VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IMessageService, Severity } from 'vs/platform/message/common/message';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
class SimpleEntry extends QuickOpenEntry {
|
||||
|
||||
@@ -77,7 +77,7 @@ export class GalleryExtensionsHandler extends QuickOpenHandler {
|
||||
@IViewletService private viewletService: IViewletService,
|
||||
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
|
||||
@IExtensionManagementService private extensionsService: IExtensionManagementService,
|
||||
@IMessageService private messageService: IMessageService
|
||||
@INotificationService private notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -100,7 +100,7 @@ export class GalleryExtensionsHandler extends QuickOpenHandler {
|
||||
.then(viewlet => viewlet as IExtensionsViewlet)
|
||||
.then(viewlet => viewlet.search(`@id:${text}`))
|
||||
.then(() => this.extensionsService.installFromGallery(galleryExtension))
|
||||
.done(null, err => this.messageService.show(Severity.Error, err));
|
||||
.done(null, err => this.notificationService.error(err));
|
||||
};
|
||||
|
||||
entries.push(new SimpleEntry(label, action));
|
||||
|
||||
@@ -42,7 +42,7 @@ export class Label implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
export class InstallWidget implements IDisposable {
|
||||
export class InstallCountWidget implements IDisposable {
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
private _extension: IExtension;
|
||||
|
||||
@@ -30,12 +30,10 @@
|
||||
.monaco-action-bar .action-item.disabled .action-label.extension-action.enable,
|
||||
.monaco-action-bar .action-item.disabled .action-label.extension-action.disable,
|
||||
.monaco-action-bar .action-item.disabled .action-label.extension-action.reload,
|
||||
.monaco-action-bar .action-item.disabled .action-label.built-in-status.user,
|
||||
.monaco-action-bar .action-item.disabled .action-label.malicious-status.not-malicious {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item .action-label.built-in-status
|
||||
.monaco-action-bar .action-item .action-label.malicious-status {
|
||||
border-radius: 4px;
|
||||
color: inherit;
|
||||
@@ -46,7 +44,6 @@
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.built-in-status,
|
||||
.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.malicious-status {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,12 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.extension-editor > .header > .details > .title > .builtin {
|
||||
font-size: 10px;
|
||||
font-style: italic;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.vs .extension-editor > .header > .details > .title > .preview {
|
||||
color: white;
|
||||
}
|
||||
@@ -171,6 +177,7 @@
|
||||
height: calc(100% - 36px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.extension-editor > .body > .content.loading {
|
||||
@@ -336,4 +343,4 @@
|
||||
font-size: 90%;
|
||||
font-weight: 600;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -91,6 +91,54 @@ code > div {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#scroll-to-top {
|
||||
position: fixed;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
right: 25px;
|
||||
bottom: 25px;
|
||||
background-color:#444444;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
box-shadow: 1px 1px 1px rgba(0,0,0,.25);
|
||||
outline: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#scroll-to-top:hover {
|
||||
background-color:#007acc;
|
||||
box-shadow: 2px 2px 2px rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
body.vscode-light #scroll-to-top {
|
||||
background-color: #949494;
|
||||
}
|
||||
|
||||
body.vscode-high-contrast #scroll-to-top:hover {
|
||||
background-color: #007acc;
|
||||
}
|
||||
|
||||
body.vscode-high-contrast #scroll-to-top {
|
||||
background-color: black;
|
||||
border: 2px solid #6fc3df;
|
||||
box-shadow: none;
|
||||
}
|
||||
body.vscode-high-contrast #scroll-to-top:hover {
|
||||
background-color: #007acc;
|
||||
}
|
||||
|
||||
|
||||
#scroll-to-top span.icon::before {
|
||||
content: "";
|
||||
display: block;
|
||||
/* Chevron up icon */
|
||||
background:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxNiAxNiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTYgMTY7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojRkZGRkZGO30KCS5zdDF7ZmlsbDpub25lO30KPC9zdHlsZT4KPHRpdGxlPnVwY2hldnJvbjwvdGl0bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04LDUuMWwtNy4zLDcuM0wwLDExLjZsOC04bDgsOGwtMC43LDAuN0w4LDUuMXoiLz4KPHJlY3QgY2xhc3M9InN0MSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ii8+Cjwvc3ZnPgo=');
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/** Theming */
|
||||
|
||||
.vscode-light {
|
||||
|
||||
BIN
src/vs/workbench/parts/extensions/browser/media/theme-icon.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
@@ -8,7 +8,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
||||
import Event from 'vs/base/common/event';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IPager } from 'vs/base/common/paging';
|
||||
import { IQueryOptions, IExtensionManifest, LocalExtensionType, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IQueryOptions, IExtensionManifest, LocalExtensionType, EnablementState, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
|
||||
export const VIEWLET_ID = 'workbench.view.extensions';
|
||||
|
||||
@@ -36,6 +36,7 @@ export interface IExtension {
|
||||
latestVersion: string;
|
||||
description: string;
|
||||
url: string;
|
||||
downloadUrl: string;
|
||||
repository: string;
|
||||
iconUrl: string;
|
||||
iconUrlFallback: string;
|
||||
@@ -51,6 +52,7 @@ export interface IExtension {
|
||||
getManifest(): TPromise<IExtensionManifest>;
|
||||
getReadme(): TPromise<string>;
|
||||
getChangelog(): TPromise<string>;
|
||||
local?: ILocalExtension;
|
||||
isMalicious: boolean;
|
||||
}
|
||||
|
||||
@@ -76,6 +78,7 @@ export interface IExtensionsWorkbenchService {
|
||||
install(vsix: string): TPromise<void>;
|
||||
install(extension: IExtension, promptToInstallDependencies?: boolean): TPromise<void>;
|
||||
uninstall(extension: IExtension): TPromise<void>;
|
||||
reinstall(extension: IExtension): TPromise<void>;
|
||||
setEnablement(extension: IExtension, enablementState: EnablementState): TPromise<void>;
|
||||
loadDependencies(extension: IExtension): TPromise<IExtensionDependencies>;
|
||||
open(extension: IExtension, sideByside?: boolean): TPromise<any>;
|
||||
@@ -85,8 +88,10 @@ export interface IExtensionsWorkbenchService {
|
||||
|
||||
export const ConfigurationKey = 'extensions';
|
||||
export const AutoUpdateConfigurationKey = 'extensions.autoUpdate';
|
||||
export const ShowRecommendationsOnlyOnDemandKey = 'extensions.showRecommendationsOnlyOnDemand';
|
||||
|
||||
export interface IExtensionsConfiguration {
|
||||
autoUpdate: boolean;
|
||||
ignoreRecommendations: boolean;
|
||||
showRecommendationsOnlyOnDemand: boolean;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import URI from 'vs/base/common/uri';
|
||||
|
||||
export class ExtensionsInput extends EditorInput {
|
||||
|
||||
static get ID() { return 'workbench.extensions.input2'; }
|
||||
static readonly ID = 'workbench.extensions.input2';
|
||||
get extension(): IExtension { return this._extension; }
|
||||
|
||||
constructor(private _extension: IExtension) {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionHostProfile, ProfileSession, IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionHostProfile, ProfileSession, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { append, $, addDisposableListener } from 'vs/base/browser/dom';
|
||||
@@ -16,6 +16,10 @@ import { StatusbarAlignment, IStatusbarRegistry, StatusbarItemDescriptor, Extens
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IExtensionHostProfileService, ProfileSessionState, RuntimeExtensionsInput } from 'vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IConfirmationService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { randomPort } from 'vs/base/node/ports';
|
||||
import product from 'vs/platform/node/product';
|
||||
|
||||
export class ExtensionHostProfileService extends Disposable implements IExtensionHostProfileService {
|
||||
|
||||
@@ -38,6 +42,8 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
|
||||
@IExtensionService private readonly _extensionService: IExtensionService,
|
||||
@IWorkbenchEditorService private readonly _editorService: IWorkbenchEditorService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IWindowsService private readonly _windowsService: IWindowsService,
|
||||
@IConfirmationService private readonly _confirmationService: IConfirmationService
|
||||
) {
|
||||
super();
|
||||
this._profile = null;
|
||||
@@ -63,13 +69,28 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
|
||||
this._onDidChangeState.fire(void 0);
|
||||
}
|
||||
|
||||
public startProfiling(): void {
|
||||
public startProfiling(): Thenable<any> {
|
||||
if (this._state !== ProfileSessionState.None) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this._extensionService.canProfileExtensionHost()) {
|
||||
return this._confirmationService.confirm({
|
||||
type: 'info',
|
||||
message: nls.localize('restart1', "Profile Extensions"),
|
||||
detail: nls.localize('restart2', "In order to profile extensions a restart is required. Do you want to restart '{0}' now?", product.nameLong),
|
||||
primaryButton: nls.localize('restart3', "Restart"),
|
||||
secondaryButton: nls.localize('cancel', "Cancel")
|
||||
}).then(restart => {
|
||||
if (restart) {
|
||||
this._windowsService.relaunch({ addArgs: [`--inspect-extensions=${randomPort()}`] });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this._setState(ProfileSessionState.Starting);
|
||||
|
||||
this._extensionService.startExtensionHostProfile().then((value) => {
|
||||
return this._extensionService.startExtensionHostProfile().then((value) => {
|
||||
this._profileSession = value;
|
||||
this._setState(ProfileSessionState.Running);
|
||||
}, (err) => {
|
||||
|
||||
@@ -10,24 +10,33 @@ import { forEach } from 'vs/base/common/collections';
|
||||
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { match } from 'vs/base/common/glob';
|
||||
import * as json from 'vs/base/common/json';
|
||||
import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, LocalExtensionType, EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, ExtensionRecommendationReason, LocalExtensionType, EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModel } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { IChoiceService, IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ShowRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction, InstallRecommendedExtensionAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder, IWorkspace, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder, IWorkspace, IWorkspaceFoldersChangeEvent, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IExtensionsConfiguration, ConfigurationKey, ShowRecommendationsOnlyOnDemandKey, IExtensionsViewlet } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as os from 'os';
|
||||
import { flatten, distinct } from 'vs/base/common/arrays';
|
||||
import { flatten, distinct, shuffle } from 'vs/base/common/arrays';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { guessMimeTypes, MIME_UNKNOWN } from 'vs/base/common/mime';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { getHashedRemotesFromUri } from 'vs/workbench/parts/stats/node/workspaceStats';
|
||||
import { IRequestService } from 'vs/platform/request/node/request';
|
||||
import { asJson } from 'vs/base/node/request';
|
||||
import { isNumber } from 'vs/base/common/types';
|
||||
import { IChoiceService, Choice } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { language, LANGUAGE_DEFAULT } from 'vs/base/common/platform';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
|
||||
interface IExtensionsContent {
|
||||
recommendations: string[];
|
||||
@@ -35,6 +44,14 @@ interface IExtensionsContent {
|
||||
|
||||
const empty: { [key: string]: any; } = Object.create(null);
|
||||
const milliSecondsInADay = 1000 * 60 * 60 * 24;
|
||||
const choiceNever = localize('neverShowAgain', "Don't Show Again");
|
||||
const searchMarketplace = localize('searchMarketplace', "Search Marketplace");
|
||||
const coreLanguages = ['de', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'tr', 'zh-cn', 'zh-tw'];
|
||||
|
||||
interface IDynamicWorkspaceRecommendations {
|
||||
remoteSet: string[];
|
||||
recommendations: string[];
|
||||
}
|
||||
|
||||
export class ExtensionTipsService extends Disposable implements IExtensionTipsService {
|
||||
|
||||
@@ -45,16 +62,16 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
private _recommendations: string[] = Object.create(null);
|
||||
private _exeBasedRecommendations: { [id: string]: string; } = Object.create(null);
|
||||
private _availableRecommendations: { [pattern: string]: string[] } = Object.create(null);
|
||||
private importantRecommendations: { [id: string]: { name: string; pattern: string; } } = Object.create(null);
|
||||
private importantRecommendationsIgnoreList: string[];
|
||||
private _allRecommendations: string[] = [];
|
||||
private _disposables: IDisposable[] = [];
|
||||
|
||||
private _allWorkspaceRecommendedExtensions: string[] = [];
|
||||
private _dynamicWorkspaceRecommendations: string[] = [];
|
||||
private _extensionsRecommendationsUrl: string;
|
||||
private _disposables: IDisposable[] = [];
|
||||
public promptWorkspaceRecommendationsPromise: TPromise<any>;
|
||||
private proactiveRecommendationsFetched: boolean = false;
|
||||
|
||||
constructor(
|
||||
@IExtensionGalleryService private _galleryService: IExtensionGalleryService,
|
||||
@IModelService private _modelService: IModelService,
|
||||
@IExtensionGalleryService private readonly _galleryService: IExtensionGalleryService,
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@IChoiceService private choiceService: IChoiceService,
|
||||
@IExtensionManagementService private extensionsService: IExtensionManagementService,
|
||||
@@ -62,36 +79,186 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
@IFileService private fileService: IFileService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@IExtensionService private extensionService: IExtensionService,
|
||||
@IRequestService private requestService: IRequestService,
|
||||
@IViewletService private viewletService: IViewletService
|
||||
) {
|
||||
super();
|
||||
|
||||
if (!this._galleryService.isEnabled()) {
|
||||
if (!this.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (product.extensionsGallery && product.extensionsGallery.recommendationsUrl) {
|
||||
this._extensionsRecommendationsUrl = product.extensionsGallery.recommendationsUrl;
|
||||
}
|
||||
|
||||
this._suggestTips();
|
||||
this._suggestWorkspaceRecommendations();
|
||||
this.getLanguageExtensionRecommendations();
|
||||
this.getCachedDynamicWorkspaceRecommendations();
|
||||
this._suggestFileBasedRecommendations();
|
||||
this.promptWorkspaceRecommendationsPromise = this._suggestWorkspaceRecommendations();
|
||||
|
||||
if (!this.configurationService.getValue<boolean>(ShowRecommendationsOnlyOnDemandKey)) {
|
||||
this.fetchProactiveRecommendations(true);
|
||||
}
|
||||
|
||||
// Executable based recommendations carry out a lot of file stats, so run them after 10 secs
|
||||
// So that the startup is not affected
|
||||
setTimeout(() => this._suggestBasedOnExecutables(this._exeBasedRecommendations), 10000);
|
||||
this._register(this.contextService.onDidChangeWorkspaceFolders(e => this.onWorkspaceFoldersChanged(e)));
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (!this.proactiveRecommendationsFetched && !this.configurationService.getValue<boolean>(ShowRecommendationsOnlyOnDemandKey)) {
|
||||
this.fetchProactiveRecommendations();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
getAllRecommendationsWithReason(): { [id: string]: string; } {
|
||||
let output: { [id: string]: string; } = Object.create(null);
|
||||
Object.keys(this._fileBasedRecommendations).forEach(x => output[x.toLowerCase()] = localize('fileBasedRecommendation', "This extension is recommended based on the files you recently opened."));
|
||||
this._allWorkspaceRecommendedExtensions.forEach(x => output[x.toLowerCase()] = localize('workspaceRecommendation', "This extension is recommended by users of the current workspace."));
|
||||
private fetchProactiveRecommendations(calledDuringStartup?: boolean): TPromise<void> {
|
||||
let fetchPromise = TPromise.as(null);
|
||||
if (!this.proactiveRecommendationsFetched) {
|
||||
this.proactiveRecommendationsFetched = true;
|
||||
|
||||
// Executable based recommendations carry out a lot of file stats, so run them after 10 secs
|
||||
// So that the startup is not affected
|
||||
|
||||
fetchPromise = new TPromise((c, e) => {
|
||||
setTimeout(() => {
|
||||
TPromise.join([this._suggestBasedOnExecutables(), this.getDynamicWorkspaceRecommendations()]).then(() => c(null));
|
||||
}, calledDuringStartup ? 10000 : 0);
|
||||
});
|
||||
|
||||
}
|
||||
return fetchPromise;
|
||||
}
|
||||
|
||||
private isEnabled(): boolean {
|
||||
return this._galleryService.isEnabled() && !this.environmentService.extensionDevelopmentPath;
|
||||
}
|
||||
|
||||
private getLanguageExtensionRecommendations() {
|
||||
const config = this.configurationService.getValue<IExtensionsConfiguration>(ConfigurationKey);
|
||||
const languagePackSuggestionIgnoreList = <string[]>JSON.parse(this.storageService.get
|
||||
('extensionsAssistant/languagePackSuggestionIgnore', StorageScope.GLOBAL, '[]'));
|
||||
|
||||
if (!language
|
||||
|| language === LANGUAGE_DEFAULT
|
||||
|| coreLanguages.some(x => language === x || language.indexOf(x + '-') === 0)
|
||||
|| config.ignoreRecommendations
|
||||
|| config.showRecommendationsOnlyOnDemand
|
||||
|| languagePackSuggestionIgnoreList.indexOf(language) > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.extensionsService.getInstalled(LocalExtensionType.User).then(locals => {
|
||||
for (var i = 0; i < locals.length; i++) {
|
||||
if (locals[i].manifest
|
||||
&& locals[i].manifest.contributes
|
||||
&& Array.isArray(locals[i].manifest.contributes.localizations)
|
||||
&& locals[i].manifest.contributes.localizations.some(x => x.languageId === language)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._galleryService.query({ text: `tag:lp-${language}` }).then(pager => {
|
||||
if (!pager || !pager.firstPage || !pager.firstPage.length) {
|
||||
return;
|
||||
}
|
||||
const message = localize('showLanguagePackExtensions', "The Marketplace has extensions that can help localizing VS Code to '{0}' locale", language);
|
||||
const options: Choice[] = [
|
||||
searchMarketplace,
|
||||
{ label: choiceNever }
|
||||
];
|
||||
|
||||
this.choiceService.choose(Severity.Info, message, options).done(choice => {
|
||||
switch (choice) {
|
||||
case 0 /* Search Marketplace */:
|
||||
/* __GDPR__
|
||||
"languagePackSuggestion:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('languagePackSuggestion:popup', { userReaction: 'ok', language });
|
||||
this.viewletService.openViewlet('workbench.view.extensions', true)
|
||||
.then(viewlet => viewlet as IExtensionsViewlet)
|
||||
.then(viewlet => {
|
||||
viewlet.search(`tag:lp-${language}`);
|
||||
viewlet.focus();
|
||||
});
|
||||
break;
|
||||
case 1 /* Never show again */:
|
||||
languagePackSuggestionIgnoreList.push(language);
|
||||
this.storageService.store(
|
||||
'extensionsAssistant/languagePackSuggestionIgnore',
|
||||
JSON.stringify(languagePackSuggestionIgnoreList),
|
||||
StorageScope.GLOBAL
|
||||
);
|
||||
/* __GDPR__
|
||||
"languagePackSuggestion:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('languagePackSuggestion:popup', { userReaction: 'neverShowAgain', language });
|
||||
break;
|
||||
}
|
||||
}, () => {
|
||||
/* __GDPR__
|
||||
"languagePackSuggestion:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"language": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('languagePackSuggestion:popup', { userReaction: 'cancelled', language });
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; } {
|
||||
let output: { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; } = Object.create(null);
|
||||
|
||||
if (!this.proactiveRecommendationsFetched) {
|
||||
return output;
|
||||
}
|
||||
|
||||
if (this.contextService.getWorkspace().folders && this.contextService.getWorkspace().folders.length === 1) {
|
||||
const currentRepo = this.contextService.getWorkspace().folders[0].name;
|
||||
this._dynamicWorkspaceRecommendations.forEach(x => output[x.toLowerCase()] = {
|
||||
reasonId: ExtensionRecommendationReason.DynamicWorkspace,
|
||||
reasonText: localize('dynamicWorkspaceRecommendation', "This extension may interest you because it's popular among users of the {0} repository.", currentRepo)
|
||||
});
|
||||
}
|
||||
|
||||
forEach(this._exeBasedRecommendations, entry => output[entry.key.toLowerCase()] = {
|
||||
reasonId: ExtensionRecommendationReason.Executable,
|
||||
reasonText: localize('exeBasedRecommendation', "This extension is recommended because you have {0} installed.", entry.value)
|
||||
});
|
||||
|
||||
Object.keys(this._fileBasedRecommendations).forEach(x => output[x.toLowerCase()] = {
|
||||
reasonId: ExtensionRecommendationReason.File,
|
||||
reasonText: localize('fileBasedRecommendation', "This extension is recommended based on the files you recently opened.")
|
||||
});
|
||||
|
||||
|
||||
this._allWorkspaceRecommendedExtensions.forEach(x => output[x.toLowerCase()] = {
|
||||
reasonId: ExtensionRecommendationReason.Workspace,
|
||||
reasonText: localize('workspaceRecommendation', "This extension is recommended by users of the current workspace.")
|
||||
});
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
this._recommendations.forEach(x => output[x.toLowerCase()] = localize('defaultRecommendations', "This extension is recommended by SQL Operations Studio."));
|
||||
forEach(this._exeBasedRecommendations, entry => output[entry.key.toLowerCase()] = localize('exeBasedRecommendation', "This extension is recommended because you have {0} installed.", entry.value));
|
||||
this._recommendations.forEach(x => output[x.toLowerCase()] = {
|
||||
reasonId: ExtensionRecommendationReason.Executable,
|
||||
reasonText: localize('defaultRecommendations', "This extension is recommended by SQL Operations Studio.")
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
getWorkspaceRecommendations(): TPromise<string[]> {
|
||||
if (!this.isEnabled()) {
|
||||
return TPromise.as([]);
|
||||
}
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
return TPromise.join([this.resolveWorkspaceRecommendations(workspace), ...workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderRecommendations(workspaceFolder))])
|
||||
.then(recommendations => {
|
||||
@@ -109,19 +276,62 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
}
|
||||
|
||||
private resolveWorkspaceFolderRecommendations(workspaceFolder: IWorkspaceFolder): TPromise<string[]> {
|
||||
// {{SQL CARBON EDIT}}
|
||||
return this.fileService.resolveContent(workspaceFolder.toResource(paths.join('.sqlops', 'extensions.json')))
|
||||
.then(content => this.processWorkspaceRecommendations(json.parse(content.value, [])), err => []);
|
||||
// {{SQL CARBON EDIT}}
|
||||
const extensionsJsonUri = workspaceFolder.toResource(paths.join('.sqlops', 'extensions.json'));
|
||||
return this.fileService.resolveFile(extensionsJsonUri).then(() => {
|
||||
return this.fileService.resolveContent(extensionsJsonUri)
|
||||
.then(content => this.processWorkspaceRecommendations(json.parse(content.value, [])), err => []);
|
||||
}, err => []);
|
||||
}
|
||||
|
||||
private processWorkspaceRecommendations(extensionsContent: IExtensionsContent): string[] {
|
||||
if (extensionsContent && extensionsContent.recommendations) {
|
||||
const regEx = new RegExp(EXTENSION_IDENTIFIER_PATTERN);
|
||||
return extensionsContent.recommendations.filter((element, position) => {
|
||||
return extensionsContent.recommendations.indexOf(element) === position && regEx.test(element);
|
||||
private processWorkspaceRecommendations(extensionsContent: IExtensionsContent): TPromise<string[]> {
|
||||
const regEx = new RegExp(EXTENSION_IDENTIFIER_PATTERN);
|
||||
|
||||
if (extensionsContent && extensionsContent.recommendations && extensionsContent.recommendations.length) {
|
||||
let countBadRecommendations = 0;
|
||||
let badRecommendationsString = '';
|
||||
let filteredRecommendations = extensionsContent.recommendations.filter((element, position) => {
|
||||
if (extensionsContent.recommendations.indexOf(element) !== position) {
|
||||
// This is a duplicate entry, it doesn't hurt anybody
|
||||
// but it shouldn't be sent in the gallery query
|
||||
return false;
|
||||
} else if (!regEx.test(element)) {
|
||||
countBadRecommendations++;
|
||||
badRecommendationsString += `${element} (bad format) Expected: <provider>.<name>\n`;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return this._galleryService.query({ names: filteredRecommendations }).then(pager => {
|
||||
let page = pager.firstPage;
|
||||
let validRecommendations = page.map(extension => {
|
||||
return extension.identifier.id.toLowerCase();
|
||||
});
|
||||
|
||||
if (validRecommendations.length !== filteredRecommendations.length) {
|
||||
filteredRecommendations.forEach(element => {
|
||||
if (validRecommendations.indexOf(element.toLowerCase()) === -1) {
|
||||
countBadRecommendations++;
|
||||
badRecommendationsString += `${element} (not found in marketplace)\n`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (countBadRecommendations > 0) {
|
||||
console.log('The below ' +
|
||||
countBadRecommendations +
|
||||
' extension(s) in workspace recommendations have issues:\n' +
|
||||
badRecommendationsString);
|
||||
}
|
||||
|
||||
return validRecommendations;
|
||||
});
|
||||
}
|
||||
return [];
|
||||
|
||||
return TPromise.as([]);
|
||||
|
||||
}
|
||||
|
||||
private onWorkspaceFoldersChanged(event: IWorkspaceFoldersChangeEvent): void {
|
||||
@@ -135,13 +345,14 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
}
|
||||
});
|
||||
}
|
||||
this._dynamicWorkspaceRecommendations = [];
|
||||
}
|
||||
|
||||
getFileBasedRecommendations(): string[] {
|
||||
const fileBased = Object.keys(this._fileBasedRecommendations)
|
||||
.sort((a, b) => {
|
||||
if (this._fileBasedRecommendations[a] === this._fileBasedRecommendations[b]) {
|
||||
if (product.extensionImportantTips[a]) {
|
||||
if (!product.extensionImportantTips || product.extensionImportantTips[a]) {
|
||||
return -1;
|
||||
}
|
||||
if (product.extensionImportantTips[b]) {
|
||||
@@ -153,27 +364,23 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
return fileBased;
|
||||
}
|
||||
|
||||
getOtherRecommendations(): string[] {
|
||||
getOtherRecommendations(): TPromise<string[]> {
|
||||
// {{SQL CARBON EDIT}}
|
||||
let recommendations = Object.keys(this._exeBasedRecommendations);
|
||||
return recommendations.concat(this._recommendations);
|
||||
return TPromise.as(recommendations.concat(this._recommendations));
|
||||
}
|
||||
|
||||
|
||||
|
||||
getKeymapRecommendations(): string[] {
|
||||
return product.keymapExtensionTips || [];
|
||||
}
|
||||
|
||||
private _suggestTips() {
|
||||
private _suggestFileBasedRecommendations() {
|
||||
const extensionTips = product.extensionTips;
|
||||
// {{SQL CARBON EDIT}}
|
||||
this._recommendations = product.recommendedExtensions;
|
||||
if (!extensionTips) {
|
||||
return;
|
||||
}
|
||||
this.importantRecommendations = product.extensionImportantTips || Object.create(null);
|
||||
this.importantRecommendationsIgnoreList = <string[]>JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]'));
|
||||
|
||||
// group ids by pattern, like {**/*.md} -> [ext.foo1, ext.bar2]
|
||||
this._availableRecommendations = Object.create(null);
|
||||
@@ -198,8 +405,9 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
}
|
||||
});
|
||||
|
||||
const allRecommendations: string[] = [];
|
||||
forEach(this._availableRecommendations, ({ value: ids }) => {
|
||||
this._allRecommendations.push(...ids);
|
||||
allRecommendations.push(...ids);
|
||||
});
|
||||
|
||||
// retrieve ids of previous recommendations
|
||||
@@ -207,7 +415,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
|
||||
if (Array.isArray<string>(storedRecommendationsJson)) {
|
||||
for (let id of <string[]>storedRecommendationsJson) {
|
||||
if (this._allRecommendations.indexOf(id) > -1) {
|
||||
if (allRecommendations.indexOf(id) > -1) {
|
||||
this._fileBasedRecommendations[id] = Date.now();
|
||||
}
|
||||
}
|
||||
@@ -216,7 +424,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
forEach(storedRecommendationsJson, entry => {
|
||||
if (typeof entry.value === 'number') {
|
||||
const diff = (now - entry.value) / milliSecondsInADay;
|
||||
if (diff <= 7 && this._allRecommendations.indexOf(entry.key) > -1) {
|
||||
if (diff <= 7 && allRecommendations.indexOf(entry.key) > -1) {
|
||||
this._fileBasedRecommendations[entry.key] = entry.value;
|
||||
}
|
||||
}
|
||||
@@ -227,8 +435,15 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
this._modelService.getModels().forEach(model => this._suggest(model));
|
||||
}
|
||||
|
||||
private _suggest(model: IModel): void {
|
||||
private getMimeTypes(path: string): TPromise<string[]> {
|
||||
return this.extensionService.whenInstalledExtensionsRegistered().then(() => {
|
||||
return guessMimeTypes(path);
|
||||
});
|
||||
}
|
||||
|
||||
private _suggest(model: ITextModel): void {
|
||||
const uri = model.uri;
|
||||
let hasSuggestion = false;
|
||||
|
||||
if (!uri || uri.scheme !== Schemas.file) {
|
||||
return;
|
||||
@@ -255,112 +470,175 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
);
|
||||
|
||||
const config = this.configurationService.getValue<IExtensionsConfiguration>(ConfigurationKey);
|
||||
|
||||
if (config.ignoreRecommendations) {
|
||||
if (config.ignoreRecommendations || config.showRecommendationsOnlyOnDemand) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.extensionsService.getInstalled(LocalExtensionType.User).done(local => {
|
||||
Object.keys(this.importantRecommendations)
|
||||
.filter(id => this.importantRecommendationsIgnoreList.indexOf(id) === -1)
|
||||
.filter(id => local.every(local => `${local.manifest.publisher}.${local.manifest.name}` !== id))
|
||||
.forEach(id => {
|
||||
const { pattern, name } = this.importantRecommendations[id];
|
||||
const importantRecommendationsIgnoreList = <string[]>JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]'));
|
||||
let recommendationsToSuggest = Object.keys(product.extensionImportantTips || [])
|
||||
.filter(id => importantRecommendationsIgnoreList.indexOf(id) === -1 && match(product.extensionImportantTips[id]['pattern'], uri.fsPath));
|
||||
|
||||
if (!match(pattern, uri.fsPath)) {
|
||||
return;
|
||||
}
|
||||
const importantTipsPromise = recommendationsToSuggest.length === 0 ? TPromise.as(null) : this.extensionsService.getInstalled(LocalExtensionType.User).then(local => {
|
||||
recommendationsToSuggest = recommendationsToSuggest.filter(id => local.every(local => `${local.manifest.publisher}.${local.manifest.name}` !== id));
|
||||
if (!recommendationsToSuggest.length) {
|
||||
return;
|
||||
}
|
||||
const id = recommendationsToSuggest[0];
|
||||
const name = product.extensionImportantTips[id]['name'];
|
||||
|
||||
let message = localize('reallyRecommended2', "The '{0}' extension is recommended for this file type.", name);
|
||||
// Temporary fix for the only extension pack we recommend. See https://github.com/Microsoft/vscode/issues/35364
|
||||
if (id === 'vscjava.vscode-java-pack') {
|
||||
message = localize('reallyRecommendedExtensionPack', "The '{0}' extension pack is recommended for this file type.", name);
|
||||
}
|
||||
// Indicates we have a suggested extension via the whitelist
|
||||
hasSuggestion = true;
|
||||
|
||||
const recommendationsAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations"));
|
||||
const installAction = this.instantiationService.createInstance(InstallRecommendedExtensionAction, id);
|
||||
const options = [
|
||||
localize('install', 'Install'),
|
||||
recommendationsAction.label,
|
||||
localize('neverShowAgain', "Don't show again"),
|
||||
localize('close', "Close")
|
||||
];
|
||||
let message = localize('reallyRecommended2', "The '{0}' extension is recommended for this file type.", name);
|
||||
// Temporary fix for the only extension pack we recommend. See https://github.com/Microsoft/vscode/issues/35364
|
||||
if (id === 'vscjava.vscode-java-pack') {
|
||||
message = localize('reallyRecommendedExtensionPack', "The '{0}' extension pack is recommended for this file type.", name);
|
||||
}
|
||||
|
||||
this.choiceService.choose(Severity.Info, message, options, 3).done(choice => {
|
||||
switch (choice) {
|
||||
case 0:
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'install', extensionId: name });
|
||||
return installAction.run();
|
||||
case 1:
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'show', extensionId: name });
|
||||
return recommendationsAction.run();
|
||||
case 2: this.importantRecommendationsIgnoreList.push(id);
|
||||
this.storageService.store(
|
||||
'extensionsAssistant/importantRecommendationsIgnore',
|
||||
JSON.stringify(this.importantRecommendationsIgnoreList),
|
||||
StorageScope.GLOBAL
|
||||
);
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: name });
|
||||
return this.ignoreExtensionRecommendations();
|
||||
case 3:
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'close', extensionId: name });
|
||||
}
|
||||
}, () => {
|
||||
const recommendationsAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations"));
|
||||
const installAction = this.instantiationService.createInstance(InstallRecommendedExtensionAction, id);
|
||||
const options: Choice[] = [
|
||||
localize('install', 'Install'),
|
||||
recommendationsAction.label,
|
||||
{ label: choiceNever }
|
||||
];
|
||||
|
||||
this.choiceService.choose(Severity.Info, message, options).done(choice => {
|
||||
switch (choice) {
|
||||
case 0 /* Install */:
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId: name });
|
||||
});
|
||||
this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'install', extensionId: name });
|
||||
return installAction.run();
|
||||
case 1 /* Show Recommendations */:
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'show', extensionId: name });
|
||||
return recommendationsAction.run();
|
||||
case 2 /* Never show again */:
|
||||
importantRecommendationsIgnoreList.push(id);
|
||||
this.storageService.store(
|
||||
'extensionsAssistant/importantRecommendationsIgnore',
|
||||
JSON.stringify(importantRecommendationsIgnoreList),
|
||||
StorageScope.GLOBAL
|
||||
);
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: name });
|
||||
return this.ignoreExtensionRecommendations();
|
||||
}
|
||||
}, () => {
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId: name });
|
||||
});
|
||||
});
|
||||
|
||||
const mimeTypesPromise = this.getMimeTypes(uri.fsPath);
|
||||
TPromise.join([importantTipsPromise, mimeTypesPromise]).then(result => {
|
||||
|
||||
const fileExtensionSuggestionIgnoreList = <string[]>JSON.parse(this.storageService.get
|
||||
('extensionsAssistant/fileExtensionsSuggestionIgnore', StorageScope.GLOBAL, '[]'));
|
||||
const mimeTypes = result[1];
|
||||
let fileExtension = paths.extname(uri.fsPath);
|
||||
if (fileExtension) {
|
||||
fileExtension = fileExtension.substr(1); // Strip the dot
|
||||
}
|
||||
|
||||
if (hasSuggestion ||
|
||||
!fileExtension ||
|
||||
mimeTypes.length !== 1 ||
|
||||
mimeTypes[0] !== MIME_UNKNOWN ||
|
||||
fileExtensionSuggestionIgnoreList.indexOf(fileExtension) > -1
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const keywords = this.getKeywordsForExtension(fileExtension);
|
||||
this._galleryService.query({ text: `tag:"__ext_${fileExtension}" ${keywords.map(tag => `tag:"${tag}"`)}` }).then(pager => {
|
||||
if (!pager || !pager.firstPage || !pager.firstPage.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = localize('showLanguageExtensions', "The Marketplace has extensions that can help with '.{0}' files", fileExtension);
|
||||
const options: Choice[] = [
|
||||
searchMarketplace,
|
||||
{ label: choiceNever }
|
||||
];
|
||||
|
||||
this.choiceService.choose(Severity.Info, message, options).done(choice => {
|
||||
switch (choice) {
|
||||
case 0 /* Search Marketplace */:
|
||||
/* __GDPR__
|
||||
"fileExtensionSuggestion:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('fileExtensionSuggestion:popup', { userReaction: 'ok', fileExtension: fileExtension });
|
||||
this.viewletService.openViewlet('workbench.view.extensions', true)
|
||||
.then(viewlet => viewlet as IExtensionsViewlet)
|
||||
.then(viewlet => {
|
||||
viewlet.search(`ext:${fileExtension}`);
|
||||
viewlet.focus();
|
||||
});
|
||||
break;
|
||||
case 1 /* Never show again */:
|
||||
fileExtensionSuggestionIgnoreList.push(fileExtension);
|
||||
this.storageService.store(
|
||||
'extensionsAssistant/fileExtensionsSuggestionIgnore',
|
||||
JSON.stringify(fileExtensionSuggestionIgnoreList),
|
||||
StorageScope.GLOBAL
|
||||
);
|
||||
/* __GDPR__
|
||||
"fileExtensionSuggestion:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('fileExtensionSuggestion:popup', { userReaction: 'neverShowAgain', fileExtension: fileExtension });
|
||||
break;
|
||||
}
|
||||
}, () => {
|
||||
/* __GDPR__
|
||||
"fileExtensionSuggestion:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('fileExtensionSuggestion:popup', { userReaction: 'cancelled', fileExtension: fileExtension });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _suggestWorkspaceRecommendations() {
|
||||
private _suggestWorkspaceRecommendations(): TPromise<any> {
|
||||
const storageKey = 'extensionsAssistant/workspaceRecommendationsIgnore';
|
||||
|
||||
if (this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const config = this.configurationService.getValue<IExtensionsConfiguration>(ConfigurationKey);
|
||||
|
||||
if (config.ignoreRecommendations) {
|
||||
return;
|
||||
}
|
||||
this.getWorkspaceRecommendations().done(allRecommendations => {
|
||||
if (!allRecommendations.length) {
|
||||
return this.getWorkspaceRecommendations().then(allRecommendations => {
|
||||
if (!allRecommendations.length || config.ignoreRecommendations || config.showRecommendationsOnlyOnDemand || this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.extensionsService.getInstalled(LocalExtensionType.User).done(local => {
|
||||
return this.extensionsService.getInstalled(LocalExtensionType.User).done(local => {
|
||||
const recommendations = allRecommendations
|
||||
.filter(id => local.every(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}` !== id));
|
||||
|
||||
@@ -372,16 +650,15 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
const showAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations"));
|
||||
const installAllAction = this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, localize('installAll', "Install All"));
|
||||
|
||||
const options = [
|
||||
const options: Choice[] = [
|
||||
installAllAction.label,
|
||||
showAction.label,
|
||||
localize('neverShowAgain', "Don't show again"),
|
||||
localize('close', "Close")
|
||||
{ label: choiceNever }
|
||||
];
|
||||
|
||||
this.choiceService.choose(Severity.Info, message, options, 3).done(choice => {
|
||||
return this.choiceService.choose(Severity.Info, message, options).done(choice => {
|
||||
switch (choice) {
|
||||
case 0:
|
||||
case 0 /* Install */:
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
@@ -389,7 +666,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'install' });
|
||||
return installAllAction.run();
|
||||
case 1:
|
||||
case 1 /* Show Recommendations */:
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
@@ -397,7 +674,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'show' });
|
||||
return showAction.run();
|
||||
case 2:
|
||||
case 2 /* Never show again */:
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
@@ -405,13 +682,6 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' });
|
||||
return this.storageService.store(storageKey, true, StorageScope.WORKSPACE);
|
||||
case 3:
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:popup" : {
|
||||
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'close' });
|
||||
}
|
||||
}, () => {
|
||||
/* __GDPR__
|
||||
@@ -429,39 +699,38 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
const message = localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?");
|
||||
const options = [
|
||||
localize('ignoreAll', "Yes, Ignore All"),
|
||||
localize('no', "No"),
|
||||
localize('cancel', "Cancel")
|
||||
localize('no', "No")
|
||||
];
|
||||
|
||||
this.choiceService.choose(Severity.Info, message, options, 2).done(choice => {
|
||||
this.choiceService.choose(Severity.Info, message, options).done(choice => {
|
||||
switch (choice) {
|
||||
case 0: // If the user ignores the current message and selects different file type
|
||||
// we should hide all the stacked up messages as he has selected Yes, Ignore All
|
||||
this.messageService.hideAll();
|
||||
return this.setIgnoreRecommendationsConfig(true);
|
||||
case 1: return this.setIgnoreRecommendationsConfig(false);
|
||||
case 1:
|
||||
return this.setIgnoreRecommendationsConfig(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _suggestBasedOnExecutables(recommendations: { [id: string]: string; }): void {
|
||||
private _suggestBasedOnExecutables(): TPromise<any> {
|
||||
const homeDir = os.homedir();
|
||||
let foundExecutables: Set<string> = new Set<string>();
|
||||
|
||||
let findExecutable = (exeName, path) => {
|
||||
let findExecutable = (exeName: string, path: string) => {
|
||||
return pfs.fileExists(path).then(exists => {
|
||||
if (exists && !foundExecutables.has(exeName)) {
|
||||
foundExecutables.add(exeName);
|
||||
(product.exeBasedExtensionTips[exeName]['recommendations'] || [])
|
||||
.forEach(x => {
|
||||
if (product.exeBasedExtensionTips[exeName]['friendlyName']) {
|
||||
recommendations[x] = product.exeBasedExtensionTips[exeName]['friendlyName'];
|
||||
this._exeBasedRecommendations[x] = product.exeBasedExtensionTips[exeName]['friendlyName'];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let promises: TPromise<any>[] = [];
|
||||
// Loop through recommended extensions
|
||||
forEach(product.exeBasedExtensionTips, entry => {
|
||||
if (typeof entry.value !== 'object' || !Array.isArray(entry.value['recommendations'])) {
|
||||
@@ -478,12 +747,14 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
.replace('%ProgramFiles(x86)%', process.env['ProgramFiles(x86)'])
|
||||
.replace('%ProgramFiles%', process.env['ProgramFiles'])
|
||||
.replace('%APPDATA%', process.env['APPDATA']);
|
||||
findExecutable(exeName, windowsPath);
|
||||
promises.push(findExecutable(exeName, windowsPath));
|
||||
} else {
|
||||
findExecutable(exeName, paths.join('/usr/local/bin', exeName));
|
||||
findExecutable(exeName, paths.join(homeDir, exeName));
|
||||
promises.push(findExecutable(exeName, paths.join('/usr/local/bin', exeName)));
|
||||
promises.push(findExecutable(exeName, paths.join(homeDir, exeName)));
|
||||
}
|
||||
});
|
||||
|
||||
return TPromise.join(promises);
|
||||
}
|
||||
|
||||
private setIgnoreRecommendationsConfig(configVal: boolean) {
|
||||
@@ -494,6 +765,84 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
}
|
||||
}
|
||||
|
||||
private getCachedDynamicWorkspaceRecommendations() {
|
||||
if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER) {
|
||||
return;
|
||||
}
|
||||
|
||||
const storageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations';
|
||||
let storedRecommendationsJson = {};
|
||||
try {
|
||||
storedRecommendationsJson = JSON.parse(this.storageService.get(storageKey, StorageScope.WORKSPACE, '{}'));
|
||||
} catch (e) {
|
||||
this.storageService.remove(storageKey, StorageScope.WORKSPACE);
|
||||
}
|
||||
|
||||
if (Array.isArray(storedRecommendationsJson['recommendations'])
|
||||
&& isNumber(storedRecommendationsJson['timestamp'])
|
||||
&& storedRecommendationsJson['timestamp'] > 0
|
||||
&& (Date.now() - storedRecommendationsJson['timestamp']) / milliSecondsInADay < 14) {
|
||||
this._dynamicWorkspaceRecommendations = storedRecommendationsJson['recommendations'];
|
||||
/* __GDPR__
|
||||
"dynamicWorkspaceRecommendations" : {
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"cache" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('dynamicWorkspaceRecommendations', { count: this._dynamicWorkspaceRecommendations.length, cache: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
private getDynamicWorkspaceRecommendations(): TPromise<void> {
|
||||
if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER
|
||||
|| this._dynamicWorkspaceRecommendations.length
|
||||
|| !this._extensionsRecommendationsUrl) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
const storageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations';
|
||||
const workspaceUri = this.contextService.getWorkspace().folders[0].uri;
|
||||
return TPromise.join([getHashedRemotesFromUri(workspaceUri, this.fileService, false), getHashedRemotesFromUri(workspaceUri, this.fileService, true)]).then(([hashedRemotes1, hashedRemotes2]) => {
|
||||
const hashedRemotes = (hashedRemotes1 || []).concat(hashedRemotes2 || []);
|
||||
if (!hashedRemotes.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.requestService.request({ type: 'GET', url: this._extensionsRecommendationsUrl }).then(context => {
|
||||
if (context.res.statusCode !== 200) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
return asJson(context).then((result) => {
|
||||
const allRecommendations: IDynamicWorkspaceRecommendations[] = Array.isArray(result['workspaceRecommendations']) ? result['workspaceRecommendations'] : [];
|
||||
if (!allRecommendations.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let foundRemote = false;
|
||||
for (let i = 0; i < hashedRemotes.length && !foundRemote; i++) {
|
||||
for (let j = 0; j < allRecommendations.length && !foundRemote; j++) {
|
||||
if (Array.isArray(allRecommendations[j].remoteSet) && allRecommendations[j].remoteSet.indexOf(hashedRemotes[i]) > -1) {
|
||||
foundRemote = true;
|
||||
this._dynamicWorkspaceRecommendations = allRecommendations[j].recommendations || [];
|
||||
this.storageService.store(storageKey, JSON.stringify({
|
||||
recommendations: this._dynamicWorkspaceRecommendations,
|
||||
timestamp: Date.now()
|
||||
}), StorageScope.WORKSPACE);
|
||||
/* __GDPR__
|
||||
"dynamicWorkspaceRecommendations" : {
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"cache" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('dynamicWorkspaceRecommendations', { count: this._dynamicWorkspaceRecommendations.length, cache: 0 });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getKeywordsForExtension(extension: string): string[] {
|
||||
const keywords = product.extensionKeywords || {};
|
||||
return keywords[extension] || [];
|
||||
|
||||
@@ -21,10 +21,9 @@ import { VIEWLET_ID, IExtensionsWorkbenchService } from '../common/extensions';
|
||||
import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService';
|
||||
import {
|
||||
OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction,
|
||||
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, UpdateAllAction,
|
||||
EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor
|
||||
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction,
|
||||
EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, OpenExtensionsFolderAction, InstallVSIXAction, ReinstallAction
|
||||
} from 'vs/workbench/parts/extensions/browser/extensionsActions';
|
||||
import { OpenExtensionsFolderAction, InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions';
|
||||
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
|
||||
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
|
||||
import { ExtensionEditor } from 'vs/workbench/parts/extensions/browser/extensionEditor';
|
||||
@@ -164,6 +163,9 @@ actionRegistry.registerWorkbenchAction(installedActionDescriptor, 'Extensions: S
|
||||
const disabledActionDescriptor = new SyncActionDescriptor(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, ShowDisabledExtensionsAction.LABEL);
|
||||
actionRegistry.registerWorkbenchAction(disabledActionDescriptor, 'Extensions: Show Disabled Extensions', ExtensionsLabel);
|
||||
|
||||
const builtinActionDescriptor = new SyncActionDescriptor(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, ShowBuiltInExtensionsAction.LABEL);
|
||||
actionRegistry.registerWorkbenchAction(builtinActionDescriptor, 'Extensions: Show Show Built-in Extensions', ExtensionsLabel);
|
||||
|
||||
const updateAllActionDescriptor = new SyncActionDescriptor(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL);
|
||||
actionRegistry.registerWorkbenchAction(updateAllActionDescriptor, 'Extensions: Update All Extensions', ExtensionsLabel);
|
||||
|
||||
@@ -191,6 +193,7 @@ actionRegistry.registerWorkbenchAction(checkForUpdatesAction, `Extensions: Check
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL), `Extensions: Enable Auto Updating Extensions`, ExtensionsLabel);
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL), `Extensions: Disable Auto Updating Extensions`, ExtensionsLabel);
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowRuntimeExtensionsAction, ShowRuntimeExtensionsAction.ID, ShowRuntimeExtensionsAction.LABEL), 'Show Running Extensions', localize('developer', "Developer"));
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReinstallAction, ReinstallAction.ID, ReinstallAction.LABEL), 'Reinstall Extension...', localize('developer', "Developer"));
|
||||
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
.registerConfiguration({
|
||||
@@ -208,6 +211,11 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
type: 'boolean',
|
||||
description: localize('extensionsIgnoreRecommendations', "If set to true, the notifications for extension recommendations will stop showing up."),
|
||||
default: false
|
||||
},
|
||||
'extensions.showRecommendationsOnlyOnDemand': {
|
||||
type: 'boolean',
|
||||
description: localize('extensionsShowRecommendationsOnlyOnDemand', "If set to true, recommendations will not be fetched or shown unless specifically requested by the user."),
|
||||
default: false
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -223,4 +231,4 @@ CommandsRegistry.registerCommand('_extensions.manage', (accessor: ServicesAccess
|
||||
if (extension.length === 1) {
|
||||
extensionService.open(extension[0]).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -5,22 +5,23 @@
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
|
||||
import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
|
||||
export class OpenExtensionsFolderAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.openExtensionsFolder';
|
||||
static readonly ID = 'workbench.extensions.action.openExtensionsFolder';
|
||||
static LABEL = localize('openExtensionsFolder', "Open Extensions Folder");
|
||||
|
||||
constructor(
|
||||
@@ -38,7 +39,7 @@ export class OpenExtensionsFolderAction extends Action {
|
||||
|
||||
return this.fileService.resolveFile(URI.file(extensionsHome)).then(file => {
|
||||
let itemToShow: string;
|
||||
if (file.hasChildren) {
|
||||
if (file.children && file.children.length > 0) {
|
||||
itemToShow = file.children[0].resource.fsPath;
|
||||
} else {
|
||||
itemToShow = paths.normalize(extensionsHome, true);
|
||||
@@ -51,40 +52,99 @@ export class OpenExtensionsFolderAction extends Action {
|
||||
|
||||
export class InstallVSIXAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.installVSIX';
|
||||
static readonly ID = 'workbench.extensions.action.installVSIX';
|
||||
static LABEL = localize('installVSIX', "Install from VSIX...");
|
||||
|
||||
constructor(
|
||||
id = InstallVSIXAction.ID,
|
||||
label = InstallVSIXAction.LABEL,
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IChoiceService private choiceService: IChoiceService,
|
||||
@IWindowService private windowsService: IWindowService
|
||||
) {
|
||||
super(id, label, 'extension-action install-vsix', true);
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
const result = this.windowsService.showOpenDialog({
|
||||
return this.windowsService.showOpenDialog({
|
||||
title: localize('installFromVSIX', "Install from VSIX"),
|
||||
filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }],
|
||||
properties: ['openFile'],
|
||||
buttonLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install"))
|
||||
});
|
||||
}).then(result => {
|
||||
if (!result) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
return TPromise.join(result.map(vsix => this.extensionsWorkbenchService.install(vsix))).then(() => {
|
||||
return this.choiceService.choose(Severity.Info, localize('InstallVSIXAction.success', "Successfully installed the extension. Reload to enable it."), [localize('InstallVSIXAction.reloadNow', "Reload Now")]).then(choice => {
|
||||
if (choice === 0) {
|
||||
return this.windowsService.reloadWindow();
|
||||
}
|
||||
|
||||
return TPromise.join(result.map(vsix => this.extensionsWorkbenchService.install(vsix))).then(() => {
|
||||
this.messageService.show(
|
||||
severity.Info,
|
||||
{
|
||||
message: localize('InstallVSIXAction.success', "Successfully installed the extension. Restart to enable it."),
|
||||
actions: [this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, localize('InstallVSIXAction.reloadNow', "Reload Now"))]
|
||||
}
|
||||
);
|
||||
return TPromise.as(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ReinstallAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.extensions.action.reinstall';
|
||||
static LABEL = localize('reinstall', "Reinstall Extension...");
|
||||
|
||||
constructor(
|
||||
id: string = ReinstallAction.ID, label: string = ReinstallAction.LABEL,
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IWindowService private windowService: IWindowService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
get enabled(): boolean {
|
||||
return this.extensionsWorkbenchService.local.filter(l => l.type === LocalExtensionType.User && l.local).length > 0;
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
return this.quickOpenService.pick(this.getEntries(), { placeHolder: localize('selectExtension', "Select Extension to Reinstall") });
|
||||
}
|
||||
|
||||
private getEntries(): TPromise<IPickOpenEntry[]> {
|
||||
return this.extensionsWorkbenchService.queryLocal()
|
||||
.then(local => {
|
||||
const entries: IPickOpenEntry[] = local
|
||||
.filter(extension => extension.type === LocalExtensionType.User)
|
||||
.map(extension => {
|
||||
return <IPickOpenEntry>{
|
||||
id: extension.id,
|
||||
label: extension.displayName,
|
||||
description: extension.id,
|
||||
run: () => this.reinstallExtension(extension),
|
||||
};
|
||||
});
|
||||
return entries;
|
||||
});
|
||||
}
|
||||
|
||||
private reinstallExtension(extension: IExtension): TPromise<void> {
|
||||
return this.extensionsWorkbenchService.reinstall(extension)
|
||||
.then(() => {
|
||||
this.notificationService.notify({
|
||||
message: localize('ReinstallAction.success', "Successfully reinstalled the extension."),
|
||||
severity: Severity.Info,
|
||||
actions: {
|
||||
primary: [<IAction>{
|
||||
id: 'reload',
|
||||
label: localize('ReinstallAction.reloadNow', "Reload Now"),
|
||||
enabled: true,
|
||||
run: () => this.windowService.reloadWindow(),
|
||||
dispose: () => null
|
||||
}]
|
||||
}
|
||||
});
|
||||
}, error => this.notificationService.error(error));
|
||||
}
|
||||
}
|
||||
@@ -13,15 +13,15 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IExtensionManagementService, ILocalExtension, IExtensionEnablementService, IExtensionTipsService, LocalExtensionType, IExtensionIdentifier, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IMessageService, Severity, IChoiceService } from 'vs/platform/message/common/message';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { BetterMergeDisabledNowKey, BetterMergeId, areSameExtensions, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
|
||||
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { Severity } from 'vs/platform/notification/common/notification';
|
||||
|
||||
export interface IExtensionStatus {
|
||||
identifier: IExtensionIdentifier;
|
||||
@@ -65,14 +65,12 @@ export class KeymapExtensions implements IWorkbenchContribution {
|
||||
}
|
||||
|
||||
private promptForDisablingOtherKeymaps(newKeymap: IExtensionStatus, oldKeymaps: IExtensionStatus[]): TPromise<void> {
|
||||
|
||||
|
||||
const message = localize('disableOtherKeymapsConfirmation', "Disable other keymaps ({0}) to avoid conflicts between keybindings?", oldKeymaps.map(k => `'${k.local.manifest.displayName}'`).join(', '));
|
||||
const options = [
|
||||
localize('yes', "Yes"),
|
||||
localize('no', "No")
|
||||
];
|
||||
return this.choiceService.choose(Severity.Info, message, options, 1, false)
|
||||
return this.choiceService.choose(Severity.Info, message, options)
|
||||
.then(value => {
|
||||
const confirmed = value === 0;
|
||||
const telemetryData: { [key: string]: any; } = {
|
||||
@@ -90,7 +88,7 @@ export class KeymapExtensions implements IWorkbenchContribution {
|
||||
this.telemetryService.publicLog('disableOtherKeymaps', telemetryData);
|
||||
if (confirmed) {
|
||||
return TPromise.join(oldKeymaps.map(keymap => {
|
||||
return this.extensionEnablementService.setEnablement(keymap.local.identifier, EnablementState.Disabled);
|
||||
return this.extensionEnablementService.setEnablement(keymap.local, EnablementState.Disabled);
|
||||
}));
|
||||
}
|
||||
return undefined;
|
||||
@@ -151,7 +149,7 @@ export class BetterMergeDisabled implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IMessageService messageService: IMessageService,
|
||||
@IChoiceService choiceService: IChoiceService,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@@ -159,17 +157,14 @@ export class BetterMergeDisabled implements IWorkbenchContribution {
|
||||
extensionService.whenInstalledExtensionsRegistered().then(() => {
|
||||
if (storageService.getBoolean(BetterMergeDisabledNowKey, StorageScope.GLOBAL, false)) {
|
||||
storageService.remove(BetterMergeDisabledNowKey, StorageScope.GLOBAL);
|
||||
messageService.show(Severity.Info, {
|
||||
message: localize('betterMergeDisabled', "The Better Merge extension is now built-in, the installed extension was disabled and can be uninstalled."),
|
||||
actions: [
|
||||
new Action('uninstall', localize('uninstall', "Uninstall"), null, true, () => {
|
||||
return extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => {
|
||||
return Promise.all(extensions.filter(e => stripVersion(e.identifier.id) === BetterMergeId)
|
||||
.map(e => extensionManagementService.uninstall(e, true)));
|
||||
});
|
||||
}),
|
||||
new Action('later', localize('later', "Later"), null, true)
|
||||
]
|
||||
|
||||
choiceService.choose(Severity.Info, localize('betterMergeDisabled', "The Better Merge extension is now built-in, the installed extension was disabled and can be uninstalled."), [localize('uninstall', "Uninstall")]).then(choice => {
|
||||
if (choice === 0) {
|
||||
extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => {
|
||||
return Promise.all(extensions.filter(e => stripVersion(e.identifier.id) === BetterMergeId)
|
||||
.map(e => extensionManagementService.uninstall(e, true)));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -24,28 +24,26 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { append, $, addStandardDisposableListener, EventType, addClass, removeClass, toggleClass } from 'vs/base/browser/dom';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, ExtensionState, AutoUpdateConfigurationKey } from '../common/extensions';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, ExtensionState, AutoUpdateConfigurationKey, ShowRecommendationsOnlyOnDemandKey } from '../common/extensions';
|
||||
import {
|
||||
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowDisabledExtensionsAction,
|
||||
ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction,
|
||||
EnableAutoUpdateAction, DisableAutoUpdateAction
|
||||
EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction
|
||||
} from 'vs/workbench/parts/extensions/browser/extensionsActions';
|
||||
import { LocalExtensionType, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions';
|
||||
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
|
||||
import { ExtensionsListView, InstalledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView } from './extensionsViews';
|
||||
import { ExtensionsListView, InstalledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInExtensionsView } from './extensionsViews';
|
||||
import { OpenGlobalSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IActivityService, ProgressBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { inputForeground, inputBackground, inputBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ViewsRegistry, ViewLocation, IViewDescriptor } from 'vs/workbench/browser/parts/views/viewsRegistry';
|
||||
import { ViewsRegistry, ViewLocation, IViewDescriptor } from 'vs/workbench/common/views';
|
||||
import { PersistentViewsViewlet, ViewsViewletPanel } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
@@ -53,7 +51,10 @@ import { IContextKeyService, ContextKeyExpr, RawContextKey, IContextKey } from '
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { getGalleryExtensionIdFromLocal, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
|
||||
interface SearchInputEvent extends Event {
|
||||
target: HTMLInputElement;
|
||||
@@ -63,7 +64,9 @@ interface SearchInputEvent extends Event {
|
||||
const NonEmptyWorkspaceContext = new RawContextKey<boolean>('nonEmptyWorkspace', false);
|
||||
const SearchExtensionsContext = new RawContextKey<boolean>('searchExtensions', false);
|
||||
const SearchInstalledExtensionsContext = new RawContextKey<boolean>('searchInstalledExtensions', false);
|
||||
const SearchBuiltInExtensionsContext = new RawContextKey<boolean>('searchBuiltInExtensions', false);
|
||||
const RecommendedExtensionsContext = new RawContextKey<boolean>('recommendedExtensions', false);
|
||||
const DefaultRecommendedExtensionsContext = new RawContextKey<boolean>('defaultRecommendedExtensions', false);
|
||||
|
||||
export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtensionsViewlet {
|
||||
|
||||
@@ -71,7 +74,9 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
private nonEmptyWorkspaceContextKey: IContextKey<boolean>;
|
||||
private searchExtensionsContextKey: IContextKey<boolean>;
|
||||
private searchInstalledExtensionsContextKey: IContextKey<boolean>;
|
||||
private searchBuiltInExtensionsContextKey: IContextKey<boolean>;
|
||||
private recommendedExtensionsContextKey: IContextKey<boolean>;
|
||||
private defaultRecommendedExtensionsContextKey: IContextKey<boolean>;
|
||||
|
||||
private searchDelayer: ThrottledDelayer<any>;
|
||||
private root: HTMLElement;
|
||||
@@ -83,13 +88,14 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@IPartService partService: IPartService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IProgressService private progressService: IProgressService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IEditorGroupService private editorInputService: IEditorGroupService,
|
||||
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IViewletService private viewletService: IViewletService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@@ -99,15 +105,17 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IExtensionService extensionService: IExtensionService
|
||||
) {
|
||||
super(VIEWLET_ID, ViewLocation.Extensions, `${VIEWLET_ID}.state`, true, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService);
|
||||
super(VIEWLET_ID, ViewLocation.Extensions, `${VIEWLET_ID}.state`, true, partService, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService);
|
||||
|
||||
this.registerViews();
|
||||
this.searchDelayer = new ThrottledDelayer(500);
|
||||
this.nonEmptyWorkspaceContextKey = NonEmptyWorkspaceContext.bindTo(contextKeyService);
|
||||
this.searchExtensionsContextKey = SearchExtensionsContext.bindTo(contextKeyService);
|
||||
this.searchInstalledExtensionsContextKey = SearchInstalledExtensionsContext.bindTo(contextKeyService);
|
||||
this.searchBuiltInExtensionsContextKey = SearchBuiltInExtensionsContext.bindTo(contextKeyService);
|
||||
this.recommendedExtensionsContextKey = RecommendedExtensionsContext.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));
|
||||
|
||||
this.configurationService.onDidChangeConfiguration(e => {
|
||||
@@ -115,6 +123,9 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
this.secondaryActions = null;
|
||||
this.updateTitleArea();
|
||||
}
|
||||
if (e.affectedKeys.indexOf(ShowRecommendationsOnlyOnDemandKey) > -1) {
|
||||
this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue<boolean>(ShowRecommendationsOnlyOnDemandKey));
|
||||
}
|
||||
}, this, this.disposables);
|
||||
}
|
||||
|
||||
@@ -123,6 +134,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
viewDescriptors.push(this.createMarketPlaceExtensionsListViewDescriptor());
|
||||
viewDescriptors.push(this.createInstalledExtensionsListViewDescriptor());
|
||||
viewDescriptors.push(this.createSearchInstalledExtensionsListViewDescriptor());
|
||||
viewDescriptors.push(this.createSearchBuiltInExtensionsListViewDescriptor());
|
||||
viewDescriptors.push(this.createDefaultRecommendedExtensionsListViewDescriptor());
|
||||
viewDescriptors.push(this.createOtherRecommendedExtensionsListViewDescriptor());
|
||||
viewDescriptors.push(this.createWorkspaceRecommendedExtensionsListViewDescriptor());
|
||||
@@ -135,8 +147,8 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
name: localize('marketPlace', "Marketplace"),
|
||||
location: ViewLocation.Extensions,
|
||||
ctor: ExtensionsListView,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('searchExtensions'), ContextKeyExpr.not('searchInstalledExtensions'), ContextKeyExpr.not('recommendedExtensions')),
|
||||
size: 100
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('searchExtensions'), ContextKeyExpr.not('searchInstalledExtensions'), ContextKeyExpr.not('searchBuiltInExtensions'), ContextKeyExpr.not('recommendedExtensions')),
|
||||
weight: 100
|
||||
};
|
||||
}
|
||||
|
||||
@@ -147,7 +159,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
location: ViewLocation.Extensions,
|
||||
ctor: InstalledExtensionsView,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions')),
|
||||
size: 200
|
||||
weight: 30
|
||||
};
|
||||
}
|
||||
|
||||
@@ -158,7 +170,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
location: ViewLocation.Extensions,
|
||||
ctor: InstalledExtensionsView,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')),
|
||||
size: 50
|
||||
weight: 100
|
||||
};
|
||||
}
|
||||
|
||||
@@ -168,8 +180,8 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
name: localize('recommendedExtensions', "Recommended"),
|
||||
location: ViewLocation.Extensions,
|
||||
ctor: RecommendedExtensionsView,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions')),
|
||||
size: 600,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('defaultRecommendedExtensions')),
|
||||
weight: 70,
|
||||
canToggleVisibility: true
|
||||
};
|
||||
}
|
||||
@@ -181,7 +193,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
location: ViewLocation.Extensions,
|
||||
ctor: RecommendedExtensionsView,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions')),
|
||||
size: 600,
|
||||
weight: 50,
|
||||
canToggleVisibility: true,
|
||||
order: 2
|
||||
};
|
||||
@@ -194,12 +206,23 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
location: ViewLocation.Extensions,
|
||||
ctor: WorkspaceRecommendedExtensionsView,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), ContextKeyExpr.has('nonEmptyWorkspace')),
|
||||
size: 200,
|
||||
weight: 50,
|
||||
canToggleVisibility: true,
|
||||
order: 1
|
||||
};
|
||||
}
|
||||
|
||||
private createSearchBuiltInExtensionsListViewDescriptor(): IViewDescriptor {
|
||||
return {
|
||||
id: 'extensions.builtInExtensionsList',
|
||||
name: localize('builtInExtensions', "Built-In"),
|
||||
location: ViewLocation.Extensions,
|
||||
ctor: BuiltInExtensionsView,
|
||||
when: ContextKeyExpr.has('searchBuiltInExtensions'),
|
||||
weight: 100
|
||||
};
|
||||
}
|
||||
|
||||
async create(parent: Builder): TPromise<void> {
|
||||
parent.addClass('extensions-viewlet');
|
||||
this.root = parent.getHTMLElement();
|
||||
@@ -292,6 +315,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL),
|
||||
this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, ShowEnabledExtensionsAction.LABEL),
|
||||
this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, ShowDisabledExtensionsAction.LABEL),
|
||||
this.instantiationService.createInstance(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, ShowBuiltInExtensionsAction.LABEL),
|
||||
this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL),
|
||||
this.instantiationService.createInstance(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL),
|
||||
new Separator(),
|
||||
@@ -329,6 +353,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
const value = this.searchBox.value || '';
|
||||
this.searchExtensionsContextKey.set(!!value);
|
||||
this.searchInstalledExtensionsContextKey.set(InstalledExtensionsView.isInsalledExtensionsQuery(value));
|
||||
this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value));
|
||||
this.recommendedExtensionsContextKey.set(ExtensionsListView.isRecommendedExtensionsQuery(value));
|
||||
this.nonEmptyWorkspaceContextKey.set(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY);
|
||||
|
||||
@@ -405,16 +430,15 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
if (/ECONNREFUSED/.test(message)) {
|
||||
const error = createError(localize('suggestProxyError', "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting."), {
|
||||
actions: [
|
||||
this.instantiationService.createInstance(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL),
|
||||
CloseAction
|
||||
this.instantiationService.createInstance(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL)
|
||||
]
|
||||
});
|
||||
|
||||
this.messageService.show(Severity.Error, error);
|
||||
this.notificationService.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
this.messageService.show(Severity.Error, err);
|
||||
this.notificationService.error(err);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -463,9 +487,9 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IExtensionManagementService private extensionsManagementService: IExtensionManagementService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IWindowService private windowService: IWindowService,
|
||||
@ILogService private logService: ILogService,
|
||||
@IMessageService private messageService: IMessageService
|
||||
@IChoiceService private choiceService: IChoiceService
|
||||
) {
|
||||
this.loopCheckForMaliciousExtensions();
|
||||
}
|
||||
@@ -486,9 +510,12 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution {
|
||||
|
||||
if (maliciousExtensions.length) {
|
||||
return TPromise.join(maliciousExtensions.map(e => this.extensionsManagementService.uninstall(e, true).then(() => {
|
||||
this.messageService.show(Severity.Warning, {
|
||||
message: localize('malicious warning', "We have uninstalled '{0}' which was reported to be malicious.", getGalleryExtensionIdFromLocal(e)),
|
||||
actions: [this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, localize('reloadNow', "Reload Now"))]
|
||||
return this.choiceService.choose(Severity.Warning, localize('malicious warning', "We have uninstalled '{0}' which was reported to be problematic.", getGalleryExtensionIdFromLocal(e)), [localize('reloadNow', "Reload Now")]).then(choice => {
|
||||
if (choice === 0) {
|
||||
return this.windowService.reloadWindow();
|
||||
}
|
||||
|
||||
return TPromise.as(undefined);
|
||||
});
|
||||
})));
|
||||
} else {
|
||||
|
||||
@@ -11,9 +11,7 @@ import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { chain } from 'vs/base/common/event';
|
||||
import { isPromiseCanceledError, create as createError } from 'vs/base/common/errors';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { PagedModel, IPagedModel, mergePagers, IPager } from 'vs/base/common/paging';
|
||||
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
|
||||
import { PagedModel, IPagedModel, IPager } from 'vs/base/common/paging';
|
||||
import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
@@ -23,7 +21,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { Delegate, Renderer } from 'vs/workbench/parts/extensions/browser/extensionsList';
|
||||
import { IExtension, IExtensionsWorkbenchService } from '../common/extensions';
|
||||
import { Query } from '../common/extensionQuery';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachBadgeStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IViewletViewOptions, IViewOptions, ViewsViewletPanel } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
@@ -35,8 +33,9 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
|
||||
import { WorkbenchPagedList, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { WorkbenchPagedList } from 'vs/platform/list/browser/listService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
export class ExtensionsListView extends ViewsViewletPanel {
|
||||
|
||||
@@ -48,11 +47,10 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
|
||||
constructor(
|
||||
private options: IViewletViewOptions,
|
||||
@IMessageService protected messageService: IMessageService,
|
||||
@INotificationService protected notificationService: INotificationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IInstantiationService protected instantiationService: IInstantiationService,
|
||||
@IListService private listService: IListService,
|
||||
@IThemeService private themeService: IThemeService,
|
||||
@IExtensionService private extensionService: IExtensionService,
|
||||
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@@ -61,9 +59,9 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
@IExtensionTipsService private tipsService: IExtensionTipsService,
|
||||
@IModeService private modeService: IModeService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: options.name }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: options.name }, keybindingService, contextMenuService, configurationService);
|
||||
}
|
||||
|
||||
renderHeader(container: HTMLElement): void {
|
||||
@@ -80,12 +78,11 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
this.messageBox = append(container, $('.message'));
|
||||
const delegate = new Delegate();
|
||||
const renderer = this.instantiationService.createInstance(Renderer);
|
||||
this.list = new WorkbenchPagedList(this.extensionsList, delegate, [renderer], {
|
||||
ariaLabel: localize('extensions', "Extensions"),
|
||||
keyboardSupport: false
|
||||
}, this.contextKeyService, this.listService, this.themeService);
|
||||
this.list = this.instantiationService.createInstance(WorkbenchPagedList, this.extensionsList, delegate, [renderer], {
|
||||
ariaLabel: localize('extensions', "Extensions")
|
||||
}) as WorkbenchPagedList<IExtension>;
|
||||
|
||||
chain(this.list.onSelectionChange)
|
||||
chain(this.list.onOpen)
|
||||
.map(e => e.elements[0])
|
||||
.filter(e => !!e)
|
||||
.on(this.openExtension, this, this.disposables);
|
||||
@@ -148,6 +145,27 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
case 'name': options = assign(options, { sortBy: SortBy.Title }); break;
|
||||
}
|
||||
|
||||
if (!value || ExtensionsListView.isBuiltInExtensionsQuery(value)) {
|
||||
// Show installed extensions
|
||||
value = value ? value.replace(/@builtin/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase() : '';
|
||||
|
||||
let result = await this.extensionsWorkbenchService.queryLocal();
|
||||
|
||||
result = result
|
||||
.filter(e => e.type === LocalExtensionType.System && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1));
|
||||
|
||||
const themesExtensions = result.filter(e => {
|
||||
return e.local.manifest
|
||||
&& e.local.manifest.contributes
|
||||
&& Array.isArray(e.local.manifest.contributes.themes)
|
||||
&& e.local.manifest.contributes.themes.length;
|
||||
});
|
||||
const themesExtensionsIds = themesExtensions.map(e => e.id);
|
||||
const others = result.filter(e => themesExtensionsIds.indexOf(e.id) === -1);
|
||||
|
||||
return new PagedModel([...this.sortExtensions(others, options), ...this.sortExtensions(themesExtensions, options)]);
|
||||
}
|
||||
|
||||
if (!value || ExtensionsListView.isInstalledExtensionsQuery(value)) {
|
||||
// Show installed extensions
|
||||
value = value ? value.replace(/@installed/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase() : '';
|
||||
@@ -218,15 +236,11 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
return this.getRecommendationsModel(query, options);
|
||||
}
|
||||
|
||||
const pagerPromises: TPromise<IPager<IExtension>>[] = [];
|
||||
let text = query.value;
|
||||
const extensionRegex = /\bext:([^\s]+)\b/g;
|
||||
|
||||
if (extensionRegex.test(query.value)) {
|
||||
let names: string[] = [];
|
||||
|
||||
text = query.value.replace(extensionRegex, (m, ext) => {
|
||||
names.push(...this.tipsService.getRecommendationsForExtension(ext));
|
||||
|
||||
// Get curated keywords
|
||||
const keywords = this.tipsService.getKeywordsForExtension(ext);
|
||||
@@ -237,12 +251,13 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
const languageTag = languageName ? ` tag:"${languageName}"` : '';
|
||||
|
||||
// Construct a rich query
|
||||
return `tag:"__ext_${ext}"${keywords.map(tag => ` tag:${tag}`)}${languageTag}`;
|
||||
return `tag:"__ext_${ext}" tag:"__ext_.${ext}" ${keywords.map(tag => `tag:"${tag}"`).join(' ')}${languageTag} tag:"${ext}"`;
|
||||
});
|
||||
|
||||
if (names.length) {
|
||||
const namesOptions = assign({}, options, { names, source: 'extRegex' });
|
||||
pagerPromises.push(this.extensionsWorkbenchService.queryGallery(namesOptions));
|
||||
if (text !== query.value) {
|
||||
options = assign(options, { text: text.substr(0, 350), source: 'file-extension-tags' });
|
||||
const pager = await this.extensionsWorkbenchService.queryGallery(options);
|
||||
return new PagedModel(pager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,11 +267,7 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
options.source = 'viewlet';
|
||||
}
|
||||
|
||||
pagerPromises.push(this.extensionsWorkbenchService.queryGallery(options));
|
||||
|
||||
const pagers = await TPromise.join(pagerPromises);
|
||||
const pager = pagers.length === 2 ? mergePagers(pagers[0], pagers[1]) : pagers[0];
|
||||
|
||||
const pager = await this.extensionsWorkbenchService.queryGallery(options);
|
||||
return new PagedModel(pager);
|
||||
}
|
||||
|
||||
@@ -287,10 +298,11 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
.then(local => {
|
||||
const installedExtensions = local.map(x => `${x.publisher}.${x.name}`);
|
||||
let fileBasedRecommendations = this.tipsService.getFileBasedRecommendations();
|
||||
let others = this.tipsService.getOtherRecommendations();
|
||||
const othersPromise = this.tipsService.getOtherRecommendations();
|
||||
const workspacePromise = this.tipsService.getWorkspaceRecommendations();
|
||||
|
||||
return this.tipsService.getWorkspaceRecommendations()
|
||||
.then(workspaceRecommendations => {
|
||||
return TPromise.join([othersPromise, workspacePromise])
|
||||
.then(([others, workspaceRecommendations]) => {
|
||||
const names = this.getTrimmedRecommendations(installedExtensions, value, fileBasedRecommendations, others, workspaceRecommendations);
|
||||
|
||||
/* __GDPR__
|
||||
@@ -320,10 +332,11 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
.then(local => {
|
||||
const installedExtensions = local.map(x => `${x.publisher}.${x.name}`);
|
||||
let fileBasedRecommendations = this.tipsService.getFileBasedRecommendations();
|
||||
let others = this.tipsService.getOtherRecommendations();
|
||||
const othersPromise = this.tipsService.getOtherRecommendations();
|
||||
const workspacePromise = this.tipsService.getWorkspaceRecommendations();
|
||||
|
||||
return this.tipsService.getWorkspaceRecommendations()
|
||||
.then(workspaceRecommendations => {
|
||||
return TPromise.join([othersPromise, workspacePromise])
|
||||
.then(([others, workspaceRecommendations]) => {
|
||||
workspaceRecommendations = workspaceRecommendations.map(x => x.toLowerCase());
|
||||
fileBasedRecommendations = fileBasedRecommendations.filter(x => workspaceRecommendations.indexOf(x.toLowerCase()) === -1);
|
||||
others = others.filter(x => workspaceRecommendations.indexOf(x.toLowerCase()) === -1);
|
||||
@@ -446,6 +459,7 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
const activeEditorInput = this.editorService.getActiveEditorInput();
|
||||
|
||||
this.editorInputService.pinEditor(activeEditor.position, activeEditorInput);
|
||||
activeEditor.focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -459,16 +473,15 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
if (/ECONNREFUSED/.test(message)) {
|
||||
const error = createError(localize('suggestProxyError', "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting."), {
|
||||
actions: [
|
||||
this.instantiationService.createInstance(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL),
|
||||
CloseAction
|
||||
this.instantiationService.createInstance(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL)
|
||||
]
|
||||
});
|
||||
|
||||
this.messageService.show(Severity.Error, error);
|
||||
this.notificationService.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
this.messageService.show(Severity.Error, err);
|
||||
this.notificationService.error(err);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -476,6 +489,10 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
static isBuiltInExtensionsQuery(query: string): boolean {
|
||||
return /@builtin/i.test(query);
|
||||
}
|
||||
|
||||
static isInstalledExtensionsQuery(query: string): boolean {
|
||||
return /@installed/i.test(query);
|
||||
}
|
||||
@@ -528,6 +545,18 @@ export class InstalledExtensionsView extends ExtensionsListView {
|
||||
}
|
||||
}
|
||||
|
||||
export class BuiltInExtensionsView extends ExtensionsListView {
|
||||
|
||||
async show(query: string): TPromise<IPagedModel<IExtension>> {
|
||||
if (!ExtensionsListView.isBuiltInExtensionsQuery(query)) {
|
||||
return super.show(query);
|
||||
}
|
||||
let searchBuiltInQuery = '@builtin';
|
||||
searchBuiltInQuery = query ? searchBuiltInQuery + ' ' + query : searchBuiltInQuery;
|
||||
return super.show(searchBuiltInQuery);
|
||||
}
|
||||
}
|
||||
|
||||
export class RecommendedExtensionsView extends ExtensionsListView {
|
||||
|
||||
async show(query: string): TPromise<IPagedModel<IExtension>> {
|
||||
@@ -546,7 +575,7 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView {
|
||||
const actionbar = new ActionBar(listActionBar, {
|
||||
animated: false
|
||||
});
|
||||
actionbar.onDidRun(({ error }) => error && this.messageService.show(Severity.Error, error));
|
||||
actionbar.onDidRun(({ error }) => error && this.notificationService.error(error));
|
||||
const installAllAction = this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, InstallWorkspaceRecommendedExtensionsAction.LABEL);
|
||||
const configureWorkspaceFolderAction = this.instantiationService.createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL);
|
||||
|
||||
|
||||
@@ -27,10 +27,6 @@
|
||||
height: calc(100% - 38px);
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .extensions-list > .monaco-list {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .list-actionbar-container .monaco-action-bar .action-item > .octicon {
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
@@ -77,7 +73,6 @@
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 1px;
|
||||
color: white;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.runtime-extensions-editor .monaco-list .monaco-list-rows > .monaco-list-row.odd {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.runtime-extensions-editor .monaco-list .monaco-list-rows > .monaco-list-row:hover:not(.odd) {
|
||||
background-color: transparent;
|
||||
.runtime-extensions-editor .monaco-list .monaco-list-rows > .monaco-list-row.odd:not(:hover):not(.focused) {
|
||||
background-color: rgba(130, 130, 130, 0.08);
|
||||
}
|
||||
|
||||
.runtime-extensions-editor .extension {
|
||||
@@ -86,11 +82,6 @@
|
||||
background: url('save-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .runtime-extensions-editor .monaco-list .monaco-list-rows > .monaco-list-row.odd,
|
||||
.hc-black .runtime-extensions-editor .monaco-list .monaco-list-rows > .monaco-list-row.odd {
|
||||
background-color: #262829;
|
||||
}
|
||||
|
||||
.runtime-extensions-editor .monaco-action-bar {
|
||||
padding-top: 21px;
|
||||
flex-shrink: 0;
|
||||
|
||||
@@ -20,14 +20,12 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IExtensionService, IExtensionDescription, IExtensionsStatus, IExtensionHostProfile } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionService, IExtensionDescription, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import { WorkbenchList, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchList } from 'vs/platform/list/browser/listService';
|
||||
import { append, $, addClass, toggleClass } from 'vs/base/browser/dom';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IMessageService, Severity } from 'vs/platform/message/common/message';
|
||||
import { ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { clipboard } from 'electron';
|
||||
@@ -39,6 +37,8 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import Event from 'vs/base/common/event';
|
||||
import { DisableForWorkspaceAction, DisableGloballyAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
export const IExtensionHostProfileService = createDecorator<IExtensionHostProfileService>('extensionHostProfileService');
|
||||
|
||||
@@ -88,7 +88,7 @@ interface IRuntimeExtension {
|
||||
|
||||
export class RuntimeExtensionsEditor extends BaseEditor {
|
||||
|
||||
static ID: string = 'workbench.editor.runtimeExtensions';
|
||||
static readonly ID: string = 'workbench.editor.runtimeExtensions';
|
||||
|
||||
private _list: WorkbenchList<IRuntimeExtension>;
|
||||
private _profileInfo: IExtensionHostProfile;
|
||||
@@ -102,9 +102,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IExtensionsWorkbenchService private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IExtensionService private readonly _extensionService: IExtensionService,
|
||||
@IListService private readonly _listService: IListService,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@IMessageService private readonly _messageService: IMessageService,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IContextMenuService private readonly _contextMenuService: IContextMenuService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
|
||||
@@ -269,7 +267,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
|
||||
const actionbar = new ActionBar(element, {
|
||||
animated: false
|
||||
});
|
||||
actionbar.onDidRun(({ error }) => error && this._messageService.show(Severity.Error, error));
|
||||
actionbar.onDidRun(({ error }) => error && this._notificationService.error(error));
|
||||
actionbar.push(new ReportExtensionIssueAction(), { icon: true, label: true });
|
||||
|
||||
const disposables = [actionbar];
|
||||
@@ -364,16 +362,22 @@ export class RuntimeExtensionsEditor extends BaseEditor {
|
||||
}
|
||||
};
|
||||
|
||||
this._list = new WorkbenchList<IRuntimeExtension>(container, delegate, [renderer], {
|
||||
this._list = this._instantiationService.createInstance(WorkbenchList, container, delegate, [renderer], {
|
||||
multipleSelectionSupport: false
|
||||
}, this._contextKeyService, this._listService, this.themeService);
|
||||
}) as WorkbenchList<IRuntimeExtension>;
|
||||
|
||||
this._list.splice(0, this._list.length, this._elements);
|
||||
|
||||
this._list.onContextMenu((e) => {
|
||||
const actions: IAction[] = [];
|
||||
|
||||
actions.push(this.saveExtensionHostProfileAction, this.extensionHostProfileAction);
|
||||
if (e.element.marketplaceInfo.type === LocalExtensionType.User) {
|
||||
actions.push(this._instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL));
|
||||
actions.push(this._instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL));
|
||||
actions.forEach((a: DisableForWorkspaceAction | DisableGloballyAction) => a.extension = e.element.marketplaceInfo);
|
||||
actions.push(new Separator());
|
||||
}
|
||||
actions.push(this.extensionHostProfileAction, this.saveExtensionHostProfileAction);
|
||||
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => e.anchor,
|
||||
@@ -406,7 +410,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
|
||||
|
||||
export class RuntimeExtensionsInput extends EditorInput {
|
||||
|
||||
static ID = 'workbench.runtimeExtensions.input';
|
||||
static readonly ID = 'workbench.runtimeExtensions.input';
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -444,7 +448,7 @@ export class RuntimeExtensionsInput extends EditorInput {
|
||||
}
|
||||
|
||||
export class ShowRuntimeExtensionsAction extends Action {
|
||||
static ID = 'workbench.action.showRuntimeExtensions';
|
||||
static readonly ID = 'workbench.action.showRuntimeExtensions';
|
||||
static LABEL = nls.localize('showRuntimeExtensions', "Show Running Extensions");
|
||||
|
||||
constructor(
|
||||
@@ -461,7 +465,7 @@ export class ShowRuntimeExtensionsAction extends Action {
|
||||
}
|
||||
|
||||
class ReportExtensionIssueAction extends Action {
|
||||
static ID = 'workbench.extensions.action.reportExtensionIssue';
|
||||
static readonly ID = 'workbench.extensions.action.reportExtensionIssue';
|
||||
static LABEL = nls.localize('reportExtensionIssue', "Report Issue");
|
||||
|
||||
constructor(
|
||||
@@ -499,7 +503,7 @@ class ReportExtensionIssueAction extends Action {
|
||||
}
|
||||
|
||||
class ExtensionHostProfileAction extends Action {
|
||||
static ID = 'workbench.extensions.action.extensionHostProfile';
|
||||
static readonly ID = 'workbench.extensions.action.extensionHostProfile';
|
||||
static LABEL_START = nls.localize('extensionHostProfileStart', "Start Extension Host Profile");
|
||||
static LABEL_STOP = nls.localize('extensionHostProfileStop', "Stop Extension Host Profile");
|
||||
static STOP_CSS_CLASS = 'extension-host-profile-stop';
|
||||
@@ -542,7 +546,7 @@ class ExtensionHostProfileAction extends Action {
|
||||
class SaveExtensionHostProfileAction extends Action {
|
||||
|
||||
static LABEL = nls.localize('saveExtensionHostProfile', "Save Extension Host Profile");
|
||||
static ID = 'workbench.extensions.action.saveExtensionHostProfile';
|
||||
static readonly ID = 'workbench.extensions.action.saveExtensionHostProfile';
|
||||
|
||||
constructor(
|
||||
id: string = SaveExtensionHostProfileAction.ID, label: string = SaveExtensionHostProfileAction.LABEL,
|
||||
@@ -558,7 +562,7 @@ class SaveExtensionHostProfileAction extends Action {
|
||||
}
|
||||
|
||||
async run(): TPromise<any> {
|
||||
let picked = this._windowService.showSaveDialog({
|
||||
let picked = await this._windowService.showSaveDialog({
|
||||
title: 'Save Extension Host Profile',
|
||||
buttonLabel: 'Save',
|
||||
defaultPath: `CPU-${new Date().toISOString().replace(/[\-:]/g, '')}.cpuprofile`,
|
||||
|
||||
@@ -20,13 +20,12 @@ import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import {
|
||||
IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, IExtensionManifest,
|
||||
InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState
|
||||
InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionTipsService
|
||||
} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { getGalleryExtensionIdFromLocal, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet } 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';
|
||||
import { IChoiceService, IMessageService } from 'vs/platform/message/common/message';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IExtension, IExtensionDependencies, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
@@ -34,6 +33,10 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
|
||||
import { IURLService } from 'vs/platform/url/common/url';
|
||||
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
interface IExtensionStateProvider<T> {
|
||||
(extension: Extension): T;
|
||||
@@ -107,13 +110,21 @@ class Extension implements IExtension {
|
||||
}
|
||||
|
||||
get url(): string {
|
||||
if (!product.extensionsGallery) {
|
||||
if (!product.extensionsGallery || !this.gallery) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return `${product.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`;
|
||||
}
|
||||
|
||||
get downloadUrl(): string {
|
||||
if (!product.extensionsGallery) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return `${product.extensionsGallery.serviceUrl}/publishers/${this.publisher}/vsextensions/${this.name}/${this.latestVersion}/vspackage`;
|
||||
}
|
||||
|
||||
get iconUrl(): string {
|
||||
return this.galleryIconUrl || this.localIconUrl || this.defaultIconUrl;
|
||||
}
|
||||
@@ -136,6 +147,16 @@ class Extension implements IExtension {
|
||||
}
|
||||
|
||||
private get defaultIconUrl(): string {
|
||||
if (this.type === LocalExtensionType.System) {
|
||||
if (this.local.manifest && this.local.manifest.contributes) {
|
||||
if (Array.isArray(this.local.manifest.contributes.themes) && this.local.manifest.contributes.themes.length) {
|
||||
return require.toUrl('../browser/media/theme-icon.png');
|
||||
}
|
||||
if (Array.isArray(this.local.manifest.contributes.languages) && this.local.manifest.contributes.languages.length) {
|
||||
return require.toUrl('../browser/media/language-icon.png');
|
||||
}
|
||||
}
|
||||
}
|
||||
return require.toUrl('../browser/media/defaultIcon.png');
|
||||
}
|
||||
|
||||
@@ -212,6 +233,14 @@ class Extension implements IExtension {
|
||||
return readFile(uri.fsPath, 'utf8');
|
||||
}
|
||||
|
||||
if (this.type === LocalExtensionType.System) {
|
||||
return TPromise.as(`# ${this.displayName || this.name}
|
||||
**Notice** This is a an extension that is bundled with Visual Studio Code.
|
||||
|
||||
${this.description}
|
||||
`);
|
||||
}
|
||||
|
||||
return TPromise.wrapError<string>(new Error('not available'));
|
||||
}
|
||||
|
||||
@@ -341,11 +370,14 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IChoiceService private choiceService: IChoiceService,
|
||||
@IURLService urlService: IURLService,
|
||||
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService,
|
||||
@IWindowService private windowService: IWindowService
|
||||
@IWindowService private windowService: IWindowService,
|
||||
@ILogService private logService: ILogService,
|
||||
@IProgressService2 private progressService: IProgressService2,
|
||||
@IExtensionTipsService private extensionTipsService: IExtensionTipsService
|
||||
) {
|
||||
this.stateProvider = ext => this.getExtensionState(ext);
|
||||
|
||||
@@ -554,7 +586,11 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
|
||||
install(extension: string | IExtension): TPromise<void> {
|
||||
if (typeof extension === 'string') {
|
||||
return this.extensionService.install(extension);
|
||||
return this.progressService.withProgress({
|
||||
location: ProgressLocation.Extensions,
|
||||
title: nls.localize('installingVSIXExtension', 'Installing extension from VSIX...'),
|
||||
tooltip: `${extension}`
|
||||
}, () => this.extensionService.install(extension));
|
||||
}
|
||||
|
||||
if (!(extension instanceof Extension)) {
|
||||
@@ -562,7 +598,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
}
|
||||
|
||||
if (extension.isMalicious) {
|
||||
return TPromise.wrapError<void>(new Error(nls.localize('malicious', "This extension is reported to be malicious.")));
|
||||
return TPromise.wrapError<void>(new Error(nls.localize('malicious', "This extension is reported to be problematic.")));
|
||||
}
|
||||
|
||||
const ext = extension as Extension;
|
||||
@@ -571,19 +607,15 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
if (!gallery) {
|
||||
return TPromise.wrapError<void>(new Error('Missing gallery'));
|
||||
}
|
||||
if (ext.gallery.assets.downloadPage && ext.gallery.assets.downloadPage.uri) {
|
||||
window.open(ext.gallery.assets.downloadPage.uri);
|
||||
return TPromise.wrap<void>(void 0);
|
||||
} else {
|
||||
return this.extensionService.installFromGallery(gallery);
|
||||
}
|
||||
|
||||
return this.progressService.withProgress({
|
||||
location: ProgressLocation.Extensions,
|
||||
title: nls.localize('installingMarketPlaceExtension', 'Installing extension from Marketplace....'),
|
||||
tooltip: `${extension.id}`
|
||||
}, () => this.extensionService.installFromGallery(gallery));
|
||||
}
|
||||
|
||||
setEnablement(extension: IExtension, enablementState: EnablementState): TPromise<void> {
|
||||
if (extension.type === LocalExtensionType.System) {
|
||||
return TPromise.wrap<void>(void 0);
|
||||
}
|
||||
|
||||
const enable = enablementState === EnablementState.Enabled || enablementState === EnablementState.WorkspaceEnabled;
|
||||
return this.promptAndSetEnablement(extension, enablementState, enable).then(reload => {
|
||||
/* __GDPR__
|
||||
@@ -616,8 +648,30 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
return TPromise.wrapError<void>(new Error('Missing local'));
|
||||
}
|
||||
|
||||
return this.extensionService.uninstall(local);
|
||||
this.logService.info(`Requested uninstalling the extension ${extension.id} from window ${this.windowService.getCurrentWindowId()}`);
|
||||
return this.progressService.withProgress({
|
||||
location: ProgressLocation.Extensions,
|
||||
title: nls.localize('uninstallingExtension', 'Uninstalling extension....'),
|
||||
tooltip: `${local.identifier.id}`
|
||||
}, () => this.extensionService.uninstall(local));
|
||||
}
|
||||
|
||||
reinstall(extension: IExtension): TPromise<void> {
|
||||
if (!(extension instanceof Extension)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const ext = extension as Extension;
|
||||
const local = ext.local || this.installed.filter(e => e.id === extension.id)[0].local;
|
||||
|
||||
if (!local) {
|
||||
return TPromise.wrapError<void>(new Error('Missing local'));
|
||||
}
|
||||
|
||||
return this.progressService.withProgress({
|
||||
location: ProgressLocation.Extensions,
|
||||
tooltip: `${local.identifier.id}`
|
||||
}, () => this.extensionService.reinstall(local));
|
||||
}
|
||||
|
||||
private promptAndSetEnablement(extension: IExtension, enablementState: EnablementState, enable: boolean): TPromise<any> {
|
||||
@@ -633,7 +687,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
}
|
||||
|
||||
private promptForDependenciesAndEnable(extension: IExtension, dependencies: IExtension[], enablementState: EnablementState, enable: boolean): TPromise<any> {
|
||||
const message = nls.localize('enableDependeciesConfirmation', "Enabling '{0}' also enable its dependencies. Would you like to continue?", extension.displayName);
|
||||
const message = nls.localize('enableDependeciesConfirmation', "Enabling '{0}' also enables its dependencies. Would you like to continue?", extension.displayName);
|
||||
const options = [
|
||||
nls.localize('enable', "Yes"),
|
||||
nls.localize('doNotEnable', "No")
|
||||
@@ -734,7 +788,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
}
|
||||
|
||||
private doSetEnablement(extension: IExtension, enablementState: EnablementState): TPromise<boolean> {
|
||||
return this.extensionEnablementService.setEnablement(extension, enablementState);
|
||||
return this.extensionEnablementService.setEnablement(extension.local, enablementState);
|
||||
}
|
||||
|
||||
get allowedBadgeProviders(): string[] {
|
||||
@@ -768,25 +822,25 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
|
||||
private onDidInstallExtension(event: DidInstallExtensionEvent): void {
|
||||
const { local, zipPath, error, gallery } = event;
|
||||
const installing = gallery ? this.installing.filter(e => areSameExtensions(e.extension, gallery.identifier))[0] : null;
|
||||
const extension: Extension = installing ? installing.extension : zipPath ? new Extension(this.galleryService, this.stateProvider, null, null, this.telemetryService) : null;
|
||||
const installingExtension = gallery ? this.installing.filter(e => areSameExtensions(e.extension, gallery.identifier))[0] : null;
|
||||
const extension: Extension = installingExtension ? installingExtension.extension : zipPath ? new Extension(this.galleryService, this.stateProvider, null, null, this.telemetryService) : null;
|
||||
if (extension) {
|
||||
this.installing = installing ? this.installing.filter(e => e !== installing) : this.installing;
|
||||
this.installing = installingExtension ? this.installing.filter(e => e !== installingExtension) : this.installing;
|
||||
|
||||
if (error) {
|
||||
if (extension.gallery) {
|
||||
// Updating extension can be only a gallery extension
|
||||
const installed = this.installed.filter(e => e.id === extension.id)[0];
|
||||
if (installed && installing) {
|
||||
installing.operation = Operation.Updating;
|
||||
if (installed && installingExtension) {
|
||||
installingExtension.operation = Operation.Updating;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
extension.local = local;
|
||||
const installed = this.installed.filter(e => e.id === extension.id)[0];
|
||||
if (installed) {
|
||||
if (installing) {
|
||||
installing.operation = Operation.Updating;
|
||||
if (installingExtension) {
|
||||
installingExtension.operation = Operation.Updating;
|
||||
}
|
||||
installed.local = local;
|
||||
} else {
|
||||
@@ -795,13 +849,14 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
}
|
||||
if (extension.gallery) {
|
||||
// Report telemetry only for gallery extensions
|
||||
this.reportTelemetry(installing, error);
|
||||
this.reportTelemetry(installingExtension, error);
|
||||
}
|
||||
}
|
||||
this._onChange.fire();
|
||||
}
|
||||
|
||||
private onUninstallExtension({ id }: IExtensionIdentifier): void {
|
||||
this.logService.info(`Uninstalling the extension ${id} from window ${this.windowService.getCurrentWindowId()}`);
|
||||
const extension = this.installed.filter(e => e.local.identifier.id === id)[0];
|
||||
const newLength = this.installed.filter(e => e.local.identifier.id !== id).length;
|
||||
// TODO: Ask @Joao why is this?
|
||||
@@ -864,12 +919,14 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
const data = active.extension.telemetryData;
|
||||
const duration = new Date().getTime() - active.start.getTime();
|
||||
const eventName = toTelemetryEventName(active.operation);
|
||||
|
||||
const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason() || {};
|
||||
const recommendationsData = extRecommendations[active.extension.id.toLowerCase()] ? { recommendationReason: extRecommendations[active.extension.id.toLowerCase()].reasonId } : {};
|
||||
/* __GDPR__
|
||||
"extensionGallery:install" : {
|
||||
"success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
||||
"duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
||||
"errorcode": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
||||
"errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"recommendationReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
@@ -879,7 +936,8 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
"extensionGallery:update" : {
|
||||
"success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
||||
"duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
||||
"errorcode": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
||||
"errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"recommendationReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
@@ -889,13 +947,14 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
"extensionGallery:uninstall" : {
|
||||
"success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
||||
"duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
||||
"errorcode": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
||||
"errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"recommendationReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog(eventName, assign(data, { success: !errorcode, duration, errorcode }));
|
||||
this.telemetryService.publicLog(eventName, assign(data, { success: !errorcode, duration, errorcode }, recommendationsData));
|
||||
}
|
||||
|
||||
private onError(err: any): void {
|
||||
@@ -909,7 +968,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
return;
|
||||
}
|
||||
|
||||
this.messageService.show(Severity.Error, err);
|
||||
this.notificationService.error(err);
|
||||
}
|
||||
|
||||
private onOpenExtensionUrl(uri: URI): void {
|
||||
@@ -922,8 +981,11 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
const extensionId = match[1];
|
||||
|
||||
this.queryLocal().then(local => {
|
||||
if (local.some(local => local.id === extensionId)) {
|
||||
return TPromise.as(null);
|
||||
const extension = local.filter(local => local.id === extensionId)[0];
|
||||
|
||||
if (extension) {
|
||||
return this.windowService.show()
|
||||
.then(() => this.open(extension));
|
||||
}
|
||||
|
||||
return this.queryGallery({ names: [extensionId], source: 'uri' }).then(result => {
|
||||
@@ -937,15 +999,14 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
||||
return this.open(extension).then(() => {
|
||||
const message = nls.localize('installConfirmation', "Would you like to install the '{0}' extension?", extension.displayName, extension.publisher);
|
||||
const options = [
|
||||
nls.localize('install', "Install"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
nls.localize('install', "Install")
|
||||
];
|
||||
return this.choiceService.choose(Severity.Info, message, options, 2, false).then(value => {
|
||||
if (value !== 0) {
|
||||
return TPromise.as(null);
|
||||
return this.choiceService.choose(Severity.Info, message, options).then(value => {
|
||||
if (value === 0) {
|
||||
return this.install(extension);
|
||||
}
|
||||
|
||||
return this.install(extension);
|
||||
return TPromise.as(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import uuid = require('vs/base/common/uuid');
|
||||
import { mkdirp } from 'vs/base/node/pfs';
|
||||
import {
|
||||
IExtensionGalleryService, IGalleryExtensionAssets, IGalleryExtension, IExtensionManagementService, LocalExtensionType,
|
||||
IExtensionEnablementService, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier
|
||||
} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService';
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { TestTextResourceConfigurationService, TestContextService, TestLifecycleService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { FileService } from 'vs/workbench/services/files/node/fileService';
|
||||
import extfs = require('vs/base/node/extfs');
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { IPager } from 'vs/base/common/paging';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IExtensionsWorkbenchService, ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService';
|
||||
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/common/extensionEnablementService.test';
|
||||
import { IURLService } from 'vs/platform/url/common/url';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
||||
|
||||
const mockExtensionGallery: IGalleryExtension[] = [
|
||||
aGalleryExtension('MockExtension1', {
|
||||
displayName: 'Mock Extension 1',
|
||||
version: '1.5',
|
||||
publisherId: 'mockPublisher1Id',
|
||||
publisher: 'mockPublisher1',
|
||||
publisherDisplayName: 'Mock Publisher 1',
|
||||
description: 'Mock Description',
|
||||
installCount: 1000,
|
||||
rating: 4,
|
||||
ratingCount: 100
|
||||
}, {
|
||||
dependencies: ['pub.1'],
|
||||
}, {
|
||||
manifest: { uri: 'uri:manifest', fallbackUri: 'fallback:manifest' },
|
||||
readme: { uri: 'uri:readme', fallbackUri: 'fallback:readme' },
|
||||
changelog: { uri: 'uri:changelog', fallbackUri: 'fallback:changlog' },
|
||||
download: { uri: 'uri:download', fallbackUri: 'fallback:download' },
|
||||
icon: { uri: 'uri:icon', fallbackUri: 'fallback:icon' },
|
||||
license: { uri: 'uri:license', fallbackUri: 'fallback:license' },
|
||||
repository: { uri: 'uri:repository', fallbackUri: 'fallback:repository' },
|
||||
}),
|
||||
aGalleryExtension('MockExtension2', {
|
||||
displayName: 'Mock Extension 2',
|
||||
version: '1.5',
|
||||
publisherId: 'mockPublisher2Id',
|
||||
publisher: 'mockPublisher2',
|
||||
publisherDisplayName: 'Mock Publisher 2',
|
||||
description: 'Mock Description',
|
||||
installCount: 1000,
|
||||
rating: 4,
|
||||
ratingCount: 100
|
||||
}, {
|
||||
dependencies: ['pub.1', 'pub.2'],
|
||||
}, {
|
||||
manifest: { uri: 'uri:manifest', fallbackUri: 'fallback:manifest' },
|
||||
readme: { uri: 'uri:readme', fallbackUri: 'fallback:readme' },
|
||||
changelog: { uri: 'uri:changelog', fallbackUri: 'fallback:changlog' },
|
||||
download: { uri: 'uri:download', fallbackUri: 'fallback:download' },
|
||||
icon: { uri: 'uri:icon', fallbackUri: 'fallback:icon' },
|
||||
license: { uri: 'uri:license', fallbackUri: 'fallback:license' },
|
||||
repository: { uri: 'uri:repository', fallbackUri: 'fallback:repository' },
|
||||
})
|
||||
];
|
||||
|
||||
const mockExtensionLocal = [
|
||||
{
|
||||
type: LocalExtensionType.User,
|
||||
identifier: mockExtensionGallery[0].identifier,
|
||||
manifest: {
|
||||
name: mockExtensionGallery[0].name,
|
||||
publisher: mockExtensionGallery[0].publisher,
|
||||
version: mockExtensionGallery[0].version
|
||||
},
|
||||
metadata: null,
|
||||
path: 'somepath',
|
||||
readmeUrl: 'some readmeUrl',
|
||||
changelogUrl: 'some changelogUrl'
|
||||
},
|
||||
{
|
||||
type: LocalExtensionType.User,
|
||||
identifier: mockExtensionGallery[1].identifier,
|
||||
manifest: {
|
||||
name: mockExtensionGallery[1].name,
|
||||
publisher: mockExtensionGallery[1].publisher,
|
||||
version: mockExtensionGallery[1].version
|
||||
},
|
||||
metadata: null,
|
||||
path: 'somepath',
|
||||
readmeUrl: 'some readmeUrl',
|
||||
changelogUrl: 'some changelogUrl'
|
||||
}
|
||||
];
|
||||
|
||||
const mockTestData = {
|
||||
recommendedExtensions: [
|
||||
'mockPublisher1.mockExtension1',
|
||||
'MOCKPUBLISHER2.mockextension2',
|
||||
'badlyformattedextension',
|
||||
'MOCKPUBLISHER2.mockextension2',
|
||||
'unknown.extension'
|
||||
],
|
||||
validRecommendedExtensions: [
|
||||
'mockPublisher1.mockExtension1',
|
||||
'MOCKPUBLISHER2.mockextension2'
|
||||
]
|
||||
};
|
||||
|
||||
function aPage<T>(...objects: T[]): IPager<T> {
|
||||
return { firstPage: objects, total: objects.length, pageSize: objects.length, getPage: () => null };
|
||||
}
|
||||
|
||||
const noAssets: IGalleryExtensionAssets = {
|
||||
changelog: null,
|
||||
download: null,
|
||||
icon: null,
|
||||
license: null,
|
||||
manifest: null,
|
||||
readme: null,
|
||||
repository: null
|
||||
};
|
||||
|
||||
function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: IGalleryExtensionAssets = noAssets): IGalleryExtension {
|
||||
const galleryExtension = <IGalleryExtension>Object.create({});
|
||||
assign(galleryExtension, { name, publisher: 'pub', version: '1.0.0', properties: {}, assets: {} }, properties);
|
||||
assign(galleryExtension.properties, { dependencies: [] }, galleryExtensionProperties);
|
||||
assign(galleryExtension.assets, assets);
|
||||
galleryExtension.identifier = { id: getGalleryExtensionId(galleryExtension.publisher, galleryExtension.name), uuid: generateUuid() };
|
||||
return <IGalleryExtension>galleryExtension;
|
||||
}
|
||||
|
||||
suite('ExtensionsTipsService Test', () => {
|
||||
test('ExtensionTipsService: No Prompt for valid workspace recommendations when galleryService is absent', () => {
|
||||
});
|
||||
});
|
||||
@@ -29,9 +29,13 @@ import { IPager } from 'vs/base/common/paging';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { TestContextService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IChoiceService } from 'vs/platform/message/common/message';
|
||||
import { TestContextService, TestWindowService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IProgressService2 } from 'vs/platform/progress/common/progress';
|
||||
import { ProgressService2 } from 'vs/workbench/services/progress/browser/progressService2';
|
||||
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
||||
|
||||
suite('ExtensionsWorkbenchService Test', () => {
|
||||
|
||||
@@ -52,6 +56,9 @@ suite('ExtensionsWorkbenchService Test', () => {
|
||||
instantiationService = new TestInstantiationService();
|
||||
instantiationService.stub(IURLService, { onOpenURL: new Emitter().event });
|
||||
instantiationService.stub(ITelemetryService, NullTelemetryService);
|
||||
instantiationService.stub(ILogService, NullLogService);
|
||||
instantiationService.stub(IWindowService, TestWindowService);
|
||||
instantiationService.stub(IProgressService2, ProgressService2);
|
||||
|
||||
instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService);
|
||||
|
||||
@@ -695,283 +702,374 @@ suite('ExtensionsWorkbenchService Test', () => {
|
||||
});
|
||||
|
||||
test('test uninstalled extensions are always enabled', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.WorkspaceDisabled);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a')));
|
||||
return testObject.queryGallery().then(pagedResponse => {
|
||||
const actual = pagedResponse.firstPage[0];
|
||||
assert.equal(actual.enablementState, EnablementState.Enabled);
|
||||
});
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.Disabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.WorkspaceDisabled))
|
||||
.then(() => {
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a')));
|
||||
return testObject.queryGallery().then(pagedResponse => {
|
||||
const actual = pagedResponse.firstPage[0];
|
||||
assert.equal(actual.enablementState, EnablementState.Enabled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('test enablement state installed enabled extension', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.WorkspaceDisabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.Disabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.WorkspaceDisabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
const actual = testObject.local[0];
|
||||
const actual = testObject.local[0];
|
||||
|
||||
assert.equal(actual.enablementState, EnablementState.Enabled);
|
||||
assert.equal(actual.enablementState, EnablementState.Enabled);
|
||||
});
|
||||
});
|
||||
|
||||
test('test workspace disabled extension', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.d' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.WorkspaceDisabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.e' }, EnablementState.WorkspaceDisabled);
|
||||
const extensionA = aLocalExtension('a');
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.Disabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('d'), EnablementState.Disabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.WorkspaceDisabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('e'), EnablementState.WorkspaceDisabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
const actual = testObject.local[0];
|
||||
|
||||
const actual = testObject.local[0];
|
||||
|
||||
assert.equal(actual.enablementState, EnablementState.WorkspaceDisabled);
|
||||
assert.equal(actual.enablementState, EnablementState.WorkspaceDisabled);
|
||||
});
|
||||
});
|
||||
|
||||
test('test globally disabled extension', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.d' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.WorkspaceDisabled);
|
||||
const localExtension = aLocalExtension('a');
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(localExtension, EnablementState.Disabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('d'), EnablementState.Disabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.WorkspaceDisabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localExtension]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
const actual = testObject.local[0];
|
||||
|
||||
const actual = testObject.local[0];
|
||||
|
||||
assert.equal(actual.enablementState, EnablementState.Disabled);
|
||||
assert.equal(actual.enablementState, EnablementState.Disabled);
|
||||
});
|
||||
});
|
||||
|
||||
test('test enablement state is updated for user extensions', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.WorkspaceDisabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.WorkspaceDisabled);
|
||||
const actual = testObject.local[0];
|
||||
|
||||
assert.equal(actual.enablementState, EnablementState.WorkspaceDisabled);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.Disabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.WorkspaceDisabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.WorkspaceDisabled)
|
||||
.then(() => {
|
||||
const actual = testObject.local[0];
|
||||
assert.equal(actual.enablementState, EnablementState.WorkspaceDisabled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('test enable extension globally when extension is disabled for workspace', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.WorkspaceDisabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Enabled);
|
||||
const actual = testObject.local[0];
|
||||
|
||||
assert.equal(actual.enablementState, EnablementState.Enabled);
|
||||
const localExtension = aLocalExtension('a');
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(localExtension, EnablementState.WorkspaceDisabled)
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localExtension]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Enabled)
|
||||
.then(() => {
|
||||
const actual = testObject.local[0];
|
||||
assert.equal(actual.enablementState, EnablementState.Enabled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('test disable extension globally', () => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
|
||||
const actual = testObject.local[0];
|
||||
|
||||
assert.equal(actual.enablementState, EnablementState.Disabled);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled)
|
||||
.then(() => {
|
||||
const actual = testObject.local[0];
|
||||
assert.equal(actual.enablementState, EnablementState.Disabled);
|
||||
});
|
||||
});
|
||||
|
||||
test('test system extensions are always enabled', () => {
|
||||
test('test system extensions can be disabled', () => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', {}, { type: LocalExtensionType.System })]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
|
||||
const actual = testObject.local[0];
|
||||
|
||||
assert.equal(actual.enablementState, EnablementState.Enabled);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled)
|
||||
.then(() => {
|
||||
const actual = testObject.local[0];
|
||||
assert.equal(actual.enablementState, EnablementState.Disabled);
|
||||
});
|
||||
});
|
||||
|
||||
test('test enablement state is updated on change from outside', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.WorkspaceDisabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
const localExtension = aLocalExtension('a');
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.Disabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.WorkspaceDisabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localExtension]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
|
||||
const actual = testObject.local[0];
|
||||
|
||||
assert.equal(actual.enablementState, EnablementState.Disabled);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(localExtension, EnablementState.Disabled)
|
||||
.then(() => {
|
||||
const actual = testObject.local[0];
|
||||
assert.equal(actual.enablementState, EnablementState.Disabled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('test disable extension with dependencies disable only itself', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] });
|
||||
const extensionB = aLocalExtension('b');
|
||||
const extensionC = aLocalExtension('c');
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Enabled);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled)
|
||||
.then(() => {
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Enabled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('test disable extension with dependencies disable all', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c')]);
|
||||
instantiationService.stubPromise(IChoiceService, 'choose', 1);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] });
|
||||
const extensionB = aLocalExtension('b');
|
||||
const extensionC = aLocalExtension('c');
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]);
|
||||
instantiationService.stubPromise(IChoiceService, 'choose', 1);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled)
|
||||
.then(() => {
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('test disable extension fails if extension is a dependent of other', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] });
|
||||
const extensionB = aLocalExtension('b');
|
||||
const extensionC = aLocalExtension('c');
|
||||
|
||||
return testObject.setEnablement(testObject.local[1], EnablementState.Disabled).then(() => assert.fail('Should fail'), error => assert.ok(true));
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
return testObject.setEnablement(testObject.local[1], EnablementState.Disabled).then(() => assert.fail('Should fail'), error => assert.ok(true));
|
||||
});
|
||||
});
|
||||
|
||||
test('test disable extension does not fail if its dependency is a dependent of other but chosen to disable only itself', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c', { extensionDependencies: ['pub.b'] })]);
|
||||
const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] });
|
||||
const extensionB = aLocalExtension('b');
|
||||
const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.b'] });
|
||||
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
|
||||
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled)
|
||||
.then(() => {
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('test disable extension fails if its dependency is a dependent of other', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c', { extensionDependencies: ['pub.b'] })]);
|
||||
instantiationService.stubPromise(IChoiceService, 'choose', 1);
|
||||
const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] });
|
||||
const extensionB = aLocalExtension('b');
|
||||
const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.b'] });
|
||||
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled).then(() => assert.fail('Should fail'), error => assert.ok(true));
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]);
|
||||
instantiationService.stubPromise(IChoiceService, 'choose', 1);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled).then(() => assert.fail('Should fail'), error => assert.ok(true));
|
||||
});
|
||||
});
|
||||
|
||||
test('test disable extension if its dependency is a dependent of other disabled extension', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c', { extensionDependencies: ['pub.b'] })]);
|
||||
instantiationService.stubPromise(IChoiceService, 'choose', 1);
|
||||
const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] });
|
||||
const extensionB = aLocalExtension('b');
|
||||
const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.b'] });
|
||||
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Disabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]);
|
||||
instantiationService.stubPromise(IChoiceService, 'choose', 1);
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled)
|
||||
.then(() => {
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('test disable extension if its dependencys dependency is itself', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b', { extensionDependencies: ['pub.a'] }), aLocalExtension('c')]);
|
||||
instantiationService.stubPromise(IChoiceService, 'choose', 1);
|
||||
const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] });
|
||||
const extensionB = aLocalExtension('b', { extensionDependencies: ['pub.a'] });
|
||||
const extensionC = aLocalExtension('c');
|
||||
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]);
|
||||
instantiationService.stubPromise(IChoiceService, 'choose', 1);
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled)
|
||||
.then(() => {
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('test disable extension if its dependency is dependent and is disabled', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c', { extensionDependencies: ['pub.b'] })]);
|
||||
instantiationService.stubPromise(IChoiceService, 'choose', 1);
|
||||
const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] });
|
||||
const extensionB = aLocalExtension('b');
|
||||
const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.b'] });
|
||||
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Disabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]);
|
||||
instantiationService.stubPromise(IChoiceService, 'choose', 1);
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled)
|
||||
.then(() => assert.equal(testObject.local[0].enablementState, EnablementState.Disabled));
|
||||
});
|
||||
});
|
||||
|
||||
test('test disable extension with cyclic dependencies', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b', { extensionDependencies: ['pub.c'] }), aLocalExtension('c', { extensionDependencies: ['pub.a'] })]);
|
||||
instantiationService.stubPromise(IChoiceService, 'choose', 1);
|
||||
const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] });
|
||||
const extensionB = aLocalExtension('b', { extensionDependencies: ['pub.c'] });
|
||||
const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.a'] });
|
||||
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]);
|
||||
instantiationService.stubPromise(IChoiceService, 'choose', 1);
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled)
|
||||
.then(() => {
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('test enable extension with dependencies enable all', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] });
|
||||
const extensionB = aLocalExtension('b');
|
||||
const extensionC = aLocalExtension('c');
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Enabled);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Disabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Disabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Disabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Enabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Enabled);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Enabled)
|
||||
.then(() => {
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Enabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Enabled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('test enable extension with cyclic dependencies', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b', { extensionDependencies: ['pub.c'] }), aLocalExtension('c', { extensionDependencies: ['pub.a'] })]);
|
||||
const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] });
|
||||
const extensionB = aLocalExtension('b', { extensionDependencies: ['pub.c'] });
|
||||
const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.a'] });
|
||||
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Disabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Disabled))
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Disabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]);
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Enabled);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Enabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Enabled);
|
||||
assert.equal(testObject.local[2].enablementState, EnablementState.Enabled);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Enabled)
|
||||
.then(() => {
|
||||
assert.equal(testObject.local[0].enablementState, EnablementState.Enabled);
|
||||
assert.equal(testObject.local[1].enablementState, EnablementState.Enabled);
|
||||
assert.equal(testObject.local[2].enablementState, EnablementState.Enabled);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('test change event is fired when disablement flags are changed', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.WorkspaceDisabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
const target = sinon.spy();
|
||||
testObject.onChange(target);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.Disabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.WorkspaceDisabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
const target = sinon.spy();
|
||||
testObject.onChange(target);
|
||||
|
||||
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
|
||||
|
||||
assert.ok(target.calledOnce);
|
||||
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled)
|
||||
.then(() => assert.ok(target.calledOnce));
|
||||
});
|
||||
});
|
||||
|
||||
test('test change event is fired when disablement flags are changed from outside', () => {
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.WorkspaceDisabled);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
const target = sinon.spy();
|
||||
testObject.onChange(target);
|
||||
const localExtension = aLocalExtension('a');
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.Disabled)
|
||||
.then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.WorkspaceDisabled))
|
||||
.then(() => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localExtension]);
|
||||
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
const target = sinon.spy();
|
||||
testObject.onChange(target);
|
||||
|
||||
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
|
||||
|
||||
assert.ok(target.calledOnce);
|
||||
return instantiationService.get(IExtensionEnablementService).setEnablement(localExtension, EnablementState.Disabled)
|
||||
.then(() => assert.ok(target.calledOnce));
|
||||
});
|
||||
});
|
||||
|
||||
function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension {
|
||||
|
||||
@@ -6,11 +6,30 @@
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { StatusbarAlignment, IStatusbarRegistry, Extensions, StatusbarItemDescriptor } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { FeedbackStatusbarItem } from './feedbackStatusbarItem';
|
||||
import { FeedbackStatusbarItem } from 'vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
||||
// Register Statusbar item
|
||||
Registry.as<IStatusbarRegistry>(Extensions.Statusbar).registerStatusbarItem(new StatusbarItemDescriptor(
|
||||
FeedbackStatusbarItem,
|
||||
StatusbarAlignment.RIGHT,
|
||||
-100 /* Low Priority */
|
||||
));
|
||||
-100 /* towards the end of the right hand side */
|
||||
));
|
||||
|
||||
// Configuration: Workbench
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': 'workbench',
|
||||
'order': 7,
|
||||
'title': localize('workbenchConfigurationTitle', "Workbench"),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'workbench.statusBar.feedback.visible': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': localize('feedbackVisibility', "Controls the visibility of the Twitter feedback (smiley) in the status bar at the bottom of the workbench.")
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -11,7 +11,6 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import { Dropdown } from 'vs/base/browser/ui/dropdown/dropdown';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import product from 'vs/platform/node/product';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
@@ -20,6 +19,10 @@ import { IIntegrityService } from 'vs/platform/integrity/common/integrity';
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { editorWidgetBackground, widgetShadow, inputBorder, inputForeground, inputBackground, inputActiveOptionBorder, editorBackground, buttonBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry';
|
||||
|
||||
export const FEEDBACK_VISIBLE_CONFIG = 'workbench.statusBar.feedback.visible';
|
||||
|
||||
export interface IFeedback {
|
||||
feedback: string;
|
||||
@@ -43,35 +46,35 @@ enum FormEvent {
|
||||
}
|
||||
|
||||
export class FeedbackDropdown extends Dropdown {
|
||||
protected maxFeedbackCharacters: number;
|
||||
private maxFeedbackCharacters: number;
|
||||
|
||||
protected feedback: string;
|
||||
protected sentiment: number;
|
||||
protected aliasEnabled: boolean;
|
||||
protected isSendingFeedback: boolean;
|
||||
protected autoHideTimeout: number;
|
||||
private feedback: string;
|
||||
private sentiment: number;
|
||||
private isSendingFeedback: boolean;
|
||||
private autoHideTimeout: number;
|
||||
|
||||
protected feedbackService: IFeedbackService;
|
||||
private feedbackService: IFeedbackService;
|
||||
|
||||
protected feedbackForm: HTMLFormElement;
|
||||
protected feedbackDescriptionInput: HTMLTextAreaElement;
|
||||
protected smileyInput: Builder;
|
||||
protected frownyInput: Builder;
|
||||
protected sendButton: Builder;
|
||||
protected remainingCharacterCount: Builder;
|
||||
private feedbackForm: HTMLFormElement;
|
||||
private feedbackDescriptionInput: HTMLTextAreaElement;
|
||||
private smileyInput: Builder;
|
||||
private frownyInput: Builder;
|
||||
private sendButton: Builder;
|
||||
private hideButton: HTMLInputElement;
|
||||
private remainingCharacterCount: Builder;
|
||||
|
||||
protected requestFeatureLink: string;
|
||||
protected reportIssueLink: string;
|
||||
private requestFeatureLink: string;
|
||||
|
||||
private _isPure: boolean;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
options: IFeedbackDropdownOptions,
|
||||
@ITelemetryService protected telemetryService: ITelemetryService,
|
||||
@ICommandService private commandService: ICommandService,
|
||||
@IIntegrityService protected integrityService: IIntegrityService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IIntegrityService private integrityService: IIntegrityService,
|
||||
@IThemeService private themeService: IThemeService,
|
||||
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService
|
||||
) {
|
||||
super(container, {
|
||||
contextViewProvider: options.contextViewProvider,
|
||||
@@ -106,14 +109,12 @@ export class FeedbackDropdown extends Dropdown {
|
||||
|
||||
this.sendButton = null;
|
||||
|
||||
this.reportIssueLink = product.reportIssueUrl;
|
||||
this.requestFeatureLink = product.requestFeatureUrl;
|
||||
this.requestFeatureLink = product.sendASmile.requestFeatureUrl;
|
||||
}
|
||||
|
||||
protected renderContents(container: HTMLElement): IDisposable {
|
||||
const $form = $('form.feedback-form').attr({
|
||||
action: 'javascript:void(0);',
|
||||
tabIndex: '-1'
|
||||
action: 'javascript:void(0);'
|
||||
}).appendTo(container);
|
||||
|
||||
$(container).addClass('monaco-menu-container');
|
||||
@@ -171,7 +172,16 @@ export class FeedbackDropdown extends Dropdown {
|
||||
$('div').append($('a').attr('target', '_blank').attr('href', '#').text(nls.localize("submit a bug", "Submit a bug")).attr('tabindex', '0'))
|
||||
.on('click', event => {
|
||||
dom.EventHelper.stop(event);
|
||||
this.commandService.executeCommand('workbench.action.reportIssues').done(null, errors.onUnexpectedError);
|
||||
const actionId = 'workbench.action.openIssueReporter';
|
||||
this.commandService.executeCommand(actionId).done(null, errors.onUnexpectedError);
|
||||
|
||||
/* __GDPR__
|
||||
"workbenchActionExecuted" : {
|
||||
"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('workbenchActionExecuted', { id: actionId, from: 'feedback' });
|
||||
})
|
||||
.appendTo($contactUsContainer);
|
||||
|
||||
@@ -197,16 +207,23 @@ export class FeedbackDropdown extends Dropdown {
|
||||
|
||||
const $buttons = $('div.form-buttons').appendTo($form);
|
||||
|
||||
const $hideButtonContainer = $('div.hide-button-container').appendTo($buttons);
|
||||
|
||||
this.hideButton = $('input.hide-button').type('checkbox').attr('checked', '').id('hide-button').appendTo($hideButtonContainer).getHTMLElement() as HTMLInputElement;
|
||||
|
||||
$('label').attr('for', 'hide-button').text(nls.localize('showFeedback', "Show Feedback Smiley in Status Bar")).appendTo($hideButtonContainer);
|
||||
|
||||
this.sendButton = this.invoke($('input.send').type('submit').attr('disabled', '').value(nls.localize('tweet', "Tweet")).appendTo($buttons), () => {
|
||||
if (this.isSendingFeedback) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onSubmit();
|
||||
});
|
||||
|
||||
this.toDispose.push(attachStylerCallback(this.themeService, { widgetShadow, editorWidgetBackground, inputBackground, inputForeground, inputBorder, editorBackground, contrastBorder }, colors => {
|
||||
$form.style('background-color', colors.editorWidgetBackground);
|
||||
$form.style('box-shadow', colors.widgetShadow ? `0 2px 8px ${colors.widgetShadow}` : null);
|
||||
$form.style('box-shadow', colors.widgetShadow ? `0 5px 8px ${colors.widgetShadow}` : null);
|
||||
|
||||
if (this.feedbackDescriptionInput) {
|
||||
this.feedbackDescriptionInput.style.backgroundColor = colors.inputBackground;
|
||||
@@ -242,7 +259,7 @@ export class FeedbackDropdown extends Dropdown {
|
||||
this.feedbackDescriptionInput.value ? this.sendButton.removeAttribute('disabled') : this.sendButton.attr('disabled', '');
|
||||
}
|
||||
|
||||
protected setSentiment(smile: boolean): void {
|
||||
private setSentiment(smile: boolean): void {
|
||||
if (smile) {
|
||||
this.smileyInput.addClass('checked');
|
||||
this.smileyInput.attr('aria-checked', 'true');
|
||||
@@ -254,14 +271,16 @@ export class FeedbackDropdown extends Dropdown {
|
||||
this.smileyInput.removeClass('checked');
|
||||
this.smileyInput.attr('aria-checked', 'false');
|
||||
}
|
||||
|
||||
this.sentiment = smile ? 1 : 0;
|
||||
this.maxFeedbackCharacters = this.feedbackService.getCharacterLimit(this.sentiment);
|
||||
this.updateCharCountText();
|
||||
$(this.feedbackDescriptionInput).attr({ maxlength: this.maxFeedbackCharacters });
|
||||
}
|
||||
|
||||
protected invoke(element: Builder, callback: () => void): Builder {
|
||||
private invoke(element: Builder, callback: () => void): Builder {
|
||||
element.on('click', callback);
|
||||
|
||||
element.on('keypress', (e) => {
|
||||
if (e instanceof KeyboardEvent) {
|
||||
const keyboardEvent = <KeyboardEvent>e;
|
||||
@@ -270,6 +289,7 @@ export class FeedbackDropdown extends Dropdown {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
@@ -283,6 +303,10 @@ export class FeedbackDropdown extends Dropdown {
|
||||
this.autoHideTimeout = null;
|
||||
}
|
||||
|
||||
if (this.hideButton && !this.hideButton.checked) {
|
||||
this.configurationService.updateValue(FEEDBACK_VISIBLE_CONFIG, false).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
super.hide();
|
||||
}
|
||||
|
||||
@@ -295,7 +319,7 @@ export class FeedbackDropdown extends Dropdown {
|
||||
}
|
||||
}
|
||||
|
||||
protected onSubmit(): void {
|
||||
private onSubmit(): void {
|
||||
if ((this.feedbackForm.checkValidity && !this.feedbackForm.checkValidity())) {
|
||||
return;
|
||||
}
|
||||
@@ -338,13 +362,13 @@ export class FeedbackDropdown extends Dropdown {
|
||||
}
|
||||
}
|
||||
|
||||
protected resetForm(): void {
|
||||
private resetForm(): void {
|
||||
if (this.feedbackDescriptionInput) {
|
||||
this.feedbackDescriptionInput.value = '';
|
||||
}
|
||||
|
||||
this.sentiment = 1;
|
||||
this.maxFeedbackCharacters = this.feedbackService.getCharacterLimit(this.sentiment);
|
||||
this.aliasEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,22 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { FeedbackDropdown, IFeedback, IFeedbackService } from './feedback';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { FeedbackDropdown, IFeedback, IFeedbackService, FEEDBACK_VISIBLE_CONFIG } from 'vs/workbench/parts/feedback/electron-browser/feedback';
|
||||
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_ITEM_HOVER_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { clearNode, EventHelper } from 'vs/base/browser/dom';
|
||||
import { $ } from 'vs/base/browser/builder';
|
||||
import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
|
||||
class TwitterFeedbackService implements IFeedbackService {
|
||||
|
||||
@@ -50,20 +57,38 @@ class TwitterFeedbackService implements IFeedbackService {
|
||||
|
||||
export class FeedbackStatusbarItem extends Themable implements IStatusbarItem {
|
||||
private dropdown: FeedbackDropdown;
|
||||
private enabled: boolean;
|
||||
private container: HTMLElement;
|
||||
private hideAction: HideAction;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IContextViewService private contextViewService: IContextViewService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
|
||||
@IThemeService themeService: IThemeService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
this.enabled = this.configurationService.getValue(FEEDBACK_VISIBLE_CONFIG);
|
||||
|
||||
this.hideAction = this.instantiationService.createInstance(HideAction);
|
||||
this.toUnbind.push(this.hideAction);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toUnbind.push(this.contextService.onDidChangeWorkbenchState(() => this.updateStyles()));
|
||||
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e)));
|
||||
}
|
||||
|
||||
private onConfigurationUpdated(event: IConfigurationChangeEvent): void {
|
||||
if (event.affectsConfiguration(FEEDBACK_VISIBLE_CONFIG)) {
|
||||
this.enabled = this.configurationService.getValue(FEEDBACK_VISIBLE_CONFIG);
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
@@ -75,17 +100,73 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem {
|
||||
}
|
||||
|
||||
public render(element: HTMLElement): IDisposable {
|
||||
if (product.sendASmile) {
|
||||
this.dropdown = this.instantiationService.createInstance(FeedbackDropdown, element, {
|
||||
contextViewProvider: this.contextViewService,
|
||||
feedbackService: this.instantiationService.createInstance(TwitterFeedbackService)
|
||||
this.container = element;
|
||||
|
||||
// Prevent showing dropdown on anything but left click
|
||||
$(this.container).on('mousedown', (e: MouseEvent) => {
|
||||
if (e.button !== 0) {
|
||||
EventHelper.stop(e, true);
|
||||
}
|
||||
}, this.toUnbind, true);
|
||||
|
||||
// Offer context menu to hide status bar entry
|
||||
$(this.container).on('contextmenu', e => {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => this.container,
|
||||
getActions: () => TPromise.as([this.hideAction])
|
||||
});
|
||||
}, this.toUnbind);
|
||||
|
||||
this.updateStyles();
|
||||
return this.update();
|
||||
}
|
||||
|
||||
return this.dropdown;
|
||||
private update(): IDisposable {
|
||||
const enabled = product.sendASmile && this.enabled;
|
||||
|
||||
// Create
|
||||
if (enabled) {
|
||||
if (!this.dropdown) {
|
||||
this.dropdown = this.instantiationService.createInstance(FeedbackDropdown, this.container, {
|
||||
contextViewProvider: this.contextViewService,
|
||||
feedbackService: this.instantiationService.createInstance(TwitterFeedbackService)
|
||||
});
|
||||
this.toUnbind.push(this.dropdown);
|
||||
|
||||
this.updateStyles();
|
||||
|
||||
return this.dropdown;
|
||||
}
|
||||
}
|
||||
|
||||
// Dispose
|
||||
else {
|
||||
dispose(this.dropdown);
|
||||
this.dropdown = void 0;
|
||||
clearNode(this.container);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class HideAction extends Action {
|
||||
|
||||
constructor(
|
||||
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService
|
||||
) {
|
||||
super('feedback.hide', localize('hide', "Hide"));
|
||||
}
|
||||
|
||||
public run(extensionId: string): TPromise<any> {
|
||||
return this.configurationService.updateValue(FEEDBACK_VISIBLE_CONFIG, false);
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
const statusBarItemHoverBackground = theme.getColor(STATUS_BAR_ITEM_HOVER_BACKGROUND);
|
||||
if (statusBarItemHoverBackground) {
|
||||
collector.addRule(`.monaco-workbench > .part.statusbar > .statusbar-item .dropdown.send-feedback:hover { background-color: ${statusBarItemHoverBackground}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -88,7 +88,6 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: .5em 0 0 0;
|
||||
padding: .5em;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
@@ -156,6 +155,19 @@
|
||||
background-color: #eaeaea;
|
||||
}
|
||||
|
||||
.monaco-shell .feedback-form .form-buttons {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-shell .feedback-form .form-buttons .hide-button-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-shell .feedback-form .form-buttons .hide-button-container input,
|
||||
.monaco-shell .feedback-form .form-buttons .hide-button-container label {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.monaco-shell .feedback-form .form-buttons .send {
|
||||
color: white;
|
||||
border: none;
|
||||
@@ -169,6 +181,7 @@
|
||||
padding-right: 12px;
|
||||
border: 4px solid #007ACC;
|
||||
border-radius: 4px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.monaco-shell .feedback-form .form-buttons .send.in-progress,
|
||||
|
||||
@@ -142,8 +142,7 @@ export class FileEditorTracker implements IWorkbenchContribution {
|
||||
// We have received reports of users seeing delete events even though the file still
|
||||
// exists (network shares issue: https://github.com/Microsoft/vscode/issues/13665).
|
||||
// Since we do not want to close an editor without reason, we have to check if the
|
||||
// file is really gone and not just a faulty file event (TODO@Ben revisit when we
|
||||
// have a more stable file watcher in place for this scenario).
|
||||
// file is really gone and not just a faulty file event.
|
||||
// This only applies to external file events, so we need to check for the isExternal
|
||||
// flag.
|
||||
let checkExists: TPromise<boolean>;
|
||||
|
||||
@@ -24,7 +24,6 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { CancelAction } from 'vs/platform/message/common/message';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
@@ -62,7 +61,7 @@ export class TextFileEditor extends BaseTextEditor {
|
||||
private onFilesChanged(e: FileChangesEvent): void {
|
||||
const deleted = e.getDeleted();
|
||||
if (deleted && deleted.length) {
|
||||
this.clearTextEditorViewState(deleted.map(d => d.resource.toString()));
|
||||
this.clearTextEditorViewState(deleted.map(d => d.resource));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +126,7 @@ export class TextFileEditor extends BaseTextEditor {
|
||||
textEditor.setModel(textFileModel.textEditorModel);
|
||||
|
||||
// Always restore View State if any associated
|
||||
const editorViewState = this.loadTextEditorViewState(this.input.getResource().toString());
|
||||
const editorViewState = this.loadTextEditorViewState(this.input.getResource());
|
||||
if (editorViewState) {
|
||||
textEditor.restoreViewState(editorViewState);
|
||||
}
|
||||
@@ -155,18 +154,13 @@ export class TextFileEditor extends BaseTextEditor {
|
||||
return TPromise.wrapError<void>(errors.create(toErrorMessage(error), {
|
||||
actions: [
|
||||
new Action('workbench.files.action.createMissingFile', nls.localize('createFile', "Create File"), null, true, () => {
|
||||
return this.fileService.updateContent(input.getResource(), '').then(() => {
|
||||
|
||||
// Open
|
||||
return this.editorService.openEditor({
|
||||
resource: input.getResource(),
|
||||
options: {
|
||||
pinned: true // new file gets pinned by default
|
||||
}
|
||||
});
|
||||
});
|
||||
}),
|
||||
CancelAction
|
||||
return this.fileService.updateContent(input.getResource(), '').then(() => this.editorService.openEditor({
|
||||
resource: input.getResource(),
|
||||
options: {
|
||||
pinned: true // new file gets pinned by default
|
||||
}
|
||||
}));
|
||||
})
|
||||
]
|
||||
}));
|
||||
}
|
||||
@@ -235,7 +229,7 @@ export class TextFileEditor extends BaseTextEditor {
|
||||
|
||||
private doSaveTextEditorViewState(input: FileEditorInput): void {
|
||||
if (input && !input.isDisposed()) {
|
||||
this.saveTextEditorViewState(input.getResource().toString());
|
||||
this.saveTextEditorViewState(input.getResource());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
|
||||
export interface IOpenFileOptions {
|
||||
editorOptions: IEditorOptions;
|
||||
sideBySide: boolean;
|
||||
element: any;
|
||||
payload: any;
|
||||
}
|
||||
|
||||
export default class FileResultsNavigation extends Disposable {
|
||||
|
||||
private _openFile: Emitter<IOpenFileOptions> = new Emitter<IOpenFileOptions>();
|
||||
public readonly openFile: Event<IOpenFileOptions> = this._openFile.event;
|
||||
|
||||
constructor(private tree: ITree) {
|
||||
super();
|
||||
this._register(this.tree.onDidChangeFocus(e => this.onFocus(e)));
|
||||
this._register(this.tree.onDidChangeSelection(e => this.onSelection(e)));
|
||||
}
|
||||
|
||||
private onFocus(event: any): void {
|
||||
const element = this.tree.getFocus();
|
||||
this.tree.setSelection([element], { fromFocus: true });
|
||||
this._openFile.fire({
|
||||
editorOptions: {
|
||||
preserveFocus: true,
|
||||
pinned: false,
|
||||
revealIfVisible: true
|
||||
},
|
||||
sideBySide: false,
|
||||
element,
|
||||
payload: event.payload
|
||||
});
|
||||
}
|
||||
|
||||
private onSelection({ payload }: any): void {
|
||||
if (payload && payload.fromFocus) {
|
||||
return;
|
||||
}
|
||||
let keyboard = payload && payload.origin === 'keyboard';
|
||||
let originalEvent: KeyboardEvent | MouseEvent = payload && payload.originalEvent;
|
||||
|
||||
let pinned = (payload && payload.origin === 'mouse' && originalEvent && originalEvent.detail === 2);
|
||||
if (pinned && originalEvent) {
|
||||
originalEvent.preventDefault(); // focus moves to editor, we need to prevent default
|
||||
}
|
||||
|
||||
let sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey));
|
||||
let preserveFocus = !((keyboard && (!payload || !payload.preserveFocus)) || pinned || (payload && payload.focusEditor));
|
||||
this._openFile.fire({
|
||||
editorOptions: {
|
||||
preserveFocus,
|
||||
pinned,
|
||||
revealIfVisible: !sideBySide
|
||||
},
|
||||
sideBySide,
|
||||
element: this.tree.getSelection()[0],
|
||||
payload
|
||||
});
|
||||
}
|
||||
}
|
||||
68
src/vs/workbench/parts/files/browser/files.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { FileStat, OpenEditor } from 'vs/workbench/parts/files/common/explorerModel';
|
||||
import { toResource } from 'vs/workbench/common/editor';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { IFileStat } from 'vs/platform/files/common/files';
|
||||
|
||||
// Commands can get exeucted from a command pallete, from a context menu or from some list using a keybinding
|
||||
// To cover all these cases we need to properly compute the resource on which the command is being executed
|
||||
export function getResourceForCommand(resource: URI, listService: IListService, editorService: IWorkbenchEditorService): URI {
|
||||
if (URI.isUri(resource)) {
|
||||
return resource;
|
||||
}
|
||||
|
||||
const list = listService.lastFocusedList;
|
||||
if (list && list.isDOMFocused()) {
|
||||
const focus = list.getFocus();
|
||||
if (focus instanceof FileStat) {
|
||||
return focus.resource;
|
||||
} else if (focus instanceof OpenEditor) {
|
||||
return focus.getResource();
|
||||
}
|
||||
}
|
||||
|
||||
return toResource(editorService.getActiveEditorInput(), { supportSideBySide: true });
|
||||
}
|
||||
|
||||
export function getMultiSelectedResources(resource: URI, listService: IListService, editorService: IWorkbenchEditorService): URI[] {
|
||||
const list = listService.lastFocusedList;
|
||||
if (list && list.isDOMFocused()) {
|
||||
// Explorer
|
||||
if (list instanceof Tree) {
|
||||
const focus: IFileStat = list.getFocus();
|
||||
// If the resource is passed it has to be a part of the returned context.
|
||||
if (focus && (!URI.isUri(resource) || focus.resource.toString() === resource.toString())) {
|
||||
const selection = list.getSelection();
|
||||
// We only respect the selection if it contains the focused element.
|
||||
if (selection && selection.indexOf(focus) >= 0) {
|
||||
return selection.map(fs => fs.resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Open editors view
|
||||
if (list instanceof List) {
|
||||
const focus = list.getFocusedElements();
|
||||
// If the resource is passed it has to be a part of the returned context.
|
||||
if (focus.length && (!URI.isUri(resource) || (focus[0] instanceof OpenEditor && focus[0].getResource().toString() === resource.toString()))) {
|
||||
const selection = list.getSelectedElements();
|
||||
// We only respect the selection if it contains the focused element.
|
||||
if (selection && selection.indexOf(focus[0]) >= 0) {
|
||||
return selection.filter(s => s instanceof OpenEditor).map((oe: OpenEditor) => oe.getResource());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = getResourceForCommand(resource, listService, editorService);
|
||||
return !!result ? [result] : [];
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle';
|
||||
import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { Verbosity } from 'vs/platform/editor/common/editor';
|
||||
import { Verbosity, IRevertOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
|
||||
@@ -214,7 +214,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
||||
return model.isDirty();
|
||||
}
|
||||
|
||||
public confirmSave(): ConfirmResult {
|
||||
public confirmSave(): TPromise<ConfirmResult> {
|
||||
return this.textFileService.confirmSave([this.resource]);
|
||||
}
|
||||
|
||||
@@ -222,8 +222,8 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
||||
return this.textFileService.save(this.resource);
|
||||
}
|
||||
|
||||
public revert(): TPromise<boolean> {
|
||||
return this.textFileService.revert(this.resource);
|
||||
public revert(options?: IRevertOptions): TPromise<boolean> {
|
||||
return this.textFileService.revert(this.resource, options);
|
||||
}
|
||||
|
||||
public getPreferredEditorId(candidates: string[]): string {
|
||||
@@ -240,7 +240,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
||||
// Resolve as text
|
||||
return this.textFileService.models.loadOrCreate(this.resource, { encoding: this.preferredEncoding, reload: refresh }).then(model => {
|
||||
|
||||
// TODO@Ben this is a bit ugly, because we first resolve the model and then resolve a model reference. the reason being that binary
|
||||
// This is a bit ugly, because we first resolve the model and then resolve a model reference. the reason being that binary
|
||||
// or very large files do not resolve to a text file model but should be opened as binary files without text. First calling into
|
||||
// loadOrCreate ensures we are not creating model references for these kind of resources.
|
||||
// In addition we have a bit of payload to take into account (encoding, reload) that the text resolver does not handle yet.
|
||||
|
||||
@@ -7,14 +7,16 @@
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import resources = require('vs/base/common/resources');
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { IFileStat, isParent } from 'vs/platform/files/common/files';
|
||||
import { IFileStat } from 'vs/platform/files/common/files';
|
||||
import { IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IEditorGroup, toResource } from 'vs/workbench/common/editor';
|
||||
import { IEditorGroup, toResource, IEditorIdentifier } from 'vs/workbench/common/editor';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { getPathLabel } from 'vs/base/common/labels';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
export class Model {
|
||||
|
||||
@@ -73,17 +75,17 @@ export class FileStat implements IFileStat {
|
||||
public mtime: number;
|
||||
public etag: string;
|
||||
private _isDirectory: boolean;
|
||||
public hasChildren: boolean;
|
||||
private _isSymbolicLink: boolean;
|
||||
public children: FileStat[];
|
||||
public parent: FileStat;
|
||||
|
||||
public isDirectoryResolved: boolean;
|
||||
|
||||
constructor(resource: URI, public root: FileStat, isDirectory?: boolean, hasChildren?: boolean, name: string = getPathLabel(resource), mtime?: number, etag?: string) {
|
||||
constructor(resource: URI, public root: FileStat, isSymbolicLink?: boolean, isDirectory?: boolean, name: string = getPathLabel(resource), mtime?: number, etag?: string) {
|
||||
this.resource = resource;
|
||||
this.name = name;
|
||||
this.isDirectory = !!isDirectory;
|
||||
this.hasChildren = isDirectory && hasChildren;
|
||||
this._isSymbolicLink = !!isSymbolicLink;
|
||||
this.etag = etag;
|
||||
this.mtime = mtime;
|
||||
|
||||
@@ -94,6 +96,10 @@ export class FileStat implements IFileStat {
|
||||
this.isDirectoryResolved = false;
|
||||
}
|
||||
|
||||
public get isSymbolicLink(): boolean {
|
||||
return this._isSymbolicLink;
|
||||
}
|
||||
|
||||
public get isDirectory(): boolean {
|
||||
return this._isDirectory;
|
||||
}
|
||||
@@ -123,7 +129,7 @@ export class FileStat implements IFileStat {
|
||||
}
|
||||
|
||||
public static create(raw: IFileStat, root: FileStat, resolveTo?: URI[]): FileStat {
|
||||
const stat = new FileStat(raw.resource, root, raw.isDirectory, raw.hasChildren, raw.name, raw.mtime, raw.etag);
|
||||
const stat = new FileStat(raw.resource, root, raw.isSymbolicLink, raw.isDirectory, raw.name, raw.mtime, raw.etag);
|
||||
|
||||
// Recursively add children if present
|
||||
if (stat.isDirectory) {
|
||||
@@ -132,7 +138,7 @@ export class FileStat implements IFileStat {
|
||||
// the folder is fully resolved if either it has a list of children or the client requested this by using the resolveTo
|
||||
// array of resource path to resolve.
|
||||
stat.isDirectoryResolved = !!raw.children || (!!resolveTo && resolveTo.some((r) => {
|
||||
return paths.isEqualOrParent(r.fsPath, stat.resource.fsPath, !isLinux /* ignorecase */);
|
||||
return resources.isEqualOrParent(r, stat.resource, !isLinux /* ignorecase */);
|
||||
}));
|
||||
|
||||
// Recurse into children
|
||||
@@ -141,7 +147,6 @@ export class FileStat implements IFileStat {
|
||||
const child = FileStat.create(raw.children[i], root, resolveTo);
|
||||
child.parent = stat;
|
||||
stat.children.push(child);
|
||||
stat.hasChildren = stat.children.length > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,7 +174,6 @@ export class FileStat implements IFileStat {
|
||||
local.resource = disk.resource;
|
||||
local.name = disk.name;
|
||||
local.isDirectory = disk.isDirectory;
|
||||
local.hasChildren = disk.isDirectory && disk.hasChildren;
|
||||
local.mtime = disk.mtime;
|
||||
local.isDirectoryResolved = disk.isDirectoryResolved;
|
||||
|
||||
@@ -217,7 +221,6 @@ export class FileStat implements IFileStat {
|
||||
child.updateResource(false);
|
||||
|
||||
this.children.push(child);
|
||||
this.hasChildren = this.children.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,8 +233,6 @@ export class FileStat implements IFileStat {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.hasChildren = this.children.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,10 +257,9 @@ export class FileStat implements IFileStat {
|
||||
|
||||
private updateResource(recursive: boolean): void {
|
||||
this.resource = this.parent.resource.with({ path: paths.join(this.parent.resource.path, this.name) });
|
||||
// this.resource = URI.file(paths.join(this.parent.resource.fsPath, this.name));
|
||||
|
||||
if (recursive) {
|
||||
if (this.isDirectory && this.hasChildren && this.children) {
|
||||
if (this.isDirectory && this.children) {
|
||||
this.children.forEach((child: FileStat) => {
|
||||
child.updateResource(true);
|
||||
});
|
||||
@@ -288,23 +288,23 @@ export class FileStat implements IFileStat {
|
||||
public find(resource: URI): FileStat {
|
||||
|
||||
// Return if path found
|
||||
if (paths.isEqual(resource.fsPath, this.resource.fsPath, !isLinux /* ignorecase */)) {
|
||||
if (resources.isEqual(resource, this.resource, !isLinux /* ignorecase */)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Return if not having any children
|
||||
if (!this.hasChildren) {
|
||||
if (!this.children) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
const child = this.children[i];
|
||||
|
||||
if (paths.isEqual(resource.fsPath, child.resource.fsPath, !isLinux /* ignorecase */)) {
|
||||
if (resources.isEqual(resource, child.resource, !isLinux /* ignorecase */)) {
|
||||
return child;
|
||||
}
|
||||
|
||||
if (child.isDirectory && isParent(resource.fsPath, child.resource.fsPath, !isLinux /* ignorecase */)) {
|
||||
if (child.isDirectory && resources.isEqualOrParent(resource, child.resource, !isLinux /* ignorecase */)) {
|
||||
return child.find(resource);
|
||||
}
|
||||
}
|
||||
@@ -335,7 +335,6 @@ export class NewStatPlaceholder extends FileStat {
|
||||
this.isDirectoryResolved = void 0;
|
||||
this.name = void 0;
|
||||
this.isDirectory = void 0;
|
||||
this.hasChildren = void 0;
|
||||
this.mtime = void 0;
|
||||
}
|
||||
|
||||
@@ -374,24 +373,26 @@ export class NewStatPlaceholder extends FileStat {
|
||||
child.parent = parent;
|
||||
parent.children.push(child);
|
||||
|
||||
parent.hasChildren = parent.children.length > 0;
|
||||
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenEditor {
|
||||
export class OpenEditor implements IEditorIdentifier {
|
||||
|
||||
constructor(private editor: IEditorInput, private group: IEditorGroup) {
|
||||
constructor(private _editor: IEditorInput, private _group: IEditorGroup) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public get editorInput() {
|
||||
return this.editor;
|
||||
public get editor() {
|
||||
return this._editor;
|
||||
}
|
||||
|
||||
public get editorGroup() {
|
||||
return this.group;
|
||||
public get editorIndex() {
|
||||
return this._group.indexOf(this.editor);
|
||||
}
|
||||
|
||||
public get group() {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
@@ -403,7 +404,7 @@ export class OpenEditor {
|
||||
}
|
||||
|
||||
public isUntitled(): boolean {
|
||||
return !!toResource(this.editor, { supportSideBySide: true, filter: 'untitled' });
|
||||
return !!toResource(this.editor, { supportSideBySide: true, filter: Schemas.untitled });
|
||||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ITextModelContentProvider } from 'vs/editor/common/services/resolverSer
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { IModel } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IMode } from 'vs/editor/common/modes';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
@@ -44,9 +44,11 @@ const openEditorsVisibleId = 'openEditorsVisible';
|
||||
const openEditorsFocusId = 'openEditorsFocus';
|
||||
const explorerViewletFocusId = 'explorerViewletFocus';
|
||||
const explorerResourceIsFolderId = 'explorerResourceIsFolder';
|
||||
const explorerResourceIsRootId = 'explorerResourceIsRoot';
|
||||
|
||||
export const ExplorerViewletVisibleContext = new RawContextKey<boolean>(explorerViewletVisibleId, true);
|
||||
export const ExplorerFolderContext = new RawContextKey<boolean>(explorerResourceIsFolderId, false);
|
||||
export const ExplorerRootContext = new RawContextKey<boolean>(explorerResourceIsRootId, false);
|
||||
export const FilesExplorerFocusedContext = new RawContextKey<boolean>(filesExplorerFocusId, true);
|
||||
export const OpenEditorsVisibleContext = new RawContextKey<boolean>(openEditorsVisibleId, false);
|
||||
export const OpenEditorsFocusedContext = new RawContextKey<boolean>(openEditorsFocusId, true);
|
||||
@@ -75,7 +77,6 @@ export interface IFilesConfiguration extends IFilesConfiguration, IWorkbenchEdit
|
||||
explorer: {
|
||||
openEditors: {
|
||||
visible: number;
|
||||
dynamicHeight: boolean;
|
||||
};
|
||||
autoReveal: boolean;
|
||||
enableDragAndDrop: boolean;
|
||||
@@ -141,7 +142,7 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider {
|
||||
) {
|
||||
}
|
||||
|
||||
public provideTextContent(resource: URI): TPromise<IModel> {
|
||||
public provideTextContent(resource: URI): TPromise<ITextModel> {
|
||||
const fileOnDiskResource = URI.file(resource.fsPath);
|
||||
|
||||
// Make sure our file from disk is resolved up to date
|
||||
@@ -165,7 +166,7 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider {
|
||||
});
|
||||
}
|
||||
|
||||
private resolveEditorModel(resource: URI, createAsNeeded = true): TPromise<IModel> {
|
||||
private resolveEditorModel(resource: URI, createAsNeeded = true): TPromise<ITextModel> {
|
||||
const fileOnDiskResource = URI.file(resource.fsPath);
|
||||
|
||||
return this.textFileService.resolveTextContent(fileOnDiskResource).then(content => {
|
||||
|
||||