Update to VS Code 1.52.1

This commit is contained in:
Asher
2021-02-09 16:08:37 +00:00
1351 changed files with 56560 additions and 38990 deletions

View File

@@ -12,7 +12,7 @@ import { URI } from 'vs/base/common/uri';
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { DefaultEndOfLine } from 'vs/editor/common/model';
import { hashPath } from 'vs/workbench/services/backup/node/backupFileService';
import { hashPath } from 'vs/workbench/services/backup/electron-browser/backupFileService';
import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-sandbox/backupTracker';
import { workbenchInstantiationService } from 'vs/workbench/test/electron-browser/workbenchTestServices';
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';

View File

@@ -10,7 +10,7 @@ import * as path from 'vs/base/common/path';
import * as pfs from 'vs/base/node/pfs';
import { URI } from 'vs/base/common/uri';
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { hashPath } from 'vs/workbench/services/backup/node/backupFileService';
import { hashPath } from 'vs/workbench/services/backup/electron-browser/backupFileService';
import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-sandbox/backupTracker';
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -33,7 +33,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { HotExitConfiguration } from 'vs/platform/files/common/files';
import { ShutdownReason, ILifecycleService, BeforeShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IFileDialogService, ConfirmResult, IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker';
import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/electron-browser/workbenchTestServices';
@@ -48,6 +48,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices';
import { CancellationToken } from 'vs/base/common/cancellation';
import { timeout } from 'vs/base/common/async';
import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace';
const userdataDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backuprestorer');
const backupHome = path.join(userdataDir, 'Backups');

View File

@@ -4,15 +4,15 @@
*--------------------------------------------------------------------------------------------*/
import { groupBy } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { compare } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { ResourceEdit } from 'vs/editor/browser/services/bulkEditService';
import { WorkspaceEditMetadata } from 'vs/editor/common/modes';
import { IProgress } from 'vs/platform/progress/common/progress';
import { UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo';
import { UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo';
import { ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
export class ResourceNotebookCellEdit extends ResourceEdit {
@@ -29,10 +29,11 @@ export class ResourceNotebookCellEdit extends ResourceEdit {
export class BulkCellEdits {
constructor(
private _undoRedoGroup: UndoRedoGroup,
private readonly _undoRedoGroup: UndoRedoGroup,
undoRedoSource: UndoRedoSource | undefined,
private readonly _progress: IProgress<void>,
private readonly _token: CancellationToken,
private readonly _edits: ResourceNotebookCellEdit[],
@INotebookService private readonly _notebookService: INotebookService,
@INotebookEditorModelResolverService private readonly _notebookModelService: INotebookEditorModelResolverService,
) { }
@@ -41,6 +42,9 @@ export class BulkCellEdits {
const editsByNotebook = groupBy(this._edits, (a, b) => compare(a.resource.toString(), b.resource.toString()));
for (let group of editsByNotebook) {
if (this._token.isCancellationRequested) {
break;
}
const [first] = group;
const ref = await this._notebookModelService.resolve(first.resource);
@@ -52,7 +56,6 @@ export class BulkCellEdits {
// apply edits
const edits = group.map(entry => entry.cellEdit);
this._notebookService.transformEditsOutputs(ref.object.notebook, edits);
ref.object.notebook.applyEdits(ref.object.notebook.versionId, edits, true, undefined, () => undefined, this._undoRedoGroup);
ref.dispose();

View File

@@ -16,7 +16,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { BulkTextEdits } from 'vs/workbench/contrib/bulkEdit/browser/bulkTextEdits';
import { BulkFileEdits } from 'vs/workbench/contrib/bulkEdit/browser/bulkFileEdits';
import { BulkCellEdits, ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkCellEdits';
import { UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo';
import { UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo';
import { LinkedList } from 'vs/base/common/linkedList';
import { CancellationToken } from 'vs/base/common/cancellation';
class BulkEdit {
@@ -24,7 +26,10 @@ class BulkEdit {
private readonly _label: string | undefined,
private readonly _editor: ICodeEditor | undefined,
private readonly _progress: IProgress<IProgressStep>,
private readonly _token: CancellationToken,
private readonly _edits: ResourceEdit[],
private readonly _undoRedoGroup: UndoRedoGroup,
private readonly _undoRedoSource: UndoRedoSource | undefined,
@IInstantiationService private readonly _instaService: IInstantiationService,
@ILogService private readonly _logService: ILogService,
) {
@@ -58,20 +63,24 @@ class BulkEdit {
}
}
this._progress.report({ total: this._edits.length });
const progress: IProgress<void> = { report: _ => this._progress.report({ increment: 1 }) };
const undoRedoGroup = new UndoRedoGroup();
// Show infinte progress when there is only 1 item since we do not know how long it takes
const increment = this._edits.length > 1 ? 0 : undefined;
this._progress.report({ increment, total: 100 });
// Increment by percentage points since progress API expects that
const progress: IProgress<void> = { report: _ => this._progress.report({ increment: 100 / this._edits.length }) };
let index = 0;
for (let range of ranges) {
if (this._token.isCancellationRequested) {
break;
}
const group = this._edits.slice(index, index + range);
if (group[0] instanceof ResourceFileEdit) {
await this._performFileEdits(<ResourceFileEdit[]>group, undoRedoGroup, progress);
await this._performFileEdits(<ResourceFileEdit[]>group, this._undoRedoGroup, this._undoRedoSource, progress);
} else if (group[0] instanceof ResourceTextEdit) {
await this._performTextEdits(<ResourceTextEdit[]>group, undoRedoGroup, progress);
await this._performTextEdits(<ResourceTextEdit[]>group, this._undoRedoGroup, this._undoRedoSource, progress);
} else if (group[0] instanceof ResourceNotebookCellEdit) {
await this._performCellEdits(<ResourceNotebookCellEdit[]>group, undoRedoGroup, progress);
await this._performCellEdits(<ResourceNotebookCellEdit[]>group, this._undoRedoGroup, this._undoRedoSource, progress);
} else {
console.log('UNKNOWN EDIT');
}
@@ -79,21 +88,21 @@ class BulkEdit {
}
}
private async _performFileEdits(edits: ResourceFileEdit[], undoRedoGroup: UndoRedoGroup, progress: IProgress<void>) {
private async _performFileEdits(edits: ResourceFileEdit[], undoRedoGroup: UndoRedoGroup, undoRedoSource: UndoRedoSource | undefined, progress: IProgress<void>) {
this._logService.debug('_performFileEdits', JSON.stringify(edits));
const model = this._instaService.createInstance(BulkFileEdits, this._label || localize('workspaceEdit', "Workspace Edit"), undoRedoGroup, progress, edits);
const model = this._instaService.createInstance(BulkFileEdits, this._label || localize('workspaceEdit', "Workspace Edit"), undoRedoGroup, undoRedoSource, progress, this._token, edits);
await model.apply();
}
private async _performTextEdits(edits: ResourceTextEdit[], undoRedoGroup: UndoRedoGroup, progress: IProgress<void>): Promise<void> {
private async _performTextEdits(edits: ResourceTextEdit[], undoRedoGroup: UndoRedoGroup, undoRedoSource: UndoRedoSource | undefined, progress: IProgress<void>): Promise<void> {
this._logService.debug('_performTextEdits', JSON.stringify(edits));
const model = this._instaService.createInstance(BulkTextEdits, this._label || localize('workspaceEdit', "Workspace Edit"), this._editor, undoRedoGroup, progress, edits);
const model = this._instaService.createInstance(BulkTextEdits, this._label || localize('workspaceEdit', "Workspace Edit"), this._editor, undoRedoGroup, undoRedoSource, progress, this._token, edits);
await model.apply();
}
private async _performCellEdits(edits: ResourceNotebookCellEdit[], undoRedoGroup: UndoRedoGroup, progress: IProgress<void>): Promise<void> {
private async _performCellEdits(edits: ResourceNotebookCellEdit[], undoRedoGroup: UndoRedoGroup, undoRedoSource: UndoRedoSource | undefined, progress: IProgress<void>): Promise<void> {
this._logService.debug('_performCellEdits', JSON.stringify(edits));
const model = this._instaService.createInstance(BulkCellEdits, undoRedoGroup, progress, edits);
const model = this._instaService.createInstance(BulkCellEdits, undoRedoGroup, undoRedoSource, progress, this._token, edits);
await model.apply();
}
}
@@ -102,6 +111,7 @@ export class BulkEditService implements IBulkEditService {
declare readonly _serviceBrand: undefined;
private readonly _activeUndoRedoGroups = new LinkedList<UndoRedoGroup>();
private _previewHandler?: IBulkEditPreviewHandler;
constructor(
@@ -129,7 +139,7 @@ export class BulkEditService implements IBulkEditService {
return { ariaSummary: localize('nothing', "Made no edits") };
}
if (this._previewHandler && (options?.showPreview || edits.some(value => value.metadata?.needsConfirmation))) {
if (this._previewHandler && !options?.suppressPreview && (options?.showPreview || edits.some(value => value.metadata?.needsConfirmation))) {
edits = await this._previewHandler(edits, options);
}
@@ -147,11 +157,33 @@ export class BulkEditService implements IBulkEditService {
codeEditor = undefined;
}
// undo-redo-group: if a group id is passed then try to find it
// in the list of active edits. otherwise (or when not found)
// create a separate undo-redo-group
let undoRedoGroup: UndoRedoGroup | undefined;
let undoRedoGroupRemove = () => { };
if (typeof options?.undoRedoGroupId === 'number') {
for (let candidate of this._activeUndoRedoGroups) {
if (candidate.id === options.undoRedoGroupId) {
undoRedoGroup = candidate;
break;
}
}
}
if (!undoRedoGroup) {
undoRedoGroup = new UndoRedoGroup();
undoRedoGroupRemove = this._activeUndoRedoGroups.push(undoRedoGroup);
}
const bulkEdit = this._instaService.createInstance(
BulkEdit,
options?.quotableLabel || options?.label,
codeEditor, options?.progress ?? Progress.None,
edits
codeEditor,
options?.progress ?? Progress.None,
options?.token ?? CancellationToken.None,
edits,
undoRedoGroup,
options?.undoRedoSource
);
try {
@@ -162,6 +194,8 @@ export class BulkEditService implements IBulkEditService {
// console.log(err);
this._logService.error(err);
throw err;
} finally {
undoRedoGroupRemove();
}
}
}

View File

@@ -5,21 +5,27 @@
import { WorkspaceFileEditOptions } from 'vs/editor/common/modes';
import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
import { IFileService, FileSystemProviderCapabilities, IFileContent } from 'vs/platform/files/common/files';
import { IProgress } from 'vs/platform/progress/common/progress';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
import { IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoService, UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo';
import { IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoService, UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo';
import { URI } from 'vs/base/common/uri';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { VSBuffer } from 'vs/base/common/buffer';
import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService';
import * as resources from 'vs/base/common/resources';
import { CancellationToken } from 'vs/base/common/cancellation';
interface IFileOperationUndoRedoInfo {
undoRedoGroupId?: number;
isUndoing?: boolean;
}
interface IFileOperation {
uris: URI[];
perform(): Promise<IFileOperation>;
perform(token: CancellationToken): Promise<IFileOperation>;
}
class Noop implements IFileOperation {
@@ -36,6 +42,7 @@ class RenameOperation implements IFileOperation {
readonly newUri: URI,
readonly oldUri: URI,
readonly options: WorkspaceFileEditOptions,
readonly undoRedoInfo: IFileOperationUndoRedoInfo,
@IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService,
@IFileService private readonly _fileService: IFileService,
) { }
@@ -44,14 +51,14 @@ class RenameOperation implements IFileOperation {
return [this.newUri, this.oldUri];
}
async perform(): Promise<IFileOperation> {
async perform(token: CancellationToken): Promise<IFileOperation> {
// rename
if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) {
return new Noop(); // not overwriting, but ignoring, and the target file exists
}
await this._workingCopyFileService.move([{ source: this.oldUri, target: this.newUri }], { overwrite: this.options.overwrite });
return new RenameOperation(this.oldUri, this.newUri, this.options, this._workingCopyFileService, this._fileService);
await this._workingCopyFileService.move([{ source: this.oldUri, target: this.newUri }], { overwrite: this.options.overwrite, ...this.undoRedoInfo }, token);
return new RenameOperation(this.oldUri, this.newUri, this.options, { isUndoing: true }, this._workingCopyFileService, this._fileService);
}
toString(): string {
@@ -64,11 +71,44 @@ class RenameOperation implements IFileOperation {
}
}
class CopyOperation implements IFileOperation {
constructor(
readonly newUri: URI,
readonly oldUri: URI,
readonly options: WorkspaceFileEditOptions,
readonly undoRedoInfo: IFileOperationUndoRedoInfo,
@IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService,
@IFileService private readonly _fileService: IFileService,
@IInstantiationService private readonly _instaService: IInstantiationService
) { }
get uris() {
return [this.newUri, this.oldUri];
}
async perform(token: CancellationToken): Promise<IFileOperation> {
// copy
if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) {
return new Noop(); // not overwriting, but ignoring, and the target file exists
}
const stat = await this._workingCopyFileService.copy([{ source: this.oldUri, target: this.newUri }], { overwrite: this.options.overwrite, ...this.undoRedoInfo }, token);
const folder = this.options.folder || (stat.length === 1 && stat[0].isDirectory);
return this._instaService.createInstance(DeleteOperation, this.newUri, { recursive: true, folder, ...this.options }, { isUndoing: true }, false);
}
toString(): string {
return `(copy ${this.oldUri} to ${this.newUri})`;
}
}
class CreateOperation implements IFileOperation {
constructor(
readonly newUri: URI,
readonly options: WorkspaceFileEditOptions,
readonly undoRedoInfo: IFileOperationUndoRedoInfo,
readonly contents: VSBuffer | undefined,
@IFileService private readonly _fileService: IFileService,
@IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService,
@@ -79,17 +119,22 @@ class CreateOperation implements IFileOperation {
return [this.newUri];
}
async perform(): Promise<IFileOperation> {
async perform(token: CancellationToken): Promise<IFileOperation> {
// create file
if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) {
return new Noop(); // not overwriting, but ignoring, and the target file exists
}
await this._workingCopyFileService.create(this.newUri, this.contents, { overwrite: this.options.overwrite });
return this._instaService.createInstance(DeleteOperation, this.newUri, this.options, true);
if (this.options.folder) {
await this._workingCopyFileService.createFolder(this.newUri, { ...this.undoRedoInfo }, token);
} else {
await this._workingCopyFileService.create(this.newUri, this.contents, { overwrite: this.options.overwrite, ...this.undoRedoInfo }, token);
}
return this._instaService.createInstance(DeleteOperation, this.newUri, this.options, { isUndoing: true }, !this.options.folder && !this.contents);
}
toString(): string {
return `(create ${resources.basename(this.newUri)} with ${this.contents?.byteLength || 0} bytes)`;
return this.options.folder ? `create ${resources.basename(this.newUri)} folder`
: `(create ${resources.basename(this.newUri)} with ${this.contents?.byteLength || 0} bytes)`;
}
}
@@ -98,6 +143,7 @@ class DeleteOperation implements IFileOperation {
constructor(
readonly oldUri: URI,
readonly options: WorkspaceFileEditOptions,
readonly undoRedoInfo: IFileOperationUndoRedoInfo,
private readonly _undoesCreateOperation: boolean,
@IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService,
@IFileService private readonly _fileService: IFileService,
@@ -110,7 +156,7 @@ class DeleteOperation implements IFileOperation {
return [this.oldUri];
}
async perform(): Promise<IFileOperation> {
async perform(token: CancellationToken): Promise<IFileOperation> {
// delete file
if (!await this._fileService.exists(this.oldUri)) {
if (!this.options.ignoreIfNotExists) {
@@ -119,18 +165,22 @@ class DeleteOperation implements IFileOperation {
return new Noop();
}
let contents: VSBuffer | undefined;
if (!this._undoesCreateOperation) {
let fileContent: IFileContent | undefined;
if (!this._undoesCreateOperation && !this.options.folder) {
try {
contents = (await this._fileService.readFile(this.oldUri)).value;
fileContent = await this._fileService.readFile(this.oldUri);
} catch (err) {
this._logService.critical(err);
}
}
const useTrash = this._fileService.hasCapability(this.oldUri, FileSystemProviderCapabilities.Trash) && this._configurationService.getValue<boolean>('files.enableTrash');
await this._workingCopyFileService.delete([this.oldUri], { useTrash, recursive: this.options.recursive });
return this._instaService.createInstance(CreateOperation, this.oldUri, this.options, contents);
const useTrash = !this.options.skipTrashBin && this._fileService.hasCapability(this.oldUri, FileSystemProviderCapabilities.Trash) && this._configurationService.getValue<boolean>('files.enableTrash');
await this._workingCopyFileService.delete([this.oldUri], { useTrash, recursive: this.options.recursive, ...this.undoRedoInfo }, token);
if (typeof this.options.maxSize === 'number' && fileContent && (fileContent?.size > this.options.maxSize)) {
return new Noop();
}
return this._instaService.createInstance(CreateOperation, this.oldUri, this.options, { isUndoing: true }, fileContent?.value);
}
toString(): string {
@@ -162,7 +212,7 @@ class FileUndoRedoElement implements IWorkspaceUndoRedoElement {
private async _reverse() {
for (let i = 0; i < this.operations.length; i++) {
const op = this.operations[i];
const undo = await op.perform();
const undo = await op.perform(CancellationToken.None);
this.operations[i] = undo;
}
}
@@ -177,7 +227,9 @@ export class BulkFileEdits {
constructor(
private readonly _label: string,
private readonly _undoRedoGroup: UndoRedoGroup,
private readonly _undoRedoSource: UndoRedoSource | undefined,
private readonly _progress: IProgress<void>,
private readonly _token: CancellationToken,
private readonly _edits: ResourceFileEdit[],
@IInstantiationService private readonly _instaService: IInstantiationService,
@IUndoRedoService private readonly _undoRedoService: IUndoRedoService,
@@ -185,27 +237,35 @@ export class BulkFileEdits {
async apply(): Promise<void> {
const undoOperations: IFileOperation[] = [];
const undoRedoInfo = { undoRedoGroupId: this._undoRedoGroup.id };
for (const edit of this._edits) {
this._progress.report(undefined);
if (this._token.isCancellationRequested) {
break;
}
const options = edit.options || {};
let op: IFileOperation | undefined;
if (edit.newResource && edit.oldResource) {
if (edit.newResource && edit.oldResource && !options.copy) {
// rename
op = this._instaService.createInstance(RenameOperation, edit.newResource, edit.oldResource, options);
op = this._instaService.createInstance(RenameOperation, edit.newResource, edit.oldResource, options, undoRedoInfo);
} else if (edit.newResource && edit.oldResource && options.copy) {
op = this._instaService.createInstance(CopyOperation, edit.newResource, edit.oldResource, options, undoRedoInfo);
} else if (!edit.newResource && edit.oldResource) {
// delete file
op = this._instaService.createInstance(DeleteOperation, edit.oldResource, options, false);
op = this._instaService.createInstance(DeleteOperation, edit.oldResource, options, undoRedoInfo, false);
} else if (edit.newResource && !edit.oldResource) {
// create file
op = this._instaService.createInstance(CreateOperation, edit.newResource, options, undefined);
op = this._instaService.createInstance(CreateOperation, edit.newResource, options, undoRedoInfo, undefined);
}
if (op) {
const undoOp = await op.perform();
const undoOp = await op.perform(this._token);
undoOperations.push(undoOp);
}
this._progress.report(undefined);
}
this._undoRedoService.pushElement(new FileUndoRedoElement(this._label, undoOperations), this._undoRedoGroup);
this._undoRedoService.pushElement(new FileUndoRedoElement(this._label, undoOperations), this._undoRedoGroup, this._undoRedoSource);
}
}

View File

@@ -14,11 +14,12 @@ import { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'v
import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
import { IProgress } from 'vs/platform/progress/common/progress';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { IUndoRedoService, UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo';
import { IUndoRedoService, UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo';
import { SingleModelEditStackElement, MultiModelEditStackElement } from 'vs/editor/common/model/editStack';
import { ResourceMap } from 'vs/base/common/map';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService';
import { CancellationToken } from 'vs/base/common/cancellation';
type ValidationResult = { canApply: true } | { canApply: false, reason: URI };
@@ -39,6 +40,18 @@ class ModelEditTask implements IDisposable {
this._modelReference.dispose();
}
isNoOp() {
if (this._edits.length > 0) {
// contains textual edits
return false;
}
if (this._newEol !== undefined && this._newEol !== this.model.getEndOfLineSequence()) {
// contains an eol change that is a real change
return false;
}
return true;
}
addEdit(resourceEdit: ResourceTextEdit): void {
this._expectedModelVersionId = resourceEdit.versionId;
const { textEdit } = resourceEdit;
@@ -122,7 +135,9 @@ export class BulkTextEdits {
private readonly _label: string,
private readonly _editor: ICodeEditor | undefined,
private readonly _undoRedoGroup: UndoRedoGroup,
private readonly _undoRedoSource: UndoRedoSource | undefined,
private readonly _progress: IProgress<void>,
private readonly _token: CancellationToken,
edits: ResourceTextEdit[],
@IEditorWorkerService private readonly _editorWorker: IEditorWorkerService,
@IModelService private readonly _modelService: IModelService,
@@ -210,6 +225,9 @@ export class BulkTextEdits {
this._validateBeforePrepare();
const tasks = await this._createEditsTasks();
if (this._token.isCancellationRequested) {
return;
}
try {
const validation = this._validateTasks(tasks);
@@ -219,10 +237,12 @@ export class BulkTextEdits {
if (tasks.length === 1) {
// This edit touches a single model => keep things simple
const task = tasks[0];
const singleModelEditStackElement = new SingleModelEditStackElement(task.model, task.getBeforeCursorState());
this._undoRedoService.pushElement(singleModelEditStackElement, this._undoRedoGroup);
task.apply();
singleModelEditStackElement.close();
if (!task.isNoOp()) {
const singleModelEditStackElement = new SingleModelEditStackElement(task.model, task.getBeforeCursorState());
this._undoRedoService.pushElement(singleModelEditStackElement, this._undoRedoGroup, this._undoRedoSource);
task.apply();
singleModelEditStackElement.close();
}
this._progress.report(undefined);
} else {
// prepare multi model undo element
@@ -230,7 +250,7 @@ export class BulkTextEdits {
this._label,
tasks.map(t => new SingleModelEditStackElement(t.model, t.getBeforeCursorState()))
);
this._undoRedoService.pushElement(multiModelEditStackElement, this._undoRedoGroup);
this._undoRedoService.pushElement(multiModelEditStackElement, this._undoRedoGroup, this._undoRedoSource);
for (const task of tasks) {
task.apply();
this._progress.report(undefined);

View File

@@ -28,6 +28,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { Codicon } from 'vs/base/common/codicons';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
async function getBulkEditPane(viewsService: IViewsService): Promise<BulkEditPane | undefined> {
const view = await viewsService.openView(BulkEditPane.ID, true);
@@ -169,7 +170,7 @@ registerAction2(class ApplyAction extends Action2 {
id: 'refactorPreview.apply',
title: { value: localize('apply', "Apply Refactoring"), original: 'Apply Refactoring' },
category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' },
icon: { id: 'codicon/check' },
icon: Codicon.check,
precondition: ContextKeyExpr.and(BulkEditPreviewContribution.ctxEnabled, BulkEditPane.ctxHasCheckedChanges),
menu: [{
id: MenuId.BulkEditTitle,
@@ -203,7 +204,7 @@ registerAction2(class DiscardAction extends Action2 {
id: 'refactorPreview.discard',
title: { value: localize('Discard', "Discard Refactoring"), original: 'Discard Refactoring' },
category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' },
icon: { id: 'codicon/clear-all' },
icon: Codicon.clearAll,
precondition: BulkEditPreviewContribution.ctxEnabled,
menu: [{
id: MenuId.BulkEditTitle,
@@ -264,7 +265,7 @@ registerAction2(class GroupByFile extends Action2 {
id: 'refactorPreview.groupByFile',
title: { value: localize('groupByFile', "Group Changes By File"), original: 'Group Changes By File' },
category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' },
icon: { id: 'codicon/ungroup-by-ref-type' },
icon: Codicon.ungroupByRefType,
precondition: ContextKeyExpr.and(BulkEditPane.ctxHasCategories, BulkEditPane.ctxGroupByFile.negate(), BulkEditPreviewContribution.ctxEnabled),
menu: [{
id: MenuId.BulkEditTitle,
@@ -291,7 +292,7 @@ registerAction2(class GroupByType extends Action2 {
id: 'refactorPreview.groupByType',
title: { value: localize('groupByType', "Group Changes By Type"), original: 'Group Changes By Type' },
category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' },
icon: { id: 'codicon/group-by-ref-type' },
icon: Codicon.groupByRefType,
precondition: ContextKeyExpr.and(BulkEditPane.ctxHasCategories, BulkEditPane.ctxGroupByFile, BulkEditPreviewContribution.ctxEnabled),
menu: [{
id: MenuId.BulkEditTitle,
@@ -318,7 +319,7 @@ registerAction2(class ToggleGrouping extends Action2 {
id: 'refactorPreview.toggleGrouping',
title: { value: localize('groupByType', "Group Changes By Type"), original: 'Group Changes By Type' },
category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' },
icon: { id: 'codicon/list-tree' },
icon: Codicon.listTree,
toggled: BulkEditPane.ctxGroupByFile.negate(),
precondition: ContextKeyExpr.and(BulkEditPane.ctxHasCategories, BulkEditPreviewContribution.ctxEnabled),
menu: [{
@@ -341,6 +342,8 @@ Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).regi
BulkEditPreviewContribution, LifecyclePhase.Ready
);
const refactorPreviewViewIcon = registerIcon('refactor-preview-view-icon', Codicon.lightbulb, localize('refactorPreviewViewIcon', 'View icon of the refactor preview view.'));
const container = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({
id: BulkEditPane.ID,
name: localize('panel', "Refactor Preview"),
@@ -349,7 +352,7 @@ const container = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.V
ViewPaneContainer,
[BulkEditPane.ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]
),
icon: Codicon.lightbulb.classNames,
icon: refactorPreviewViewIcon,
storageId: BulkEditPane.ID
}, ViewContainerLocation.Panel);
@@ -358,5 +361,5 @@ Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).registerViews
name: localize('panel', "Refactor Preview"),
when: BulkEditPreviewContribution.ctxEnabled,
ctorDescriptor: new SyncDescriptor(BulkEditPane),
containerIcon: Codicon.lightbulb.classNames,
containerIcon: refactorPreviewViewIcon,
}], container);

View File

@@ -12,7 +12,7 @@ import { registerThemingParticipant, IColorTheme, ICssStyleCollector, IThemeServ
import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistry';
import { localize } from 'vs/nls';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { BulkEditPreviewProvider, BulkFileOperations, BulkFileOperationType } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview';
import { ILabelService } from 'vs/platform/label/common/label';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
@@ -26,7 +26,7 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl
import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { basename } from 'vs/base/common/resources';
import { basename, dirname } from 'vs/base/common/resources';
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { IAction } from 'vs/base/common/actions';
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
@@ -34,7 +34,7 @@ import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import type { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -128,7 +128,7 @@ export class BulkEditPane extends ViewPane {
this._tree = <WorkbenchAsyncDataTree<BulkFileOperations, BulkEditElement, FuzzyScore>>this._instaService.createInstance(
WorkbenchAsyncDataTree, this.id, treeContainer,
new BulkEditDelegate(),
[new TextEditElementRenderer(), this._instaService.createInstance(FileElementRenderer, resourceLabels), new CategoryElementRenderer()],
[this._instaService.createInstance(TextEditElementRenderer), this._instaService.createInstance(FileElementRenderer, resourceLabels), this._instaService.createInstance(CategoryElementRenderer)],
this._treeDataSource,
{
accessibilityProvider: this._instaService.createInstance(BulkEditAccessibilityProvider),
@@ -293,12 +293,12 @@ export class BulkEditPane extends ViewPane {
this._setTreeInput(input);
// (3) remember preference
this._storageService.store(BulkEditPane._memGroupByFile, this._treeDataSource.groupByFile, StorageScope.GLOBAL);
this._storageService.store(BulkEditPane._memGroupByFile, this._treeDataSource.groupByFile, StorageScope.GLOBAL, StorageTarget.USER);
this._ctxGroupByFile.set(this._treeDataSource.groupByFile);
}
}
private async _openElementAsEditor(e: IOpenEvent<BulkEditElement | null>): Promise<void> {
private async _openElementAsEditor(e: IOpenEvent<BulkEditElement | undefined>): Promise<void> {
type Mutable<T> = {
-readonly [P in keyof T]: T[P]
};
@@ -356,8 +356,9 @@ export class BulkEditPane extends ViewPane {
leftResource,
rightResource: previewUri,
label,
description: this._labelService.getUriLabel(dirname(leftResource), { relative: true }),
options
});
}, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
}
}

View File

@@ -22,6 +22,7 @@ import { ResourceMap } from 'vs/base/common/map';
import { localize } from 'vs/nls';
import { extUri } from 'vs/base/common/resources';
import { ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService';
import { Codicon } from 'vs/base/common/codicons';
export class CheckedStates<T extends object> {
@@ -116,7 +117,7 @@ export class BulkCategory {
private static readonly _defaultMetadata = Object.freeze({
label: localize('default', "Other"),
icon: { id: 'codicon/symbol-file' },
icon: Codicon.symbolFile,
needsConfirmation: false
});

View File

@@ -21,7 +21,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
import type { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { basename } from 'vs/base/common/resources';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { compare } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
@@ -386,6 +386,8 @@ export class CategoryElementRenderer implements ITreeRenderer<CategoryElement, F
readonly templateId: string = CategoryElementRenderer.id;
constructor(@IThemeService private readonly _themeService: IThemeService) { }
renderTemplate(container: HTMLElement): CategoryElementTemplate {
return new CategoryElementTemplate(container);
}
@@ -394,12 +396,15 @@ export class CategoryElementRenderer implements ITreeRenderer<CategoryElement, F
template.icon.style.setProperty('--background-dark', null);
template.icon.style.setProperty('--background-light', null);
template.icon.style.color = '';
const { metadata } = node.element.category;
if (ThemeIcon.isThemeIcon(metadata.iconPath)) {
// css
const className = ThemeIcon.asClassName(metadata.iconPath);
template.icon.className = className ? `theme-icon ${className}` : '';
template.icon.style.color = metadata.iconPath.color ? this._themeService.getColorTheme().getColor(metadata.iconPath.color.id)?.toString() ?? '' : '';
} else if (URI.isUri(metadata.iconPath)) {
// background-image
@@ -532,7 +537,7 @@ class TextEditElementTemplate {
private readonly _icon: HTMLDivElement;
private readonly _label: HighlightedLabel;
constructor(container: HTMLElement) {
constructor(container: HTMLElement, @IThemeService private readonly _themeService: IThemeService) {
container.classList.add('textedit');
this._checkbox = document.createElement('input');
@@ -597,6 +602,8 @@ class TextEditElementTemplate {
// css
const className = ThemeIcon.asClassName(iconPath);
this._icon.className = className ? `theme-icon ${className}` : '';
this._icon.style.color = iconPath.color ? this._themeService.getColorTheme().getColor(iconPath.color.id)?.toString() ?? '' : '';
} else if (URI.isUri(iconPath)) {
// background-image
@@ -623,8 +630,10 @@ export class TextEditElementRenderer implements ITreeRenderer<TextEditElement, F
readonly templateId: string = TextEditElementRenderer.id;
constructor(@IThemeService private readonly _themeService: IThemeService) { }
renderTemplate(container: HTMLElement): TextEditElementTemplate {
return new TextEditElementTemplate(container);
return new TextEditElementTemplate(container, this._themeService);
}
renderElement({ element }: ITreeNode<TextEditElement, FuzzyScore>, _index: number, template: TextEditElementTemplate): void {

View File

@@ -18,12 +18,13 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { PeekContext } from 'vs/editor/contrib/peekView/peekView';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { Range } from 'vs/editor/common/core/range';
import { IPosition } from 'vs/editor/common/core/position';
import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { registerIcon, Codicon } from 'vs/base/common/codicons';
import { Codicon } from 'vs/base/common/codicons';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
const _ctxHasCallHierarchyProvider = new RawContextKey<boolean>('editorHasCallHierarchyProvider', false);
const _ctxCallHierarchyVisible = new RawContextKey<boolean>('callHierarchyVisible', false);
@@ -128,7 +129,7 @@ class CallHierarchyController implements IEditorContribution {
this._widget.showLoading();
this._sessionDisposables.add(this._widget.onDidClose(() => {
this.endCallHierarchy();
this._storageService.store(CallHierarchyController._StorageDirection, this._widget!.direction, StorageScope.GLOBAL);
this._storageService.store(CallHierarchyController._StorageDirection, this._widget!.direction, StorageScope.GLOBAL, StorageTarget.USER);
}));
this._sessionDisposables.add({ dispose() { cts.dispose(true); } });
this._sessionDisposables.add(this._widget);
@@ -207,7 +208,7 @@ registerAction2(class extends EditorAction2 {
super({
id: 'editor.showIncomingCalls',
title: { value: localize('title.incoming', "Show Incoming Calls"), original: 'Show Incoming Calls' },
icon: registerIcon('callhierarchy-incoming', Codicon.callIncoming),
icon: registerIcon('callhierarchy-incoming', Codicon.callIncoming, localize('showIncomingCallsIcons', 'Icon for incoming calls in the call hierarchy view.')),
precondition: ContextKeyExpr.and(_ctxCallHierarchyVisible, _ctxCallHierarchyDirection.isEqualTo(CallHierarchyDirection.CallsFrom)),
keybinding: {
weight: KeybindingWeight.WorkbenchContrib,
@@ -232,7 +233,7 @@ registerAction2(class extends EditorAction2 {
super({
id: 'editor.showOutgoingCalls',
title: { value: localize('title.outgoing', "Show Outgoing Calls"), original: 'Show Outgoing Calls' },
icon: registerIcon('callhierarchy-outgoing', Codicon.callOutgoing),
icon: registerIcon('callhierarchy-outgoing', Codicon.callOutgoing, localize('showOutgoingCallsIcon', 'Icon for outgoing calls in the call hierarchy view.')),
precondition: ContextKeyExpr.and(_ctxCallHierarchyVisible, _ctxCallHierarchyDirection.isEqualTo(CallHierarchyDirection.CallsTo)),
keybinding: {
weight: KeybindingWeight.WorkbenchContrib,

View File

@@ -28,7 +28,7 @@ import { registerThemingParticipant, themeColorFromId, IThemeService, IColorThem
import { IPosition } from 'vs/editor/common/core/position';
import { IAction } from 'vs/base/common/actions';
import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { Color } from 'vs/base/common/color';
import { TreeMouseEventTarget, ITreeNode } from 'vs/base/browser/ui/tree/tree';
import { URI } from 'vs/base/common/uri';
@@ -45,7 +45,7 @@ const enum State {
class LayoutInfo {
static store(info: LayoutInfo, storageService: IStorageService): void {
storageService.store('callHierarchyPeekLayout', JSON.stringify(info), StorageScope.GLOBAL);
storageService.store('callHierarchyPeekLayout', JSON.stringify(info), StorageScope.GLOBAL, StorageTarget.MACHINE);
}
static retrieve(storageService: IStorageService): LayoutInfo {
@@ -277,7 +277,7 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget {
this.dispose();
this._editorService.openEditor({
resource: e.element.item.uri,
options: { selection: e.element.item.selectionRange }
options: { selection: e.element.item.selectionRange, pinned: true }
});
}
}));
@@ -289,7 +289,7 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget {
this.dispose();
this._editorService.openEditor({
resource: element.item.uri,
options: { selection: element.item.selectionRange }
options: { selection: element.item.selectionRange, pinned: true }
});
}
}));

View File

@@ -8,12 +8,8 @@ import * as path from 'vs/base/common/path';
import * as cp from 'child_process';
import * as pfs from 'vs/base/node/pfs';
import * as extpath from 'vs/base/node/extpath';
import * as platform from 'vs/base/common/platform';
import { promisify } from 'util';
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 { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import product from 'vs/platform/product/common/product';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
@@ -21,6 +17,9 @@ import Severity from 'vs/base/common/severity';
import { ILogService } from 'vs/platform/log/common/log';
import { FileAccess } from 'vs/base/common/network';
import { IProductService } from 'vs/platform/product/common/productService';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IsMacNativeContext } from 'vs/platform/contextkey/common/contextkeys';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
function ignore<T>(code: string, value: T): (err: any) => Promise<T> {
return err => err.code === code ? Promise.resolve<T>(value) : Promise.reject<T>(err);
@@ -39,45 +38,65 @@ function isAvailable(): Promise<boolean> {
return Promise.resolve(pfs.exists(getSource()));
}
class InstallAction extends Action {
const category = nls.localize('shellCommand', "Shell Command");
static readonly ID = 'workbench.action.installCommandLine';
static readonly LABEL = nls.localize('install', "Install '{0}' command in PATH", product.applicationName);
class InstallAction extends Action2 {
constructor(
id: string,
label: string,
@INotificationService private readonly notificationService: INotificationService,
@IDialogService private readonly dialogService: IDialogService,
@ILogService private readonly logService: ILogService,
@IProductService private readonly productService: IProductService
) {
super(id, label);
constructor() {
super({
id: 'workbench.action.installCommandLine',
title: {
value: nls.localize('install', "Install '{0}' command in PATH", product.applicationName),
original: `Shell Command: Install \'${product.applicationName}\' command in PATH`
},
category,
f1: true,
precondition: ContextKeyExpr.and(IsMacNativeContext, ContextKeyExpr.equals('remoteName', ''))
});
}
private get target(): string {
return `/usr/local/bin/${this.productService.applicationName}`;
}
run(accessor: ServicesAccessor): Promise<void> {
const productService = accessor.get(IProductService);
const notificationService = accessor.get(INotificationService);
const logService = accessor.get(ILogService);
const dialogService = accessor.get(IDialogService);
const target = `/usr/local/bin/${productService.applicationName}`;
run(): Promise<void> {
return isAvailable().then(isAvailable => {
if (!isAvailable) {
const message = nls.localize('not available', "This command is not available");
this.notificationService.info(message);
notificationService.info(message);
return undefined;
}
return this.isInstalled()
return this.isInstalled(target)
.then(isInstalled => {
if (!isAvailable || isInstalled) {
return Promise.resolve(null);
} else {
return pfs.unlink(this.target)
return pfs.unlink(target)
.then(undefined, ignore('ENOENT', null))
.then(() => pfs.symlink(getSource(), this.target))
.then(() => pfs.symlink(getSource(), target))
.then(undefined, err => {
if (err.code === 'EACCES' || err.code === 'ENOENT') {
return this.createBinFolderAndSymlinkAsAdmin();
return new Promise<void>((resolve, reject) => {
const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")];
dialogService.show(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), buttons, { cancelId: 1 }).then(result => {
switch (result.choice) {
case 0 /* OK */:
const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'' + getSource() + '\' \'' + target + '\'\\" with administrator privileges"';
promisify(cp.exec)(command, {})
.then(undefined, _ => Promise.reject(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'."))))
.then(() => resolve(), reject);
break;
case 1 /* Cancel */:
reject(new Error(nls.localize('aborted', "Aborted")));
break;
}
});
});
}
return Promise.reject(err);
@@ -85,113 +104,84 @@ class InstallAction extends Action {
}
})
.then(() => {
this.logService.trace('cli#install', this.target);
this.notificationService.info(nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", this.productService.applicationName));
logService.trace('cli#install', target);
notificationService.info(nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", productService.applicationName));
});
});
}
private isInstalled(): Promise<boolean> {
return pfs.lstat(this.target)
private isInstalled(target: string): Promise<boolean> {
return pfs.lstat(target)
.then(stat => stat.isSymbolicLink())
.then(() => extpath.realpath(this.target))
.then(() => extpath.realpath(target))
.then(link => link === getSource())
.then(undefined, ignore('ENOENT', false));
}
private createBinFolderAndSymlinkAsAdmin(): Promise<void> {
return new Promise<void>((resolve, reject) => {
const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")];
this.dialogService.show(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), buttons, { cancelId: 1 }).then(result => {
switch (result.choice) {
case 0 /* OK */:
const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'' + getSource() + '\' \'' + this.target + '\'\\" with administrator privileges"';
promisify(cp.exec)(command, {})
.then(undefined, _ => Promise.reject(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'."))))
.then(() => resolve(), reject);
break;
case 1 /* Cancel */:
reject(new Error(nls.localize('aborted', "Aborted")));
break;
}
});
});
}
}
class UninstallAction extends Action {
class UninstallAction extends Action2 {
static readonly ID = 'workbench.action.uninstallCommandLine';
static readonly LABEL = nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName);
constructor(
id: string,
label: string,
@INotificationService private readonly notificationService: INotificationService,
@ILogService private readonly logService: ILogService,
@IDialogService private readonly dialogService: IDialogService,
@IProductService private readonly productService: IProductService
) {
super(id, label);
constructor() {
super({
id: 'workbench.action.uninstallCommandLine',
title: {
value: nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName),
original: `Shell Command: Uninstall \'${product.applicationName}\' command from PATH`
},
category,
f1: true,
precondition: ContextKeyExpr.and(IsMacNativeContext, ContextKeyExpr.equals('remoteName', ''))
});
}
private get target(): string {
return `/usr/local/bin/${this.productService.applicationName}`;
}
run(accessor: ServicesAccessor): Promise<void> {
const productService = accessor.get(IProductService);
const notificationService = accessor.get(INotificationService);
const logService = accessor.get(ILogService);
const dialogService = accessor.get(IDialogService);
const target = `/usr/local/bin/${productService.applicationName}`;
run(): Promise<void> {
return isAvailable().then(isAvailable => {
if (!isAvailable) {
const message = nls.localize('not available', "This command is not available");
this.notificationService.info(message);
notificationService.info(message);
return undefined;
}
const uninstall = () => {
return pfs.unlink(this.target)
return pfs.unlink(target)
.then(undefined, ignore('ENOENT', null));
};
return uninstall().then(undefined, err => {
if (err.code === 'EACCES') {
return this.deleteSymlinkAsAdmin();
return new Promise<void>(async (resolve, reject) => {
const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")];
const { choice } = await dialogService.show(Severity.Info, nls.localize('warnEscalationUninstall', "Code will now prompt with 'osascript' for Administrator privileges to uninstall the shell command."), buttons, { cancelId: 1 });
switch (choice) {
case 0 /* OK */:
const command = 'osascript -e "do shell script \\"rm \'' + target + '\'\\" with administrator privileges"';
promisify(cp.exec)(command, {})
.then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", target))))
.then(() => resolve(), reject);
break;
case 1 /* Cancel */:
reject(new Error(nls.localize('aborted', "Aborted")));
break;
}
});
}
return Promise.reject(err);
}).then(() => {
this.logService.trace('cli#uninstall', this.target);
this.notificationService.info(nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", this.productService.applicationName));
logService.trace('cli#uninstall', target);
notificationService.info(nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", productService.applicationName));
});
});
}
private deleteSymlinkAsAdmin(): Promise<void> {
return new Promise<void>(async (resolve, reject) => {
const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")];
const { choice } = await this.dialogService.show(Severity.Info, nls.localize('warnEscalationUninstall', "Code will now prompt with 'osascript' for Administrator privileges to uninstall the shell command."), buttons, { cancelId: 1 });
switch (choice) {
case 0 /* OK */:
const command = 'osascript -e "do shell script \\"rm \'' + this.target + '\'\\" with administrator privileges"';
promisify(cp.exec)(command, {})
.then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", this.target))))
.then(() => resolve(), reject);
break;
case 1 /* Cancel */:
reject(new Error(nls.localize('aborted', "Aborted")));
break;
}
});
}
}
if (platform.isMacintosh) {
const category = nls.localize('shellCommand', "Shell Command");
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(InstallAction), `Shell Command: Install \'${product.applicationName}\' command in PATH`, category);
workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(UninstallAction), `Shell Command: Uninstall \'${product.applicationName}\' command from PATH`, category);
}
registerAction2(InstallAction);
registerAction2(UninstallAction);

View File

@@ -21,7 +21,7 @@ import { IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOpti
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -120,7 +120,7 @@ class AccessibilityHelpWidget extends Widget implements IOverlayWidget {
if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_E)) {
alert(nls.localize('emergencyConfOn', "Now changing the setting `editor.accessibilitySupport` to 'on'."));
this._configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
this._configurationService.updateValue('editor.accessibilitySupport', 'on');
e.preventDefault();
e.stopPropagation();

View File

@@ -50,7 +50,7 @@ class DiffEditorHelperContribution extends Disposable implements IDiffEditorCont
[{
label: nls.localize('removeTimeout', "Remove limit"),
run: () => {
this._configurationService.updateValue('diffEditor.maxComputationTime', 0, ConfigurationTarget.USER);
this._configurationService.updateValue('diffEditor.maxComputationTime', 0);
}
}],
{}

View File

@@ -12,7 +12,7 @@ import { Delayer } from 'vs/base/common/async';
import { KeyCode } from 'vs/base/common/keyCodes';
import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState';
import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { SimpleButton, findCloseIcon, findNextMatchIcon, findPreviousMatchIcon, findReplaceIcon, findReplaceAllIcon } from 'vs/editor/contrib/find/findWidget';
import { SimpleButton, findNextMatchIcon, findPreviousMatchIcon, findReplaceIcon, findReplaceAllIcon } from 'vs/editor/contrib/find/findWidget';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry';
@@ -21,6 +21,7 @@ import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/b
import { ReplaceInput, IReplaceInputStyles } from 'vs/base/browser/ui/findinput/replaceInput';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { widgetClose } from 'vs/platform/theme/common/iconRegistry';
const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find");
const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find");
@@ -146,7 +147,7 @@ export abstract class SimpleFindReplaceWidget extends Widget {
this.prevBtn = this._register(new SimpleButton({
label: NLS_PREVIOUS_MATCH_BTN_LABEL,
className: findPreviousMatchIcon.classNames,
icon: findPreviousMatchIcon,
onTrigger: () => {
this.find(true);
}
@@ -154,7 +155,7 @@ export abstract class SimpleFindReplaceWidget extends Widget {
this.nextBtn = this._register(new SimpleButton({
label: NLS_NEXT_MATCH_BTN_LABEL,
className: findNextMatchIcon.classNames,
icon: findNextMatchIcon,
onTrigger: () => {
this.find(false);
}
@@ -162,7 +163,7 @@ export abstract class SimpleFindReplaceWidget extends Widget {
const closeBtn = this._register(new SimpleButton({
label: NLS_CLOSE_BTN_LABEL,
className: findCloseIcon.classNames,
icon: widgetClose,
onTrigger: () => {
this.hide();
}
@@ -220,7 +221,7 @@ export abstract class SimpleFindReplaceWidget extends Widget {
this._replaceBtn = this._register(new SimpleButton({
label: NLS_REPLACE_BTN_LABEL,
className: findReplaceIcon.classNames,
icon: findReplaceIcon,
onTrigger: () => {
this.replaceOne();
}
@@ -229,7 +230,7 @@ export abstract class SimpleFindReplaceWidget extends Widget {
// Replace all button
this._replaceAllBtn = this._register(new SimpleButton({
label: NLS_REPLACE_ALL_BTN_LABEL,
className: findReplaceAllIcon.classNames,
icon: findReplaceAllIcon,
onTrigger: () => {
this.replaceAll();
}
@@ -452,6 +453,6 @@ registerThemingParticipant((theme, collector) => {
const widgetShadowColor = theme.getColor(widgetShadow);
if (widgetShadowColor) {
collector.addRule(`.monaco-workbench .simple-fr-find-part-wrapper { box-shadow: 0 2px 8px ${widgetShadowColor}; }`);
collector.addRule(`.monaco-workbench .simple-fr-find-part-wrapper { box-shadow: 0 0 8px 2px ${widgetShadowColor}; }`);
}
});

View File

@@ -12,12 +12,13 @@ import { Delayer } from 'vs/base/common/async';
import { KeyCode } from 'vs/base/common/keyCodes';
import { FindReplaceState } from 'vs/editor/contrib/find/findState';
import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { SimpleButton, findPreviousMatchIcon, findNextMatchIcon, findCloseIcon } from 'vs/editor/contrib/find/findWidget';
import { SimpleButton, findPreviousMatchIcon, findNextMatchIcon } from 'vs/editor/contrib/find/findWidget';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { ContextScopedFindInput } from 'vs/platform/browser/contextScopedHistoryWidget';
import { widgetClose } from 'vs/platform/theme/common/iconRegistry';
const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find");
const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find");
@@ -94,7 +95,7 @@ export abstract class SimpleFindWidget extends Widget {
this.prevBtn = this._register(new SimpleButton({
label: NLS_PREVIOUS_MATCH_BTN_LABEL,
className: findPreviousMatchIcon.classNames,
icon: findPreviousMatchIcon,
onTrigger: () => {
this.find(true);
}
@@ -102,7 +103,7 @@ export abstract class SimpleFindWidget extends Widget {
this.nextBtn = this._register(new SimpleButton({
label: NLS_NEXT_MATCH_BTN_LABEL,
className: findNextMatchIcon.classNames,
icon: findNextMatchIcon,
onTrigger: () => {
this.find(false);
}
@@ -110,7 +111,7 @@ export abstract class SimpleFindWidget extends Widget {
const closeBtn = this._register(new SimpleButton({
label: NLS_CLOSE_BTN_LABEL,
className: findCloseIcon.classNames,
icon: widgetClose,
onTrigger: () => {
this.hide();
}
@@ -285,6 +286,6 @@ registerThemingParticipant((theme, collector) => {
const widgetShadowColor = theme.getColor(widgetShadow);
if (widgetShadowColor) {
collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 2px 8px ${widgetShadowColor}; }`);
collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 0 8px 2px ${widgetShadowColor}; }`);
}
});

View File

@@ -11,7 +11,6 @@ import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
/**
* Shows a message when opening a large file which has been memory optimized (and features disabled).
@@ -24,14 +23,9 @@ export class LargeFileOptimizationsWarner extends Disposable implements IEditorC
private readonly _editor: ICodeEditor,
@INotificationService private readonly _notificationService: INotificationService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
// opt-in to syncing
const neverShowAgainId = 'editor.contrib.largeFileOptimizationsWarner';
storageKeysSyncRegistryService.registerStorageKey({ key: neverShowAgainId, version: 1 });
this._register(this._editor.onDidChangeModel((e) => {
const model = this._editor.getModel();
if (!model) {
@@ -61,7 +55,7 @@ export class LargeFileOptimizationsWarner extends Disposable implements IEditorC
});
}
}
], { neverShowAgain: { id: neverShowAgainId } });
], { neverShowAgain: { id: 'editor.contrib.largeFileOptimizationsWarner' } });
}
}));
}

View File

@@ -5,7 +5,6 @@
import { localize } from 'vs/nls';
import { IKeyMods, IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IEditor } from 'vs/editor/common/editorCommon';
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { IRange } from 'vs/editor/common/core/range';
import { AbstractGotoLineQuickAccessProvider } from 'vs/editor/contrib/quickAccess/gotoLineQuickAccess';
@@ -17,6 +16,7 @@ import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
export class GotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProvider {
@@ -41,10 +41,12 @@ export class GotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProv
return this.editorService.activeTextEditorControl;
}
protected gotoLocation(editor: IEditor, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void {
protected gotoLocation(context: IQuickAccessTextEditorContext, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void {
// Check for sideBySide use
if ((options.keyMods.ctrlCmd || options.forceSideBySide) && this.editorService.activeEditor) {
context.restoreViewState?.(); // since we open to the side, restore view state in this editor
this.editorService.openEditor(this.editorService.activeEditor, {
selection: options.range,
pinned: options.keyMods.alt || this.configuration.openEditorPinned,
@@ -54,7 +56,7 @@ export class GotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProv
// Otherwise let parent handle it
else {
super.gotoLocation(editor, options);
super.gotoLocation(context, options);
}
}
}

View File

@@ -26,6 +26,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccessProvider {
@@ -42,11 +43,12 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess
//#region DocumentSymbols (text editor required)
protected provideWithTextEditor(editor: IEditor, picker: IQuickPick<IGotoSymbolQuickPickItem>, token: CancellationToken): IDisposable {
protected provideWithTextEditor(context: IQuickAccessTextEditorContext, picker: IQuickPick<IGotoSymbolQuickPickItem>, token: CancellationToken): IDisposable {
if (this.canPickFromTableOfContents()) {
return this.doGetTableOfContentsPicks(picker);
}
return super.provideWithTextEditor(editor, picker, token);
return super.provideWithTextEditor(context, picker, token);
}
private get configuration() {
@@ -62,10 +64,12 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess
return this.editorService.activeTextEditorControl;
}
protected gotoLocation(editor: IEditor, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void {
protected gotoLocation(context: IQuickAccessTextEditorContext, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void {
// Check for sideBySide use
if ((options.keyMods.ctrlCmd || options.forceSideBySide) && this.editorService.activeEditor) {
context.restoreViewState?.(); // since we open to the side, restore view state in this editor
this.editorService.openEditor(this.editorService.activeEditor, {
selection: options.range,
pinned: options.keyMods.alt || this.configuration.openEditorPinned,
@@ -75,7 +79,7 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess
// Otherwise let parent handle it
else {
super.gotoLocation(editor, options);
super.gotoLocation(context, options);
}
}

View File

@@ -287,8 +287,7 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant {
? setting
: Object.keys(setting).filter(x => setting[x]);
const codeActionsOnSave = settingItems
.map(x => new CodeActionKind(x));
const codeActionsOnSave = this.createCodeActionsOnSave(settingItems);
if (!Array.isArray(setting)) {
codeActionsOnSave.sort((a, b) => {
@@ -319,6 +318,15 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant {
await this.applyOnSaveActions(textEditorModel, codeActionsOnSave, excludedActions, progress, token);
}
private createCodeActionsOnSave(settingItems: readonly string[]): CodeActionKind[] {
const kinds = settingItems.map(x => new CodeActionKind(x));
// Remove subsets
return kinds.filter(kind => {
return kinds.every(otherKind => otherKind.equals(kind) || !otherKind.contains(kind));
});
}
private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
const getActionProgress = new class implements IProgress<CodeActionProvider> {

View File

@@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
@@ -42,7 +42,7 @@ export class ToggleColumnSelectionAction extends Action {
public async run(): Promise<any> {
const oldValue = this._configurationService.getValue<boolean>('editor.columnSelection');
const codeEditor = this._getCodeEditor();
await this._configurationService.updateValue('editor.columnSelection', !oldValue, ConfigurationTarget.USER);
await this._configurationService.updateValue('editor.columnSelection', !oldValue);
const newValue = this._configurationService.getValue<boolean>('editor.columnSelection');
if (!codeEditor || codeEditor !== this._getCodeEditor() || oldValue === newValue || !codeEditor.hasModel()) {
return;

View File

@@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { Registry } from 'vs/platform/registry/common/platform';
import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
@@ -25,7 +25,7 @@ export class ToggleMinimapAction extends Action {
public run(): Promise<any> {
const newValue = !this._configurationService.getValue<boolean>('editor.minimap.enabled');
return this._configurationService.updateValue('editor.minimap.enabled', newValue, ConfigurationTarget.USER);
return this._configurationService.updateValue('editor.minimap.enabled', newValue);
}
}

View File

@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import * as platform from 'vs/base/common/platform';
import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -33,7 +33,7 @@ export class ToggleMultiCursorModifierAction extends Action {
const editorConf = this.configurationService.getValue<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor');
const newValue: 'ctrlCmd' | 'alt' = (editorConf.multiCursorModifier === 'ctrlCmd' ? 'alt' : 'ctrlCmd');
return this.configurationService.updateValue(ToggleMultiCursorModifierAction.multiCursorModifierConfigurationKey, newValue, ConfigurationTarget.USER);
return this.configurationService.updateValue(ToggleMultiCursorModifierAction.multiCursorModifierConfigurationKey, newValue);
}
}

View File

@@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { Registry } from 'vs/platform/registry/common/platform';
import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
@@ -26,7 +26,7 @@ export class ToggleRenderControlCharacterAction extends Action {
public run(): Promise<any> {
let newRenderControlCharacters = !this._configurationService.getValue<boolean>('editor.renderControlCharacters');
return this._configurationService.updateValue('editor.renderControlCharacters', newRenderControlCharacters, ConfigurationTarget.USER);
return this._configurationService.updateValue('editor.renderControlCharacters', newRenderControlCharacters);
}
}

View File

@@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { Registry } from 'vs/platform/registry/common/platform';
import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
@@ -34,7 +34,7 @@ export class ToggleRenderWhitespaceAction extends Action {
newRenderWhitespace = 'none';
}
return this._configurationService.updateValue('editor.renderWhitespace', newRenderWhitespace, ConfigurationTarget.USER);
return this._configurationService.updateValue('editor.renderWhitespace', newRenderWhitespace);
}
}

View File

@@ -10,33 +10,25 @@ import { URI } from 'vs/base/common/uri';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { EditorOption, EditorOptions } from 'vs/editor/common/config/editorOptions';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { DefaultSettingsEditorContribution } from 'vs/workbench/contrib/preferences/browser/preferencesEditor';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { Codicon } from 'vs/base/common/codicons';
const transientWordWrapState = 'transientWordWrapState';
const isWordWrapMinifiedKey = 'isWordWrapMinified';
const isDominatedByLongLinesKey = 'isDominatedByLongLines';
const inDiffEditorKey = 'inDiffEditor';
/**
* State written/read by the toggle word wrap action and associated with a particular model.
*/
interface IWordWrapTransientState {
readonly forceWordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded';
readonly forceWordWrapMinified: boolean;
}
interface IWordWrapState {
readonly configuredWordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded' | undefined;
readonly configuredWordWrapMinified: boolean;
readonly transientState: IWordWrapTransientState | null;
readonly wordWrapOverride: 'on' | 'off';
}
/**
@@ -49,70 +41,10 @@ export function writeTransientState(model: ITextModel, state: IWordWrapTransient
/**
* Read (in memory) the word wrap state for a particular model.
*/
function readTransientState(model: ITextModel, codeEditorService: ICodeEditorService): IWordWrapTransientState {
function readTransientState(model: ITextModel, codeEditorService: ICodeEditorService): IWordWrapTransientState | null {
return codeEditorService.getTransientModelProperty(model, transientWordWrapState);
}
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 : undefined;
// Compatibility with old true or false values
if (<any>_configuredWordWrap === true) {
_configuredWordWrap = 'on';
} else if (<any>_configuredWordWrap === false) {
_configuredWordWrap = 'off';
}
const _configuredWordWrapMinified = editorConfig && typeof editorConfig.wordWrapMinified === 'boolean' ? editorConfig.wordWrapMinified : undefined;
const _transientState = readTransientState(model, codeEditorService);
return {
configuredWordWrap: _configuredWordWrap,
configuredWordWrapMinified: (typeof _configuredWordWrapMinified === 'boolean' ? _configuredWordWrapMinified : EditorOptions.wordWrapMinified.defaultValue),
transientState: _transientState
};
}
function toggleWordWrap(editor: ICodeEditor, state: IWordWrapState): IWordWrapState {
if (state.transientState) {
// toggle off => go to null
return {
configuredWordWrap: state.configuredWordWrap,
configuredWordWrapMinified: state.configuredWordWrapMinified,
transientState: null
};
}
let transientState: IWordWrapTransientState;
const actualWrappingInfo = editor.getOption(EditorOption.wrappingInfo);
if (actualWrappingInfo.isWordWrapMinified) {
// => wrapping due to minified file
transientState = {
forceWordWrap: 'off',
forceWordWrapMinified: false
};
} else if (state.configuredWordWrap !== 'off') {
// => wrapping is configured to be on (or some variant)
transientState = {
forceWordWrap: 'off',
forceWordWrapMinified: false
};
} else {
// => wrapping is configured to be off
transientState = {
forceWordWrap: 'on',
forceWordWrapMinified: state.configuredWordWrapMinified
};
}
return {
configuredWordWrap: state.configuredWordWrap,
configuredWordWrapMinified: state.configuredWordWrapMinified,
transientState: transientState
};
}
const TOGGLE_WORD_WRAP_ID = 'editor.action.toggleWordWrap';
class ToggleWordWrapAction extends EditorAction {
@@ -138,14 +70,7 @@ class ToggleWordWrapAction extends EditorAction {
if (!editor.hasModel()) {
return;
}
if (editor.getOption(EditorOption.inDiffEditor)) {
// Cannot change wrapping settings inside the diff editor
const notificationService = accessor.get(INotificationService);
notificationService.info(nls.localize('wordWrap.notInDiffEditor', "Cannot toggle word wrap in a diff editor."));
return;
}
const textResourceConfigurationService = accessor.get(ITextResourceConfigurationService);
const codeEditorService = accessor.get(ICodeEditorService);
const model = editor.getModel();
@@ -154,12 +79,21 @@ class ToggleWordWrapAction extends EditorAction {
}
// Read the current state
const currentState = readWordWrapState(model, textResourceConfigurationService, codeEditorService);
const transientState = readTransientState(model, codeEditorService);
// Compute the new state
const newState = toggleWordWrap(editor, currentState);
let newState: IWordWrapTransientState | null;
if (transientState) {
newState = null;
} else {
const actualWrappingInfo = editor.getOption(EditorOption.wrappingInfo);
const wordWrapOverride = (actualWrappingInfo.wrappingColumn === -1 ? 'on' : 'off');
newState = { wordWrapOverride };
}
// Write the new state
// (this will cause an event and the controller will apply the state)
writeTransientState(model, newState.transientState, codeEditorService);
writeTransientState(model, newState, codeEditorService);
}
}
@@ -168,93 +102,75 @@ class ToggleWordWrapController extends Disposable implements IEditorContribution
public static readonly ID = 'editor.contrib.toggleWordWrapController';
constructor(
private readonly editor: ICodeEditor,
@IContextKeyService readonly contextKeyService: IContextKeyService,
@ITextResourceConfigurationService readonly configurationService: ITextResourceConfigurationService,
@ICodeEditorService readonly codeEditorService: ICodeEditorService
private readonly _editor: ICodeEditor,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService
) {
super();
const options = this.editor.getOptions();
const options = this._editor.getOptions();
const wrappingInfo = options.get(EditorOption.wrappingInfo);
const isWordWrapMinified = this.contextKeyService.createKey(isWordWrapMinifiedKey, wrappingInfo.isWordWrapMinified);
const isDominatedByLongLines = this.contextKeyService.createKey(isDominatedByLongLinesKey, wrappingInfo.isDominatedByLongLines);
const inDiffEditor = this.contextKeyService.createKey(inDiffEditorKey, options.get(EditorOption.inDiffEditor));
const isWordWrapMinified = this._contextKeyService.createKey(isWordWrapMinifiedKey, wrappingInfo.isWordWrapMinified);
const isDominatedByLongLines = this._contextKeyService.createKey(isDominatedByLongLinesKey, wrappingInfo.isDominatedByLongLines);
let currentlyApplyingEditorConfig = false;
this._register(editor.onDidChangeConfiguration((e) => {
if (!e.hasChanged(EditorOption.wrappingInfo) && !e.hasChanged(EditorOption.inDiffEditor)) {
this._register(_editor.onDidChangeConfiguration((e) => {
if (!e.hasChanged(EditorOption.wrappingInfo)) {
return;
}
const options = this.editor.getOptions();
const options = this._editor.getOptions();
const wrappingInfo = options.get(EditorOption.wrappingInfo);
isWordWrapMinified.set(wrappingInfo.isWordWrapMinified);
isDominatedByLongLines.set(wrappingInfo.isDominatedByLongLines);
inDiffEditor.set(options.get(EditorOption.inDiffEditor));
if (!currentlyApplyingEditorConfig) {
// I am not the cause of the word wrap getting changed
ensureWordWrapSettings();
}
}));
this._register(editor.onDidChangeModel((e) => {
this._register(_editor.onDidChangeModel((e) => {
ensureWordWrapSettings();
}));
this._register(codeEditorService.onDidChangeTransientModelProperty(() => {
this._register(_codeEditorService.onDidChangeTransientModelProperty(() => {
ensureWordWrapSettings();
}));
const ensureWordWrapSettings = () => {
if (this.editor.getContribution(DefaultSettingsEditorContribution.ID)) {
if (this._editor.getContribution(DefaultSettingsEditorContribution.ID)) {
// in the settings editor...
return;
}
if (this.editor.isSimpleWidget) {
if (this._editor.isSimpleWidget) {
// in a simple widget...
return;
}
// Ensure correct word wrap settings
const newModel = this.editor.getModel();
const newModel = this._editor.getModel();
if (!newModel) {
return;
}
if (this.editor.getOption(EditorOption.inDiffEditor)) {
return;
}
if (!canToggleWordWrap(newModel.uri)) {
return;
}
// Read current configured values and toggle state
const desiredState = readWordWrapState(newModel, this.configurationService, this.codeEditorService);
const transientState = readTransientState(newModel, this._codeEditorService);
// Apply the state
try {
currentlyApplyingEditorConfig = true;
this._applyWordWrapState(desiredState);
this._applyWordWrapState(transientState);
} finally {
currentlyApplyingEditorConfig = false;
}
};
}
private _applyWordWrapState(state: IWordWrapState): void {
if (state.transientState) {
// toggle is on
this.editor.updateOptions({
wordWrap: state.transientState.forceWordWrap,
wordWrapMinified: state.transientState.forceWordWrapMinified
});
return;
}
// toggle is off
this.editor.updateOptions({
wordWrap: state.configuredWordWrap,
wordWrapMinified: state.configuredWordWrapMinified
private _applyWordWrapState(state: IWordWrapTransientState | null): void {
const wordWrapOverride2 = state ? state.wordWrapOverride : 'inherit';
this._editor.updateOptions({
wordWrapOverride2: wordWrapOverride2
});
}
}
@@ -275,14 +191,11 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
id: TOGGLE_WORD_WRAP_ID,
title: nls.localize('unwrapMinified', "Disable wrapping for this file"),
icon: {
id: 'codicon/word-wrap'
}
icon: Codicon.wordWrap
},
group: 'navigation',
order: 1,
when: ContextKeyExpr.and(
ContextKeyExpr.not(inDiffEditorKey),
ContextKeyExpr.has(isDominatedByLongLinesKey),
ContextKeyExpr.has(isWordWrapMinifiedKey)
)
@@ -291,14 +204,12 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
id: TOGGLE_WORD_WRAP_ID,
title: nls.localize('wrapMinified', "Enable wrapping for this file"),
icon: {
id: 'codicon/word-wrap'
}
icon: Codicon.wordWrap
},
group: 'navigation',
order: 1,
when: ContextKeyExpr.and(
ContextKeyExpr.not(inDiffEditorKey),
EditorContextKeys.inDiffEditor.negate(),
ContextKeyExpr.has(isDominatedByLongLinesKey),
ContextKeyExpr.not(isWordWrapMinifiedKey)
)

View File

@@ -70,7 +70,8 @@ class StartDebugTextMate extends Action {
};
await this._hostService.openWindow([{ fileUri: URI.file(pathInTemp) }], { forceNewWindow: true });
const textEditorPane = await this._editorService.openEditor({
resource: model.uri
resource: model.uri,
options: { pinned: true }
});
if (!textEditorPane) {
return;

View File

@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import './displayChangeRemeasureFonts';
import './inputClipboardActions';
import './selectionClipboard';
import './sleepResumeRepaintMinimap';

View File

@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { Disposable } from 'vs/base/common/lifecycle';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { IDisplayMainService } from 'vs/platform/display/common/displayMainService';
import { createChannelSender } from 'vs/base/parts/ipc/common/ipc';
import { clearAllFontInfos } from 'vs/editor/browser/config/configuration';
class DisplayChangeRemeasureFonts extends Disposable implements IWorkbenchContribution {
constructor(
@IMainProcessService mainProcessService: IMainProcessService
) {
super();
const displayMainService = createChannelSender<IDisplayMainService>(mainProcessService.getChannel('display'));
displayMainService.onDidDisplayChanged(() => {
clearAllFontInfos();
});
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DisplayChangeRemeasureFonts, LifecyclePhase.Eventually);

View File

@@ -34,6 +34,7 @@ import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commen
import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor';
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem';
import { Codicon } from 'vs/base/common/codicons';
export class CommentNode extends Disposable {
private _domNode: HTMLElement;
@@ -156,7 +157,7 @@ export class CommentNode extends Disposable {
{
actionViewItemProvider: action => this.actionViewItemProvider(action as Action),
actionRunner: this.actionRunner,
classNames: ['toolbar-toggle-pickReactions', 'codicon', 'codicon-reactions'],
classNames: ['toolbar-toggle-pickReactions', ...Codicon.reactions.classNamesArray],
anchorAlignmentProvider: () => AnchorAlignment.RIGHT
}
);

View File

@@ -6,7 +6,6 @@
import * as dom from 'vs/base/browser/dom';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action, IAction } from 'vs/base/common/actions';
import * as arrays from 'vs/base/common/arrays';
import { Color } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
@@ -32,7 +31,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { contrastBorder, editorForeground, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, transparent } from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { IColorTheme, IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions';
import { CommentGlyphWidget } from 'vs/workbench/contrib/comments/browser/commentGlyphWidget';
import { CommentMenus } from 'vs/workbench/contrib/comments/browser/commentMenus';
@@ -48,9 +47,14 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor';
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { PANEL_BORDER } from 'vs/workbench/common/theme';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
import { Codicon } from 'vs/base/common/codicons';
const collapseIcon = registerIcon('review-comment-collapse', Codicon.chevronUp, nls.localize('collapseIcon', 'Icon to collapse a review comment.'));
export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration';
const COLLAPSE_ACTION_CLASS = 'expand-review-action codicon-chevron-up';
const COLLAPSE_ACTION_CLASS = 'expand-review-action ' + ThemeIcon.asClassName(collapseIcon);
const COMMENT_SCHEME = 'comment';
@@ -712,10 +716,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
label = this._commentThread.label;
if (label === undefined) {
if (this._commentThread.comments && this._commentThread.comments.length) {
const participantsList = this._commentThread.comments.filter(arrays.uniqueFilter(comment => comment.userName)).map(comment => `@${comment.userName}`).join(', ');
label = nls.localize('commentThreadParticipants', "Participants: {0}", participantsList);
} else {
if (!(this._commentThread.comments && this._commentThread.comments.length)) {
label = nls.localize('startThread', "Start discussion");
}
}
@@ -884,13 +885,18 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}
private _applyTheme(theme: IColorTheme) {
const borderColor = theme.getColor(peekViewBorder) || Color.transparent;
const borderColor = theme.getColor(peekViewBorder);
this.style({
arrowColor: borderColor,
frameColor: borderColor
arrowColor: borderColor || Color.transparent,
frameColor: borderColor || Color.transparent
});
const content: string[] = [];
if (borderColor) {
content.push(`.monaco-editor .review-widget > .body { border-top: 1px solid ${borderColor} }`);
}
const linkColor = theme.getColor(textLinkForeground);
if (linkColor) {
content.push(`.monaco-editor .review-widget .body .comment-body a { color: ${linkColor} }`);
@@ -945,7 +951,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
const fontInfo = this.editor.getOption(EditorOption.fontInfo);
content.push(`.monaco-editor .review-widget .body code {
font-family: ${fontInfo.fontFamily};
font-family: '${fontInfo.fontFamily}';
font-size: ${fontInfo.fontSize}px;
font-weight: ${fontInfo.fontWeight};
}`);

View File

@@ -34,7 +34,6 @@ export class CommentsPanel extends ViewPane {
private tree!: CommentsList;
private treeContainer!: HTMLElement;
private messageBoxContainer!: HTMLElement;
private messageBox!: HTMLElement;
private commentsModel!: CommentsModel;
private collapseAllAction?: IAction;
@@ -85,6 +84,18 @@ export class CommentsPanel extends ViewPane {
this.renderComments();
}
public focus(): void {
if (this.tree && this.tree.getHTMLElement() === document.activeElement) {
return;
}
if (!this.commentsModel.hasCommentThreads() && this.messageBoxContainer) {
this.messageBoxContainer.focus();
} else if (this.tree) {
this.tree.domFocus();
}
}
private applyStyles(styleElement: HTMLStyleElement) {
const content: string[] = [];
@@ -114,8 +125,8 @@ export class CommentsPanel extends ViewPane {
private async renderComments(): Promise<void> {
this.treeContainer.classList.toggle('hidden', !this.commentsModel.hasCommentThreads());
await this.tree.setInput(this.commentsModel);
this.renderMessage();
await this.tree.setInput(this.commentsModel);
}
public getActions(): IAction[] {
@@ -138,12 +149,11 @@ export class CommentsPanel extends ViewPane {
private createMessageBox(parent: HTMLElement): void {
this.messageBoxContainer = dom.append(parent, dom.$('.message-box-container'));
this.messageBox = dom.append(this.messageBoxContainer, dom.$('span'));
this.messageBox.setAttribute('tabindex', '0');
this.messageBoxContainer.setAttribute('tabIndex', '0');
}
private renderMessage(): void {
this.messageBox.textContent = this.commentsModel.getMessage();
this.messageBoxContainer.textContent = this.commentsModel.getMessage();
this.messageBoxContainer.classList.toggle('hidden', this.commentsModel.hasCommentThreads());
}
@@ -231,18 +241,23 @@ export class CommentsPanel extends ViewPane {
return true;
}
private refresh(): void {
private async refresh(): Promise<void> {
if (this.isVisible()) {
if (this.collapseAllAction) {
this.collapseAllAction.enabled = this.commentsModel.hasCommentThreads();
}
this.treeContainer.classList.toggle('hidden', !this.commentsModel.hasCommentThreads());
this.tree.updateChildren().then(() => {
this.renderMessage();
}, (e) => {
console.log(e);
});
this.renderMessage();
await this.tree.updateChildren();
if (this.tree.getSelection().length === 0 && this.commentsModel.hasCommentThreads()) {
const firstComment = this.commentsModel.resourceCommentThreads[0].commentThreads[0];
if (firstComment) {
this.tree.setFocus([firstComment]);
this.tree.setSelection([firstComment]);
}
}
}
}

View File

@@ -48,10 +48,7 @@
.comments-panel .comments-panel-container .message-box-container {
line-height: 22px;
padding-left: 20px;
}
.comments-panel .comments-panel-container .message-box-container span:focus {
outline: none;
height: inherit;
}
.comments-panel .comments-panel-container .tree-container .count-badge-wrapper {
@@ -61,4 +58,4 @@
.comments-panel .comments-panel-container .tree-container .comment-container {
line-height: 22px;
margin-right: 5px;
}
}

View File

@@ -134,7 +134,7 @@ export class CommentsModel {
public getMessage(): string {
if (!this.resourceCommentThreads.length) {
return localize('noComments', "There are no comments on this review.");
return localize('noComments', "There are no comments in this workspace yet.");
} else {
return '';
}

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { coalesce, distinct } from 'vs/base/common/arrays';
import { Codicon } from 'vs/base/common/codicons';
import { Emitter, Event } from 'vs/base/common/event';
import { Lazy } from 'vs/base/common/lazy';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
@@ -173,7 +174,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
: undefined,
detail: editorDescriptor.providerDisplayName,
buttons: resourceExt ? [{
iconClass: 'codicon-settings-gear',
iconClass: Codicon.settingsGear.classNames,
tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt)
}] : undefined
}));
@@ -447,6 +448,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo
constructor(
@IEditorService private readonly editorService: IEditorService,
@ICustomEditorService private readonly customEditorService: ICustomEditorService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
super();
@@ -651,7 +653,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo
if (modifiedOverride || originalOverride) {
return {
override: (async () => {
const input = new DiffEditorInput(editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput, true);
const input = this.instantiationService.createInstance(DiffEditorInput, editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput, true);
return this.editorService.openEditor(input, { ...options, override: false }, group);
})(),
};

View File

@@ -8,12 +8,12 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { Memento } from 'vs/workbench/common/memento';
import { CustomEditorDescriptor, CustomEditorInfo, CustomEditorPriority } from 'vs/workbench/contrib/customEditor/common/customEditor';
import { customEditorsExtensionPoint, ICustomEditorsExtensionPoint } from 'vs/workbench/contrib/customEditor/common/extensionPoint';
import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { DEFAULT_EDITOR_ID } from 'vs/workbench/services/editor/common/editorOpenWith';
import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in");
@@ -40,7 +40,7 @@ export class ContributedCustomEditors extends Disposable {
this._memento = new Memento(ContributedCustomEditors.CUSTOM_EDITORS_STORAGE_ID, storageService);
const mementoObject = this._memento.getMemento(StorageScope.GLOBAL);
const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE);
for (const info of (mementoObject[ContributedCustomEditors.CUSTOM_EDITORS_ENTRY_ID] || []) as CustomEditorDescriptor[]) {
this.add(new CustomEditorInfo(info));
}
@@ -68,7 +68,7 @@ export class ContributedCustomEditors extends Disposable {
}
}
const mementoObject = this._memento.getMemento(StorageScope.GLOBAL);
const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE);
mementoObject[ContributedCustomEditors.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._editors.values());
this._memento.saveMemento();

View File

@@ -22,7 +22,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/browser/breakpointsView';
import { getBreakpointMessageAndIcon } from 'vs/workbench/contrib/debug/browser/breakpointsView';
import { generateUuid } from 'vs/base/common/uuid';
import { memoize } from 'vs/base/common/decorators';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
@@ -32,9 +32,10 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
import { isSafari } from 'vs/base/browser/browser';
import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService';
import { registerThemingParticipant, themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
import { ILabelService } from 'vs/platform/label/common/label';
import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons';
const $ = dom.$;
@@ -46,7 +47,7 @@ interface IBreakpointDecoration {
}
const breakpointHelperDecoration: IModelDecorationOptions = {
glyphMarginClassName: 'codicon-debug-hint',
glyphMarginClassName: ThemeIcon.asClassName(icons.debugBreakpointHint),
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
};
@@ -72,7 +73,7 @@ export function createBreakpointDecorations(model: ITextModel, breakpoints: Read
}
function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoint, state: State, breakpointsActivated: boolean, showBreakpointsInOverviewRuler: boolean): IModelDecorationOptions {
const { className, message } = getBreakpointMessageAndClassName(state, breakpointsActivated, breakpoint, undefined);
const { icon, message } = getBreakpointMessageAndIcon(state, breakpointsActivated, breakpoint, undefined);
let glyphMarginHoverMessage: MarkdownString | undefined;
if (message) {
@@ -94,7 +95,7 @@ function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoi
const renderInline = breakpoint.column && (breakpoint.column > model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber));
return {
glyphMarginClassName: `${className}`,
glyphMarginClassName: ThemeIcon.asClassName(icon),
glyphMarginHoverMessage,
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
beforeContentClassName: renderInline ? `debug-breakpoint-placeholder` : undefined,
@@ -174,7 +175,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi
private registerListeners(): void {
this.toDispose.push(this.editor.onMouseDown(async (e: IEditorMouseEvent) => {
if (!this.debugService.getConfigurationManager().hasDebuggers()) {
if (!this.debugService.getAdapterManager().hasDebuggers()) {
return;
}
@@ -183,7 +184,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi
if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
return;
}
const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(model);
const canSetBreakpoints = this.debugService.canSetBreakpointsIn(model);
const lineNumber = e.target.position.lineNumber;
const uri = model.uri;
@@ -252,13 +253,13 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi
* 2. When users click on line numbers, the breakpoint hint displays immediately, however it doesn't create the breakpoint unless users click on the left gutter. On a touch screen, it's hard to click on that small area.
*/
this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => {
if (!this.debugService.getConfigurationManager().hasDebuggers()) {
if (!this.debugService.getAdapterManager().hasDebuggers()) {
return;
}
let showBreakpointHintAtLineNumber = -1;
const model = this.editor.getModel();
if (model && e.target.position && (e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.type === MouseTargetType.GUTTER_LINE_NUMBERS) && this.debugService.getConfigurationManager().canSetBreakpointsIn(model) &&
if (model && e.target.position && (e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.type === MouseTargetType.GUTTER_LINE_NUMBERS) && this.debugService.canSetBreakpointsIn(model) &&
this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
const data = e.target.detail as IMarginData;
if (!data.isAfterLines) {
@@ -453,9 +454,9 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi
// Candidate decoration has a breakpoint attached when a breakpoint is already at that location and we did not yet set a decoration there
// In practice this happens for the first breakpoint that was set on a line
// We could have also rendered this first decoration as part of desiredBreakpointDecorations however at that moment we have no location information
const cssClass = candidate.breakpoint ? getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), candidate.breakpoint, this.labelService).className : 'codicon-debug-breakpoint-disabled';
const icon = candidate.breakpoint ? getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), candidate.breakpoint, this.labelService).icon : icons.debugBreakpointDisabled;
const contextMenuActions = () => this.getContextMenuActions(candidate.breakpoint ? [candidate.breakpoint] : [], activeCodeEditor.getModel().uri, candidate.range.startLineNumber, candidate.range.startColumn);
const inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, cssClass, candidate.breakpoint, this.debugService, this.contextMenuService, contextMenuActions);
const inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, ThemeIcon.asClassName(icon), candidate.breakpoint, this.debugService, this.contextMenuService, contextMenuActions);
return {
decorationId,
@@ -575,9 +576,8 @@ class InlineBreakpointWidget implements IContentWidget, IDisposable {
private create(cssClass: string | null | undefined): void {
this.domNode = $('.inline-breakpoint-widget');
this.domNode.classList.add('codicon');
if (cssClass) {
this.domNode.classList.add(cssClass);
this.domNode.classList.add(...cssClass.split(' '));
}
this.toDispose.push(dom.addDisposableListener(this.domNode, dom.EventType.CLICK, async e => {
if (this.breakpoint) {
@@ -645,15 +645,15 @@ registerThemingParticipant((theme, collector) => {
const debugIconBreakpointColor = theme.getColor(debugIconBreakpointForeground);
if (debugIconBreakpointColor) {
collector.addRule(`
.monaco-workbench .codicon-debug-breakpoint,
.monaco-workbench .codicon-debug-breakpoint-conditional,
.monaco-workbench .codicon-debug-breakpoint-log,
.monaco-workbench .codicon-debug-breakpoint-function,
.monaco-workbench .codicon-debug-breakpoint-data,
.monaco-workbench .codicon-debug-breakpoint-unsupported,
.monaco-workbench .codicon-debug-hint:not([class*='codicon-debug-breakpoint']):not([class*='codicon-debug-stackframe']),
.monaco-workbench .codicon-debug-breakpoint.codicon-debug-stackframe-focused::after,
.monaco-workbench .codicon-debug-breakpoint.codicon-debug-stackframe::after {
.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpoint)},
.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointConditional)},
.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointLog)},
.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointFunction)},
.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointData)},
.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointUnsupported)},
.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointHint)}:not([class*='codicon-debug-breakpoint']):not([class*='codicon-debug-stackframe']),
.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpoint)}${ThemeIcon.asCSSSelector(icons.debugStackframeFocused)}::after,
.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpoint)}${ThemeIcon.asCSSSelector(icons.debugStackframe)}::after {
color: ${debugIconBreakpointColor} !important;
}
`);
@@ -680,7 +680,7 @@ registerThemingParticipant((theme, collector) => {
const debugIconBreakpointCurrentStackframeForegroundColor = theme.getColor(debugIconBreakpointCurrentStackframeForeground);
if (debugIconBreakpointCurrentStackframeForegroundColor) {
collector.addRule(`
.monaco-workbench .codicon-debug-stackframe,
.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStackframe)},
.monaco-editor .debug-top-stack-frame-column::before {
color: ${debugIconBreakpointCurrentStackframeForegroundColor} !important;
}
@@ -690,7 +690,7 @@ registerThemingParticipant((theme, collector) => {
const debugIconBreakpointStackframeFocusedColor = theme.getColor(debugIconBreakpointStackframeForeground);
if (debugIconBreakpointStackframeFocusedColor) {
collector.addRule(`
.monaco-workbench .codicon-debug-stackframe-focused {
.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStackframeFocused)} {
color: ${debugIconBreakpointStackframeFocusedColor} !important;
}
`);

View File

@@ -13,7 +13,7 @@ import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAl
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { Constants } from 'vs/base/common/uint';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IListVirtualDelegate, IListContextMenuEvent, IListRenderer } from 'vs/base/browser/ui/list/list';
@@ -37,6 +37,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons';
const $ = dom.$;
@@ -93,6 +94,7 @@ export class BreakpointsView extends ViewPane {
this.list = <WorkbenchList<BreakpointItem>>this.instantiationService.createInstance(WorkbenchList, 'Breakpoints', container, delegate, [
this.instantiationService.createInstance(BreakpointsRenderer),
new ExceptionBreakpointsRenderer(this.debugService),
new ExceptionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService),
this.instantiationService.createInstance(FunctionBreakpointsRenderer),
this.instantiationService.createInstance(DataBreakpointsRenderer),
new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService, this.labelService)
@@ -122,7 +124,7 @@ export class BreakpointsView extends ViewPane {
const resourceNavigator = this._register(new ListResourceNavigator(this.list, { configurationService: this.configurationService }));
this._register(resourceNavigator.onDidOpen(async e => {
if (e.element === null) {
if (!e.element) {
return;
}
@@ -130,14 +132,12 @@ export class BreakpointsView extends ViewPane {
return;
}
const element = this.list.element(e.element);
if (element instanceof Breakpoint) {
openBreakpointSource(element, e.sideBySide, e.editorOptions.preserveFocus || false, this.debugService, this.editorService);
if (e.element instanceof Breakpoint) {
openBreakpointSource(e.element, e.sideBySide, e.editorOptions.preserveFocus || false, e.editorOptions.pinned || !e.editorOptions.preserveFocus, this.debugService, this.editorService);
}
if (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2 && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) {
if (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2 && e.element instanceof FunctionBreakpoint && e.element !== this.debugService.getViewModel().getSelectedBreakpoint()) {
// double click
this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
this.debugService.getViewModel().setSelectedBreakpoint(e.element);
this.onBreakpointsChange();
}
}));
@@ -188,38 +188,48 @@ export class BreakpointsView extends ViewPane {
const actions: IAction[] = [];
const element = e.element;
const breakpointType = element instanceof Breakpoint && element.logMessage ? nls.localize('Logpoint', "Logpoint") : nls.localize('Breakpoint', "Breakpoint");
if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) {
actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editBreakpoint', "Edit {0}...", breakpointType), '', true, async () => {
if (element instanceof Breakpoint) {
const editor = await openBreakpointSource(element, false, false, this.debugService, this.editorService);
if (editor) {
const codeEditor = editor.getControl();
if (isCodeEditor(codeEditor)) {
codeEditor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
}
}
} else {
this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
if (element instanceof ExceptionBreakpoint) {
if (element.supportsCondition) {
actions.push(new Action('workbench.action.debug.editExceptionBreakpointCondition', nls.localize('editCondition', "Edit Condition"), '', true, async () => {
this.debugService.getViewModel().setSelectedBreakpoint(element);
this.onBreakpointsChange();
}
}));
}));
}
} else {
const breakpointType = element instanceof Breakpoint && element.logMessage ? nls.localize('Logpoint', "Logpoint") : nls.localize('Breakpoint', "Breakpoint");
if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) {
actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editBreakpoint', "Edit {0}...", breakpointType), '', true, async () => {
if (element instanceof Breakpoint) {
const editor = await openBreakpointSource(element, false, false, true, this.debugService, this.editorService);
if (editor) {
const codeEditor = editor.getControl();
if (isCodeEditor(codeEditor)) {
codeEditor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
}
}
} else {
this.debugService.getViewModel().setSelectedBreakpoint(element);
this.onBreakpointsChange();
}
}));
actions.push(new Separator());
}
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService));
if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length >= 1) {
actions.push(new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
actions.push(new Separator());
actions.push(new EnableAllBreakpointsAction(EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
actions.push(new DisableAllBreakpointsAction(DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
}
actions.push(new Separator());
actions.push(new ReapplyBreakpointsAction(ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL, this.debugService, this.keybindingService));
}
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService));
if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) {
actions.push(new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
actions.push(new Separator());
actions.push(new EnableAllBreakpointsAction(EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
actions.push(new DisableAllBreakpointsAction(DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
}
actions.push(new Separator());
actions.push(new ReapplyBreakpointsAction(ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL, this.debugService, this.keybindingService));
this.contextMenuService.showContextMenu({
getAnchor: () => e.anchor,
getActions: () => actions,
@@ -285,7 +295,7 @@ class BreakpointsDelegate implements IListVirtualDelegate<BreakpointItem> {
return BreakpointsRenderer.ID;
}
if (element instanceof FunctionBreakpoint) {
const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint();
const selected = this.debugService.getViewModel().getSelectedBreakpoint();
if (!element.name || (selected && selected.getId() === element.getId())) {
return FunctionBreakpointInputRenderer.ID;
}
@@ -293,6 +303,10 @@ class BreakpointsDelegate implements IListVirtualDelegate<BreakpointItem> {
return FunctionBreakpointsRenderer.ID;
}
if (element instanceof ExceptionBreakpoint) {
const selected = this.debugService.getViewModel().getSelectedBreakpoint();
if (selected && selected.getId() === element.getId()) {
return ExceptionBreakpointInputRenderer.ID;
}
return ExceptionBreakpointsRenderer.ID;
}
if (element instanceof DataBreakpoint) {
@@ -320,7 +334,11 @@ interface IBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {
filePath: HTMLElement;
}
interface IInputTemplateData {
interface IExceptionBreakpointTemplateData extends IBaseBreakpointTemplateData {
condition: HTMLElement;
}
interface IFunctionBreakpointInputTemplateData {
inputBox: InputBox;
checkbox: HTMLInputElement;
icon: HTMLElement;
@@ -329,6 +347,14 @@ interface IInputTemplateData {
toDispose: IDisposable[];
}
interface IExceptionBreakpointInputTemplateData {
inputBox: InputBox;
checkbox: HTMLInputElement;
breakpoint: IExceptionBreakpoint;
reactedOnEvent: boolean;
toDispose: IDisposable[];
}
class BreakpointsRenderer implements IListRenderer<IBreakpoint, IBreakpointTemplateData> {
constructor(
@@ -379,8 +405,8 @@ class BreakpointsRenderer implements IListRenderer<IBreakpoint, IBreakpointTempl
data.filePath.textContent = this.labelService.getUriLabel(resources.dirname(breakpoint.uri), { relative: true });
data.checkbox.checked = breakpoint.enabled;
const { message, className } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), breakpoint, this.labelService);
data.icon.className = `codicon ${className}`;
const { message, icon } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), breakpoint, this.labelService);
data.icon.className = ThemeIcon.asClassName(icon);
data.breakpoint.title = breakpoint.message || message || '';
const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;
@@ -394,7 +420,7 @@ class BreakpointsRenderer implements IListRenderer<IBreakpoint, IBreakpointTempl
}
}
class ExceptionBreakpointsRenderer implements IListRenderer<IExceptionBreakpoint, IBaseBreakpointTemplateData> {
class ExceptionBreakpointsRenderer implements IListRenderer<IExceptionBreakpoint, IExceptionBreakpointTemplateData> {
constructor(
private debugService: IDebugService
@@ -408,8 +434,8 @@ class ExceptionBreakpointsRenderer implements IListRenderer<IExceptionBreakpoint
return ExceptionBreakpointsRenderer.ID;
}
renderTemplate(container: HTMLElement): IBaseBreakpointTemplateData {
const data: IBreakpointTemplateData = Object.create(null);
renderTemplate(container: HTMLElement): IExceptionBreakpointTemplateData {
const data: IExceptionBreakpointTemplateData = Object.create(null);
data.breakpoint = dom.append(container, $('.breakpoint'));
data.checkbox = createCheckbox();
@@ -421,19 +447,22 @@ class ExceptionBreakpointsRenderer implements IListRenderer<IExceptionBreakpoint
dom.append(data.breakpoint, data.checkbox);
data.name = dom.append(data.breakpoint, $('span.name'));
data.condition = dom.append(data.breakpoint, $('span.condition'));
data.breakpoint.classList.add('exception');
return data;
}
renderElement(exceptionBreakpoint: IExceptionBreakpoint, index: number, data: IBaseBreakpointTemplateData): void {
renderElement(exceptionBreakpoint: IExceptionBreakpoint, index: number, data: IExceptionBreakpointTemplateData): void {
data.context = exceptionBreakpoint;
data.name.textContent = exceptionBreakpoint.label || `${exceptionBreakpoint.filter} exceptions`;
data.breakpoint.title = data.name.textContent;
data.checkbox.checked = exceptionBreakpoint.enabled;
data.condition.textContent = exceptionBreakpoint.condition || '';
data.condition.title = nls.localize('expressionCondition', "Expression condition: {0}", exceptionBreakpoint.condition);
}
disposeTemplate(templateData: IBaseBreakpointTemplateData): void {
disposeTemplate(templateData: IExceptionBreakpointTemplateData): void {
dispose(templateData.toDispose);
}
}
@@ -475,8 +504,8 @@ class FunctionBreakpointsRenderer implements IListRenderer<FunctionBreakpoint, I
renderElement(functionBreakpoint: FunctionBreakpoint, _index: number, data: IBaseBreakpointWithIconTemplateData): void {
data.context = functionBreakpoint;
data.name.textContent = functionBreakpoint.name;
const { className, message } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), functionBreakpoint, this.labelService);
data.icon.className = `codicon ${className}`;
const { icon, message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), functionBreakpoint, this.labelService);
data.icon.className = ThemeIcon.asClassName(icon);
data.icon.title = message ? message : '';
data.checkbox.checked = functionBreakpoint.enabled;
data.breakpoint.title = message ? message : '';
@@ -531,8 +560,8 @@ class DataBreakpointsRenderer implements IListRenderer<DataBreakpoint, IBaseBrea
renderElement(dataBreakpoint: DataBreakpoint, _index: number, data: IBaseBreakpointWithIconTemplateData): void {
data.context = dataBreakpoint;
data.name.textContent = dataBreakpoint.description;
const { className, message } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), dataBreakpoint, this.labelService);
data.icon.className = `codicon ${className}`;
const { icon, message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), dataBreakpoint, this.labelService);
data.icon.className = ThemeIcon.asClassName(icon);
data.icon.title = message ? message : '';
data.checkbox.checked = dataBreakpoint.enabled;
data.breakpoint.title = message ? message : '';
@@ -550,7 +579,7 @@ class DataBreakpointsRenderer implements IListRenderer<DataBreakpoint, IBaseBrea
}
}
class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoint, IInputTemplateData> {
class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoint, IFunctionBreakpointInputTemplateData> {
constructor(
private debugService: IDebugService,
@@ -567,8 +596,8 @@ class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoi
return FunctionBreakpointInputRenderer.ID;
}
renderTemplate(container: HTMLElement): IInputTemplateData {
const template: IInputTemplateData = Object.create(null);
renderTemplate(container: HTMLElement): IFunctionBreakpointInputTemplateData {
const template: IFunctionBreakpointInputTemplateData = Object.create(null);
const breakpoint = dom.append(container, $('.breakpoint'));
template.icon = $('.icon');
@@ -587,7 +616,7 @@ class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoi
const wrapUp = (renamed: boolean) => {
if (!template.reactedOnEvent) {
template.reactedOnEvent = true;
this.debugService.getViewModel().setSelectedFunctionBreakpoint(undefined);
this.debugService.getViewModel().setSelectedBreakpoint(undefined);
if (inputBox.value && (renamed || template.breakpoint.name)) {
this.debugService.renameFunctionBreakpoint(template.breakpoint.getId(), renamed ? inputBox.value : template.breakpoint.name);
} else {
@@ -619,12 +648,12 @@ class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoi
return template;
}
renderElement(functionBreakpoint: FunctionBreakpoint, _index: number, data: IInputTemplateData): void {
renderElement(functionBreakpoint: FunctionBreakpoint, _index: number, data: IFunctionBreakpointInputTemplateData): void {
data.breakpoint = functionBreakpoint;
data.reactedOnEvent = false;
const { className, message } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), functionBreakpoint, this.labelService);
const { icon, message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), functionBreakpoint, this.labelService);
data.icon.className = `codicon ${className}`;
data.icon.className = ThemeIcon.asClassName(icon);
data.icon.title = message ? message : '';
data.checkbox.checked = functionBreakpoint.enabled;
data.checkbox.disabled = true;
@@ -635,7 +664,89 @@ class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoi
}, 0);
}
disposeTemplate(templateData: IInputTemplateData): void {
disposeTemplate(templateData: IFunctionBreakpointInputTemplateData): void {
dispose(templateData.toDispose);
}
}
class ExceptionBreakpointInputRenderer implements IListRenderer<IExceptionBreakpoint, IExceptionBreakpointInputTemplateData> {
constructor(
private debugService: IDebugService,
private contextViewService: IContextViewService,
private themeService: IThemeService
) {
// noop
}
static readonly ID = 'exceptionbreakpointinput';
get templateId() {
return ExceptionBreakpointInputRenderer.ID;
}
renderTemplate(container: HTMLElement): IExceptionBreakpointInputTemplateData {
const template: IExceptionBreakpointInputTemplateData = Object.create(null);
const breakpoint = dom.append(container, $('.breakpoint'));
breakpoint.classList.add('exception');
template.checkbox = createCheckbox();
dom.append(breakpoint, template.checkbox);
const inputBoxContainer = dom.append(breakpoint, $('.inputBoxContainer'));
const inputBox = new InputBox(inputBoxContainer, this.contextViewService, {
placeholder: nls.localize('exceptionBreakpointPlaceholder', "Break when expression evaluates to true"),
ariaLabel: nls.localize('exceptionBreakpointAriaLabel', "Type exception breakpoint condition")
});
const styler = attachInputBoxStyler(inputBox, this.themeService);
const toDispose: IDisposable[] = [inputBox, styler];
const wrapUp = (success: boolean) => {
if (!template.reactedOnEvent) {
template.reactedOnEvent = true;
this.debugService.getViewModel().setSelectedBreakpoint(undefined);
let newCondition = template.breakpoint.condition;
if (success) {
newCondition = inputBox.value !== '' ? inputBox.value : undefined;
}
this.debugService.setExceptionBreakpointCondition(template.breakpoint, newCondition);
}
};
toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {
const isEscape = e.equals(KeyCode.Escape);
const isEnter = e.equals(KeyCode.Enter);
if (isEscape || isEnter) {
e.preventDefault();
e.stopPropagation();
wrapUp(isEnter);
}
}));
toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
// Need to react with a timeout on the blur event due to possible concurent splices #56443
setTimeout(() => {
wrapUp(true);
});
}));
template.inputBox = inputBox;
template.toDispose = toDispose;
return template;
}
renderElement(exceptionBreakpoint: ExceptionBreakpoint, _index: number, data: IExceptionBreakpointInputTemplateData): void {
data.breakpoint = exceptionBreakpoint;
data.reactedOnEvent = false;
data.checkbox.checked = exceptionBreakpoint.enabled;
data.checkbox.disabled = true;
data.inputBox.value = exceptionBreakpoint.condition || '';
setTimeout(() => {
data.inputBox.focus();
data.inputBox.select();
}, 0);
}
disposeTemplate(templateData: IExceptionBreakpointInputTemplateData): void {
dispose(templateData.toDispose);
}
}
@@ -664,14 +775,14 @@ class BreakpointsAccessibilityProvider implements IListAccessibilityProvider<Bre
return element.toString();
}
const { message } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), element as IBreakpoint | IDataBreakpoint | IFunctionBreakpoint, this.labelService);
const { message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), element as IBreakpoint | IDataBreakpoint | IFunctionBreakpoint, this.labelService);
const toString = element.toString();
return message ? `${toString}, ${message}` : toString;
}
}
export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): Promise<IEditorPane | undefined> {
export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, pinned: boolean, debugService: IDebugService, editorService: IEditorService): Promise<IEditorPane | undefined> {
if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) {
return Promise.resolve(undefined);
}
@@ -695,17 +806,17 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea
selection,
revealIfOpened: true,
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport,
pinned: !preserveFocus
pinned
}
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
}
export function getBreakpointMessageAndClassName(state: State, breakpointsActivated: boolean, breakpoint: IBreakpoint | IFunctionBreakpoint | IDataBreakpoint, labelService?: ILabelService): { message?: string, className: string } {
export function getBreakpointMessageAndIcon(state: State, breakpointsActivated: boolean, breakpoint: IBreakpoint | IFunctionBreakpoint | IDataBreakpoint, labelService?: ILabelService): { message?: string, icon: ThemeIcon } {
const debugActive = state === State.Running || state === State.Stopped;
if (!breakpoint.enabled || !breakpointsActivated) {
return {
className: breakpoint instanceof DataBreakpoint ? 'codicon-debug-breakpoint-data-disabled' : breakpoint instanceof FunctionBreakpoint ? 'codicon-debug-breakpoint-function-disabled' : breakpoint.logMessage ? 'codicon-debug-breakpoint-log-disabled' : 'codicon-debug-breakpoint-disabled',
icon: breakpoint instanceof DataBreakpoint ? icons.debugBreakpointDataDisabled : breakpoint instanceof FunctionBreakpoint ? icons.debugBreakpointFunctionDisabled : breakpoint.logMessage ? icons.debugBreakpointLogDisabled : icons.debugBreakpointDisabled,
message: breakpoint.logMessage ? nls.localize('disabledLogpoint', "Disabled Logpoint") : nls.localize('disabledBreakpoint', "Disabled Breakpoint"),
};
}
@@ -715,7 +826,7 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva
};
if (debugActive && !breakpoint.verified) {
return {
className: breakpoint instanceof DataBreakpoint ? 'codicon-debug-breakpoint-data-unverified' : breakpoint instanceof FunctionBreakpoint ? 'codicon-debug-breakpoint-function-unverified' : breakpoint.logMessage ? 'codicon-debug-breakpoint-log-unverified' : 'codicon-debug-breakpoint-unverified',
icon: breakpoint instanceof DataBreakpoint ? icons.debugBreakpointDataUnverified : breakpoint instanceof FunctionBreakpoint ? icons.debugBreakpointFunctionUnverified : breakpoint.logMessage ? icons.debugBreakpointLogUnverified : icons.debugBreakpointUnverified,
message: ('message' in breakpoint && breakpoint.message) ? breakpoint.message : (breakpoint.logMessage ? nls.localize('unverifiedLogpoint', "Unverified Logpoint") : nls.localize('unverifiedBreakopint', "Unverified Breakpoint")),
};
}
@@ -723,13 +834,13 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva
if (breakpoint instanceof FunctionBreakpoint) {
if (!breakpoint.supported) {
return {
className: 'codicon-debug-breakpoint-function-unverified',
icon: icons.debugBreakpointFunctionUnverified,
message: nls.localize('functionBreakpointUnsupported', "Function breakpoints not supported by this debug type"),
};
}
return {
className: 'codicon-debug-breakpoint-function',
icon: icons.debugBreakpointFunction,
message: breakpoint.message || nls.localize('functionBreakpoint', "Function Breakpoint")
};
}
@@ -737,13 +848,13 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva
if (breakpoint instanceof DataBreakpoint) {
if (!breakpoint.supported) {
return {
className: 'codicon-debug-breakpoint-data-unverified',
icon: icons.debugBreakpointDataUnverified,
message: nls.localize('dataBreakpointUnsupported', "Data breakpoints not supported by this debug type"),
};
}
return {
className: 'codicon-debug-breakpoint-data',
icon: icons.debugBreakpointData,
message: breakpoint.message || nls.localize('dataBreakpoint', "Data Breakpoint")
};
}
@@ -753,7 +864,7 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva
if (!breakpoint.supported) {
return {
className: 'codicon-debug-breakpoint-unsupported',
icon: icons.debugBreakpointUnsupported,
message: nls.localize('breakpointUnsupported', "Breakpoints of this type are not supported by the debugger"),
};
}
@@ -762,21 +873,21 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva
messages.push(nls.localize('logMessage', "Log Message: {0}", breakpoint.logMessage));
}
if (breakpoint.condition) {
messages.push(nls.localize('expression', "Expression: {0}", breakpoint.condition));
messages.push(nls.localize('expression', "Expression condition: {0}", breakpoint.condition));
}
if (breakpoint.hitCondition) {
messages.push(nls.localize('hitCount', "Hit Count: {0}", breakpoint.hitCondition));
}
return {
className: breakpoint.logMessage ? 'codicon-debug-breakpoint-log' : 'codicon-debug-breakpoint-conditional',
icon: breakpoint.logMessage ? icons.debugBreakpointLog : icons.debugBreakpointConditional,
message: appendMessage(messages.join('\n'))
};
}
const message = ('message' in breakpoint && breakpoint.message) ? breakpoint.message : breakpoint instanceof Breakpoint && labelService ? labelService.getUriLabel(breakpoint.uri) : nls.localize('breakpoint', "Breakpoint");
return {
className: 'codicon-debug-breakpoint',
icon: icons.debugBreakpoint,
message
};
}

View File

@@ -5,9 +5,9 @@
import { Constants } from 'vs/base/common/uint';
import { Range, IRange } from 'vs/editor/common/core/range';
import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model';
import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions, OverviewRulerLane } from 'vs/editor/common/model';
import { IDebugService, IStackFrame } from 'vs/workbench/contrib/debug/common/debug';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { registerThemingParticipant, themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
import { localize } from 'vs/nls';
import { Event } from 'vs/base/common/event';
@@ -16,17 +16,28 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { distinct } from 'vs/base/common/arrays';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons';
const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#ffff0033' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.'));
const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#7abd7a4d' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.'));
const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
// we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement.
const TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = {
glyphMarginClassName: 'codicon-debug-stackframe',
stickiness
glyphMarginClassName: ThemeIcon.asClassName(debugStackframe),
stickiness,
overviewRuler: {
position: OverviewRulerLane.Full,
color: themeColorFromId(topStackFrameColor)
}
};
const FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = {
glyphMarginClassName: 'codicon-debug-stackframe-focused',
stickiness
glyphMarginClassName: ThemeIcon.asClassName(debugStackframeFocused),
stickiness,
overviewRuler: {
position: OverviewRulerLane.Full,
color: themeColorFromId(focusedStackFrameColor)
}
};
const TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = {
isWholeLine: true,
@@ -152,6 +163,3 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.monaco-editor .view-overlays .debug-focused-stack-frame-line { background: ${focusedStackFrame}; }`);
}
});
const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#ffff0033' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.'));
const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#7abd7a4d' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.'));

View File

@@ -36,7 +36,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { CollapseAction } from 'vs/workbench/browser/viewlet';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
@@ -46,6 +46,7 @@ import { posix } from 'vs/base/common/path';
import { ITreeCompressionDelegate } from 'vs/base/browser/ui/tree/asyncDataTree';
import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree';
import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons';
const $ = dom.$;
@@ -206,7 +207,7 @@ export class CallStackView extends ViewPane {
getActions(): IAction[] {
if (this.stateMessage.hidden) {
return [new CollapseAction(() => this.tree, true, 'explorer-action codicon-collapse-all')];
return [new CollapseAction(() => this.tree, true, 'explorer-action ' + ThemeIcon.asClassName(icons.debugCollapseAll))];
}
return [];
@@ -501,7 +502,7 @@ class SessionsRenderer implements ICompressibleTreeRenderer<IDebugSession, Fuzzy
renderTemplate(container: HTMLElement): ISessionTemplateData {
const session = dom.append(container, $('.session'));
dom.append(session, $('.codicon.codicon-bug'));
dom.append(session, $(ThemeIcon.asCSSSelector(icons.callstackViewSession)));
const name = dom.append(session, $('.name'));
const stateLabel = dom.append(session, $('span.state.label.monaco-count-badge.long'));
const label = new HighlightedLabel(name, false);
@@ -659,7 +660,7 @@ class StackFramesRenderer implements ICompressibleTreeRenderer<IStackFrame, Fuzz
data.actionBar.clear();
if (hasActions) {
const action = new Action('debug.callStack.restartFrame', nls.localize('restartFrame', "Restart Frame"), 'codicon-debug-restart-frame', true, async () => {
const action = new Action('debug.callStack.restartFrame', nls.localize('restartFrame', "Restart Frame"), ThemeIcon.asClassName(icons.debugRestartFrame), true, async () => {
try {
await stackFrame.restart();
} catch (e) {
@@ -918,7 +919,7 @@ class CallStackDataSource implements IAsyncDataSource<IDebugModel, CallStackItem
if (thread.stoppedDetails && thread.stoppedDetails.framesErrorMessage) {
callStack = callStack.concat([thread.stoppedDetails.framesErrorMessage]);
}
if (thread.stoppedDetails && thread.stoppedDetails.totalFrames && thread.stoppedDetails.totalFrames > callStack.length && callStack.length > 1) {
if (!thread.reachedEndOfCallStack && thread.stoppedDetails) {
callStack = callStack.concat([new ThreadAndSessionIds(thread.session.getId(), thread.threadId)]);
}
@@ -994,7 +995,7 @@ class StopAction extends Action {
private readonly session: IDebugSession,
@ICommandService private readonly commandService: ICommandService
) {
super(`action.${STOP_ID}`, STOP_LABEL, 'debug-action codicon-debug-stop');
super(`action.${STOP_ID}`, STOP_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugStop));
}
public run(): Promise<any> {
@@ -1008,7 +1009,7 @@ class DisconnectAction extends Action {
private readonly session: IDebugSession,
@ICommandService private readonly commandService: ICommandService
) {
super(`action.${DISCONNECT_ID}`, DISCONNECT_LABEL, 'debug-action codicon-debug-disconnect');
super(`action.${DISCONNECT_ID}`, DISCONNECT_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugDisconnect));
}
public run(): Promise<any> {
@@ -1022,7 +1023,7 @@ class RestartAction extends Action {
private readonly session: IDebugSession,
@ICommandService private readonly commandService: ICommandService
) {
super(`action.${RESTART_SESSION_ID}`, RESTART_LABEL, 'debug-action codicon-debug-restart');
super(`action.${RESTART_SESSION_ID}`, RESTART_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugRestart));
}
public run(): Promise<any> {
@@ -1036,7 +1037,7 @@ class StepOverAction extends Action {
private readonly thread: IThread,
@ICommandService private readonly commandService: ICommandService
) {
super(`action.${STEP_OVER_ID}`, STEP_OVER_LABEL, 'debug-action codicon-debug-step-over', thread.stopped);
super(`action.${STEP_OVER_ID}`, STEP_OVER_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugStepOver), thread.stopped);
}
public run(): Promise<any> {
@@ -1050,7 +1051,7 @@ class StepIntoAction extends Action {
private readonly thread: IThread,
@ICommandService private readonly commandService: ICommandService
) {
super(`action.${STEP_INTO_ID}`, STEP_INTO_LABEL, 'debug-action codicon-debug-step-into', thread.stopped);
super(`action.${STEP_INTO_ID}`, STEP_INTO_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugStepInto), thread.stopped);
}
public run(): Promise<any> {
@@ -1064,7 +1065,7 @@ class StepOutAction extends Action {
private readonly thread: IThread,
@ICommandService private readonly commandService: ICommandService
) {
super(`action.${STEP_OUT_ID}`, STEP_OUT_LABEL, 'debug-action codicon-debug-step-out', thread.stopped);
super(`action.${STEP_OUT_ID}`, STEP_OUT_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugStepOut), thread.stopped);
}
public run(): Promise<any> {
@@ -1078,7 +1079,7 @@ class PauseAction extends Action {
private readonly thread: IThread,
@ICommandService private readonly commandService: ICommandService
) {
super(`action.${PAUSE_ID}`, PAUSE_LABEL, 'debug-action codicon-debug-pause', !thread.stopped);
super(`action.${PAUSE_ID}`, PAUSE_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugPause), !thread.stopped);
}
public run(): Promise<any> {
@@ -1092,7 +1093,7 @@ class ContinueAction extends Action {
private readonly thread: IThread,
@ICommandService private readonly commandService: ICommandService
) {
super(`action.${CONTINUE_ID}`, CONTINUE_LABEL, 'debug-action codicon-debug-continue', thread.stopped);
super(`action.${CONTINUE_ID}`, CONTINUE_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugContinue), thread.stopped);
}
public run(): Promise<any> {

View File

@@ -49,10 +49,10 @@ import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/pl
import { StartDebugQuickAccessProvider } from 'vs/workbench/contrib/debug/browser/debugQuickAccess';
import { DebugProgressContribution } from 'vs/workbench/contrib/debug/browser/debugProgress';
import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debugTitle';
import { Codicon } from 'vs/base/common/codicons';
import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors';
import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debugEditorContribution';
import { FileAccess } from 'vs/base/common/network';
import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons';
const registry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionRegistryExtensions.WorkbenchActions);
const debugCategory = nls.localize('debugCategory', "Debug");
@@ -153,16 +153,16 @@ function registerCommandsAndActions(): void {
});
};
registerDebugToolBarItem(CONTINUE_ID, CONTINUE_LABEL, 10, { id: 'codicon/debug-continue' }, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(PAUSE_ID, PAUSE_LABEL, 10, { id: 'codicon/debug-pause' }, CONTEXT_DEBUG_STATE.notEqualsTo('stopped'), CONTEXT_DEBUG_STATE.isEqualTo('running'));
registerDebugToolBarItem(STOP_ID, STOP_LABEL, 70, { id: 'codicon/debug-stop' }, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated());
registerDebugToolBarItem(DISCONNECT_ID, DISCONNECT_LABEL, 70, { id: 'codicon/debug-disconnect' }, CONTEXT_FOCUSED_SESSION_IS_ATTACH);
registerDebugToolBarItem(STEP_OVER_ID, STEP_OVER_LABEL, 20, { id: 'codicon/debug-step-over' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(STEP_INTO_ID, STEP_INTO_LABEL, 30, { id: 'codicon/debug-step-into' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(STEP_OUT_ID, STEP_OUT_LABEL, 40, { id: 'codicon/debug-step-out' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(RESTART_SESSION_ID, RESTART_LABEL, 60, { id: 'codicon/debug-restart' });
registerDebugToolBarItem(STEP_BACK_ID, nls.localize('stepBackDebug', "Step Back"), 50, { id: 'codicon/debug-step-back' }, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(REVERSE_CONTINUE_ID, nls.localize('reverseContinue', "Reverse"), 60, { id: 'codicon/debug-reverse-continue' }, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(CONTINUE_ID, CONTINUE_LABEL, 10, icons.debugContinue, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(PAUSE_ID, PAUSE_LABEL, 10, icons.debugPause, CONTEXT_DEBUG_STATE.notEqualsTo('stopped'), CONTEXT_DEBUG_STATE.isEqualTo('running'));
registerDebugToolBarItem(STOP_ID, STOP_LABEL, 70, icons.debugStop, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated());
registerDebugToolBarItem(DISCONNECT_ID, DISCONNECT_LABEL, 70, icons.debugDisconnect, CONTEXT_FOCUSED_SESSION_IS_ATTACH);
registerDebugToolBarItem(STEP_OVER_ID, STEP_OVER_LABEL, 20, icons.debugStepOver, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(STEP_INTO_ID, STEP_INTO_LABEL, 30, icons.debugStepInto, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(STEP_OUT_ID, STEP_OUT_LABEL, 40, icons.debugStepOut, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(RESTART_SESSION_ID, RESTART_LABEL, 60, icons.debugRestart);
registerDebugToolBarItem(STEP_BACK_ID, nls.localize('stepBackDebug', "Step Back"), 50, icons.debugStepBack, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
registerDebugToolBarItem(REVERSE_CONTINUE_ID, nls.localize('reverseContinue', "Reverse"), 60, icons.debugReverseContinue, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped'));
// Debug callstack context menu
const registerDebugViewMenuItem = (menuId: MenuId, id: string, title: string, order: number, when?: ContextKeyExpression, precondition?: ContextKeyExpression, group = 'navigation') => {
@@ -460,7 +460,7 @@ function registerDebugPanel(): void {
const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
id: DEBUG_PANEL_ID,
name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'),
icon: Codicon.debugConsole.classNames,
icon: icons.debugConsoleViewIcon,
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]),
storageId: DEBUG_PANEL_ID,
focusCommand: { id: OpenDebugConsoleAction.ID },
@@ -471,7 +471,7 @@ function registerDebugPanel(): void {
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews([{
id: REPL_VIEW_ID,
name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'),
containerIcon: Codicon.debugConsole.classNames,
containerIcon: icons.debugConsoleViewIcon,
canToggleVisibility: false,
canMoveView: true,
when: CONTEXT_DEBUGGERS_AVAILABLE,
@@ -481,12 +481,13 @@ function registerDebugPanel(): void {
registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugConsoleAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Y }), 'View: Debug Console', CATEGORIES.View.value, CONTEXT_DEBUGGERS_AVAILABLE);
}
function registerDebugView(): void {
const viewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
id: VIEWLET_ID,
name: nls.localize('run', "Run"),
ctorDescriptor: new SyncDescriptor(DebugViewPaneContainer),
icon: Codicon.debugAlt.classNames,
icon: icons.runViewIcon,
alwaysUseContainerInfo: true,
order: 2
}, ViewContainerLocation.Sidebar);
@@ -494,12 +495,12 @@ function registerDebugView(): void {
// Register default debug views
const viewsRegistry = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry);
viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);
viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer);
viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);
viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), containerIcon: icons.variablesViewIcon, ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), containerIcon: icons.watchViewIcon, ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), containerIcon: icons.callStackViewIcon, ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), containerIcon: icons.breakpointsViewIcon, ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);
viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: icons.runViewIcon, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer);
viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: icons.loadedScriptsViewIcon, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);
}
function registerConfiguration(): void {

View File

@@ -7,13 +7,13 @@ import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
import { RGBA, Color } from 'vs/base/common/color';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ansiColorIdentifiers } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
/**
* @param text The content to stylize.
* @returns An {@link HTMLSpanElement} that contains the potentially stylized text.
*/
export function handleANSIOutput(text: string, linkDetector: LinkDetector, themeService: IThemeService, debugSession: IDebugSession): HTMLSpanElement {
export function handleANSIOutput(text: string, linkDetector: LinkDetector, themeService: IThemeService, workspaceFolder: IWorkspaceFolder | undefined): HTMLSpanElement {
const root: HTMLSpanElement = document.createElement('span');
const textLength: number = text.length;
@@ -54,7 +54,7 @@ export function handleANSIOutput(text: string, linkDetector: LinkDetector, theme
if (sequenceFound) {
// Flush buffer with previous styles.
appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, debugSession, customFgColor, customBgColor);
appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, workspaceFolder, customFgColor, customBgColor);
buffer = '';
@@ -100,7 +100,7 @@ export function handleANSIOutput(text: string, linkDetector: LinkDetector, theme
// Flush remaining text buffer if not empty.
if (buffer) {
appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, debugSession, customFgColor, customBgColor);
appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, workspaceFolder, customFgColor, customBgColor);
}
return root;
@@ -268,7 +268,7 @@ export function appendStylizedStringToContainer(
stringContent: string,
cssClasses: string[],
linkDetector: LinkDetector,
debugSession: IDebugSession,
workspaceFolder: IWorkspaceFolder | undefined,
customTextColor?: RGBA,
customBackgroundColor?: RGBA
): void {
@@ -276,7 +276,7 @@ export function appendStylizedStringToContainer(
return;
}
const container = linkDetector.linkify(stringContent, true, debugSession.root);
const container = linkDetector.linkify(stringContent, true, workspaceFolder);
container.className = cssClasses.join(' ');
if (customTextColor) {

View File

@@ -12,7 +12,7 @@ import { SelectBox, ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selec
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IDebugService, IDebugSession, IDebugConfiguration, IConfig, ILaunch } from 'vs/workbench/contrib/debug/common/debug';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { selectBorder, selectBackground } from 'vs/platform/theme/common/colorRegistry';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
@@ -20,6 +20,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ADD_CONFIGURATION_ID } from 'vs/workbench/contrib/debug/browser/debugCommands';
import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { debugStart } from 'vs/workbench/contrib/debug/browser/debugIcons';
const $ = dom.$;
@@ -68,7 +69,7 @@ export class StartDebugActionViewItem implements IActionViewItem {
render(container: HTMLElement): void {
this.container = container;
container.classList.add('start-debug-action-item');
this.start = dom.append(container, $('.codicon.codicon-debug-start'));
this.start = dom.append(container, $(ThemeIcon.asCSSSelector(debugStart)));
this.start.title = this.action.label;
this.start.setAttribute('role', 'button');
this.start.tabIndex = 0;
@@ -188,23 +189,35 @@ export class StartDebugActionViewItem implements IActionViewItem {
});
});
if (this.options.length === 0) {
this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: async () => false });
} else {
this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) });
disabledIdxs.push(this.options.length - 1);
}
this.providers.forEach(p => {
if (p.type === manager.selectedConfiguration.type) {
// Only take 3 elements from the recent dynamic configurations to not clutter the dropdown
manager.getRecentDynamicConfigurations().slice(0, 3).forEach(({ name, type }) => {
if (type === manager.selectedConfiguration.type && manager.selectedConfiguration.name === name) {
this.selected = this.options.length;
}
this.options.push({
label: name,
handler: async () => {
await manager.selectConfiguration(undefined, name, undefined, { type });
return true;
}
});
});
if (this.options.length === 0) {
this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: async () => false });
}
this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) });
disabledIdxs.push(this.options.length - 1);
this.providers.forEach(p => {
this.options.push({
label: `${p.label}...`, handler: async () => {
label: `${p.label}...`,
handler: async () => {
const picked = await p.pick();
if (picked) {
await manager.selectConfiguration(picked.launch, picked.config.name, picked.config, p.type);
await manager.selectConfiguration(picked.launch, picked.config.name, picked.config, { type: p.type });
return true;
}
return false;
@@ -212,11 +225,6 @@ export class StartDebugActionViewItem implements IActionViewItem {
});
});
if (this.providers.length > 0) {
this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) });
disabledIdxs.push(this.options.length - 1);
}
manager.getLaunches().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({

View File

@@ -14,6 +14,8 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { deepClone } from 'vs/base/common/objects';
import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
export abstract class AbstractDebugAction extends Action {
@@ -64,7 +66,7 @@ export class ConfigureAction extends AbstractDebugAction {
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IQuickInputService private readonly quickInputService: IQuickInputService
) {
super(id, label, 'debug-action codicon codicon-gear', debugService, keybindingService);
super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.debugConfigure), debugService, keybindingService);
this._register(debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateClass()));
this.updateClass();
}
@@ -79,7 +81,7 @@ export class ConfigureAction extends AbstractDebugAction {
private updateClass(): void {
const configurationManager = this.debugService.getConfigurationManager();
this.class = configurationManager.selectedConfiguration.name ? 'debug-action codicon codicon-gear' : 'debug-action codicon codicon-gear notification';
this.class = configurationManager.selectedConfiguration.name ? 'debug-action' + ThemeIcon.asClassName(icons.debugConfigure) : 'debug-action ' + ThemeIcon.asClassName(icons.debugConfigure) + ' notification';
}
async run(): Promise<any> {
@@ -132,7 +134,8 @@ export class StartAction extends AbstractDebugAction {
}
async run(): Promise<boolean> {
let { launch, name, config } = this.debugService.getConfigurationManager().selectedConfiguration;
let { launch, name, getConfig } = this.debugService.getConfigurationManager().selectedConfiguration;
const config = await getConfig();
const clonedConfig = deepClone(config);
return this.debugService.startDebugging(launch, clonedConfig || name, { noDebug: this.isNoDebug() });
}
@@ -147,10 +150,10 @@ export class StartAction extends AbstractDebugAction {
if (debugService.state === State.Initializing) {
return false;
}
let { name, config } = debugService.getConfigurationManager().selectedConfiguration;
let nameToStart = name || config?.name;
let { name, launch } = debugService.getConfigurationManager().selectedConfiguration;
let nameToStart = name;
if (sessions.some(s => s.configuration.name === nameToStart)) {
if (sessions.some(s => s.configuration.name === nameToStart && s.root === launch?.workspace)) {
// There is already a debug session running and we do not have any launch configuration selected
return false;
}
@@ -209,7 +212,7 @@ export class RemoveAllBreakpointsAction extends AbstractDebugAction {
static readonly LABEL = nls.localize('removeAllBreakpoints', "Remove All Breakpoints");
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
super(id, label, 'debug-action codicon-close-all', debugService, keybindingService);
super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.breakpointsRemoveAll), debugService, keybindingService);
this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
}
@@ -267,7 +270,7 @@ export class ToggleBreakpointsActivatedAction extends AbstractDebugAction {
static readonly DEACTIVATE_LABEL = nls.localize('deactivateBreakpoints', "Deactivate Breakpoints");
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
super(id, label, 'debug-action codicon-activate-breakpoints', debugService, keybindingService);
super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.breakpointsActivate), debugService, keybindingService);
this.updateLabel(this.debugService.getModel().areBreakpointsActivated() ? ToggleBreakpointsActivatedAction.DEACTIVATE_LABEL : ToggleBreakpointsActivatedAction.ACTIVATE_LABEL);
this._register(this.debugService.getModel().onDidChangeBreakpoints(() => {
@@ -310,7 +313,7 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction {
static readonly LABEL = nls.localize('addFunctionBreakpoint', "Add Function Breakpoint");
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
super(id, label, 'debug-action codicon-add', debugService, keybindingService);
super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.watchExpressionsAddFuncBreakpoint), debugService, keybindingService);
this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
}
@@ -319,7 +322,7 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction {
}
protected isEnabled(_: State): boolean {
return !this.debugService.getViewModel().getSelectedFunctionBreakpoint()
return !this.debugService.getViewModel().getSelectedBreakpoint()
&& this.debugService.getModel().getFunctionBreakpoints().every(fbp => !!fbp.name);
}
}
@@ -329,7 +332,7 @@ export class AddWatchExpressionAction extends AbstractDebugAction {
static readonly LABEL = nls.localize('addWatchExpression', "Add Expression");
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
super(id, label, 'debug-action codicon-add', debugService, keybindingService);
super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.watchExpressionsAdd), debugService, keybindingService);
this._register(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement()));
this._register(this.debugService.getViewModel().onDidSelectExpression(() => this.updateEnablement()));
}
@@ -349,7 +352,7 @@ export class RemoveAllWatchExpressionsAction extends AbstractDebugAction {
static readonly LABEL = nls.localize('removeAllWatchExpressions', "Remove All Expressions");
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
super(id, label, 'debug-action codicon-close-all', debugService, keybindingService);
super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.watchExpressionsRemoveAll), debugService, keybindingService);
this._register(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement()));
}

View File

@@ -0,0 +1,268 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import * as strings from 'vs/base/common/strings';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { ITextModel } from 'vs/editor/common/model';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IDebugConfiguration, IConfig, IDebugAdapterDescriptorFactory, IDebugAdapter, IDebugSession, IAdapterDescriptor, IDebugAdapterFactory, CONTEXT_DEBUGGERS_AVAILABLE, IAdapterManager } from 'vs/workbench/contrib/debug/common/debug';
import { Debugger } from 'vs/workbench/contrib/debug/common/debugger';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { launchSchema, debuggersExtPoint, breakpointsExtPoint } from 'vs/workbench/contrib/debug/common/debugSchemas';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
export class AdapterManager implements IAdapterManager {
private debuggers: Debugger[];
private adapterDescriptorFactories: IDebugAdapterDescriptorFactory[];
private debugAdapterFactories = new Map<string, IDebugAdapterFactory>();
private debuggersAvailable: IContextKey<boolean>;
private readonly _onDidRegisterDebugger = new Emitter<void>();
private readonly _onDidDebuggersExtPointRead = new Emitter<void>();
private breakpointModeIdsSet = new Set<string>();
constructor(
@IEditorService private readonly editorService: IEditorService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ICommandService private readonly commandService: ICommandService,
@IExtensionService private readonly extensionService: IExtensionService,
@IContextKeyService contextKeyService: IContextKeyService
) {
this.adapterDescriptorFactories = [];
this.debuggers = [];
this.registerListeners();
this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService);
}
private registerListeners(): void {
debuggersExtPoint.setHandler((extensions, delta) => {
delta.added.forEach(added => {
added.value.forEach(rawAdapter => {
if (!rawAdapter.type || (typeof rawAdapter.type !== 'string')) {
added.collector.error(nls.localize('debugNoType', "Debugger 'type' can not be omitted and must be of type 'string'."));
}
if (rawAdapter.enableBreakpointsFor && rawAdapter.enableBreakpointsFor.languageIds) {
rawAdapter.enableBreakpointsFor.languageIds.forEach(modeId => {
this.breakpointModeIdsSet.add(modeId);
});
}
if (rawAdapter.type !== '*') {
const existing = this.getDebugger(rawAdapter.type);
if (existing) {
existing.merge(rawAdapter, added.description);
} else {
this.debuggers.push(this.instantiationService.createInstance(Debugger, this, rawAdapter, added.description));
}
}
});
});
// take care of all wildcard contributions
extensions.forEach(extension => {
extension.value.forEach(rawAdapter => {
if (rawAdapter.type === '*') {
this.debuggers.forEach(dbg => dbg.merge(rawAdapter, extension.description));
}
});
});
delta.removed.forEach(removed => {
const removedTypes = removed.value.map(rawAdapter => rawAdapter.type);
this.debuggers = this.debuggers.filter(d => removedTypes.indexOf(d.type) === -1);
});
// update the schema to include all attributes, snippets and types from extensions.
this.debuggers.forEach(adapter => {
const items = (<IJSONSchema>launchSchema.properties!['configurations'].items);
const schemaAttributes = adapter.getSchemaAttributes();
if (schemaAttributes && items.oneOf) {
items.oneOf.push(...schemaAttributes);
}
const configurationSnippets = adapter.configurationSnippets;
if (configurationSnippets && items.defaultSnippets) {
items.defaultSnippets.push(...configurationSnippets);
}
});
this._onDidDebuggersExtPointRead.fire();
});
breakpointsExtPoint.setHandler((extensions, delta) => {
delta.removed.forEach(removed => {
removed.value.forEach(breakpoints => this.breakpointModeIdsSet.delete(breakpoints.language));
});
delta.added.forEach(added => {
added.value.forEach(breakpoints => this.breakpointModeIdsSet.add(breakpoints.language));
});
});
}
registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable {
debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher));
this.debuggersAvailable.set(this.debugAdapterFactories.size > 0);
this._onDidRegisterDebugger.fire();
return {
dispose: () => {
debugTypes.forEach(debugType => this.debugAdapterFactories.delete(debugType));
}
};
}
hasDebuggers(): boolean {
return this.debugAdapterFactories.size > 0;
}
createDebugAdapter(session: IDebugSession): IDebugAdapter | undefined {
let factory = this.debugAdapterFactories.get(session.configuration.type);
if (factory) {
return factory.createDebugAdapter(session);
}
return undefined;
}
substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig> {
let factory = this.debugAdapterFactories.get(debugType);
if (factory) {
return factory.substituteVariables(folder, config);
}
return Promise.resolve(config);
}
runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined> {
let factory = this.debugAdapterFactories.get(debugType);
if (factory) {
return factory.runInTerminal(args);
}
return Promise.resolve(void 0);
}
registerDebugAdapterDescriptorFactory(debugAdapterProvider: IDebugAdapterDescriptorFactory): IDisposable {
this.adapterDescriptorFactories.push(debugAdapterProvider);
return {
dispose: () => {
this.unregisterDebugAdapterDescriptorFactory(debugAdapterProvider);
}
};
}
unregisterDebugAdapterDescriptorFactory(debugAdapterProvider: IDebugAdapterDescriptorFactory): void {
const ix = this.adapterDescriptorFactories.indexOf(debugAdapterProvider);
if (ix >= 0) {
this.adapterDescriptorFactories.splice(ix, 1);
}
}
getDebugAdapterDescriptor(session: IDebugSession): Promise<IAdapterDescriptor | undefined> {
const config = session.configuration;
const providers = this.adapterDescriptorFactories.filter(p => p.type === config.type && p.createDebugAdapterDescriptor);
if (providers.length === 1) {
return providers[0].createDebugAdapterDescriptor(session);
} else {
// TODO@AW handle n > 1 case
}
return Promise.resolve(undefined);
}
getDebuggerLabel(type: string): string | undefined {
const dbgr = this.getDebugger(type);
if (dbgr) {
return dbgr.label;
}
return undefined;
}
get onDidRegisterDebugger(): Event<void> {
return this._onDidRegisterDebugger.event;
}
get onDidDebuggersExtPointRead(): Event<void> {
return this._onDidDebuggersExtPointRead.event;
}
canSetBreakpointsIn(model: ITextModel): boolean {
const modeId = model.getLanguageIdentifier().language;
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) {
return true;
}
return this.breakpointModeIdsSet.has(modeId);
}
getDebugger(type: string): Debugger | undefined {
return this.debuggers.find(dbg => strings.equalsIgnoreCase(dbg.type, type));
}
isDebuggerInterestedInLanguage(language: string): boolean {
return !!this.debuggers.find(a => language && a.languages && a.languages.indexOf(language) >= 0);
}
async guessDebugger(type?: string): Promise<Debugger | undefined> {
if (type) {
const adapter = this.getDebugger(type);
return Promise.resolve(adapter);
}
const activeTextEditorControl = this.editorService.activeTextEditorControl;
let candidates: Debugger[] | undefined;
if (isCodeEditor(activeTextEditorControl)) {
const model = activeTextEditorControl.getModel();
const language = model ? model.getLanguageIdentifier().language : undefined;
const adapters = this.debuggers.filter(a => language && a.languages && a.languages.indexOf(language) >= 0);
if (adapters.length === 1) {
return adapters[0];
}
if (adapters.length > 1) {
candidates = adapters;
}
}
if (!candidates) {
await this.activateDebuggers('onDebugInitialConfigurations');
candidates = this.debuggers.filter(dbg => dbg.hasInitialConfiguration() || dbg.hasConfigurationProvider());
}
candidates.sort((first, second) => first.label.localeCompare(second.label));
const picks = candidates.map(c => ({ label: c.label, debugger: c }));
return this.quickInputService.pick<{ label: string, debugger: Debugger | undefined }>([...picks, { type: 'separator' }, { label: nls.localize('more', "More..."), debugger: undefined }], { placeHolder: nls.localize('selectDebug', "Select Environment") })
.then(picked => {
if (picked && picked.debugger) {
return picked.debugger;
}
if (picked) {
this.commandService.executeCommand('debug.installAdditionalDebuggers');
}
return undefined;
});
}
async activateDebuggers(activationEvent: string, debugType?: string): Promise<void> {
const promises: Promise<any>[] = [
this.extensionService.activateByEvent(activationEvent),
this.extensionService.activateByEvent('onDebug')
];
if (debugType) {
promises.push(this.extensionService.activateByEvent(`${activationEvent}:${debugType}`));
}
await Promise.all(promises);
}
}

View File

@@ -113,9 +113,9 @@ function isSessionContext(obj: any): obj is CallStackContext {
export function registerCommands(): void {
// These commands are used in call stack context menu, call stack inline actions, command pallete, debug toolbar, mac native touch bar
// These commands are used in call stack context menu, call stack inline actions, command palette, debug toolbar, mac native touch bar
// When the command is exectued in the context of a thread(context menu on a thread, inline call stack action) we pass the thread id
// Otherwise when it is executed "globaly"(using the touch bar, debug toolbar, command pallete) we do not pass any id and just take whatever is the focussed thread
// Otherwise when it is executed "globaly"(using the touch bar, debug toolbar, command palette) we do not pass any id and just take whatever is the focussed thread
// Same for stackFrame commands and session commands.
CommandsRegistry.registerCommand({
id: COPY_STACK_TRACE_ID,
@@ -521,7 +521,7 @@ export function registerCommands(): void {
const control = editorService.activeTextEditorControl;
if (isCodeEditor(control)) {
const position = control.getPosition();
if (position && control.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(control.getModel())) {
if (position && control.hasModel() && debugService.canSetBreakpointsIn(control.getModel())) {
const modelUri = control.getModel().uri;
const breakpointAlreadySet = debugService.getModel().getBreakpoints({ lineNumber: position.lineNumber, uri: modelUri })
.some(bp => (bp.sessionAgnosticData.column === position.column || (!bp.column && position.column <= 1)));
@@ -564,7 +564,7 @@ export function registerCommands(): void {
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(IEditorService));
return openBreakpointSource(focus[0], true, false, true, accessor.get(IDebugService), accessor.get(IEditorService));
}
}

View File

@@ -6,30 +6,25 @@
import * as nls from 'vs/nls';
import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import * as strings from 'vs/base/common/strings';
import * as objects from 'vs/base/common/objects';
import * as json from 'vs/base/common/json';
import { URI as uri } from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { ITextModel } from 'vs/editor/common/model';
import { IEditorPane } from 'vs/workbench/common/editor';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IFileService } from 'vs/platform/files/common/files';
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterDescriptorFactory, IDebugAdapter, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory, IConfigPresentation, CONTEXT_DEBUGGERS_AVAILABLE } from 'vs/workbench/contrib/debug/common/debug';
import { Debugger } from 'vs/workbench/contrib/debug/common/debugger';
import { IDebugConfigurationProvider, ICompound, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, CONTEXT_DEBUG_CONFIGURATION_TYPE, IConfigPresentation } from 'vs/workbench/contrib/debug/common/debug';
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { Registry } from 'vs/platform/registry/common/platform';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { launchSchema, debuggersExtPoint, breakpointsExtPoint } from 'vs/workbench/contrib/debug/common/debugSchemas';
import { launchSchema } from 'vs/workbench/contrib/debug/common/debugSchemas';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
@@ -37,10 +32,13 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance
import { withUndefinedAsNull } from 'vs/base/common/types';
import { sequence } from 'vs/base/common/async';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { flatten } from 'vs/base/common/arrays';
import { flatten, distinct } from 'vs/base/common/arrays';
import { getVisibleAndSorted } from 'vs/workbench/contrib/debug/common/debugUtils';
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { AdapterManager } from 'vs/workbench/contrib/debug/browser/debugAdapterManager';
import { debugConfigure } from 'vs/workbench/contrib/debug/browser/debugIcons';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
jsonRegistry.registerSchema(launchSchemaId, launchSchema);
@@ -49,33 +47,27 @@ const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname';
const DEBUG_SELECTED_ROOT = 'debug.selectedroot';
// Debug type is only stored if a dynamic configuration is used for better restore
const DEBUG_SELECTED_TYPE = 'debug.selectedtype';
const DEBUG_RECENT_DYNAMIC_CONFIGURATIONS = 'debug.recentdynamicconfigurations';
interface IDynamicPickItem { label: string, launch: ILaunch, config: IConfig }
export class ConfigurationManager implements IConfigurationManager {
private debuggers: Debugger[];
private breakpointModeIdsSet = new Set<string>();
private launches!: ILaunch[];
private selectedName: string | undefined;
private selectedLaunch: ILaunch | undefined;
private selectedConfig: IConfig | undefined;
private getSelectedConfig: () => Promise<IConfig | undefined> = () => Promise.resolve(undefined);
private selectedType: string | undefined;
private toDispose: IDisposable[];
private readonly _onDidSelectConfigurationName = new Emitter<void>();
private configProviders: IDebugConfigurationProvider[];
private adapterDescriptorFactories: IDebugAdapterDescriptorFactory[];
private debugAdapterFactories = new Map<string, IDebugAdapterFactory>();
private debugConfigurationTypeContext: IContextKey<string>;
private debuggersAvailable: IContextKey<boolean>;
private readonly _onDidRegisterDebugger = new Emitter<void>();
constructor(
private readonly adapterManager: AdapterManager,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IEditorService private readonly editorService: IEditorService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ICommandService private readonly commandService: ICommandService,
@IStorageService private readonly storageService: IStorageService,
@IExtensionService private readonly extensionService: IExtensionService,
@IHistoryService private readonly historyService: IHistoryService,
@@ -83,8 +75,6 @@ export class ConfigurationManager implements IConfigurationManager {
@IContextKeyService contextKeyService: IContextKeyService
) {
this.configProviders = [];
this.adapterDescriptorFactories = [];
this.debuggers = [];
this.toDispose = [];
this.initLaunches();
this.registerListeners();
@@ -93,111 +83,13 @@ export class ConfigurationManager implements IConfigurationManager {
const previousSelectedLaunch = this.launches.find(l => l.uri.toString() === previousSelectedRoot);
const previousSelectedName = this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE);
this.debugConfigurationTypeContext = CONTEXT_DEBUG_CONFIGURATION_TYPE.bindTo(contextKeyService);
this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService);
if (previousSelectedLaunch && previousSelectedLaunch.getConfigurationNames().length) {
this.selectConfiguration(previousSelectedLaunch, previousSelectedName, undefined, previousSelectedType);
this.selectConfiguration(previousSelectedLaunch, previousSelectedName, undefined, { type: previousSelectedType });
} else if (this.launches.length > 0) {
this.selectConfiguration(undefined, previousSelectedName, undefined, previousSelectedType);
this.selectConfiguration(undefined, previousSelectedName, undefined, { type: previousSelectedType });
}
}
// debuggers
registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable {
debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher));
this.debuggersAvailable.set(this.debugAdapterFactories.size > 0);
this._onDidRegisterDebugger.fire();
return {
dispose: () => {
debugTypes.forEach(debugType => this.debugAdapterFactories.delete(debugType));
}
};
}
hasDebuggers(): boolean {
return this.debugAdapterFactories.size > 0;
}
createDebugAdapter(session: IDebugSession): IDebugAdapter | undefined {
let factory = this.debugAdapterFactories.get(session.configuration.type);
if (factory) {
return factory.createDebugAdapter(session);
}
return undefined;
}
substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig> {
let factory = this.debugAdapterFactories.get(debugType);
if (factory) {
return factory.substituteVariables(folder, config);
}
return Promise.resolve(config);
}
runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined> {
let factory = this.debugAdapterFactories.get(debugType);
if (factory) {
return factory.runInTerminal(args);
}
return Promise.resolve(void 0);
}
// debug adapter
registerDebugAdapterDescriptorFactory(debugAdapterProvider: IDebugAdapterDescriptorFactory): IDisposable {
this.adapterDescriptorFactories.push(debugAdapterProvider);
return {
dispose: () => {
this.unregisterDebugAdapterDescriptorFactory(debugAdapterProvider);
}
};
}
unregisterDebugAdapterDescriptorFactory(debugAdapterProvider: IDebugAdapterDescriptorFactory): void {
const ix = this.adapterDescriptorFactories.indexOf(debugAdapterProvider);
if (ix >= 0) {
this.adapterDescriptorFactories.splice(ix, 1);
}
}
getDebugAdapterDescriptor(session: IDebugSession): Promise<IAdapterDescriptor | undefined> {
const config = session.configuration;
// first try legacy proposed API: DebugConfigurationProvider.debugAdapterExecutable
const providers0 = this.configProviders.filter(p => p.type === config.type && p.debugAdapterExecutable);
if (providers0.length === 1 && providers0[0].debugAdapterExecutable) {
return providers0[0].debugAdapterExecutable(session.root ? session.root.uri : undefined);
} else {
// TODO@AW handle n > 1 case
}
// new API
const providers = this.adapterDescriptorFactories.filter(p => p.type === config.type && p.createDebugAdapterDescriptor);
if (providers.length === 1) {
return providers[0].createDebugAdapterDescriptor(session);
} else {
// TODO@AW handle n > 1 case
}
return Promise.resolve(undefined);
}
getDebuggerLabel(type: string): string | undefined {
const dbgr = this.getDebugger(type);
if (dbgr) {
return dbgr.label;
}
return undefined;
}
get onDidRegisterDebugger(): Event<void> {
return this._onDidRegisterDebugger.event;
}
// debug configurations
registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable {
this.configProviders.push(debugConfigurationProvider);
return {
@@ -298,7 +190,7 @@ export class ConfigurationManager implements IConfigurationManager {
return debugDynamicExtensionsTypes.map(type => {
return {
label: this.getDebuggerLabel(type)!,
label: this.adapterManager.getDebuggerLabel(type)!,
getProvider: async () => {
await this.activateDebuggers(onDebugDynamicConfigurationsName, type);
return this.configProviders.find(p => p.type === type && p.triggerKind === DebugConfigurationProviderTriggerKind.Dynamic && p.provideDebugConfigurations);
@@ -313,7 +205,6 @@ export class ConfigurationManager implements IConfigurationManager {
input.placeholder = nls.localize('selectConfiguration', "Select Launch Configuration");
input.show();
let chosenDidCancel = false;
const chosenPromise = new Promise<IDynamicPickItem | undefined>(resolve => {
disposables.add(input.onDidAccept(() => resolve(input.activeItems[0])));
disposables.add(input.onDidTriggerItemButton(async (context) => {
@@ -324,7 +215,6 @@ export class ConfigurationManager implements IConfigurationManager {
await (launch as Launch).writeConfiguration(config);
await this.selectConfiguration(launch, config.name);
}));
disposables.add(input.onDidHide(() => { chosenDidCancel = true; resolve(undefined); }));
});
const token = new CancellationTokenSource();
@@ -337,7 +227,7 @@ export class ConfigurationManager implements IConfigurationManager {
description: launch.name,
config,
buttons: [{
iconClass: 'codicon-gear',
iconClass: ThemeIcon.asClassName(debugConfigure),
tooltip: nls.localize('editLaunchConfig', "Edit Debug Configuration in launch.json")
}],
launch
@@ -348,16 +238,9 @@ export class ConfigurationManager implements IConfigurationManager {
const nestedPicks = await Promise.all(picks);
const items = flatten(nestedPicks);
let chosen: IDynamicPickItem | undefined;
// If there's exactly one item to choose from, pick it automatically
if (items.length === 1 && !chosenDidCancel) {
chosen = items[0];
} else {
input.items = items;
input.busy = false;
chosen = await chosenPromise;
}
input.items = items;
input.busy = false;
const chosen = await chosenPromise;
disposables.dispose();
@@ -387,69 +270,11 @@ export class ConfigurationManager implements IConfigurationManager {
return getVisibleAndSorted(all);
}
getRecentDynamicConfigurations(): { name: string, type: string }[] {
return JSON.parse(this.storageService.get(DEBUG_RECENT_DYNAMIC_CONFIGURATIONS, StorageScope.WORKSPACE, '[]'));
}
private registerListeners(): void {
debuggersExtPoint.setHandler((extensions, delta) => {
delta.added.forEach(added => {
added.value.forEach(rawAdapter => {
if (!rawAdapter.type || (typeof rawAdapter.type !== 'string')) {
added.collector.error(nls.localize('debugNoType', "Debugger 'type' can not be omitted and must be of type 'string'."));
}
if (rawAdapter.enableBreakpointsFor) {
rawAdapter.enableBreakpointsFor.languageIds.forEach(modeId => {
this.breakpointModeIdsSet.add(modeId);
});
}
if (rawAdapter.type !== '*') {
const existing = this.getDebugger(rawAdapter.type);
if (existing) {
existing.merge(rawAdapter, added.description);
} else {
this.debuggers.push(this.instantiationService.createInstance(Debugger, this, rawAdapter, added.description));
}
}
});
});
// take care of all wildcard contributions
extensions.forEach(extension => {
extension.value.forEach(rawAdapter => {
if (rawAdapter.type === '*') {
this.debuggers.forEach(dbg => dbg.merge(rawAdapter, extension.description));
}
});
});
delta.removed.forEach(removed => {
const removedTypes = removed.value.map(rawAdapter => rawAdapter.type);
this.debuggers = this.debuggers.filter(d => removedTypes.indexOf(d.type) === -1);
});
// update the schema to include all attributes, snippets and types from extensions.
this.debuggers.forEach(adapter => {
const items = (<IJSONSchema>launchSchema.properties!['configurations'].items);
const schemaAttributes = adapter.getSchemaAttributes();
if (schemaAttributes && items.oneOf) {
items.oneOf.push(...schemaAttributes);
}
const configurationSnippets = adapter.configurationSnippets;
if (configurationSnippets && items.defaultSnippets) {
items.defaultSnippets.push(...configurationSnippets);
}
});
this.setCompoundSchemaValues();
});
breakpointsExtPoint.setHandler((extensions, delta) => {
delta.removed.forEach(removed => {
removed.value.forEach(breakpoints => this.breakpointModeIdsSet.delete(breakpoints.language));
});
delta.added.forEach(added => {
added.value.forEach(breakpoints => this.breakpointModeIdsSet.add(breakpoints.language));
});
});
this.toDispose.push(Event.any<IWorkspaceFoldersChangeEvent | WorkbenchState>(this.contextService.onDidChangeWorkspaceFolders, this.contextService.onDidChangeWorkbenchState)(() => {
this.initLaunches();
this.selectConfiguration(undefined);
@@ -462,14 +287,17 @@ export class ConfigurationManager implements IConfigurationManager {
this.setCompoundSchemaValues();
}
}));
this.toDispose.push(this.adapterManager.onDidDebuggersExtPointRead(() => {
this.setCompoundSchemaValues();
}));
}
private initLaunches(): void {
this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, folder));
this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, this.adapterManager, folder));
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
this.launches.push(this.instantiationService.createInstance(WorkspaceLaunch, this));
this.launches.push(this.instantiationService.createInstance(WorkspaceLaunch, this, this.adapterManager));
}
this.launches.push(this.instantiationService.createInstance(UserLaunch, this));
this.launches.push(this.instantiationService.createInstance(UserLaunch, this, this.adapterManager));
if (this.selectedLaunch && this.launches.indexOf(this.selectedLaunch) === -1) {
this.selectConfiguration(undefined);
@@ -501,11 +329,11 @@ export class ConfigurationManager implements IConfigurationManager {
return this.launches.find(l => l.workspace && this.uriIdentityService.extUri.isEqual(l.workspace.uri, workspaceUri));
}
get selectedConfiguration(): { launch: ILaunch | undefined, name: string | undefined, config: IConfig | undefined, type: string | undefined } {
get selectedConfiguration(): { launch: ILaunch | undefined, name: string | undefined, getConfig: () => Promise<IConfig | undefined>, type: string | undefined } {
return {
launch: this.selectedLaunch,
name: this.selectedName,
config: this.selectedConfig,
getConfig: this.getSelectedConfig,
type: this.selectedType
};
}
@@ -522,7 +350,7 @@ export class ConfigurationManager implements IConfigurationManager {
return undefined;
}
async selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, type?: string): Promise<void> {
async selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, dynamicConfig?: { type?: string }): Promise<void> {
if (typeof launch === 'undefined') {
const rootUri = this.historyService.getLastActiveWorkspaceRoot();
launch = this.getLaunch(rootUri);
@@ -536,40 +364,56 @@ export class ConfigurationManager implements IConfigurationManager {
this.selectedLaunch = launch;
if (this.selectedLaunch) {
this.storageService.store(DEBUG_SELECTED_ROOT, this.selectedLaunch.uri.toString(), StorageScope.WORKSPACE);
this.storageService.store(DEBUG_SELECTED_ROOT, this.selectedLaunch.uri.toString(), StorageScope.WORKSPACE, StorageTarget.MACHINE);
} else {
this.storageService.remove(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE);
}
const names = launch ? launch.getConfigurationNames() : [];
if ((name && names.indexOf(name) >= 0) || config) {
this.getSelectedConfig = () => Promise.resolve(config);
let type = config?.type;
if (name && names.indexOf(name) >= 0) {
this.setSelectedLaunchName(name);
} else if (!this.selectedName || names.indexOf(this.selectedName) === -1) {
// We could not find the previously used name. We should get all dynamic configurations from providers
} else if (dynamicConfig && dynamicConfig.type) {
// We could not find the previously used name and config is not passed. We should get all dynamic configurations from providers
// And potentially auto select the previously used dynamic configuration #96293
const providers = (await this.getDynamicProviders()).filter(p => p.type === type);
const activatedProviders = await Promise.all(providers.map(p => p.getProvider()));
const provider = activatedProviders.find(p => p && p.type === type);
let nameToSet = names.length ? names[0] : undefined;
if (provider && launch && launch.workspace) {
const token = new CancellationTokenSource();
const dynamicConfigs = await provider.provideDebugConfigurations!(launch.workspace.uri, token.token);
const dynamicConfig = dynamicConfigs.find(c => c.name === name);
if (dynamicConfig) {
config = dynamicConfig;
nameToSet = name;
}
}
type = dynamicConfig.type;
if (!config) {
const providers = (await this.getDynamicProviders()).filter(p => p.type === type);
this.getSelectedConfig = async () => {
const activatedProviders = await Promise.all(providers.map(p => p.getProvider()));
const provider = activatedProviders.find(p => p && p.type === type);
if (provider && launch && launch.workspace) {
const token = new CancellationTokenSource();
const dynamicConfigs = await provider.provideDebugConfigurations!(launch.workspace.uri, token.token);
const dynamicConfig = dynamicConfigs.find(c => c.name === name);
if (dynamicConfig) {
return dynamicConfig;
}
}
return undefined;
};
}
this.setSelectedLaunchName(name);
let recentDynamicProviders = this.getRecentDynamicConfigurations();
if (name && dynamicConfig.type) {
// We need to store the recently used dynamic configurations to be able to show them in UI #110009
recentDynamicProviders.unshift({ name, type: dynamicConfig.type });
recentDynamicProviders = distinct(recentDynamicProviders, t => `${t.name} : ${t.type}`);
this.storageService.store(DEBUG_RECENT_DYNAMIC_CONFIGURATIONS, JSON.stringify(recentDynamicProviders), StorageScope.WORKSPACE, StorageTarget.USER);
}
} else if (!this.selectedName || names.indexOf(this.selectedName) === -1) {
// We could not find the configuration to select, pick the first one, or reset the selection if there is no launch configuration
const nameToSet = names.length ? names[0] : undefined;
this.setSelectedLaunchName(nameToSet);
}
this.selectedConfig = config;
this.selectedType = type || this.selectedConfig?.type;
this.storageService.store(DEBUG_SELECTED_TYPE, this.selectedType, StorageScope.WORKSPACE);
const configForType = this.selectedConfig || (this.selectedLaunch && this.selectedName ? this.selectedLaunch.getConfiguration(this.selectedName) : undefined);
if (configForType) {
this.debugConfigurationTypeContext.set(configForType.type);
this.selectedType = dynamicConfig?.type || config?.type;
this.storageService.store(DEBUG_SELECTED_TYPE, this.selectedType, StorageScope.WORKSPACE, StorageTarget.MACHINE);
if (type) {
this.debugConfigurationTypeContext.set(type);
} else {
this.debugConfigurationTypeContext.reset();
}
@@ -579,66 +423,6 @@ export class ConfigurationManager implements IConfigurationManager {
}
}
canSetBreakpointsIn(model: ITextModel): boolean {
const modeId = model.getLanguageIdentifier().language;
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) {
return true;
}
return this.breakpointModeIdsSet.has(modeId);
}
getDebugger(type: string): Debugger | undefined {
return this.debuggers.find(dbg => strings.equalsIgnoreCase(dbg.type, type));
}
isDebuggerInterestedInLanguage(language: string): boolean {
return !!this.debuggers.find(a => language && a.languages && a.languages.indexOf(language) >= 0);
}
async guessDebugger(type?: string): Promise<Debugger | undefined> {
if (type) {
const adapter = this.getDebugger(type);
return Promise.resolve(adapter);
}
const activeTextEditorControl = this.editorService.activeTextEditorControl;
let candidates: Debugger[] | undefined;
if (isCodeEditor(activeTextEditorControl)) {
const model = activeTextEditorControl.getModel();
const language = model ? model.getLanguageIdentifier().language : undefined;
const adapters = this.debuggers.filter(a => language && a.languages && a.languages.indexOf(language) >= 0);
if (adapters.length === 1) {
return adapters[0];
}
if (adapters.length > 1) {
candidates = adapters;
}
}
if (!candidates) {
await this.activateDebuggers('onDebugInitialConfigurations');
candidates = this.debuggers.filter(dbg => dbg.hasInitialConfiguration() || dbg.hasConfigurationProvider());
}
candidates.sort((first, second) => first.label.localeCompare(second.label));
const picks = candidates.map(c => ({ label: c.label, debugger: c }));
return this.quickInputService.pick<{ label: string, debugger: Debugger | undefined }>([...picks, { type: 'separator' }, { label: nls.localize('more', "More..."), debugger: undefined }], { placeHolder: nls.localize('selectDebug', "Select Environment") })
.then(picked => {
if (picked && picked.debugger) {
return picked.debugger;
}
if (picked) {
this.commandService.executeCommand('debug.installAdditionalDebuggers');
}
return undefined;
});
}
async activateDebuggers(activationEvent: string, debugType?: string): Promise<void> {
const promises: Promise<any>[] = [
this.extensionService.activateByEvent(activationEvent),
@@ -654,7 +438,7 @@ export class ConfigurationManager implements IConfigurationManager {
this.selectedName = selectedName;
if (this.selectedName) {
this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE);
this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE, StorageTarget.MACHINE);
} else {
this.storageService.remove(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE);
}
@@ -668,8 +452,10 @@ export class ConfigurationManager implements IConfigurationManager {
abstract class AbstractLaunch {
protected abstract getConfig(): IGlobalConfig | undefined;
constructor(protected configurationManager: ConfigurationManager) {
}
constructor(
protected configurationManager: ConfigurationManager,
private readonly adapterManager: AdapterManager
) { }
getCompound(name: string): ICompound | undefined {
const config = this.getConfig();
@@ -722,7 +508,7 @@ abstract class AbstractLaunch {
async getInitialConfigurationContent(folderUri?: uri, type?: string, token?: CancellationToken): Promise<string> {
let content = '';
const adapter = await this.configurationManager.guessDebugger(type);
const adapter = await this.adapterManager.guessDebugger(type);
if (adapter) {
const initialConfigs = await this.configurationManager.provideDebugConfigurations(folderUri, adapter.type, token || CancellationToken.None);
content = await adapter.getInitialConfigurationContent(initialConfigs);
@@ -739,13 +525,14 @@ class Launch extends AbstractLaunch implements ILaunch {
constructor(
configurationManager: ConfigurationManager,
adapterManager: AdapterManager,
public workspace: IWorkspaceFolder,
@IFileService private readonly fileService: IFileService,
@ITextFileService private readonly textFileService: ITextFileService,
@IEditorService private readonly editorService: IEditorService,
@IConfigurationService private readonly configurationService: IConfigurationService
) {
super(configurationManager);
super(configurationManager, adapterManager);
}
get uri(): uri {
@@ -822,11 +609,12 @@ class Launch extends AbstractLaunch implements ILaunch {
class WorkspaceLaunch extends AbstractLaunch implements ILaunch {
constructor(
configurationManager: ConfigurationManager,
adapterManager: AdapterManager,
@IEditorService private readonly editorService: IEditorService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService
) {
super(configurationManager);
super(configurationManager, adapterManager);
}
get workspace(): undefined {
@@ -873,10 +661,11 @@ class UserLaunch extends AbstractLaunch implements ILaunch {
constructor(
configurationManager: ConfigurationManager,
adapterManager: AdapterManager,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(configurationManager);
super(configurationManager, adapterManager);
}
get workspace(): undefined {
@@ -900,7 +689,7 @@ class UserLaunch extends AbstractLaunch implements ILaunch {
}
async openConfigFile(preserveFocus: boolean): Promise<{ editor: IEditorPane | null, created: boolean }> {
const editor = await this.preferencesService.openGlobalSettings(true, { preserveFocus });
const editor = await this.preferencesService.openGlobalSettings(true, { preserveFocus, revealSetting: { key: 'launch' } });
return ({
editor: withUndefinedAsNull(editor),
created: false

View File

@@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, WATCH_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, WATCH_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_EXCEPTION_WIDGET_VISIBLE } from 'vs/workbench/contrib/debug/common/debug';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView';
@@ -20,6 +20,10 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { Action } from 'vs/base/common/actions';
import { getDomNodePagePosition } from 'vs/base/browser/dom';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { Position } from 'vs/editor/common/core/position';
import { URI } from 'vs/base/common/uri';
import { IDisposable } from 'vs/base/common/lifecycle';
import { raceTimeout } from 'vs/base/common/async';
export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint';
class ToggleBreakpointAction extends EditorAction {
@@ -41,7 +45,7 @@ class ToggleBreakpointAction extends EditorAction {
if (editor.hasModel()) {
const debugService = accessor.get(IDebugService);
const modelUri = editor.getModel().uri;
const canSet = debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel());
const canSet = debugService.canSetBreakpointsIn(editor.getModel());
// Does not account for multi line selections, Set to remove multiple cursor on the same line
const lineNumbers = [...new Set(editor.getSelections().map(s => s.getPosition().lineNumber))];
@@ -75,7 +79,7 @@ class ConditionalBreakpointAction extends EditorAction {
const debugService = accessor.get(IDebugService);
const position = editor.getPosition();
if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) {
if (position && editor.hasModel() && debugService.canSetBreakpointsIn(editor.getModel())) {
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, undefined, BreakpointWidgetContext.CONDITION);
}
}
@@ -97,7 +101,7 @@ class LogPointAction extends EditorAction {
const debugService = accessor.get(IDebugService);
const position = editor.getPosition();
if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) {
if (position && editor.hasModel() && debugService.canSetBreakpointsIn(editor.getModel())) {
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, position.column, BreakpointWidgetContext.LOG_MESSAGE);
}
}
@@ -123,14 +127,37 @@ export class RunToCursorAction extends EditorAction {
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
const debugService = accessor.get(IDebugService);
const uriIdentityService = accessor.get(IUriIdentityService);
const focusedSession = debugService.getViewModel().focusedSession;
if (debugService.state !== State.Stopped || !focusedSession) {
return;
}
let breakpointToRemove: IBreakpoint;
const oneTimeListener = focusedSession.onDidChangeState(() => {
const position = editor.getPosition();
if (!(editor.hasModel() && position)) {
return;
}
const uri = editor.getModel().uri;
const bpExists = !!(debugService.getModel().getBreakpoints({ column: position.column, lineNumber: position.lineNumber, uri }).length);
let breakpointToRemove: IBreakpoint | undefined;
let threadToContinue = debugService.getViewModel().focusedThread;
if (!bpExists) {
const addResult = await this.addBreakpoints(accessor, uri, position);
if (addResult.thread) {
threadToContinue = addResult.thread;
}
if (addResult.breakpoint) {
breakpointToRemove = addResult.breakpoint;
}
}
if (!threadToContinue) {
return;
}
const oneTimeListener = threadToContinue.session.onDidChangeState(() => {
const state = focusedSession.state;
if (state === State.Stopped || state === State.Inactive) {
if (breakpointToRemove) {
@@ -140,27 +167,90 @@ export class RunToCursorAction extends EditorAction {
}
});
const position = editor.getPosition();
if (editor.hasModel() && position) {
const uri = editor.getModel().uri;
const bpExists = !!(debugService.getModel().getBreakpoints({ column: position.column, lineNumber: position.lineNumber, uri }).length);
if (!bpExists) {
let column = 0;
const focusedStackFrame = debugService.getViewModel().focusedStackFrame;
if (focusedStackFrame && uriIdentityService.extUri.isEqual(focusedStackFrame.source.uri, uri) && focusedStackFrame.range.startLineNumber === position.lineNumber) {
// If the cursor is on a line different than the one the debugger is currently paused on, then send the breakpoint at column 0 on the line
// otherwise set it at the precise column #102199
column = position.column;
}
await threadToContinue.continue();
}
const breakpoints = await debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column }], false);
if (breakpoints && breakpoints.length) {
breakpointToRemove = breakpoints[0];
private async addBreakpoints(accessor: ServicesAccessor, uri: URI, position: Position) {
const debugService = accessor.get(IDebugService);
const debugModel = debugService.getModel();
const viewModel = debugService.getViewModel();
const uriIdentityService = accessor.get(IUriIdentityService);
let column = 0;
const focusedStackFrame = viewModel.focusedStackFrame;
if (focusedStackFrame && uriIdentityService.extUri.isEqual(focusedStackFrame.source.uri, uri) && focusedStackFrame.range.startLineNumber === position.lineNumber) {
// If the cursor is on a line different than the one the debugger is currently paused on, then send the breakpoint at column 0 on the line
// otherwise set it at the precise column #102199
column = position.column;
}
const breakpoints = await debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column }], false);
const breakpoint = breakpoints?.[0];
if (!breakpoint) {
return { breakpoint: undefined, thread: viewModel.focusedThread };
}
// If the breakpoint was not initially verified, wait up to 2s for it to become so.
// Inherently racey if multiple sessions can verify async, but not solvable...
if (!breakpoint.verified) {
let listener: IDisposable;
await raceTimeout(new Promise<void>(resolve => {
listener = debugModel.onDidChangeBreakpoints(() => {
if (breakpoint.verified) {
resolve();
}
});
}), 2000);
listener!.dispose();
}
// Look at paused threads for sessions that verified this bp. Prefer, in order:
const enum Score {
/** The focused thread */
Focused,
/** Any other stopped thread of a session that verified the bp */
Verified,
/** Any thread that verified and paused in the same file */
VerifiedAndPausedInFile,
/** The focused thread if it verified the breakpoint */
VerifiedAndFocused,
}
let bestThread = viewModel.focusedThread;
let bestScore = Score.Focused;
for (const sessionId of breakpoint.sessionsThatVerified) {
const session = debugModel.getSession(sessionId);
if (!session) {
continue;
}
const threads = session.getAllThreads().filter(t => t.stopped);
if (bestScore < Score.VerifiedAndFocused) {
if (viewModel.focusedThread && threads.includes(viewModel.focusedThread)) {
bestThread = viewModel.focusedThread;
bestScore = Score.VerifiedAndFocused;
}
}
await debugService.getViewModel().focusedThread!.continue();
if (bestScore < Score.VerifiedAndPausedInFile) {
const pausedInThisFile = threads.find(t => {
const top = t.getTopStackFrame();
return top && uriIdentityService.extUri.isEqual(top.source.uri, uri);
});
if (pausedInThisFile) {
bestThread = pausedInThisFile;
bestScore = Score.VerifiedAndPausedInFile;
}
}
if (bestScore < Score.Verified) {
bestThread = threads[0];
bestScore = Score.VerifiedAndPausedInFile;
}
}
return { thread: bestThread, breakpoint };
}
}
@@ -336,7 +426,7 @@ class GoToBreakpointAction extends EditorAction {
}
if (moveBreakpoint) {
return openBreakpointSource(moveBreakpoint, false, true, debugService, editorService);
return openBreakpointSource(moveBreakpoint, false, true, false, debugService, editorService);
}
}
}
@@ -364,6 +454,27 @@ class GoToPreviousBreakpointAction extends GoToBreakpointAction {
}
}
class CloseExceptionWidgetAction extends EditorAction {
constructor() {
super({
id: 'editor.debug.action.closeExceptionWidget',
label: nls.localize('closeExceptionWidget', "Close Exception Widget"),
alias: 'Close Exception Widget',
precondition: CONTEXT_EXCEPTION_WIDGET_VISIBLE,
kbOpts: {
primary: KeyCode.Escape,
weight: KeybindingWeight.EditorContrib
}
});
}
async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
const contribution = editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID);
contribution.closeExceptionWidget();
}
}
export function registerEditorActions(): void {
registerEditorAction(ToggleBreakpointAction);
registerEditorAction(ConditionalBreakpointAction);
@@ -375,4 +486,5 @@ export function registerEditorActions(): void {
registerEditorAction(ShowDebugHoverAction);
registerEditorAction(GoToNextBreakpointAction);
registerEditorAction(GoToPreviousBreakpointAction);
registerEditorAction(CloseExceptionWidgetAction);
}

View File

@@ -21,7 +21,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
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 { IDebugEditorContribution, IDebugService, State, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugEditorContribution, IDebugService, State, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo, IDebugSession, CONTEXT_EXCEPTION_WIDGET_VISIBLE } from 'vs/workbench/contrib/debug/common/debug';
import { ExceptionWidget } from 'vs/workbench/contrib/debug/browser/exceptionWidget';
import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets';
import { Position } from 'vs/editor/common/core/position';
@@ -39,6 +39,7 @@ import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { Event } from 'vs/base/common/event';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
const LAUNCH_JSON_REGEX = /\.vscode\/launch\.json$/;
const INLINE_VALUE_DECORATION_KEY = 'inlinevaluedecoration';
@@ -173,6 +174,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
private hoverWidget: DebugHoverWidget;
private hoverRange: Range | null = null;
private mouseDown = false;
private exceptionWidgetVisible: IContextKey<boolean>;
private static readonly MEMOIZER = createMemoizer();
private exceptionWidget: ExceptionWidget | undefined;
@@ -189,13 +191,15 @@ export class DebugEditorContribution implements IDebugEditorContribution {
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IHostService private readonly hostService: IHostService,
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
@IContextKeyService contextKeyService: IContextKeyService
) {
this.hoverWidget = this.instantiationService.createInstance(DebugHoverWidget, this.editor);
this.toDispose = [];
this.registerListeners();
this.updateConfigurationWidgetVisibility();
this.codeEditorService.registerDecorationType(INLINE_VALUE_DECORATION_KEY, {});
this.exceptionWidgetVisible = CONTEXT_EXCEPTION_WIDGET_VISIBLE.bindTo(contextKeyService);
this.toggleExceptionWidget();
}
@@ -355,7 +359,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
}
private hideHoverWidget(): void {
if (!this.hideHoverScheduler.isScheduled() && this.hoverWidget.isVisible()) {
if (!this.hideHoverScheduler.isScheduled() && this.hoverWidget.willBeVisible()) {
this.hideHoverScheduler.schedule();
}
this.showHoverScheduler.cancel();
@@ -441,13 +445,20 @@ export class DebugEditorContribution implements IDebugEditorContribution {
this.exceptionWidget = this.instantiationService.createInstance(ExceptionWidget, this.editor, exceptionInfo, debugSession);
this.exceptionWidget.show({ lineNumber, column }, 0);
this.exceptionWidget.focus();
this.editor.revealLine(lineNumber);
this.exceptionWidgetVisible.set(true);
}
private closeExceptionWidget(): void {
closeExceptionWidget(): void {
if (this.exceptionWidget) {
const shouldFocusEditor = this.exceptionWidget.hasfocus();
this.exceptionWidget.dispose();
this.exceptionWidget = undefined;
this.exceptionWidgetVisible.set(false);
if (shouldFocusEditor) {
this.editor.focus();
}
}
}

View File

@@ -31,7 +31,8 @@ import { coalesce } from 'vs/base/common/arrays';
import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView';
import { EvaluatableExpressionProviderRegistry } from 'vs/editor/common/modes';
import { CancellationToken } from 'vs/base/common/cancellation';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { isMacintosh } from 'vs/base/common/platform';
const $ = dom.$;
@@ -70,6 +71,7 @@ export class DebugHoverWidget implements IContentWidget {
allowEditorOverflow = true;
private _isVisible: boolean;
private showCancellationSource?: CancellationTokenSource;
private domNode!: HTMLElement;
private tree!: AsyncDataTree<IExpression, IExpression, any>;
private showAtPosition: Position | null;
@@ -102,6 +104,8 @@ 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');
const tip = dom.append(this.complexValueContainer, $('.tip'));
tip.textContent = nls.localize('quickTip', 'Hold {0} key to switch to editor language hover', isMacintosh ? 'Option' : 'Alt');
const dataSource = new DebugHoverDataSource();
this.tree = <WorkbenchAsyncDataTree<IExpression, IExpression, any>>this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'DebugHover', this.treeContainer, new DebugHoverDelegate(), [this.instantiationService.createInstance(VariablesRenderer)],
@@ -161,13 +165,17 @@ export class DebugHoverWidget implements IContentWidget {
}
isHovered(): boolean {
return this.domNode.matches(':hover');
return !!this.domNode?.matches(':hover');
}
isVisible(): boolean {
return this._isVisible;
}
willBeVisible(): boolean {
return !!this.showCancellationSource;
}
getId(): string {
return DebugHoverWidget.ID;
}
@@ -177,6 +185,8 @@ export class DebugHoverWidget implements IContentWidget {
}
async showAt(range: Range, focus: boolean): Promise<void> {
this.showCancellationSource?.cancel();
const cancellationSource = this.showCancellationSource = new CancellationTokenSource();
const session = this.debugService.getViewModel().focusedSession;
if (!session || !this.editor.hasModel()) {
@@ -193,7 +203,7 @@ export class DebugHoverWidget implements IContentWidget {
const supports = EvaluatableExpressionProviderRegistry.ordered(model);
const promises = supports.map(support => {
return Promise.resolve(support.provideEvaluatableExpression(model, pos, CancellationToken.None)).then(expression => {
return Promise.resolve(support.provideEvaluatableExpression(model, pos, cancellationSource.token)).then(expression => {
return expression;
}, err => {
//onUnexpectedExternalError(err);
@@ -236,7 +246,7 @@ export class DebugHoverWidget implements IContentWidget {
}
}
if (!expression || (expression instanceof Expression && !expression.available)) {
if (cancellationSource.token.isCancellationRequested || !expression || (expression instanceof Expression && !expression.available)) {
this.hide();
return;
}
@@ -315,6 +325,11 @@ export class DebugHoverWidget implements IContentWidget {
hide(): void {
if (this.showCancellationSource) {
this.showCancellationSource.cancel();
this.showCancellationSource = undefined;
}
if (!this._isVisible) {
return;
}

View File

@@ -0,0 +1,71 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Codicon } from 'vs/base/common/codicons';
import { localize } from 'vs/nls';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
export const debugConsoleViewIcon = registerIcon('debug-console-view-icon', Codicon.debugConsole, localize('debugConsoleViewIcon', 'View icon of the debug console view.'));
export const runViewIcon = registerIcon('run-view-icon', Codicon.debugAlt, localize('runViewIcon', 'View icon of the run view.'));
export const variablesViewIcon = registerIcon('variables-view-icon', Codicon.debugAlt, localize('variablesViewIcon', 'View icon of the variables view.'));
export const watchViewIcon = registerIcon('watch-view-icon', Codicon.debugAlt, localize('watchViewIcon', 'View icon of the watch view.'));
export const callStackViewIcon = registerIcon('callstack-view-icon', Codicon.debugAlt, localize('callStackViewIcon', 'View icon of the call stack view.'));
export const breakpointsViewIcon = registerIcon('breakpoints-view-icon', Codicon.debugAlt, localize('breakpointsViewIcon', 'View icon of the breakpoints view.'));
export const loadedScriptsViewIcon = registerIcon('loaded-scripts-view-icon', Codicon.debugAlt, localize('loadedScriptsViewIcon', 'View icon of the loaded scripts view.'));
export const debugBreakpoint = registerIcon('debug-breakpoint', Codicon.debugBreakpoint, localize('debugBreakpoint', 'Icon for breakpoints.'));
export const debugBreakpointDisabled = registerIcon('debug-breakpoint-disabled', Codicon.debugBreakpointDisabled, localize('debugBreakpointDisabled', 'Icon for disabled breakpoints.'));
export const debugBreakpointUnverified = registerIcon('debug-breakpoint-unverified', Codicon.debugBreakpointUnverified, localize('debugBreakpointUnverified', 'Icon for unverified breakpoints.'));
export const debugBreakpointHint = registerIcon('debug-hint', Codicon.debugHint, localize('debugBreakpointHint', 'Icon for breakpoint hints shown on hover in editor glyph margin.'));
export const debugBreakpointFunction = registerIcon('debug-breakpoint-function', Codicon.debugBreakpointFunction, localize('debugBreakpointFunction', 'Icon for function breakpoints.'));
export const debugBreakpointFunctionUnverified = registerIcon('debug-breakpoint-function-unverified', Codicon.debugBreakpointFunctionUnverified, localize('debugBreakpointFunctionUnverified', 'Icon for unverified function breakpoints.'));
export const debugBreakpointFunctionDisabled = registerIcon('debug-breakpoint-function-disabled', Codicon.debugBreakpointFunctionDisabled, localize('debugBreakpointFunctionDisabled', 'Icon for disabled function breakpoints.'));
export const debugBreakpointUnsupported = registerIcon('debug-breakpoint-unsupported', Codicon.debugBreakpointUnsupported, localize('debugBreakpointUnsupported', 'Icon for unsupported breakpoints.'));
export const debugBreakpointConditionalUnverified = registerIcon('debug-breakpoint-conditional-unverified', Codicon.debugBreakpointConditionalUnverified, localize('debugBreakpointConditionalUnverified', 'Icon for unverified conditional breakpoints.'));
export const debugBreakpointConditional = registerIcon('debug-breakpoint-conditional', Codicon.debugBreakpointConditional, localize('debugBreakpointConditional', 'Icon for conditional breakpoints.'));
export const debugBreakpointConditionalDisabled = registerIcon('debug-breakpoint-conditional-disabled', Codicon.debugBreakpointConditionalDisabled, localize('debugBreakpointConditionalDisabled', 'Icon for disabled conditional breakpoints.'));
export const debugBreakpointDataUnverified = registerIcon('debug-breakpoint-data-unverified', Codicon.debugBreakpointDataUnverified, localize('debugBreakpointDataUnverified', 'Icon for unverified data breakpoints.'));
export const debugBreakpointData = registerIcon('debug-breakpoint-data', Codicon.debugBreakpointData, localize('debugBreakpointData', 'Icon for data breakpoints.'));
export const debugBreakpointDataDisabled = registerIcon('debug-breakpoint-data-disabled', Codicon.debugBreakpointDataDisabled, localize('debugBreakpointDataDisabled', 'Icon for disabled data breakpoints.'));
export const debugBreakpointLogUnverified = registerIcon('debug-breakpoint-log-unverified', Codicon.debugBreakpointLogUnverified, localize('debugBreakpointLogUnverified', 'Icon for unverified log breakpoints.'));
export const debugBreakpointLog = registerIcon('debug-breakpoint-log', Codicon.debugBreakpointLog, localize('debugBreakpointLog', 'Icon for log breakpoints.'));
export const debugBreakpointLogDisabled = registerIcon('debug-breakpoint-log-disabled', Codicon.debugBreakpointLogDisabled, localize('debugBreakpointLogDisabled', 'Icon for disabled log breakpoint.'));
export const debugStackframe = registerIcon('debug-stackframe', Codicon.debugStackframe, localize('debugStackframe', 'Icon for a stackframe shown in the editor glyph margin.'));
export const debugStackframeFocused = registerIcon('debug-stackframe-focused', Codicon.debugStackframeFocused, localize('debugStackframeFocused', 'Icon for a focused stackframe shown in the editor glyph margin.'));
export const debugGripper = registerIcon('debug-gripper', Codicon.gripper, localize('debugGripper', 'Icon for the debug bar gripper.'));
export const debugRestartFrame = registerIcon('debug-restart-frame', Codicon.debugRestartFrame, localize('debugRestartFrame', 'Icon for the debug restart frame action.'));
export const debugStop = registerIcon('debug-stop', Codicon.debugStop, localize('debugStop', 'Icon for the debug stop action.'));
export const debugDisconnect = registerIcon('debug-disconnect', Codicon.debugDisconnect, localize('debugDisconnect', 'Icon for the debug disconnect action.'));
export const debugRestart = registerIcon('debug-restart', Codicon.debugRestart, localize('debugRestart', 'Icon for the debug restart action.'));
export const debugStepOver = registerIcon('debug-step-over', Codicon.debugStepOver, localize('debugStepOver', 'Icon for the debug step over action.'));
export const debugStepInto = registerIcon('debug-step-into', Codicon.debugStepInto, localize('debugStepInto', 'Icon for the debug step into action.'));
export const debugStepOut = registerIcon('debug-step-out', Codicon.debugStepOut, localize('debugStepOut', 'Icon for the debug step out action.'));
export const debugStepBack = registerIcon('debug-step-back', Codicon.debugStepBack, localize('debugStepBack', 'Icon for the debug step back action.'));
export const debugPause = registerIcon('debug-pause', Codicon.debugPause, localize('debugPause', 'Icon for the debug pause action.'));
export const debugContinue = registerIcon('debug-continue', Codicon.debugContinue, localize('debugContinue', 'Icon for the debug continue action.'));
export const debugReverseContinue = registerIcon('debug-reverse-continue', Codicon.debugReverseContinue, localize('debugReverseContinue', 'Icon for the debug reverse continue action.'));
export const debugStart = registerIcon('debug-start', Codicon.debugStart, localize('debugStart', 'Icon for the debug start action.'));
export const debugConfigure = registerIcon('debug-configure', Codicon.gear, localize('debugConfigure', 'Icon for the debug configure action.'));
export const debugConsole = registerIcon('debug-console', Codicon.gear, localize('debugConsole', 'Icon for the debug console open action.'));
export const debugCollapseAll = registerIcon('debug-collapse-all', Codicon.collapseAll, localize('debugCollapseAll', 'Icon for the collapse all action in the debug views.'));
export const callstackViewSession = registerIcon('callstack-view-session', Codicon.bug, localize('callstackViewSession', 'Icon for the session icon in the call stack view.'));
export const debugConsoleClearAll = registerIcon('debug-console-clear-all', Codicon.clearAll, localize('debugConsoleClearAll', 'Icon for the clear all action in the debug console.'));
export const watchExpressionsRemoveAll = registerIcon('watch-expressions-remove-all', Codicon.closeAll, localize('watchExpressionsRemoveAll', 'Icon for the remove all action in the watch view.'));
export const watchExpressionsAdd = registerIcon('watch-expressions-add', Codicon.add, localize('watchExpressionsAdd', 'Icon for the add action in the watch view.'));
export const watchExpressionsAddFuncBreakpoint = registerIcon('watch-expressions-add-function-breakpoint', Codicon.add, localize('watchExpressionsAddFuncBreakpoint', 'Icon for the add function breakpoint action in the watch view.'));
export const breakpointsRemoveAll = registerIcon('breakpoints-remove-all', Codicon.closeAll, localize('breakpointsRemoveAll', 'Icon for the remove all action in the breakpoints view.'));
export const breakpointsActivate = registerIcon('breakpoints-activate', Codicon.activateBreakpoints, localize('breakpointsActivate', 'Icon for the activate action in the breakpoints view.'));
export const debugConsoleEvaluationInput = registerIcon('debug-console-evaluation-input', Codicon.arrowSmallRight, localize('debugConsoleEvaluationInput', 'Icon for the debug evaluation input marker.'));
export const debugConsoleEvaluationPrompt = registerIcon('debug-console-evaluation-prompt', Codicon.chevronRight, localize('debugConsoleEvaluationPrompt', 'Icon for the debug evaluation prompt.'));

View File

@@ -39,7 +39,7 @@ export class DebugProgressContribution implements IWorkbenchContribution {
if (viewsService.isViewContainerVisible(VIEWLET_ID)) {
progressService.withProgress({ location: VIEWLET_ID }, () => promise);
}
const source = debugService.getConfigurationManager().getDebuggerLabel(session.configuration.type);
const source = debugService.getAdapterManager().getDebuggerLabel(session.configuration.type);
progressService.withProgress({
location: ProgressLocation.Notification,
title: progressStartEvent.body.title,

View File

@@ -13,6 +13,8 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { matchesFuzzy } from 'vs/base/common/filters';
import { withNullAsUndefined } from 'vs/base/common/types';
import { ADD_CONFIGURATION_ID } from 'vs/workbench/contrib/debug/browser/debugCommands';
import { debugConfigure } from 'vs/workbench/contrib/debug/browser/debugIcons';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider<IPickerQuickAccessItem> {
@@ -55,7 +57,7 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider<IPi
description: this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? config.launch.name : '',
highlights: { label: highlights },
buttons: [{
iconClass: 'codicon-gear',
iconClass: ThemeIcon.asClassName(debugConfigure),
tooltip: localize('customizeLaunchConfig', "Configure Launch Configuration")
}],
trigger: () => {
@@ -64,7 +66,7 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider<IPi
return TriggerAction.CLOSE_PICKER;
},
accept: async () => {
await this.debugService.getConfigurationManager().selectConfiguration(config.launch, config.name);
await configManager.selectConfiguration(config.launch, config.name);
try {
await this.debugService.startDebugging(config.launch);
} catch (error) {
@@ -86,6 +88,26 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider<IPi
});
}
configManager.getRecentDynamicConfigurations().forEach(({ name, type }) => {
const highlights = matchesFuzzy(filter, name, true);
if (highlights) {
picks.push({
label: name,
highlights: { label: highlights },
accept: async () => {
await configManager.selectConfiguration(undefined, name, undefined, { type });
try {
const { launch, getConfig } = configManager.selectedConfiguration;
const config = await getConfig();
await this.debugService.startDebugging(launch, config);
} catch (error) {
this.notificationService.error(error);
}
}
});
}
});
dynamicProviders.forEach(provider => {
picks.push({
label: `$(folder) ${provider.label}...`,
@@ -93,6 +115,7 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider<IPi
accept: async () => {
const pick = await provider.pick();
if (pick) {
await configManager.selectConfiguration(pick.launch, pick.config.name, pick.config, { type: pick.config.type });
this.debugService.startDebugging(pick.launch, pick.config);
}
}

View File

@@ -32,7 +32,7 @@ import { IAction, Action } from 'vs/base/common/actions';
import { deepClone, equals } from 'vs/base/common/objects';
import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IStackFrame, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, IGlobalConfig, CALLSTACK_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IStackFrame, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, IGlobalConfig, CALLSTACK_VIEW_ID, IAdapterManager, IExceptionBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
import { getExtensionHostDebugSession } from 'vs/workbench/contrib/debug/common/debugUtils';
import { isErrorWithActions } from 'vs/base/common/errorsWithActions';
import { RunOnceScheduler } from 'vs/base/common/async';
@@ -49,6 +49,8 @@ import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompou
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { AdapterManager } from 'vs/workbench/contrib/debug/browser/debugAdapterManager';
import { ITextModel } from 'vs/editor/common/model';
export class DebugService implements IDebugService {
declare readonly _serviceBrand: undefined;
@@ -63,6 +65,7 @@ export class DebugService implements IDebugService {
private telemetry: DebugTelemetry;
private taskRunner: DebugTaskRunner;
private configurationManager: ConfigurationManager;
private adapterManager: AdapterManager;
private toDispose: IDisposable[];
private debugType!: IContextKey<string>;
private debugState!: IContextKey<string>;
@@ -105,7 +108,8 @@ export class DebugService implements IDebugService {
this._onWillNewSession = new Emitter<IDebugSession>();
this._onDidEndSession = new Emitter<IDebugSession>();
this.configurationManager = this.instantiationService.createInstance(ConfigurationManager);
this.adapterManager = this.instantiationService.createInstance(AdapterManager);
this.configurationManager = this.instantiationService.createInstance(ConfigurationManager, this.adapterManager);
this.toDispose.push(this.configurationManager);
contextKeyService.bufferChangeEvents(() => {
@@ -113,7 +117,7 @@ export class DebugService implements IDebugService {
this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService);
this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService);
this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService);
this.debugUx.set((this.configurationManager.hasDebuggers() && !!this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple');
this.debugUx.set((this.adapterManager.hasDebuggers() && !!this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple');
this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService);
});
@@ -162,8 +166,8 @@ export class DebugService implements IDebugService {
this.toDispose.push(this.viewModel.onDidFocusSession(() => {
this.onStateChange();
}));
this.toDispose.push(Event.any(this.configurationManager.onDidRegisterDebugger, this.configurationManager.onDidSelectConfiguration)(() => {
this.debugUx.set(!!(this.state !== State.Inactive || (this.configurationManager.selectedConfiguration.name && this.configurationManager.hasDebuggers())) ? 'default' : 'simple');
this.toDispose.push(Event.any(this.adapterManager.onDidRegisterDebugger, this.configurationManager.onDidSelectConfiguration)(() => {
this.debugUx.set(!!(this.state !== State.Inactive || (this.configurationManager.selectedConfiguration.name && this.adapterManager.hasDebuggers())) ? 'default' : 'simple');
}));
this.toDispose.push(this.model.onDidChangeCallStack(() => {
const numberOfSessions = this.model.getSessions().filter(s => !s.parentSession).length;
@@ -192,6 +196,10 @@ export class DebugService implements IDebugService {
return this.configurationManager;
}
getAdapterManager(): IAdapterManager {
return this.adapterManager;
}
sourceIsNotAvailable(uri: uri): void {
this.model.sourceIsNotAvailable(uri);
}
@@ -245,7 +253,7 @@ export class DebugService implements IDebugService {
this.debugState.set(getStateLabel(state));
this.inDebugMode.set(state !== State.Inactive);
// Only show the simple ux if debug is not yet started and if no launch.json exists
this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || (this.configurationManager.hasDebuggers() && this.configurationManager.selectedConfiguration.name)) ? 'default' : 'simple');
this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || (this.adapterManager.hasDebuggers() && this.configurationManager.selectedConfiguration.name)) ? 'default' : 'simple');
});
this.previousState = state;
this._onDidChangeState.fire(state);
@@ -297,7 +305,7 @@ export class DebugService implements IDebugService {
const sessions = this.model.getSessions();
const alreadyRunningMessage = nls.localize('configurationAlreadyRunning', "There is already a debug configuration \"{0}\" running.", configOrName);
if (sessions.some(s => s.configuration.name === configOrName && (!launch || !launch.workspace || !s.root || this.uriIdentityService.extUri.isEqual(s.root.uri, launch.workspace.uri)))) {
if (sessions.some(s => (s.configuration.name === configOrName && s.root === launch.workspace) && (!launch || !launch.workspace || !s.root || this.uriIdentityService.extUri.isEqual(s.root.uri, launch.workspace.uri)))) {
throw new Error(alreadyRunningMessage);
}
if (compound && compound.configurations && sessions.some(p => compound!.configurations.indexOf(p.configuration.name) !== -1)) {
@@ -397,7 +405,7 @@ export class DebugService implements IDebugService {
const unresolvedConfig = deepClone(config);
if (!type) {
const guess = await this.configurationManager.guessDebugger();
const guess = await this.adapterManager.guessDebugger();
if (guess) {
type = guess.type;
}
@@ -437,7 +445,7 @@ export class DebugService implements IDebugService {
}
resolvedConfig = cfg;
if (!this.configurationManager.getDebugger(resolvedConfig.type) || (configByProviders.request !== 'attach' && configByProviders.request !== 'launch')) {
if (!this.adapterManager.getDebugger(resolvedConfig.type) || (configByProviders.request !== 'attach' && configByProviders.request !== 'launch')) {
let message: string;
if (configByProviders.request !== 'attach' && configByProviders.request !== 'launch') {
message = configByProviders.request ? nls.localize('debugRequestNotSupported', "Attribute '{0}' has an unsupported value '{1}' in the chosen debug configuration.", 'request', configByProviders.request)
@@ -549,7 +557,7 @@ export class DebugService implements IDebugService {
}
private async launchOrAttachToSession(session: IDebugSession, forceFocus = false): Promise<void> {
const dbgr = this.configurationManager.getDebugger(session.configuration.type);
const dbgr = this.adapterManager.getDebugger(session.configuration.type);
try {
await session.initialize(dbgr!);
await session.launchOrAttach(session.configuration);
@@ -754,7 +762,7 @@ export class DebugService implements IDebugService {
}
private async substituteVariables(launch: ILaunch | undefined, config: IConfig): Promise<IConfig | undefined> {
const dbg = this.configurationManager.getDebugger(config.type);
const dbg = this.adapterManager.getDebugger(config.type);
if (dbg) {
let folder: IWorkspaceFolder | undefined = undefined;
if (launch && launch.workspace) {
@@ -842,6 +850,10 @@ export class DebugService implements IDebugService {
//---- breakpoints
canSetBreakpointsIn(model: ITextModel): boolean {
return this.adapterManager.canSetBreakpointsIn(model);
}
async enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): Promise<void> {
if (breakpoint) {
this.model.setEnablement(breakpoint, enable);
@@ -906,7 +918,7 @@ export class DebugService implements IDebugService {
addFunctionBreakpoint(name?: string, id?: string): void {
const newFunctionBreakpoint = this.model.addFunctionBreakpoint(name || '', id);
this.viewModel.setSelectedFunctionBreakpoint(newFunctionBreakpoint);
this.viewModel.setSelectedBreakpoint(newFunctionBreakpoint);
}
async renameFunctionBreakpoint(id: string, newFunctionName: string): Promise<void> {
@@ -934,6 +946,12 @@ export class DebugService implements IDebugService {
await this.sendDataBreakpoints();
}
async setExceptionBreakpointCondition(exceptionBreakpoint: IExceptionBreakpoint, condition: string | undefined): Promise<void> {
this.model.setExceptionBreakpointCondition(exceptionBreakpoint, condition);
this.debugStorage.storeBreakpoints(this.model);
await this.sendExceptionBreakpoints();
}
async sendAllBreakpoints(session?: IDebugSession): Promise<any> {
await Promise.all(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session)));
await this.sendFunctionBreakpoints(session);

View File

@@ -394,7 +394,18 @@ export class DebugSession implements IDebugSession {
}
if (this.raw.readyForBreakpoints) {
await this.raw.setExceptionBreakpoints({ filters: exbpts.map(exb => exb.filter) });
const args: DebugProtocol.SetExceptionBreakpointsArguments = this.capabilities.supportsExceptionFilterOptions ? {
filters: [],
filterOptions: exbpts.map(exb => {
if (exb.condition) {
return { filterId: exb.filter, condition: exb.condition };
}
return { filterId: exb.filter };
})
} : { filters: exbpts.map(exb => exb.filter) };
await this.raw.setExceptionBreakpoints(args);
}
}

View File

@@ -16,9 +16,9 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IDebugConfiguration, IDebugService, State } from 'vs/workbench/contrib/debug/common/debug';
import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { registerThemingParticipant, IThemeService, Themable } from 'vs/platform/theme/common/themeService';
import { registerThemingParticipant, IThemeService, Themable, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { registerColor, contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { localize } from 'vs/nls';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -29,6 +29,7 @@ import { IMenu, IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from '
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons';
const DEBUG_TOOLBAR_POSITION_KEY = 'debug.actionswidgetposition';
const DEBUG_TOOLBAR_Y_KEY = 'debug.actionswidgety';
@@ -64,7 +65,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
this.$el = dom.$('div.debug-toolbar');
this.$el.style.top = `${layoutService.offset?.top ?? 0}px`;
this.dragArea = dom.append(this.$el, dom.$('div.drag-area.codicon.codicon-gripper'));
this.dragArea = dom.append(this.$el, dom.$('div.drag-area' + ThemeIcon.asCSSSelector(icons.debugGripper)));
const actionBarContainer = dom.append(this.$el, dom.$('div.action-bar-container'));
this.debugToolBarMenu = menuService.createMenu(MenuId.DebugToolBar, contextKeyService);
@@ -169,7 +170,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
const left = dom.getComputedStyle(this.$el).left;
if (left) {
const position = parseFloat(left) / window.innerWidth;
this.storageService.store(DEBUG_TOOLBAR_POSITION_KEY, position, StorageScope.GLOBAL);
this.storageService.store(DEBUG_TOOLBAR_POSITION_KEY, position, StorageScope.GLOBAL, StorageTarget.MACHINE);
}
}
@@ -180,7 +181,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
this.$el.style.backgroundColor = this.getColor(debugToolBarBackground) || '';
const widgetShadowColor = this.getColor(widgetShadow);
this.$el.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : '';
this.$el.style.boxShadow = widgetShadowColor ? `0 0 8px 2px ${widgetShadowColor}` : '';
const contrastBorderColor = this.getColor(contrastBorder);
const borderColor = this.getColor(debugToolBarBorder);
@@ -220,7 +221,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
if ((y < titleAreaHeight / 2) || (y > titleAreaHeight + titleAreaHeight / 2)) {
const moveToTop = y < titleAreaHeight;
this.setYCoordinate(moveToTop ? 0 : titleAreaHeight);
this.storageService.store(DEBUG_TOOLBAR_Y_KEY, moveToTop ? 0 : 2 * titleAreaHeight, StorageScope.GLOBAL);
this.storageService.store(DEBUG_TOOLBAR_Y_KEY, moveToTop ? 0 : 2 * titleAreaHeight, StorageScope.GLOBAL, StorageTarget.MACHINE);
}
}
@@ -351,51 +352,51 @@ registerThemingParticipant((theme, collector) => {
const debugIconStartColor = theme.getColor(debugIconStartForeground);
if (debugIconStartColor) {
collector.addRule(`.monaco-workbench .codicon-debug-start { color: ${debugIconStartColor} !important; }`);
collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStart)} { color: ${debugIconStartColor} !important; }`);
}
const debugIconPauseColor = theme.getColor(debugIconPauseForeground);
if (debugIconPauseColor) {
collector.addRule(`.monaco-workbench .codicon-debug-pause { color: ${debugIconPauseColor} !important; }`);
collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugPause)} { color: ${debugIconPauseColor} !important; }`);
}
const debugIconStopColor = theme.getColor(debugIconStopForeground);
if (debugIconStopColor) {
collector.addRule(`.monaco-workbench .codicon-debug-stop, .monaco-workbench .debug-view-content .codicon-record { color: ${debugIconStopColor} !important; }`);
collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStop)} { color: ${debugIconStopColor} !important; }`);
}
const debugIconDisconnectColor = theme.getColor(debugIconDisconnectForeground);
if (debugIconDisconnectColor) {
collector.addRule(`.monaco-workbench .codicon-debug-disconnect { color: ${debugIconDisconnectColor} !important; }`);
collector.addRule(`.monaco-workbench .debug-view-content ${ThemeIcon.asCSSSelector(icons.debugDisconnect)}, .monaco-workbench .debug-toolbar ${ThemeIcon.asCSSSelector(icons.debugDisconnect)} { color: ${debugIconDisconnectColor} !important; }`);
}
const debugIconRestartColor = theme.getColor(debugIconRestartForeground);
if (debugIconRestartColor) {
collector.addRule(`.monaco-workbench .codicon-debug-restart, .monaco-workbench .codicon-debug-restart-frame { color: ${debugIconRestartColor} !important; }`);
collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugRestart)}, .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugRestartFrame)} { color: ${debugIconRestartColor} !important; }`);
}
const debugIconStepOverColor = theme.getColor(debugIconStepOverForeground);
if (debugIconStepOverColor) {
collector.addRule(`.monaco-workbench .codicon-debug-step-over { color: ${debugIconStepOverColor} !important; }`);
collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStepOver)} { color: ${debugIconStepOverColor} !important; }`);
}
const debugIconStepIntoColor = theme.getColor(debugIconStepIntoForeground);
if (debugIconStepIntoColor) {
collector.addRule(`.monaco-workbench .codicon-debug-step-into { color: ${debugIconStepIntoColor} !important; }`);
collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStepInto)} { color: ${debugIconStepIntoColor} !important; }`);
}
const debugIconStepOutColor = theme.getColor(debugIconStepOutForeground);
if (debugIconStepOutColor) {
collector.addRule(`.monaco-workbench .codicon-debug-step-out { color: ${debugIconStepOutColor} !important; }`);
collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStepOut)} { color: ${debugIconStepOutColor} !important; }`);
}
const debugIconContinueColor = theme.getColor(debugIconContinueForeground);
if (debugIconContinueColor) {
collector.addRule(`.monaco-workbench .codicon-debug-continue,.monaco-workbench .codicon-debug-reverse-continue { color: ${debugIconContinueColor} !important; }`);
collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugContinue)}, .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugReverseContinue)} { color: ${debugIconContinueColor} !important; }`);
}
const debugIconStepBackColor = theme.getColor(debugIconStepBackForeground);
if (debugIconStepBackColor) {
collector.addRule(`.monaco-workbench .codicon-debug-step-back { color: ${debugIconStepBackColor} !important; }`);
collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStepBack)} { color: ${debugIconStepBackColor} !important; }`);
}
});

View File

@@ -15,7 +15,7 @@ import { IProgressService } from 'vs/platform/progress/common/progress';
import { IWorkspaceContextService, WorkbenchState } 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 { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
@@ -33,6 +33,7 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { ShowViewletAction } from 'vs/workbench/browser/viewlet';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { debugConsole } from 'vs/workbench/contrib/debug/browser/debugIcons';
export class DebugViewPaneContainer extends ViewPaneContainer {
@@ -247,7 +248,7 @@ export class OpenDebugConsoleAction extends ToggleViewAction {
@IContextKeyService contextKeyService: IContextKeyService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
) {
super(id, label, REPL_VIEW_ID, viewsService, viewDescriptorService, contextKeyService, layoutService, 'codicon-debug-console');
super(id, label, REPL_VIEW_ID, viewsService, viewDescriptorService, contextKeyService, layoutService, ThemeIcon.asClassName(debugConsole));
}
}

View File

@@ -8,14 +8,17 @@ import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IExceptionInfo, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
import { IExceptionInfo, IDebugSession, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID } from 'vs/workbench/contrib/debug/common/debug';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService';
import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { Color } from 'vs/base/common/color';
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action } from 'vs/base/common/actions';
import { widgetClose } from 'vs/platform/theme/common/iconRegistry';
const $ = dom.$;
// theming
@@ -25,18 +28,19 @@ export const debugExceptionWidgetBackground = registerColor('debugExceptionWidge
export class ExceptionWidget extends ZoneWidget {
private _backgroundColor?: Color;
private backgroundColor: Color | undefined;
constructor(editor: ICodeEditor, private exceptionInfo: IExceptionInfo, private debugSession: IDebugSession | undefined,
constructor(
editor: ICodeEditor,
private exceptionInfo: IExceptionInfo,
private debugSession: IDebugSession | undefined,
@IThemeService themeService: IThemeService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
super(editor, { showFrame: true, showArrow: true, frameWidth: 1, className: 'exception-widget-container' });
super(editor, { showFrame: true, showArrow: true, isAccessible: true, frameWidth: 1, className: 'exception-widget-container' });
this._backgroundColor = Color.white;
this._applyTheme(themeService.getColorTheme());
this._disposables.add(themeService.onDidColorThemeChange(this._applyTheme.bind(this)));
this.applyTheme(themeService.getColorTheme());
this._disposables.add(themeService.onDidColorThemeChange(this.applyTheme.bind(this)));
this.create();
const onDidLayoutChangeScheduler = new RunOnceScheduler(() => this._doLayout(undefined, undefined), 50);
@@ -44,8 +48,8 @@ export class ExceptionWidget extends ZoneWidget {
this._disposables.add(onDidLayoutChangeScheduler);
}
private _applyTheme(theme: IColorTheme): void {
this._backgroundColor = theme.getColor(debugExceptionWidgetBackground);
private applyTheme(theme: IColorTheme): void {
this.backgroundColor = theme.getColor(debugExceptionWidgetBackground);
const frameColor = theme.getColor(debugExceptionWidgetBorder);
this.style({
arrowColor: frameColor,
@@ -55,7 +59,7 @@ export class ExceptionWidget extends ZoneWidget {
protected _applyStyles(): void {
if (this.container) {
this.container.style.backgroundColor = this._backgroundColor ? this._backgroundColor.toString() : '';
this.container.style.backgroundColor = this.backgroundColor ? this.backgroundColor.toString() : '';
}
super._applyStyles();
}
@@ -66,14 +70,27 @@ export class ExceptionWidget extends ZoneWidget {
const fontInfo = this.editor.getOption(EditorOption.fontInfo);
container.style.fontSize = `${fontInfo.fontSize}px`;
container.style.lineHeight = `${fontInfo.lineHeight}px`;
container.tabIndex = 0;
const title = $('.title');
const label = $('.label');
dom.append(title, label);
const actions = $('.actions');
dom.append(title, actions);
label.textContent = this.exceptionInfo.id ? nls.localize('exceptionThrownWithId', 'Exception has occurred: {0}', this.exceptionInfo.id) : nls.localize('exceptionThrown', 'Exception has occurred.');
let ariaLabel = label.textContent;
const actionBar = new ActionBar(actions);
actionBar.push(new Action('editor.closeExceptionWidget', nls.localize('close', "Close"), ThemeIcon.asClassName(widgetClose), true, async () => {
const contribution = this.editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID);
contribution.closeExceptionWidget();
}), { label: false, icon: true });
let title = $('.title');
title.textContent = this.exceptionInfo.id ? nls.localize('exceptionThrownWithId', 'Exception has occurred: {0}', this.exceptionInfo.id) : nls.localize('exceptionThrown', 'Exception has occurred.');
dom.append(container, title);
if (this.exceptionInfo.description) {
let description = $('.description');
description.textContent = this.exceptionInfo.description;
ariaLabel += ', ' + this.exceptionInfo.description;
dom.append(container, description);
}
@@ -83,7 +100,9 @@ export class ExceptionWidget extends ZoneWidget {
const linkedStackTrace = linkDetector.linkify(this.exceptionInfo.details.stackTrace, true, this.debugSession ? this.debugSession.root : undefined);
stackTrace.appendChild(linkedStackTrace);
dom.append(container, stackTrace);
ariaLabel += ', ' + this.exceptionInfo.details.stackTrace;
}
container.setAttribute('aria-label', ariaLabel);
}
protected _doLayout(_heightInPixel: number | undefined, _widthInPixel: number | undefined): void {
@@ -96,4 +115,13 @@ export class ExceptionWidget extends ZoneWidget {
this._relayout(computedLinesNumber);
}
focus(): void {
// Focus into the container for accessibility purposes so the exception and stack trace gets read
this.container?.focus();
}
hasfocus(): boolean {
return dom.isAncestor(document.activeElement, this.container);
}
}

View File

@@ -16,7 +16,7 @@ import { IDebugSession, IDebugService, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE } from '
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { tildify } from 'vs/base/common/labels';
import { normalizeDriveLetter, tildify } from 'vs/base/common/labels';
import { isWindows } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { ltrim } from 'vs/base/common/strings';
@@ -350,7 +350,9 @@ class SessionTreeItem extends BaseTreeItem {
} else {
// on unix try to tildify absolute paths
path = normalize(path);
if (!isWindows) {
if (isWindows) {
path = normalizeDriveLetter(path);
} else {
path = tildify(path, (await this._pathService.userHome()).fsPath);
}
}

View File

@@ -19,7 +19,8 @@
max-width: 700px;
}
.monaco-editor .debug-hover-widget .complex-value .title {
.monaco-editor .debug-hover-widget .complex-value .title,
.monaco-editor .debug-hover-widget .complex-value .tip {
padding-left: 15px;
padding-right: 2px;
font-size: 11px;
@@ -29,9 +30,17 @@
height: 18px;
overflow: hidden;
white-space: pre;
}
.monaco-editor .debug-hover-widget .complex-value .title {
border-bottom: 1px solid rgba(128, 128, 128, 0.35);
}
.monaco-editor .debug-hover-widget .complex-value .tip {
border-top: 1px solid rgba(128, 128, 128, 0.35);
opacity: 0.5;
}
.monaco-editor .debug-hover-widget .debug-hover-tree {
line-height: 18px;
cursor: pointer;

View File

@@ -84,6 +84,7 @@
/* Make icons and text the same color as the list foreground on focus selection */
.debug-pane .monaco-list:focus .monaco-list-row.selected .state.label,
.debug-pane .monaco-list:focus .monaco-list-row.selected .load-all,
.debug-pane .monaco-list:focus .monaco-list-row.selected.focused .state.label,
.debug-pane .monaco-list:focus .monaco-list-row.selected .codicon,
.debug-pane .monaco-list:focus .monaco-list-row.selected.focused .codicon {
@@ -317,10 +318,10 @@
justify-content: center;
}
.debug-pane .debug-breakpoints .breakpoint > .file-path {
.debug-pane .debug-breakpoints .breakpoint > .file-path,
.debug-pane .debug-breakpoints .breakpoint.exception > .condition {
opacity: 0.7;
font-size: 0.9em;
margin-left: 0.8em;
margin-left: 0.9em;
flex: 1;
text-overflow: ellipsis;
overflow: hidden;

View File

@@ -15,9 +15,17 @@
}
.monaco-editor .zone-widget .zone-widget-container.exception-widget .title {
display: flex;
}
.monaco-editor .zone-widget .zone-widget-container.exception-widget .title .label {
font-weight: bold;
}
.monaco-editor .zone-widget .zone-widget-container.exception-widget .title .actions {
flex: 1;
}
.monaco-editor .zone-widget .zone-widget-container.exception-widget .description,
.monaco-editor .zone-widget .zone-widget-container.exception-widget .stack-trace {
font-family: var(--monaco-monospace-font);
@@ -27,7 +35,7 @@
margin-top: 0.5em;
}
.monaco-editor .zone-widget .zone-widget-container.exception-widget a {
.monaco-editor .zone-widget .zone-widget-container.exception-widget .stack-trace a {
text-decoration: underline;
cursor: pointer;
}

View File

@@ -38,6 +38,15 @@
margin-right: 4px;
}
.monaco-workbench .repl .repl-tree .output.expression.value-and-source .count-badge-wrapper {
margin-right: 4px;
}
/* Allow the badge to be a bit shorter so it does not look cut off */
.monaco-workbench .repl .repl-tree .output.expression.value-and-source .count-badge-wrapper .monaco-count-badge {
min-height: 16px;
}
.monaco-workbench .repl .repl-tree .monaco-tl-contents .arrow {
position:absolute;
left: 2px;

View File

@@ -620,10 +620,10 @@ export class RawDebugSession implements IDisposable {
}
}
let env: IProcessEnvironment = {};
if (vscodeArgs.env) {
let env: IProcessEnvironment = processEnv;
if (vscodeArgs.env && Object.keys(vscodeArgs.env).length > 0) {
// merge environment variables into a copy of the process.env
env = objects.mixin(processEnv, vscodeArgs.env);
env = objects.mixin(objects.deepClone(processEnv), vscodeArgs.env);
// and delete some if necessary
Object.keys(env).filter(k => env[k] === null).forEach(key => delete env[key]);
}

View File

@@ -19,8 +19,8 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { memoize } from 'vs/base/common/decorators';
import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle';
@@ -59,10 +59,12 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions';
import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor';
import { ReplFilter, ReplFilterState, ReplFilterActionViewItem } from 'vs/workbench/contrib/debug/browser/replFilter';
import { debugConsoleClearAll, debugConsoleEvaluationPrompt } from 'vs/workbench/contrib/debug/browser/debugIcons';
const $ = dom.$;
const HISTORY_STORAGE_KEY = 'debug.repl.history';
const FILTER_HISTORY_STORAGE_KEY = 'debug.repl.filterHistory';
const DECORATION_KEY = 'replinputdecoration';
const FILTER_ACTION_ID = `workbench.actions.treeView.repl.filter`;
@@ -303,7 +305,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget {
if (this.styleElement) {
const debugConsole = this.configurationService.getValue<IDebugConfiguration>('debug').console;
const fontSize = debugConsole.fontSize;
const fontFamily = debugConsole.fontFamily === 'default' ? 'var(--monaco-monospace-font)' : debugConsole.fontFamily;
const fontFamily = debugConsole.fontFamily === 'default' ? 'var(--monaco-monospace-font)' : `'${debugConsole.fontFamily}'`;
const lineHeight = debugConsole.lineHeight ? `${debugConsole.lineHeight}px` : '1.4em';
const backgroundColor = this.themeService.getColorTheme().getColor(this.getBackgroundColor());
@@ -462,7 +464,9 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget {
const session = (this.tree ? this.tree.getInput() : undefined) ?? this.debugService.getViewModel().focusedSession;
return this.instantiationService.createInstance(SelectReplActionViewItem, this.selectReplAction, session);
} else if (action.id === FILTER_ACTION_ID) {
this.filterActionViewItem = this.instantiationService.createInstance(ReplFilterActionViewItem, action, localize({ key: 'workbench.debug.filter.placeholder', comment: ['Text in the brackets after e.g. is not localizable'] }, "Filter (e.g. text, !exclude)"), this.filterState);
const filterHistory = JSON.parse(this.storageService.get(FILTER_HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')) as string[];
this.filterActionViewItem = this.instantiationService.createInstance(ReplFilterActionViewItem, action,
localize({ key: 'workbench.debug.filter.placeholder', comment: ['Text in the brackets after e.g. is not localizable'] }, "Filter (e.g. text, !exclude)"), this.filterState, filterHistory);
return this.filterActionViewItem;
}
@@ -592,7 +596,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget {
private createReplInput(container: HTMLElement): void {
this.replInputContainer = dom.append(container, $('.repl-input-wrapper'));
dom.append(this.replInputContainer, $('.repl-input-chevron.codicon.codicon-chevron-right'));
dom.append(this.replInputContainer, $('.repl-input-chevron' + ThemeIcon.asCSSSelector(debugConsoleEvaluationPrompt)));
const { scopedContextKeyService, historyNavigationEnablement } = createAndBindHistoryNavigationWidgetScopedContextKeyService(this.contextKeyService, { target: this.replInputContainer, historyNavigator: this });
this.historyNavigationEnablement = historyNavigationEnablement;
@@ -708,10 +712,18 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget {
saveState(): void {
const replHistory = this.history.getHistory();
if (replHistory.length) {
this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE);
this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE, StorageTarget.USER);
} else {
this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE);
}
if (this.filterActionViewItem) {
const filterHistory = this.filterActionViewItem.getHistory();
if (filterHistory.length) {
this.storageService.store(FILTER_HISTORY_STORAGE_KEY, JSON.stringify(filterHistory), StorageScope.WORKSPACE, StorageTarget.USER);
} else {
this.storageService.remove(FILTER_HISTORY_STORAGE_KEY, StorageScope.WORKSPACE);
}
}
super.saveState();
}
@@ -845,7 +857,7 @@ export class ClearReplAction extends Action {
constructor(id: string, label: string,
@IViewsService private readonly viewsService: IViewsService
) {
super(id, label, 'debug-action codicon-clear-all');
super(id, label, 'debug-action ' + ThemeIcon.asClassName(debugConsoleClearAll));
}
async run(): Promise<any> {

View File

@@ -136,6 +136,7 @@ export class ReplFilterActionViewItem extends BaseActionViewItem {
action: IAction,
private placeholder: string,
private filters: ReplFilterState,
private history: string[],
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IThemeService private readonly themeService: IThemeService,
@IContextViewService private readonly contextViewService: IContextViewService) {
@@ -159,6 +160,10 @@ export class ReplFilterActionViewItem extends BaseActionViewItem {
this.filterInputBox.focus();
}
getHistory(): string[] {
return this.filterInputBox.getHistory();
}
private clearFilterText(): void {
this.filterInputBox.value = '';
}
@@ -166,7 +171,7 @@ export class ReplFilterActionViewItem extends BaseActionViewItem {
private createInput(container: HTMLElement): void {
this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, {
placeholder: this.placeholder,
history: []
history: this.history
}));
this._register(attachInputBoxStyler(this.filterInputBox, this.themeService));
this.filterInputBox.value = this.filters.filterText;

View File

@@ -21,8 +21,11 @@ import { FuzzyScore, createMatches } from 'vs/base/common/filters';
import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { IReplElementSource, IDebugService, IExpression, IReplElement, IDebugConfiguration, IDebugSession, IExpressionContainer } from 'vs/workbench/contrib/debug/common/debug';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { localize } from 'vs/nls';
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
import { attachBadgeStyler } from 'vs/platform/theme/common/styler';
import { debugConsoleEvaluationInput } from 'vs/workbench/contrib/debug/browser/debugIcons';
const $ = dom.$;
@@ -40,10 +43,13 @@ interface IReplEvaluationResultTemplateData {
interface ISimpleReplElementTemplateData {
container: HTMLElement;
count: CountBadge;
countContainer: HTMLElement;
value: HTMLElement;
source: HTMLElement;
getReplElementSource(): IReplElementSource | undefined;
toDispose: IDisposable[];
elementListener: IDisposable;
}
interface IRawObjectReplTemplateData {
@@ -62,7 +68,7 @@ export class ReplEvaluationInputsRenderer implements ITreeRenderer<ReplEvaluatio
}
renderTemplate(container: HTMLElement): IReplEvaluationInputTemplateData {
dom.append(container, $('span.arrow.codicon.codicon-arrow-small-right'));
dom.append(container, $('span.arrow' + ThemeIcon.asCSSSelector(debugConsoleEvaluationInput)));
const input = dom.append(container, $('.expression'));
const label = new HighlightedLabel(input, false);
return { label };
@@ -101,7 +107,7 @@ export class ReplGroupRenderer implements ITreeRenderer<ReplGroup, FuzzyScore, I
}
}
export class ReplEvaluationResultsRenderer implements ITreeRenderer<ReplEvaluationResult, FuzzyScore, IReplEvaluationResultTemplateData> {
export class ReplEvaluationResultsRenderer implements ITreeRenderer<ReplEvaluationResult | Variable, FuzzyScore, IReplEvaluationResultTemplateData> {
static readonly ID = 'replEvaluationResult';
get templateId(): string {
@@ -117,7 +123,7 @@ export class ReplEvaluationResultsRenderer implements ITreeRenderer<ReplEvaluati
return { value };
}
renderElement(element: ITreeNode<ReplEvaluationResult, FuzzyScore>, index: number, templateData: IReplEvaluationResultTemplateData): void {
renderElement(element: ITreeNode<ReplEvaluationResult | Variable, FuzzyScore>, index: number, templateData: IReplEvaluationResultTemplateData): void {
const expression = element.element;
renderExpressionValue(expression, templateData.value, {
showHover: false,
@@ -151,9 +157,12 @@ export class ReplSimpleElementsRenderer implements ITreeRenderer<SimpleReplEleme
const expression = dom.append(container, $('.output.expression.value-and-source'));
data.container = container;
data.countContainer = dom.append(expression, $('.count-badge-wrapper'));
data.count = new CountBadge(data.countContainer);
data.value = dom.append(expression, $('span.value'));
data.source = dom.append(expression, $('.source'));
data.toDispose = [];
data.toDispose.push(attachBadgeStyler(data.count, this.themeService));
data.toDispose.push(dom.addDisposableListener(data.source, 'click', e => {
e.preventDefault();
e.stopPropagation();
@@ -172,11 +181,13 @@ export class ReplSimpleElementsRenderer implements ITreeRenderer<SimpleReplEleme
}
renderElement({ element }: ITreeNode<SimpleReplElement, FuzzyScore>, index: number, templateData: ISimpleReplElementTemplateData): void {
this.setElementCount(element, templateData);
templateData.elementListener = element.onDidChangeCount(() => this.setElementCount(element, templateData));
// value
dom.clearNode(templateData.value);
// Reset classes to clear ansi decorations since templates are reused
templateData.value.className = 'value';
const result = handleANSIOutput(element.value, this.linkDetector, this.themeService, element.session);
const result = handleANSIOutput(element.value, this.linkDetector, this.themeService, element.session.root);
templateData.value.appendChild(result);
templateData.value.classList.add((element.severity === severity.Warning) ? 'warn' : (element.severity === severity.Error) ? 'error' : (element.severity === severity.Ignore) ? 'ignore' : 'info');
@@ -185,9 +196,22 @@ export class ReplSimpleElementsRenderer implements ITreeRenderer<SimpleReplEleme
templateData.getReplElementSource = () => element.sourceData;
}
private setElementCount(element: SimpleReplElement, templateData: ISimpleReplElementTemplateData): void {
if (element.count >= 2) {
templateData.count.setCount(element.count);
templateData.countContainer.hidden = false;
} else {
templateData.countContainer.hidden = true;
}
}
disposeTemplate(templateData: ISimpleReplElementTemplateData): void {
dispose(templateData.toDispose);
}
disposeElement(_element: ITreeNode<SimpleReplElement, FuzzyScore>, _index: number, templateData: ISimpleReplElementTemplateData): void {
templateData.elementListener.dispose();
}
}
export class ReplVariablesRenderer extends AbstractExpressionsRenderer {
@@ -296,14 +320,14 @@ export class ReplDelegate extends CachedListVirtualDelegate<IReplElement> {
if (element instanceof Variable && element.name) {
return ReplVariablesRenderer.ID;
}
if (element instanceof ReplEvaluationResult) {
if (element instanceof ReplEvaluationResult || (element instanceof Variable && !element.name)) {
// Variable with no name is a top level variable which should be rendered like a repl element #17404
return ReplEvaluationResultsRenderer.ID;
}
if (element instanceof ReplEvaluationInput) {
return ReplEvaluationInputsRenderer.ID;
}
if (element instanceof SimpleReplElement || (element instanceof Variable && !element.name)) {
// Variable with no name is a top level variable which should be rendered like a repl element #17404
if (element instanceof SimpleReplElement) {
return ReplSimpleElementsRenderer.ID;
}
if (element instanceof ReplGroup) {
@@ -359,13 +383,13 @@ export class ReplAccessibilityProvider implements IListAccessibilityProvider<IRe
return localize('replVariableAriaLabel', "Variable {0}, value {1}", element.name, element.value);
}
if (element instanceof SimpleReplElement || element instanceof ReplEvaluationInput || element instanceof ReplEvaluationResult) {
return localize('replValueOutputAriaLabel', "{0}", element.value);
return element.value + (element instanceof SimpleReplElement && element.count > 1 ? localize('occurred', ", occured {0} times", element.count) : '');
}
if (element instanceof RawObjectReplElement) {
return localize('replRawObjectAriaLabel', "Debug console variable {0}, value {1}", element.name, element.value);
}
if (element instanceof ReplGroup) {
return localize('replGroup', "Debug console group {0}, read eval print loop, debug", element.name);
return localize('replGroup', "Debug console group {0}", element.name);
}
return '';

View File

@@ -30,12 +30,13 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c
import { dispose } from 'vs/base/common/lifecycle';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { withUndefinedAsNull } from 'vs/base/common/types';
import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions';
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { debugCollapseAll } from 'vs/workbench/contrib/debug/browser/debugIcons';
const $ = dom.$;
let forgetScopes = true;
@@ -179,7 +180,7 @@ export class VariablesView extends ViewPane {
}
getActions(): IAction[] {
return [new CollapseAction(() => this.tree, true, 'explorer-action codicon-collapse-all')];
return [new CollapseAction(() => this.tree, true, 'explorer-action ' + ThemeIcon.asClassName(debugCollapseAll))];
}
layoutBody(width: number, height: number): void {
@@ -207,8 +208,8 @@ export class VariablesView extends ViewPane {
this.variableEvaluateName.set(!!variable.evaluateName);
this.breakWhenValueChangesSupported.reset();
if (session && session.capabilities.supportsDataBreakpoints) {
const response = await session.dataBreakpointInfo(variable.name, variable.parent.reference);
const dataBreakpointId = response?.dataId;
dataBreakpointInfoResponse = await session.dataBreakpointInfo(variable.name, variable.parent.reference);
const dataBreakpointId = dataBreakpointInfoResponse?.dataId;
this.breakWhenValueChangesSupported.set(!!dataBreakpointId);
}

View File

@@ -30,8 +30,9 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { dispose } from 'vs/base/common/lifecycle';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { debugCollapseAll } from 'vs/workbench/contrib/debug/browser/debugIcons';
const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
let ignoreViewUpdates = false;
@@ -160,7 +161,7 @@ export class WatchExpressionsView extends ViewPane {
getActions(): IAction[] {
return [
new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService),
new CollapseAction(() => this.tree, true, 'explorer-action codicon-collapse-all'),
new CollapseAction(() => this.tree, true, 'explorer-action ' + ThemeIcon.asClassName(debugCollapseAll)),
new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService)
];
}

View File

@@ -22,7 +22,7 @@ import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys';
import { OpenFolderAction, OpenFileAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
import { isMacintosh } from 'vs/base/common/platform';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { DisposableStore } from 'vs/base/common/lifecycle';
@@ -65,10 +65,10 @@ export class WelcomeView extends ViewPane {
if (isCodeEditor(editorControl)) {
const model = editorControl.getModel();
const language = model ? model.getLanguageIdentifier().language : undefined;
if (language && this.debugService.getConfigurationManager().isDebuggerInterestedInLanguage(language)) {
if (language && this.debugService.getAdapterManager().isDebuggerInterestedInLanguage(language)) {
this.debugStartLanguageContext.set(language);
this.debuggerInterestedContext.set(true);
storageSevice.store(debugStartLanguageKey, language, StorageScope.WORKSPACE);
storageSevice.store(debugStartLanguageKey, language, StorageScope.WORKSPACE, StorageTarget.MACHINE);
return;
}
}
@@ -88,7 +88,7 @@ export class WelcomeView extends ViewPane {
setContextKey();
}));
this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(setContextKey));
this._register(this.debugService.getAdapterManager().onDidRegisterDebugger(setContextKey));
this._register(this.onDidChangeBodyVisibility(visible => {
if (visible) {
setContextKey();
@@ -117,7 +117,6 @@ let debugKeybindingLabel = '';
viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, {
content: localize({ key: 'runAndDebugAction', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
"[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID),
preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR],
when: CONTEXT_DEBUGGERS_AVAILABLE,
group: ViewContentGroups.Debug
});

View File

@@ -64,6 +64,7 @@ export const CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT = new RawContextKey<st
export const CONTEXT_SET_VARIABLE_SUPPORTED = new RawContextKey<boolean>('debugSetVariableSupported', false);
export const CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED = new RawContextKey<boolean>('breakWhenValueChangesSupported', false);
export const CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT = new RawContextKey<boolean>('variableEvaluateNamePresent', false);
export const CONTEXT_EXCEPTION_WIDGET_VISIBLE = new RawContextKey<boolean>('exceptionWidgetVisible', false);
export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug';
export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint';
@@ -379,6 +380,7 @@ export interface IBaseBreakpoint extends IEnablement {
readonly logMessage?: string;
readonly verified: boolean;
readonly supported: boolean;
readonly sessionsThatVerified: string[];
getIdFromAdapter(sessionId: string): number | undefined;
}
@@ -400,6 +402,7 @@ export interface IFunctionBreakpoint extends IBaseBreakpoint {
export interface IExceptionBreakpoint extends IEnablement {
readonly filter: string;
readonly label: string;
readonly condition: string | undefined;
}
export interface IDataBreakpoint extends IBaseBreakpoint {
@@ -434,9 +437,9 @@ export interface IViewModel extends ITreeElement {
readonly focusedStackFrame: IStackFrame | undefined;
getSelectedExpression(): IExpression | undefined;
getSelectedFunctionBreakpoint(): IFunctionBreakpoint | undefined;
getSelectedBreakpoint(): IFunctionBreakpoint | IExceptionBreakpoint | undefined;
setSelectedExpression(expression: IExpression | undefined): void;
setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void;
setSelectedBreakpoint(functionBreakpoint: IFunctionBreakpoint | IExceptionBreakpoint | undefined): void;
updateViews(): void;
isMultiSessionView(): boolean;
@@ -615,8 +618,6 @@ export interface IPlatformSpecificAdapterContribution {
export interface IDebuggerContribution extends IPlatformSpecificAdapterContribution {
type: string;
label?: string;
// debug adapter executable
adapterExecutableCommand?: string;
win?: IPlatformSpecificAdapterContribution;
winx86?: IPlatformSpecificAdapterContribution;
windows?: IPlatformSpecificAdapterContribution;
@@ -628,7 +629,7 @@ export interface IDebuggerContribution extends IPlatformSpecificAdapterContribut
// supported languages
languages?: string[];
enableBreakpointsFor?: { languageIds: string[] };
enableBreakpointsFor?: { languageIds?: string[] };
// debug configuration support
configurationAttributes?: any;
@@ -643,7 +644,6 @@ export interface IDebugConfigurationProvider {
resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined>;
resolveDebugConfigurationWithSubstitutedVariables?(folderUri: uri | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined>;
provideDebugConfigurations?(folderUri: uri | undefined, token: CancellationToken): Promise<IConfig[]>;
debugAdapterExecutable?(folderUri: uri | undefined): Promise<IAdapterDescriptor>; // TODO@AW legacy
}
export interface IDebugAdapterDescriptorFactory {
@@ -660,57 +660,54 @@ export interface ITerminalLauncher {
}
export interface IConfigurationManager {
/**
* Returns true if breakpoints can be set for a given editor model. Depends on mode.
*/
canSetBreakpointsIn(model: EditorIModel): boolean;
/**
* Returns an object containing the selected launch configuration and the selected configuration name. Both these fields can be null (no folder workspace).
*/
readonly selectedConfiguration: {
launch: ILaunch | undefined;
config: IConfig | undefined;
// Potentially activates extensions
getConfig: () => Promise<IConfig | undefined>;
name: string | undefined;
// Type is used when matching dynamic configurations to their corresponding provider
type: string | undefined;
};
selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, type?: string): Promise<void>;
selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, dynamicConfigOptions?: { type?: string }): Promise<void>;
getLaunches(): ReadonlyArray<ILaunch>;
hasDebuggers(): boolean;
getLaunch(workspaceUri: uri | undefined): ILaunch | undefined;
getAllConfigurations(): { launch: ILaunch, name: string, presentation?: IConfigPresentation }[];
getRecentDynamicConfigurations(): { name: string, type: string }[];
/**
* Allows to register on change of selected debug configuration.
*/
onDidSelectConfiguration: Event<void>;
onDidRegisterDebugger: Event<void>;
activateDebuggers(activationEvent: string, debugType?: string): Promise<void>;
isDebuggerInterestedInLanguage(language: string): boolean;
hasDebugConfigurationProvider(debugType: string): boolean;
getDynamicProviders(): Promise<{ label: string, type: string, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]>;
registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable;
unregisterDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): void;
registerDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): IDisposable;
unregisterDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): void;
resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any, token: CancellationToken): Promise<any>;
}
export interface IAdapterManager {
onDidRegisterDebugger: Event<void>;
hasDebuggers(): boolean;
getDebugAdapterDescriptor(session: IDebugSession): Promise<IAdapterDescriptor | undefined>;
getDebuggerLabel(type: string): string | undefined;
isDebuggerInterestedInLanguage(language: string): boolean;
activateDebuggers(activationEvent: string, debugType?: string): Promise<void>;
registerDebugAdapterFactory(debugTypes: string[], debugAdapterFactory: IDebugAdapterFactory): IDisposable;
createDebugAdapter(session: IDebugSession): IDebugAdapter | undefined;
registerDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): IDisposable;
unregisterDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): void;
substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig>;
runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined>;
@@ -795,15 +792,25 @@ export interface IDebugService {
onDidEndSession: Event<IDebugSession>;
/**
* Gets the current configuration manager.
* Gets the configuration manager.
*/
getConfigurationManager(): IConfigurationManager;
/**
* Gets the adapter manager.
*/
getAdapterManager(): IAdapterManager;
/**
* Sets the focused stack frame and evaluates all expressions against the newly focused stack frame,
*/
focusStackFrame(focusedStackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): Promise<void>;
/**
* Returns true if breakpoints can be set for a given editor model. Depends on mode.
*/
canSetBreakpointsIn(model: EditorIModel): boolean;
/**
* Adds new breakpoints to the model for the file specified with the uri. Notifies debug adapter of breakpoint changes.
*/
@@ -860,6 +867,8 @@ export interface IDebugService {
*/
removeDataBreakpoints(id?: string): Promise<void>;
setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string | undefined): Promise<void>;
/**
* Sends all breakpoints to the passed session.
* If session is not passed, sends all breakpoints to each session.
@@ -932,6 +941,7 @@ export const enum BreakpointWidgetContext {
export interface IDebugEditorContribution extends editorCommon.IEditorContribution {
showHover(range: Range, focus: boolean): Promise<void>;
addLaunchConfiguration(): Promise<any>;
closeExceptionWidget(): void;
}
export interface IBreakpointEditorContribution extends editorCommon.IEditorContribution {

View File

@@ -393,6 +393,7 @@ export class Thread implements IThread {
private callStackCancellationTokens: CancellationTokenSource[] = [];
public stoppedDetails: IRawStoppedDetails | undefined;
public stopped: boolean;
public reachedEndOfCallStack = false;
constructor(public session: IDebugSession, public name: string, public threadId: number) {
this.callStack = [];
@@ -445,6 +446,7 @@ export class Thread implements IThread {
if (this.stopped) {
const start = this.callStack.length;
const callStack = await this.getCallStackImpl(start, levels);
this.reachedEndOfCallStack = callStack.length < levels;
if (start < this.callStack.length) {
// Set the stack frames for exact position we requested. To make sure no concurrent requests create duplicate stack frames #30660
this.callStack.splice(start, this.callStack.length - start);
@@ -613,6 +615,17 @@ export abstract class BaseBreakpoint extends Enablement implements IBaseBreakpoi
return this.data ? this.data.verified : true;
}
get sessionsThatVerified() {
const sessionIds: string[] = [];
for (const [sessionId, data] of this.sessionData) {
if (data.verified) {
sessionIds.push(sessionId);
}
}
return sessionIds;
}
abstract get supported(): boolean;
getIdFromAdapter(sessionId: string): number | undefined {
@@ -847,7 +860,7 @@ export class DataBreakpoint extends BaseBreakpoint implements IDataBreakpoint {
export class ExceptionBreakpoint extends Enablement implements IExceptionBreakpoint {
constructor(public filter: string, public label: string, enabled: boolean) {
constructor(public filter: string, public label: string, enabled: boolean, public supportsCondition: boolean, public condition: string | undefined) {
super(enabled, generateUuid());
}
@@ -856,6 +869,8 @@ export class ExceptionBreakpoint extends Enablement implements IExceptionBreakpo
result.filter = this.filter;
result.label = this.label;
result.enabled = this.enabled;
result.supportsCondition = this.supportsCondition;
result.condition = this.condition;
return result;
}
@@ -1049,19 +1064,24 @@ export class DebugModel implements IDebugModel {
setExceptionBreakpoints(data: DebugProtocol.ExceptionBreakpointsFilter[]): void {
if (data) {
if (this.exceptionBreakpoints.length === data.length && this.exceptionBreakpoints.every((exbp, i) => exbp.filter === data[i].filter && exbp.label === data[i].label)) {
if (this.exceptionBreakpoints.length === data.length && this.exceptionBreakpoints.every((exbp, i) => exbp.filter === data[i].filter && exbp.label === data[i].label && exbp.supportsCondition === data[i].supportsCondition)) {
// No change
return;
}
this.exceptionBreakpoints = data.map(d => {
const ebp = this.exceptionBreakpoints.filter(ebp => ebp.filter === d.filter).pop();
return new ExceptionBreakpoint(d.filter, d.label, ebp ? ebp.enabled : !!d.default);
return new ExceptionBreakpoint(d.filter, d.label, ebp ? ebp.enabled : !!d.default, !!d.supportsCondition, ebp?.condition);
});
this._onDidChangeBreakpoints.fire(undefined);
}
}
setExceptionBreakpointCondition(exceptionBreakpoint: IExceptionBreakpoint, condition: string | undefined): void {
(exceptionBreakpoint as ExceptionBreakpoint).condition = condition;
this._onDidChangeBreakpoints.fire(undefined);
}
areBreakpointsActivated(): boolean {
return this.breakpointsActivated;
}

View File

@@ -16,7 +16,7 @@ declare module DebugProtocol {
/** Message type.
Values: 'request', 'response', 'event', etc.
*/
type: string;
type: 'request' | 'response' | 'event' | string;
}
/** A client or debug adapter initiated request. */
@@ -56,7 +56,7 @@ declare module DebugProtocol {
'cancelled': request was cancelled.
etc.
*/
message?: string;
message?: 'cancelled' | string;
/** Contains request result if success is true and optional error details if success is false. */
body?: any;
}
@@ -129,7 +129,7 @@ declare module DebugProtocol {
For backward compatibility this string is shown in the UI if the 'description' attribute is missing (but it must not be translated).
Values: 'step', 'breakpoint', 'exception', 'pause', 'entry', 'goto', 'function breakpoint', 'data breakpoint', 'instruction breakpoint', etc.
*/
reason: string;
reason: 'step' | 'breakpoint' | 'exception' | 'pause' | 'entry' | 'goto' | 'function breakpoint' | 'data breakpoint' | 'instruction breakpoint' | string;
/** The full reason for the event, e.g. 'Paused on exception'. This string is shown in the UI as is and must be translated. */
description?: string;
/** The thread which was stopped. */
@@ -194,7 +194,7 @@ declare module DebugProtocol {
/** The reason for the event.
Values: 'started', 'exited', etc.
*/
reason: string;
reason: 'started' | 'exited' | string;
/** The identifier of the thread. */
threadId: number;
};
@@ -209,7 +209,7 @@ declare module DebugProtocol {
/** The output category. If not specified, 'console' is assumed.
Values: 'console', 'stdout', 'stderr', 'telemetry', etc.
*/
category?: string;
category?: 'console' | 'stdout' | 'stderr' | 'telemetry' | string;
/** The output to report. */
output: string;
/** Support for keeping an output log organized by grouping related messages.
@@ -221,7 +221,7 @@ declare module DebugProtocol {
A non empty 'output' attribute is shown as the unindented end of the group.
*/
group?: 'start' | 'startCollapsed' | 'end';
/** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. The value should be less than or equal to 2147483647 (2^31 - 1). */
/** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. The value should be less than or equal to 2147483647 (2^31-1). */
variablesReference?: number;
/** An optional source location where the output was produced. */
source?: Source;
@@ -243,7 +243,7 @@ declare module DebugProtocol {
/** The reason for the event.
Values: 'changed', 'new', 'removed', etc.
*/
reason: string;
reason: 'changed' | 'new' | 'removed' | string;
/** The 'id' attribute is used to find the target breakpoint and the other attributes are used as the new values. */
breakpoint: Breakpoint;
};
@@ -383,7 +383,7 @@ declare module DebugProtocol {
export interface InvalidatedEvent extends Event {
// event: 'invalidated';
body: {
/** Optional set of logical areas that got invalidated. If this property is missing or empty, a single value 'all' is assumed. */
/** Optional set of logical areas that got invalidated. This property has a hint characteristic: a client can only be expected to make a 'best effort' in honouring the areas but there are no guarantees. If this property is missing, empty, or if values are not understand the client should assume a single value 'all'. */
areas?: InvalidatedAreas[];
/** If specified, the client only needs to refetch data related to this thread. */
threadId?: number;
@@ -408,7 +408,7 @@ declare module DebugProtocol {
kind?: 'integrated' | 'external';
/** Optional title of the terminal. */
title?: string;
/** Working directory of the command. */
/** Working directory for the command. For non-empty, valid paths this typically results in execution of a change directory command. */
cwd: string;
/** List of arguments. The first argument is the command to run. */
args: string[];
@@ -419,9 +419,9 @@ declare module DebugProtocol {
/** Response to 'runInTerminal' request. */
export interface RunInTerminalResponse extends Response {
body: {
/** The process ID. The value should be less than or equal to 2147483647 (2^31 - 1). */
/** The process ID. The value should be less than or equal to 2147483647 (2^31-1). */
processId?: number;
/** The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31 - 1). */
/** The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31-1). */
shellProcessId?: number;
};
}
@@ -455,7 +455,7 @@ declare module DebugProtocol {
/** Determines in what format paths are specified. The default is 'path', which is the native format.
Values: 'path', 'uri', etc.
*/
pathFormat?: string;
pathFormat?: 'path' | 'uri' | string;
/** Client supports the optional type attribute for variables. */
supportsVariableType?: boolean;
/** Client supports the paging of variables. */
@@ -712,8 +712,10 @@ declare module DebugProtocol {
/** Arguments for 'setExceptionBreakpoints' request. */
export interface SetExceptionBreakpointsArguments {
/** IDs of checked exception options. The set of IDs is returned via the 'exceptionBreakpointFilters' capability. */
/** Set of exception filters specified by their ID. The set of all possible exception filters is defined by the 'exceptionBreakpointFilters' capability. The 'filter' and 'filterOptions' sets are additive. */
filters: string[];
/** Set of exception filters and their options. The set of all possible exception filters is defined by the 'exceptionBreakpointFilters' capability. This attribute is only honored by a debug adapter if the capability 'supportsExceptionFilterOptions' is true. The 'filter' and 'filterOptions' sets are additive. */
filterOptions?: ExceptionFilterOptions[];
/** Configuration options for selected exceptions.
The attribute is only honored by a debug adapter if the capability 'supportsExceptionOptions' is true.
*/
@@ -1009,7 +1011,8 @@ declare module DebugProtocol {
}
/** StackTrace request; value of command field is 'stackTrace'.
The request returns a stacktrace from the current execution state.
The request returns a stacktrace from the current execution state of a given thread.
A client can request all stack frames by omitting the startFrame and levels arguments. For performance conscious clients stack frames can be retrieved in a piecemeal way with the startFrame and levels arguments. The response of the stackTrace request may contain a totalFrames property that hints at the total number of frames in the stack. If a client needs this total number upfront, it can issue a request for a single (first) frame and depending on the value of totalFrames decide how to proceed. In any case a client should be prepared to receive less frames than requested, which is an indication that the end of the stack has been reached.
*/
export interface StackTraceRequest extends Request {
// command: 'stackTrace';
@@ -1037,7 +1040,7 @@ declare module DebugProtocol {
This means that there is no location information available.
*/
stackFrames: StackFrame[];
/** The total number of frames available. */
/** The total number of frames available in the stack. If omitted or if totalFrames is larger than the available frames, a client is expected to request frames until a request returns less frames than requested (which indicates the end of the stack). Returning monotonically increasing totalFrames values for subsequent requests can be used to enforce paging in the client. */
totalFrames?: number;
};
}
@@ -1125,17 +1128,17 @@ declare module DebugProtocol {
/** The type of the new value. Typically shown in the UI when hovering over the value. */
type?: string;
/** If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest.
The value should be less than or equal to 2147483647 (2^31 - 1).
The value should be less than or equal to 2147483647 (2^31-1).
*/
variablesReference?: number;
/** The number of named child variables.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks.
The value should be less than or equal to 2147483647 (2^31 - 1).
The value should be less than or equal to 2147483647 (2^31-1).
*/
namedVariables?: number;
/** The number of indexed child variables.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks.
The value should be less than or equal to 2147483647 (2^31 - 1).
The value should be less than or equal to 2147483647 (2^31-1).
*/
indexedVariables?: number;
};
@@ -1275,7 +1278,7 @@ declare module DebugProtocol {
The attribute is only honored by a debug adapter if the capability 'supportsClipboardContext' is true.
etc.
*/
context?: string;
context?: 'watch' | 'repl' | 'hover' | 'clipboard' | string;
/** Specifies details on how to format the Evaluate result.
The attribute is only honored by a debug adapter if the capability 'supportsValueFormattingOptions' is true.
*/
@@ -1294,17 +1297,17 @@ declare module DebugProtocol {
/** Properties of a evaluate result that can be used to determine how to render the result in the UI. */
presentationHint?: VariablePresentationHint;
/** If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest.
The value should be less than or equal to 2147483647 (2^31 - 1).
The value should be less than or equal to 2147483647 (2^31-1).
*/
variablesReference: number;
/** The number of named child variables.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks.
The value should be less than or equal to 2147483647 (2^31 - 1).
The value should be less than or equal to 2147483647 (2^31-1).
*/
namedVariables?: number;
/** The number of indexed child variables.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks.
The value should be less than or equal to 2147483647 (2^31 - 1).
The value should be less than or equal to 2147483647 (2^31-1).
*/
indexedVariables?: number;
/** Optional memory reference to a location appropriate for this result.
@@ -1349,17 +1352,17 @@ declare module DebugProtocol {
/** Properties of a value that can be used to determine how to render the result in the UI. */
presentationHint?: VariablePresentationHint;
/** If variablesReference is > 0, the value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest.
The value should be less than or equal to 2147483647 (2^31 - 1).
The value should be less than or equal to 2147483647 (2^31-1).
*/
variablesReference?: number;
/** The number of named child variables.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks.
The value should be less than or equal to 2147483647 (2^31 - 1).
The value should be less than or equal to 2147483647 (2^31-1).
*/
namedVariables?: number;
/** The number of indexed child variables.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks.
The value should be less than or equal to 2147483647 (2^31 - 1).
The value should be less than or equal to 2147483647 (2^31-1).
*/
indexedVariables?: number;
};
@@ -1556,7 +1559,7 @@ declare module DebugProtocol {
supportsHitConditionalBreakpoints?: boolean;
/** The debug adapter supports a (side effect free) evaluate request for data hovers. */
supportsEvaluateForHovers?: boolean;
/** Available filters or options for the setExceptionBreakpoints request. */
/** Available exception filter options for the 'setExceptionBreakpoints' request. */
exceptionBreakpointFilters?: ExceptionBreakpointsFilter[];
/** The debug adapter supports stepping back via the 'stepBack' and 'reverseContinue' requests. */
supportsStepBack?: boolean;
@@ -1616,16 +1619,20 @@ declare module DebugProtocol {
supportsSteppingGranularity?: boolean;
/** The debug adapter supports adding breakpoints based on instruction references. */
supportsInstructionBreakpoints?: boolean;
/** The debug adapter supports 'filterOptions' as an argument on the 'setExceptionBreakpoints' request. */
supportsExceptionFilterOptions?: boolean;
}
/** An ExceptionBreakpointsFilter is shown in the UI as an option for configuring how exceptions are dealt with. */
/** An ExceptionBreakpointsFilter is shown in the UI as an filter option for configuring how exceptions are dealt with. */
export interface ExceptionBreakpointsFilter {
/** The internal ID of the filter. This value is passed to the setExceptionBreakpoints request. */
/** The internal ID of the filter option. This value is passed to the 'setExceptionBreakpoints' request. */
filter: string;
/** The name of the filter. This will be shown in the UI. */
/** The name of the filter option. This will be shown in the UI. */
label: string;
/** Initial value of the filter. If not specified a value 'false' is assumed. */
/** Initial value of the filter option. If not specified a value 'false' is assumed. */
default?: boolean;
/** Controls whether a condition can be specified for this filter option. If false or missing, a condition can not be set. */
supportsCondition?: boolean;
}
/** A structured message object. Used to return errors from requests. */
@@ -1730,7 +1737,7 @@ declare module DebugProtocol {
path?: string;
/** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified).
A sourceReference is only valid for a session, so it must not be used to persist a source.
The value should be less than or equal to 2147483647 (2^31 - 1).
The value should be less than or equal to 2147483647 (2^31-1).
*/
sourceReference?: number;
/** An optional hint for how to present the source in the UI.
@@ -1788,7 +1795,7 @@ declare module DebugProtocol {
'registers': Scope contains registers. Only a single 'registers' scope should be returned from a 'scopes' request.
etc.
*/
presentationHint?: string;
presentationHint?: 'arguments' | 'locals' | 'registers' | string;
/** The variables of this scope can be retrieved by passing the value of variablesReference to the VariablesRequest. */
variablesReference: number;
/** The number of named variables in this scope.
@@ -1867,7 +1874,7 @@ declare module DebugProtocol {
'dataBreakpoint': Indicates that a data breakpoint is registered for the object.
etc.
*/
kind?: string;
kind?: 'property' | 'method' | 'class' | 'data' | 'event' | 'baseClass' | 'innerClass' | 'interface' | 'mostDerivedClass' | 'virtual' | 'dataBreakpoint' | string;
/** Set of attributes represented as an array of strings. Before introducing additional values, try to use the listed values.
Values:
'static': Indicates that the object is static.
@@ -1879,11 +1886,11 @@ declare module DebugProtocol {
'hasSideEffects': Indicates that the evaluation had side effects.
etc.
*/
attributes?: string[];
attributes?: ('static' | 'constant' | 'readOnly' | 'rawString' | 'hasObjectId' | 'canHaveObjectId' | 'hasSideEffects' | string)[];
/** Visibility of variable. Before introducing additional values, try to use the listed values.
Values: 'public', 'private', 'protected', 'internal', 'final', etc.
*/
visibility?: string;
visibility?: 'public' | 'private' | 'protected' | 'internal' | 'final' | string;
}
/** Properties of a breakpoint location returned from the 'breakpointLocations' request. */
@@ -2108,6 +2115,16 @@ declare module DebugProtocol {
includeAll?: boolean;
}
/** An ExceptionFilterOptions is used to specify an exception filter together with a condition for the setExceptionsFilter request. */
export interface ExceptionFilterOptions {
/** ID of an exception filter returned by the 'exceptionBreakpointFilters' capability. */
filterId: string;
/** An optional expression for conditional exceptions.
The exception will break into the debugger if the result of the condition is true.
*/
condition?: string;
}
/** An ExceptionOptions assigns configuration options to a set of exceptions. */
export interface ExceptionOptions {
/** A path that selects a single or multiple exceptions in a tree. If 'path' is missing, the whole tree is selected.
@@ -2179,11 +2196,13 @@ declare module DebugProtocol {
}
/** Logical areas that can be invalidated by the 'invalidated' event.
Values:
'all': All previously fetched data has become invalid and needs to be refetched.
'stacks': Previously fetched stack related data has become invalid and needs to be refetched.
'threads': Previously fetched thread related data has become invalid and needs to be refetched.
'variables': Previously fetched variable data has become invalid and needs to be refetched.
etc.
*/
export type InvalidatedAreas = 'all' | 'stacks' | 'threads' | 'variables';
export type InvalidatedAreas = 'all' | 'stacks' | 'threads' | 'variables' | string;
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage';
import { StorageScope, IStorageService, StorageTarget } from 'vs/platform/storage/common/storage';
import { ExceptionBreakpoint, Expression, Breakpoint, FunctionBreakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
import { IEvaluate, IExpression, IDebugModel } from 'vs/workbench/contrib/debug/common/debug';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
@@ -49,7 +49,7 @@ export class DebugStorage {
let result: ExceptionBreakpoint[] | undefined;
try {
result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => {
return new ExceptionBreakpoint(exBreakpoint.filter, exBreakpoint.label, exBreakpoint.enabled);
return new ExceptionBreakpoint(exBreakpoint.filter, exBreakpoint.label, exBreakpoint.enabled, exBreakpoint.supportsCondition, exBreakpoint.condition);
});
} catch (e) { }
@@ -80,7 +80,7 @@ export class DebugStorage {
storeWatchExpressions(watchExpressions: (IExpression & IEvaluate)[]): void {
if (watchExpressions.length) {
this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(watchExpressions.map(we => ({ name: we.name, id: we.getId() }))), StorageScope.WORKSPACE);
this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(watchExpressions.map(we => ({ name: we.name, id: we.getId() }))), StorageScope.WORKSPACE, StorageTarget.USER);
} else {
this.storageService.remove(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE);
}
@@ -89,28 +89,28 @@ export class DebugStorage {
storeBreakpoints(debugModel: IDebugModel): void {
const breakpoints = debugModel.getBreakpoints();
if (breakpoints.length) {
this.storageService.store(DEBUG_BREAKPOINTS_KEY, JSON.stringify(breakpoints), StorageScope.WORKSPACE);
this.storageService.store(DEBUG_BREAKPOINTS_KEY, JSON.stringify(breakpoints), StorageScope.WORKSPACE, StorageTarget.USER);
} else {
this.storageService.remove(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE);
}
const functionBreakpoints = debugModel.getFunctionBreakpoints();
if (functionBreakpoints.length) {
this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(functionBreakpoints), StorageScope.WORKSPACE);
this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(functionBreakpoints), StorageScope.WORKSPACE, StorageTarget.USER);
} else {
this.storageService.remove(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE);
}
const dataBreakpoints = debugModel.getDataBreakpoints().filter(dbp => dbp.canPersist);
if (dataBreakpoints.length) {
this.storageService.store(DEBUG_DATA_BREAKPOINTS_KEY, JSON.stringify(dataBreakpoints), StorageScope.WORKSPACE);
this.storageService.store(DEBUG_DATA_BREAKPOINTS_KEY, JSON.stringify(dataBreakpoints), StorageScope.WORKSPACE, StorageTarget.USER);
} else {
this.storageService.remove(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE);
}
const exceptionBreakpoints = debugModel.getExceptionBreakpoints();
if (exceptionBreakpoints.length) {
this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(exceptionBreakpoints), StorageScope.WORKSPACE);
this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(exceptionBreakpoints), StorageScope.WORKSPACE, StorageTarget.USER);
} else {
this.storageService.remove(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE);
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug';
import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, IExceptionBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { isSessionAttach } from 'vs/workbench/contrib/debug/common/debugUtils';
@@ -16,7 +16,7 @@ export class ViewModel implements IViewModel {
private _focusedSession: IDebugSession | undefined;
private _focusedThread: IThread | undefined;
private selectedExpression: IExpression | undefined;
private selectedFunctionBreakpoint: IFunctionBreakpoint | undefined;
private selectedBreakpoint: IFunctionBreakpoint | IExceptionBreakpoint | undefined;
private readonly _onDidFocusSession = new Emitter<IDebugSession | undefined>();
private readonly _onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame | undefined, explicit: boolean }>();
private readonly _onDidSelectExpression = new Emitter<IExpression | undefined>();
@@ -112,8 +112,8 @@ export class ViewModel implements IViewModel {
return this._onDidSelectExpression.event;
}
getSelectedFunctionBreakpoint(): IFunctionBreakpoint | undefined {
return this.selectedFunctionBreakpoint;
getSelectedBreakpoint(): IFunctionBreakpoint | IExceptionBreakpoint | undefined {
return this.selectedBreakpoint;
}
updateViews(): void {
@@ -124,9 +124,9 @@ export class ViewModel implements IViewModel {
return this._onWillUpdateViews.event;
}
setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void {
this.selectedFunctionBreakpoint = functionBreakpoint;
this.breakpointSelectedContextKey.set(!!functionBreakpoint);
setSelectedBreakpoint(breakpoint: IFunctionBreakpoint | IExceptionBreakpoint | undefined): void {
this.selectedBreakpoint = breakpoint;
this.breakpointSelectedContextKey.set(!!breakpoint);
}
isMultiSessionView(): boolean {

View File

@@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects';
import { isObject } from 'vs/base/common/types';
import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IConfig, IDebuggerContribution, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, IDebugger, IDebugSession, IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug';
import { IConfig, IDebuggerContribution, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IDebugAdapter, IDebugger, IDebugSession, IDebugHelperService, IAdapterManager, IDebugService } from 'vs/workbench/contrib/debug/common/debug';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import * as ConfigurationResolverUtils from 'vs/workbench/services/configurationResolver/common/configurationResolverUtils';
@@ -30,14 +30,15 @@ export class Debugger implements IDebugger {
private mainExtensionDescription: IExtensionDescription | undefined;
constructor(
private configurationManager: IConfigurationManager,
private adapterManager: IAdapterManager,
dbgContribution: IDebuggerContribution,
extensionDescription: IExtensionDescription,
@IConfigurationService private readonly configurationService: IConfigurationService,
@ITextResourcePropertiesService private readonly resourcePropertiesService: ITextResourcePropertiesService,
@IConfigurationResolverService private readonly configurationResolverService: IConfigurationResolverService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IDebugHelperService private readonly debugHelperService: IDebugHelperService
@IDebugHelperService private readonly debugHelperService: IDebugHelperService,
@IDebugService private readonly debugService: IDebugService
) {
this.debuggerContribution = { type: dbgContribution.type };
this.merge(dbgContribution, extensionDescription);
@@ -97,8 +98,8 @@ export class Debugger implements IDebugger {
}
createDebugAdapter(session: IDebugSession): Promise<IDebugAdapter> {
return this.configurationManager.activateDebuggers('onDebugAdapterProtocolTracker', this.type).then(_ => {
const da = this.configurationManager.createDebugAdapter(session);
return this.adapterManager.activateDebuggers('onDebugAdapterProtocolTracker', this.type).then(_ => {
const da = this.adapterManager.createDebugAdapter(session);
if (da) {
return Promise.resolve(da);
}
@@ -107,13 +108,13 @@ export class Debugger implements IDebugger {
}
substituteVariables(folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig> {
return this.configurationManager.substituteVariables(this.type, folder, config).then(config => {
return this.adapterManager.substituteVariables(this.type, folder, config).then(config => {
return this.configurationResolverService.resolveWithInteractionReplace(folder, config, 'launch', this.variables, config.__configurationTarget);
});
}
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined> {
return this.configurationManager.runInTerminal(this.type, args);
return this.adapterManager.runInTerminal(this.type, args);
}
get label(): string {
@@ -141,7 +142,7 @@ export class Debugger implements IDebugger {
}
hasConfigurationProvider(): boolean {
return this.configurationManager.hasDebugConfigurationProvider(this.type);
return this.debugService.getConfigurationManager().hasDebugConfigurationProvider(this.type);
}
getInitialConfigurationContent(initialConfigs?: IConfig[]): Promise<string> {

View File

@@ -11,12 +11,16 @@ import { isString, isUndefinedOrNull, isObject } from 'vs/base/common/types';
import { basenameOrAuthority } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { Emitter } from 'vs/base/common/event';
import { Emitter, Event } from 'vs/base/common/event';
const MAX_REPL_LENGTH = 10000;
let topReplElementCounter = 0;
export class SimpleReplElement implements IReplElement {
private _count = 1;
private _onDidChangeCount = new Emitter<void>();
constructor(
public session: IDebugSession,
private id: string,
@@ -33,6 +37,19 @@ export class SimpleReplElement implements IReplElement {
getId(): string {
return this.id;
}
set count(value: number) {
this._count = value;
this._onDidChangeCount.fire();
}
get count(): number {
return this._count;
}
get onDidChangeCount(): Event<void> {
return this._onDidChangeCount.event;
}
}
export class RawObjectReplElement implements IExpression {
@@ -202,13 +219,21 @@ export class ReplModel {
if (typeof data === 'string') {
const previousElement = this.replElements.length ? this.replElements[this.replElements.length - 1] : undefined;
if (previousElement instanceof SimpleReplElement && previousElement.severity === sev && !previousElement.value.endsWith('\n') && !previousElement.value.endsWith('\r\n')) {
previousElement.value += data;
this._onDidChangeElements.fire();
} else {
const element = new SimpleReplElement(session, `topReplElement:${topReplElementCounter++}`, data, sev, source);
this.addReplElement(element);
if (previousElement instanceof SimpleReplElement && previousElement.severity === sev) {
if (previousElement.value === data) {
previousElement.count++;
// No need to fire an event, just the count updates and badge will adjust automatically
return;
}
if (!previousElement.value.endsWith('\n') && !previousElement.value.endsWith('\r\n') && previousElement.count === 1) {
previousElement.value += data;
this._onDidChangeElements.fire();
return;
}
}
const element = new SimpleReplElement(session, `topReplElement:${topReplElementCounter++}`, data, sev, source);
this.addReplElement(element);
} else {
// TODO@Isidor hack, we should introduce a new type which is an output that can fetch children like an expression
(<any>data).severity = sev;

View File

@@ -199,9 +199,9 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter {
"Cannot determine executable for debug adapter '{0}'.", this.debugType));
}
let env = objects.mixin({}, process.env);
if (options.env) {
env = objects.mixin(env, options.env);
let env = process.env;
if (options.env && Object.keys(options.env).length > 0) {
env = objects.mixin(objects.deepClone(process.env), options.env);
}
if (command === 'node') {
@@ -263,6 +263,8 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter {
channel.append(sanitize(data));
}
});
} else {
this.serverProcess.stderr!.resume();
}
// finally connect to the DA

View File

@@ -9,6 +9,7 @@ import { WindowsExternalTerminalService, MacExternalTerminalService, LinuxExtern
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { extractDriveLetter } from 'vs/base/common/labels';
let externalTerminalService: IExternalTerminalService | undefined = undefined;
@@ -111,7 +112,11 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env?
};
if (cwd) {
command += `cd '${cwd}'; `;
const driveLetter = extractDriveLetter(cwd);
if (driveLetter) {
command += `${driveLetter}:; `;
}
command += `cd ${quote(cwd)}; `;
}
if (env) {
for (let key in env) {
@@ -140,6 +145,10 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env?
};
if (cwd) {
const driveLetter = extractDriveLetter(cwd);
if (driveLetter) {
command += `${driveLetter}: && `;
}
command += `cd ${quote(cwd)} && `;
}
if (env) {

View File

@@ -6,7 +6,7 @@
import * as assert from 'assert';
import { URI as uri } from 'vs/base/common/uri';
import { DebugModel, Breakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
import { getExpandedBodySize, getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/browser/breakpointsView';
import { getExpandedBodySize, getBreakpointMessageAndIcon } from 'vs/workbench/contrib/debug/browser/breakpointsView';
import { dispose } from 'vs/base/common/lifecycle';
import { Range } from 'vs/editor/common/core/range';
import { IBreakpointData, IBreakpointUpdateData, State } from 'vs/workbench/contrib/debug/common/debug';
@@ -258,40 +258,40 @@ suite('Debug - Breakpoints', () => {
]);
const breakpoints = model.getBreakpoints();
let result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[0]);
assert.equal(result.message, 'Expression: x > 5');
assert.equal(result.className, 'codicon-debug-breakpoint-conditional');
let result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[0]);
assert.equal(result.message, 'Expression condition: x > 5');
assert.equal(result.icon.id, 'debug-breakpoint-conditional');
result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[1]);
result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[1]);
assert.equal(result.message, 'Disabled Breakpoint');
assert.equal(result.className, 'codicon-debug-breakpoint-disabled');
assert.equal(result.icon.id, 'debug-breakpoint-disabled');
result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[2]);
result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[2]);
assert.equal(result.message, 'Log Message: hello');
assert.equal(result.className, 'codicon-debug-breakpoint-log');
assert.equal(result.icon.id, 'debug-breakpoint-log');
result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[3]);
result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[3]);
assert.equal(result.message, 'Hit Count: 12');
assert.equal(result.className, 'codicon-debug-breakpoint-conditional');
assert.equal(result.icon.id, 'debug-breakpoint-conditional');
result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[4]);
result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[4]);
assert.equal(result.message, 'Breakpoint');
assert.equal(result.className, 'codicon-debug-breakpoint');
assert.equal(result.icon.id, 'debug-breakpoint');
result = getBreakpointMessageAndClassName(State.Stopped, false, breakpoints[2]);
result = getBreakpointMessageAndIcon(State.Stopped, false, breakpoints[2]);
assert.equal(result.message, 'Disabled Logpoint');
assert.equal(result.className, 'codicon-debug-breakpoint-log-disabled');
assert.equal(result.icon.id, 'debug-breakpoint-log-disabled');
model.addDataBreakpoint('label', 'id', true, ['read']);
const dataBreakpoints = model.getDataBreakpoints();
result = getBreakpointMessageAndClassName(State.Stopped, true, dataBreakpoints[0]);
result = getBreakpointMessageAndIcon(State.Stopped, true, dataBreakpoints[0]);
assert.equal(result.message, 'Data Breakpoint');
assert.equal(result.className, 'codicon-debug-breakpoint-data');
assert.equal(result.icon.id, 'debug-breakpoint-data');
const functionBreakpoint = model.addFunctionBreakpoint('foo', '1');
result = getBreakpointMessageAndClassName(State.Stopped, true, functionBreakpoint);
result = getBreakpointMessageAndIcon(State.Stopped, true, functionBreakpoint);
assert.equal(result.message, 'Function Breakpoint');
assert.equal(result.className, 'codicon-debug-breakpoint-function');
assert.equal(result.icon.id, 'debug-breakpoint-function');
const data = new Map<string, DebugProtocol.Breakpoint>();
data.set(breakpoints[0].getId(), { verified: false, line: 10 });
@@ -300,17 +300,17 @@ suite('Debug - Breakpoints', () => {
data.set(functionBreakpoint.getId(), { verified: true });
model.setBreakpointSessionData('mocksessionid', { supportsFunctionBreakpoints: false, supportsDataBreakpoints: true, supportsLogPoints: true }, data);
result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[0]);
result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[0]);
assert.equal(result.message, 'Unverified Breakpoint');
assert.equal(result.className, 'codicon-debug-breakpoint-unverified');
assert.equal(result.icon.id, 'debug-breakpoint-unverified');
result = getBreakpointMessageAndClassName(State.Stopped, true, functionBreakpoint);
result = getBreakpointMessageAndIcon(State.Stopped, true, functionBreakpoint);
assert.equal(result.message, 'Function breakpoints not supported by this debug type');
assert.equal(result.className, 'codicon-debug-breakpoint-function-unverified');
assert.equal(result.icon.id, 'debug-breakpoint-function-unverified');
result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[2]);
result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[2]);
assert.equal(result.message, 'Log Message: hello, world');
assert.equal(result.className, 'codicon-debug-breakpoint-log');
assert.equal(result.icon.id, 'debug-breakpoint-log');
});
test('decorations', () => {
@@ -337,7 +337,7 @@ suite('Debug - Breakpoints', () => {
assert.equal(decorations[0].options.beforeContentClassName, undefined);
assert.equal(decorations[1].options.beforeContentClassName, `debug-breakpoint-placeholder`);
assert.equal(decorations[0].options.overviewRuler?.position, OverviewRulerLane.Left);
const expected = new MarkdownString().appendCodeblock(languageIdentifier.language, 'Expression: x > 5');
const expected = new MarkdownString().appendCodeblock(languageIdentifier.language, 'Expression condition: x > 5');
assert.deepEqual(decorations[0].options.glyphMarginHoverMessage, expected);
decorations = createBreakpointDecorations(textModel, breakpoints, State.Running, true, false);

View File

@@ -17,6 +17,8 @@ import { Constants } from 'vs/base/common/uint';
import { getContext, getContextForContributedActions, getSpecificSourceName } from 'vs/workbench/contrib/debug/browser/callStackView';
import { getStackFrameThreadAndSessionToFocus } from 'vs/workbench/contrib/debug/browser/debugService';
import { generateUuid } from 'vs/base/common/uuid';
import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
export function createMockSession(model: DebugModel, name = 'mockSession', options?: IDebugSessionOptions): DebugSession {
return new DebugSession(generateUuid(), { resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, {
@@ -308,7 +310,7 @@ suite('Debug - CallStack', () => {
let decorations = createDecorationsForStackFrame(firstStackFrame, firstStackFrame.range, true);
assert.equal(decorations.length, 2);
assert.deepEqual(decorations[0].range, new Range(1, 2, 1, 1));
assert.equal(decorations[0].options.glyphMarginClassName, 'codicon-debug-stackframe');
assert.equal(decorations[0].options.glyphMarginClassName, ThemeIcon.asClassName(debugStackframe));
assert.deepEqual(decorations[1].range, new Range(1, Constants.MAX_SAFE_SMALL_INTEGER, 1, 1));
assert.equal(decorations[1].options.className, 'debug-top-stack-frame-line');
assert.equal(decorations[1].options.isWholeLine, true);
@@ -316,7 +318,7 @@ suite('Debug - CallStack', () => {
decorations = createDecorationsForStackFrame(secondStackFrame, firstStackFrame.range, true);
assert.equal(decorations.length, 2);
assert.deepEqual(decorations[0].range, new Range(1, 2, 1, 1));
assert.equal(decorations[0].options.glyphMarginClassName, 'codicon-debug-stackframe-focused');
assert.equal(decorations[0].options.glyphMarginClassName, ThemeIcon.asClassName(debugStackframeFocused));
assert.deepEqual(decorations[1].range, new Range(1, Constants.MAX_SAFE_SMALL_INTEGER, 1, 1));
assert.equal(decorations[1].options.className, 'debug-focused-stack-frame-line');
assert.equal(decorations[1].options.isWholeLine, true);
@@ -324,7 +326,7 @@ suite('Debug - CallStack', () => {
decorations = createDecorationsForStackFrame(firstStackFrame, new Range(1, 5, 1, 6), true);
assert.equal(decorations.length, 3);
assert.deepEqual(decorations[0].range, new Range(1, 2, 1, 1));
assert.equal(decorations[0].options.glyphMarginClassName, 'codicon-debug-stackframe');
assert.equal(decorations[0].options.glyphMarginClassName, ThemeIcon.asClassName(debugStackframe));
assert.deepEqual(decorations[1].range, new Range(1, Constants.MAX_SAFE_SMALL_INTEGER, 1, 1));
assert.equal(decorations[1].options.className, 'debug-top-stack-frame-line');
assert.equal(decorations[1].options.isWholeLine, true);

View File

@@ -7,7 +7,7 @@ import { URI as uri } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { Position, IPosition } from 'vs/editor/common/core/position';
import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint, IDebugSessionOptions, IEvaluate } from 'vs/workbench/contrib/debug/common/debug';
import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint, IDebugSessionOptions, IEvaluate, IAdapterManager } from 'vs/workbench/contrib/debug/common/debug';
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import Severity from 'vs/base/common/severity';
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
@@ -17,6 +17,7 @@ import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompou
import { CancellationToken } from 'vs/base/common/cancellation';
import { TestFileService } from 'vs/workbench/test/browser/workbenchTestServices';
import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService';
import { ITextModel } from 'vs/editor/common/model';
const fileService = new TestFileService();
export const mockUriIdentityService = new UriIdentityService(fileService);
@@ -49,6 +50,14 @@ export class MockDebugService implements IDebugService {
throw new Error('not implemented');
}
getAdapterManager(): IAdapterManager {
throw new Error('Method not implemented.');
}
canSetBreakpointsIn(model: ITextModel): boolean {
throw new Error('Method not implemented.');
}
public focusStackFrame(focusedStackFrame: IStackFrame): Promise<void> {
throw new Error('not implemented');
}
@@ -77,6 +86,10 @@ export class MockDebugService implements IDebugService {
throw new Error('not implemented');
}
setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string): Promise<void> {
throw new Error('Method not implemented.');
}
public addFunctionBreakpoint(): void { }
public moveWatchExpression(id: string, position: number): void { }

View File

@@ -64,6 +64,41 @@ suite('Debug - REPL', () => {
assert.equal(elements[0], '1\n');
assert.equal(elements[1], '23\n45\n');
assert.equal(elements[2], '6');
repl.removeReplExpressions();
repl.appendToRepl(session, 'first line\n', severity.Info);
repl.appendToRepl(session, 'first line\n', severity.Info);
repl.appendToRepl(session, 'first line\n', severity.Info);
repl.appendToRepl(session, 'second line', severity.Info);
repl.appendToRepl(session, 'second line', severity.Info);
repl.appendToRepl(session, 'third line', severity.Info);
elements = <SimpleReplElement[]>repl.getReplElements();
assert.equal(elements.length, 3);
assert.equal(elements[0], 'first line\n');
assert.equal(elements[0].count, 3);
assert.equal(elements[1], 'second line');
assert.equal(elements[1].count, 2);
assert.equal(elements[2], 'third line');
assert.equal(elements[2].count, 1);
});
test('repl output count', () => {
const session = createMockSession(model);
const repl = new ReplModel();
repl.appendToRepl(session, 'first line\n', severity.Info);
repl.appendToRepl(session, 'first line\n', severity.Info);
repl.appendToRepl(session, 'first line\n', severity.Info);
repl.appendToRepl(session, 'second line', severity.Info);
repl.appendToRepl(session, 'second line', severity.Info);
repl.appendToRepl(session, 'third line', severity.Info);
const elements = <SimpleReplElement[]>repl.getReplElements();
assert.equal(elements.length, 3);
assert.equal(elements[0], 'first line\n');
assert.equal(elements[0].count, 3);
assert.equal(elements[1], 'second line');
assert.equal(elements[1].count, 2);
assert.equal(elements[2], 'third line');
assert.equal(elements[2].count, 1);
});
test('repl merging', () => {
@@ -85,7 +120,7 @@ suite('Debug - REPL', () => {
assert.equal(grandChild.getReplElements().length, 1);
assert.equal(child3.getReplElements().length, 0);
grandChild.appendToRepl('1\n', severity.Info);
grandChild.appendToRepl('2\n', severity.Info);
assert.equal(parentChanges, 2);
assert.equal(parent.getReplElements().length, 2);
assert.equal(child1.getReplElements().length, 0);
@@ -93,7 +128,7 @@ suite('Debug - REPL', () => {
assert.equal(grandChild.getReplElements().length, 2);
assert.equal(child3.getReplElements().length, 0);
child3.appendToRepl('1\n', severity.Info);
child3.appendToRepl('3\n', severity.Info);
assert.equal(parentChanges, 2);
assert.equal(parent.getReplElements().length, 2);
assert.equal(child1.getReplElements().length, 0);
@@ -101,7 +136,7 @@ suite('Debug - REPL', () => {
assert.equal(grandChild.getReplElements().length, 2);
assert.equal(child3.getReplElements().length, 1);
child1.appendToRepl('1\n', severity.Info);
child1.appendToRepl('4\n', severity.Info);
assert.equal(parentChanges, 2);
assert.equal(parent.getReplElements().length, 2);
assert.equal(child1.getReplElements().length, 1);

View File

@@ -49,8 +49,8 @@ suite('Debug - ANSI Handling', () => {
assert.equal(0, root.children.length);
appendStylizedStringToContainer(root, 'content1', ['class1', 'class2'], linkDetector, session);
appendStylizedStringToContainer(root, 'content2', ['class2', 'class3'], linkDetector, session);
appendStylizedStringToContainer(root, 'content1', ['class1', 'class2'], linkDetector, session.root);
appendStylizedStringToContainer(root, 'content2', ['class2', 'class3'], linkDetector, session.root);
assert.equal(2, root.children.length);
@@ -80,7 +80,7 @@ suite('Debug - ANSI Handling', () => {
* @returns An {@link HTMLSpanElement} that contains the stylized text.
*/
function getSequenceOutput(sequence: string): HTMLSpanElement {
const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, themeService, session);
const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, themeService, session.root);
assert.equal(1, root.children.length);
const child: Node = root.lastChild!;
if (child instanceof HTMLSpanElement) {
@@ -320,7 +320,7 @@ suite('Debug - ANSI Handling', () => {
if (elementsExpected === undefined) {
elementsExpected = assertions.length;
}
const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, themeService, session);
const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, themeService, session.root);
assert.equal(elementsExpected, root.children.length);
for (let i = 0; i < elementsExpected; i++) {
const child: Node = root.children[i];

View File

@@ -6,7 +6,7 @@
import * as assert from 'assert';
import { join, normalize } from 'vs/base/common/path';
import * as platform from 'vs/base/common/platform';
import { IDebugAdapterExecutable, IConfigurationManager, IConfig, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugAdapterExecutable, IConfig, IDebugSession, IAdapterManager } from 'vs/workbench/contrib/debug/common/debug';
import { Debugger } from 'vs/workbench/contrib/debug/common/debugger';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { URI } from 'vs/base/common/uri';
@@ -22,7 +22,6 @@ suite('Debug - Debugger', () => {
const debuggerContribution = {
type: 'mock',
label: 'Mock Debug',
enableBreakpointsFor: { 'languageIds': ['markdown'] },
program: './out/mock/mockDebug.js',
args: ['arg1', 'arg2'],
configurationAttributes: {
@@ -123,7 +122,7 @@ suite('Debug - Debugger', () => {
};
const configurationManager = <IConfigurationManager>{
const adapterManager = <IAdapterManager>{
getDebugAdapterDescriptor(session: IDebugSession, config: IConfig): Promise<IDebugAdapterExecutable | undefined> {
return Promise.resolve(undefined);
}
@@ -133,7 +132,7 @@ suite('Debug - Debugger', () => {
const testResourcePropertiesService = new TestTextResourcePropertiesService(configurationService);
setup(() => {
_debugger = new Debugger(configurationManager, debuggerContribution, extensionDescriptor0, configurationService, testResourcePropertiesService, undefined!, undefined!, undefined!);
_debugger = new Debugger(adapterManager, debuggerContribution, extensionDescriptor0, configurationService, testResourcePropertiesService, undefined!, undefined!, undefined!, undefined!);
});
teardown(() => {

View File

@@ -1,39 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { MenuId } from 'vs/platform/actions/common/actions';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
const EMMET_COMMANDS_PREFIX = '>Emmet: ';
class ShowEmmetCommandsAction extends EditorAction {
constructor() {
super({
id: 'workbench.action.showEmmetCommands',
label: nls.localize('showEmmetCommands', "Show Emmet Commands"),
alias: 'Show Emmet Commands',
precondition: EditorContextKeys.writable,
menuOpts: {
menuId: MenuId.MenubarEditMenu,
group: '5_insert',
title: nls.localize({ key: 'miShowEmmetCommands', comment: ['&& denotes a mnemonic'] }, "E&&mmet..."),
order: 4
}
});
}
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
const quickInputService = accessor.get(IQuickInputService);
quickInputService.quickAccess.show(EMMET_COMMANDS_PREFIX);
}
}
registerEditorAction(ShowEmmetCommandsAction);

View File

@@ -3,6 +3,5 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import './actions/showEmmetCommands';
import './actions/expandAbbreviation';

View File

@@ -5,7 +5,7 @@
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Emitter, Event } from 'vs/base/common/event';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry';
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -222,7 +222,7 @@ export class ExperimentService extends Disposable implements IExperimentService
const storageKey = 'experiments.' + experimentId;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
experimentState.state = ExperimentState.Complete;
this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL);
this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL, StorageTarget.MACHINE);
}
protected async getExperiments(): Promise<IRawExperiment[] | null> {
@@ -280,7 +280,7 @@ export class ExperimentService extends Disposable implements IExperimentService
});
}
if (enabledExperiments.length) {
this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL);
this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL, StorageTarget.MACHINE);
} else {
this.storageService.remove('allExperiments', StorageScope.GLOBAL);
}
@@ -350,7 +350,7 @@ export class ExperimentService extends Disposable implements IExperimentService
return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => {
experimentState.state = processedExperiment.state = state;
this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL);
this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL, StorageTarget.MACHINE);
if (state === ExperimentState.Run) {
this.fireRunExperiment(processedExperiment);
@@ -370,7 +370,7 @@ export class ExperimentService extends Disposable implements IExperimentService
// Ensure we dont store duplicates
const distinctExperiments = distinct(runExperimentIdsFromStorage);
if (runExperimentIdsFromStorage.length !== distinctExperiments.length) {
this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL);
this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL, StorageTarget.MACHINE);
}
}
@@ -399,7 +399,7 @@ export class ExperimentService extends Disposable implements IExperimentService
const key = experimentEventStorageKey(event);
const record = getCurrentActivationRecord(safeParse(this.storageService.get(key, StorageScope.GLOBAL), undefined));
record.count[0]++;
this.storageService.store(key, JSON.stringify(record), StorageScope.GLOBAL);
this.storageService.store(key, JSON.stringify(record), StorageScope.GLOBAL, StorageTarget.MACHINE);
this._experiments
.filter(e => e.state === ExperimentState.Evaluating && e.raw?.condition?.activationEvent?.event === event)
@@ -558,12 +558,12 @@ export class ExperimentService extends Disposable implements IExperimentService
if (filePathCheck && workspaceCheck) {
latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1;
latestExperimentState.lastEditedDate = date;
this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL);
this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL, StorageTarget.MACHINE);
}
});
if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) {
processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun;
this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL);
this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL, StorageTarget.MACHINE);
if (latestExperimentState.state === ExperimentState.Run && processedExperiment.action && ExperimentActionType[processedExperiment.action.type] === ExperimentActionType.Prompt) {
this.fireRunExperiment(processedExperiment);
}

View File

@@ -64,7 +64,7 @@ suite('Experimental Prompts', () => {
storageData = {};
instantiationService.stub(IStorageService, <Partial<IStorageService>>{
get: (a: string, b: StorageScope, c?: string) => a === 'experiments.experiment1' ? JSON.stringify(storageData) : c,
store: (a, b, c) => {
store: (a, b, c, d) => {
if (a === 'experiments.experiment1') {
storageData = JSON.parse(b + '');
}

View File

@@ -0,0 +1,474 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/runtimeExtensionsEditor';
import * as nls from 'vs/nls';
import { Action, IAction, Separator } from 'vs/base/common/actions';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IExtensionService, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions';
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
import { WorkbenchList } from 'vs/platform/list/browser/listService';
import { append, $, reset, Dimension, clearNode } from 'vs/base/browser/dom';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { RunOnceScheduler } from 'vs/base/common/async';
import { EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { memoize } from 'vs/base/common/decorators';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { Event } from 'vs/base/common/event';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ILabelService } from 'vs/platform/label/common/label';
import { renderCodicons } from 'vs/base/browser/codicons';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Schemas } from 'vs/base/common/network';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
import { domEvent } from 'vs/base/browser/event';
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput';
interface IExtensionProfileInformation {
/**
* segment when the extension was running.
* 2*i = segment start time
* 2*i+1 = segment end time
*/
segments: number[];
/**
* total time when the extension was running.
* (sum of all segment lengths).
*/
totalTime: number;
}
export interface IRuntimeExtension {
originalIndex: number;
description: IExtensionDescription;
marketplaceInfo: IExtension;
status: IExtensionsStatus;
profileInfo?: IExtensionProfileInformation;
unresponsiveProfile?: IExtensionHostProfile;
}
export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
public static readonly ID: string = 'workbench.editor.runtimeExtensions';
private _list: WorkbenchList<IRuntimeExtension> | null;
private _elements: IRuntimeExtension[] | null;
private _updateSoon: RunOnceScheduler;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IContextKeyService contextKeyService: IContextKeyService,
@IExtensionsWorkbenchService private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionService private readonly _extensionService: IExtensionService,
@INotificationService private readonly _notificationService: INotificationService,
@IContextMenuService private readonly _contextMenuService: IContextMenuService,
@IInstantiationService protected readonly _instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ILabelService private readonly _labelService: ILabelService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
) {
super(AbstractRuntimeExtensionsEditor.ID, telemetryService, themeService, storageService);
this._list = null;
this._elements = null;
this._updateSoon = this._register(new RunOnceScheduler(() => this._updateExtensions(), 200));
this._register(this._extensionService.onDidChangeExtensionsStatus(() => this._updateSoon.schedule()));
this._updateExtensions();
}
protected async _updateExtensions(): Promise<void> {
this._elements = await this._resolveExtensions();
if (this._list) {
this._list.splice(0, this._list.length, this._elements);
}
}
private async _resolveExtensions(): Promise<IRuntimeExtension[]> {
// We only deal with extensions with source code!
const extensionsDescriptions = (await this._extensionService.getExtensions()).filter((extension) => {
return Boolean(extension.main) || Boolean(extension.browser);
});
let marketplaceMap: { [id: string]: IExtension; } = Object.create(null);
const marketPlaceExtensions = await this._extensionsWorkbenchService.queryLocal();
for (let extension of marketPlaceExtensions) {
marketplaceMap[ExtensionIdentifier.toKey(extension.identifier.id)] = extension;
}
let statusMap = this._extensionService.getExtensionsStatus();
// group profile segments by extension
let segments: { [id: string]: number[]; } = Object.create(null);
const profileInfo = this._getProfileInfo();
if (profileInfo) {
let currentStartTime = profileInfo.startTime;
for (let i = 0, len = profileInfo.deltas.length; i < len; i++) {
const id = profileInfo.ids[i];
const delta = profileInfo.deltas[i];
let extensionSegments = segments[ExtensionIdentifier.toKey(id)];
if (!extensionSegments) {
extensionSegments = [];
segments[ExtensionIdentifier.toKey(id)] = extensionSegments;
}
extensionSegments.push(currentStartTime);
currentStartTime = currentStartTime + delta;
extensionSegments.push(currentStartTime);
}
}
let result: IRuntimeExtension[] = [];
for (let i = 0, len = extensionsDescriptions.length; i < len; i++) {
const extensionDescription = extensionsDescriptions[i];
let profileInfo: IExtensionProfileInformation | null = null;
if (profileInfo) {
let extensionSegments = segments[ExtensionIdentifier.toKey(extensionDescription.identifier)] || [];
let extensionTotalTime = 0;
for (let j = 0, lenJ = extensionSegments.length / 2; j < lenJ; j++) {
const startTime = extensionSegments[2 * j];
const endTime = extensionSegments[2 * j + 1];
extensionTotalTime += (endTime - startTime);
}
profileInfo = {
segments: extensionSegments,
totalTime: extensionTotalTime
};
}
result[i] = {
originalIndex: i,
description: extensionDescription,
marketplaceInfo: marketplaceMap[ExtensionIdentifier.toKey(extensionDescription.identifier)],
status: statusMap[extensionDescription.identifier.value],
profileInfo: profileInfo || undefined,
unresponsiveProfile: this._getUnresponsiveProfile(extensionDescription.identifier)
};
}
result = result.filter(element => element.status.activationTimes);
// bubble up extensions that have caused slowness
const isUnresponsive = (extension: IRuntimeExtension): boolean =>
extension.unresponsiveProfile === profileInfo;
const profileTime = (extension: IRuntimeExtension): number =>
extension.profileInfo?.totalTime ?? 0;
const activationTime = (extension: IRuntimeExtension): number =>
(extension.status.activationTimes?.codeLoadingTime ?? 0) +
(extension.status.activationTimes?.activateCallTime ?? 0);
result = result.sort((a, b) => {
if (isUnresponsive(a) || isUnresponsive(b)) {
return +isUnresponsive(b) - +isUnresponsive(a);
} else if (profileTime(a) || profileTime(b)) {
return profileTime(b) - profileTime(a);
} else if (activationTime(a) || activationTime(b)) {
return activationTime(b) - activationTime(a);
}
return a.originalIndex - b.originalIndex;
});
return result;
}
protected createEditor(parent: HTMLElement): void {
parent.classList.add('runtime-extensions-editor');
const TEMPLATE_ID = 'runtimeExtensionElementTemplate';
const delegate = new class implements IListVirtualDelegate<IRuntimeExtension>{
getHeight(element: IRuntimeExtension): number {
return 62;
}
getTemplateId(element: IRuntimeExtension): string {
return TEMPLATE_ID;
}
};
interface IRuntimeExtensionTemplateData {
root: HTMLElement;
element: HTMLElement;
icon: HTMLImageElement;
name: HTMLElement;
version: HTMLElement;
msgContainer: HTMLElement;
actionbar: ActionBar;
activationTime: HTMLElement;
profileTime: HTMLElement;
disposables: IDisposable[];
elementDisposables: IDisposable[];
}
const renderer: IListRenderer<IRuntimeExtension, IRuntimeExtensionTemplateData> = {
templateId: TEMPLATE_ID,
renderTemplate: (root: HTMLElement): IRuntimeExtensionTemplateData => {
const element = append(root, $('.extension'));
const iconContainer = append(element, $('.icon-container'));
const icon = append(iconContainer, $<HTMLImageElement>('img.icon'));
const desc = append(element, $('div.desc'));
const headerContainer = append(desc, $('.header-container'));
const header = append(headerContainer, $('.header'));
const name = append(header, $('div.name'));
const version = append(header, $('span.version'));
const msgContainer = append(desc, $('div.msg'));
const actionbar = new ActionBar(desc, { animated: false });
actionbar.onDidRun(({ error }) => error && this._notificationService.error(error));
const timeContainer = append(element, $('.time'));
const activationTime = append(timeContainer, $('div.activation-time'));
const profileTime = append(timeContainer, $('div.profile-time'));
const disposables = [actionbar];
return {
root,
element,
icon,
name,
version,
actionbar,
activationTime,
profileTime,
msgContainer,
disposables,
elementDisposables: [],
};
},
renderElement: (element: IRuntimeExtension, index: number, data: IRuntimeExtensionTemplateData): void => {
data.elementDisposables = dispose(data.elementDisposables);
data.root.classList.toggle('odd', index % 2 === 1);
const onError = Event.once(domEvent(data.icon, 'error'));
onError(() => data.icon.src = element.marketplaceInfo.iconUrlFallback, null, data.elementDisposables);
data.icon.src = element.marketplaceInfo.iconUrl;
if (!data.icon.complete) {
data.icon.style.visibility = 'hidden';
data.icon.onload = () => data.icon.style.visibility = 'inherit';
} else {
data.icon.style.visibility = 'inherit';
}
data.name.textContent = element.marketplaceInfo.displayName;
data.version.textContent = element.description.version;
const activationTimes = element.status.activationTimes!;
let syncTime = activationTimes.codeLoadingTime + activationTimes.activateCallTime;
data.activationTime.textContent = activationTimes.activationReason.startup ? `Startup Activation: ${syncTime}ms` : `Activation: ${syncTime}ms`;
data.actionbar.clear();
const slowExtensionAction = this._createSlowExtensionAction(element);
if (slowExtensionAction) {
data.actionbar.push(slowExtensionAction, { icon: true, label: true });
}
if (isNonEmptyArray(element.status.runtimeErrors)) {
const reportExtensionIssueAction = this._createReportExtensionIssueAction(element);
if (reportExtensionIssueAction) {
data.actionbar.push(reportExtensionIssueAction, { icon: true, label: true });
}
}
let title: string;
const activationId = activationTimes.activationReason.extensionId.value;
const activationEvent = activationTimes.activationReason.activationEvent;
if (activationEvent === '*') {
title = nls.localize('starActivation', "Activated by {0} on start-up", activationId);
} else if (/^workspaceContains:/.test(activationEvent)) {
let fileNameOrGlob = activationEvent.substr('workspaceContains:'.length);
if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
title = nls.localize({
key: 'workspaceContainsGlobActivation',
comment: [
'{0} will be a glob pattern'
]
}, "Activated by {1} because a file matching {1} exists in your workspace", fileNameOrGlob, activationId);
} else {
title = nls.localize({
key: 'workspaceContainsFileActivation',
comment: [
'{0} will be a file name'
]
}, "Activated by {1} because file {0} exists in your workspace", fileNameOrGlob, activationId);
}
} else if (/^workspaceContainsTimeout:/.test(activationEvent)) {
const glob = activationEvent.substr('workspaceContainsTimeout:'.length);
title = nls.localize({
key: 'workspaceContainsTimeout',
comment: [
'{0} will be a glob pattern'
]
}, "Activated by {1} because searching for {0} took too long", glob, activationId);
} else if (activationEvent === 'onStartupFinished') {
title = nls.localize({
key: 'startupFinishedActivation',
comment: [
'This refers to an extension. {0} will be an activation event.'
]
}, "Activated by {0} after start-up finished", activationId);
} else if (/^onLanguage:/.test(activationEvent)) {
let language = activationEvent.substr('onLanguage:'.length);
title = nls.localize('languageActivation', "Activated by {1} because you opened a {0} file", language, activationId);
} else {
title = nls.localize({
key: 'workspaceGenericActivation',
comment: [
'The {0} placeholder will be an activation event, like e.g. \'language:typescript\', \'debug\', etc.'
]
}, "Activated by {1} on {0}", activationEvent, activationId);
}
data.activationTime.title = title;
clearNode(data.msgContainer);
if (this._getUnresponsiveProfile(element.description.identifier)) {
const el = $('span', undefined, ...renderCodicons(` $(alert) Unresponsive`));
el.title = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze.");
data.msgContainer.appendChild(el);
}
if (isNonEmptyArray(element.status.runtimeErrors)) {
const el = $('span', undefined, ...renderCodicons(`$(bug) ${nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length)}`));
data.msgContainer.appendChild(el);
}
if (element.status.messages && element.status.messages.length > 0) {
const el = $('span', undefined, ...renderCodicons(`$(alert) ${element.status.messages[0].message}`));
data.msgContainer.appendChild(el);
}
if (element.description.extensionLocation.scheme === Schemas.vscodeRemote) {
const el = $('span', undefined, ...renderCodicons(`$(remote) ${element.description.extensionLocation.authority}`));
data.msgContainer.appendChild(el);
const hostLabel = this._labelService.getHostLabel(Schemas.vscodeRemote, this._environmentService.remoteAuthority);
if (hostLabel) {
reset(el, ...renderCodicons(`$(remote) ${hostLabel}`));
}
}
if (element.profileInfo) {
data.profileTime.textContent = `Profile: ${(element.profileInfo.totalTime / 1000).toFixed(2)}ms`;
} else {
data.profileTime.textContent = '';
}
},
disposeTemplate: (data: IRuntimeExtensionTemplateData): void => {
data.disposables = dispose(data.disposables);
}
};
this._list = <WorkbenchList<IRuntimeExtension>>this._instantiationService.createInstance(WorkbenchList,
'RuntimeExtensions',
parent, delegate, [renderer], {
multipleSelectionSupport: false,
setRowLineHeight: false,
horizontalScrolling: false,
overrideStyles: {
listBackground: editorBackground
},
accessibilityProvider: new class implements IListAccessibilityProvider<IRuntimeExtension> {
getWidgetAriaLabel(): string {
return nls.localize('runtimeExtensions', "Runtime Extensions");
}
getAriaLabel(element: IRuntimeExtension): string | null {
return element.description.name;
}
}
});
this._list.splice(0, this._list.length, this._elements || undefined);
this._list.onContextMenu((e) => {
if (!e.element) {
return;
}
const actions: IAction[] = [];
const reportExtensionIssueAction = this._createReportExtensionIssueAction(e.element);
if (reportExtensionIssueAction) {
actions.push(reportExtensionIssueAction);
actions.push(new Separator());
}
actions.push(new Action('runtimeExtensionsEditor.action.disableWorkspace', nls.localize('disable workspace', "Disable (Workspace)"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledWorkspace)));
actions.push(new Action('runtimeExtensionsEditor.action.disable', nls.localize('disable', "Disable"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledGlobally)));
actions.push(new Separator());
const profileAction = this._createProfileAction();
if (profileAction) {
actions.push(profileAction);
}
const saveExtensionHostProfileAction = this.saveExtensionHostProfileAction;
if (saveExtensionHostProfileAction) {
actions.push(saveExtensionHostProfileAction);
}
this._contextMenuService.showContextMenu({
getAnchor: () => e.anchor,
getActions: () => actions
});
});
}
@memoize
private get saveExtensionHostProfileAction(): IAction | null {
return this._createSaveExtensionHostProfileAction();
}
public layout(dimension: Dimension): void {
if (this._list) {
this._list.layout(dimension.height);
}
}
protected abstract _getProfileInfo(): IExtensionHostProfile | null;
protected abstract _getUnresponsiveProfile(extensionId: ExtensionIdentifier): IExtensionHostProfile | undefined;
protected abstract _createSlowExtensionAction(element: IRuntimeExtension): Action | null;
protected abstract _createReportExtensionIssueAction(element: IRuntimeExtension): Action | null;
protected abstract _createSaveExtensionHostProfileAction(): Action | null;
protected abstract _createProfileAction(): Action | null;
}
export class ShowRuntimeExtensionsAction extends Action {
static readonly ID = 'workbench.action.showRuntimeExtensions';
static readonly LABEL = nls.localize('showRuntimeExtensions', "Show Running Extensions");
constructor(
id: string, label: string,
@IEditorService private readonly _editorService: IEditorService
) {
super(id, label);
}
public async run(e?: any): Promise<any> {
await this._editorService.openEditor(RuntimeExtensionsInput.instance, { revealIfOpened: true, pinned: true });
}
}

View File

@@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Action } from 'vs/base/common/actions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IExtensionService, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ILabelService } from 'vs/platform/label/common/label';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { AbstractRuntimeExtensionsEditor, IRuntimeExtension } from 'vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor';
export class RuntimeExtensionsEditor extends AbstractRuntimeExtensionsEditor {
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IContextKeyService contextKeyService: IContextKeyService,
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionService extensionService: IExtensionService,
@INotificationService notificationService: INotificationService,
@IContextMenuService contextMenuService: IContextMenuService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ILabelService labelService: ILabelService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
) {
super(telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService);
}
protected _getProfileInfo(): IExtensionHostProfile | null {
return null;
}
protected _getUnresponsiveProfile(extensionId: ExtensionIdentifier): IExtensionHostProfile | undefined {
return undefined;
}
protected _createSlowExtensionAction(element: IRuntimeExtension): Action | null {
return null;
}
protected _createReportExtensionIssueAction(element: IRuntimeExtension): Action | null {
return null;
}
protected _createSaveExtensionHostProfileAction(): Action | null {
return null;
}
protected _createProfileAction(): Action | null {
return null;
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IFileService } from 'vs/platform/files/common/files';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -81,7 +81,7 @@ export class DynamicWorkspaceRecommendations extends ExtensionRecommendations {
const workspaceTip = workspacesTips.filter(workspaceTip => isNonEmptyArray(workspaceTip.remoteSet) && workspaceTip.remoteSet.indexOf(hashedRemote) > -1)[0];
if (workspaceTip) {
this._recommendations = workspaceTip.recommendations.map(id => this.toExtensionRecommendation(id, folder));
this.storageService.store(dynamicWorkspaceRecommendationsStorageKey, JSON.stringify(<IStoredDynamicWorkspaceRecommendations>{ recommendations: workspaceTip.recommendations, timestamp: Date.now() }), StorageScope.WORKSPACE);
this.storageService.store(dynamicWorkspaceRecommendationsStorageKey, JSON.stringify(<IStoredDynamicWorkspaceRecommendations>{ recommendations: workspaceTip.recommendations, timestamp: Date.now() }), StorageScope.WORKSPACE, StorageTarget.MACHINE);
this.telemetryService.publicLog2<{ count: number, cache: number }, DynamicWorkspaceRecommendationsClassification>('dynamicWorkspaceRecommendations', { count: this._recommendations.length, cache: 0 });
return;
}

View File

@@ -14,7 +14,7 @@ import { Action, IAction } from 'vs/base/common/actions';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { dispose, toDisposable, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { domEvent } from 'vs/base/browser/event';
import { append, $, finalHandler, join, hide, show, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { append, $, finalHandler, join, hide, show, addDisposableListener, EventType, setParentFlowTo } from 'vs/base/browser/dom';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -27,7 +27,12 @@ import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID,
import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets';
import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { UpdateAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, ExtensionToolTipAction, SystemDisabledWarningAction, LocalInstallAction, SyncIgnoredIconAction, SetProductIconThemeAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import {
UpdateAction, ReloadAction, MaliciousStatusLabelAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction,
RemoteInstallAction, ExtensionToolTipAction, SystemDisabledWarningAction, LocalInstallAction, ToggleSyncExtensionAction, SetProductIconThemeAction,
ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction,
InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction
} from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener';
@@ -38,7 +43,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Color } from 'vs/base/common/color';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { CancellationToken } from 'vs/base/common/cancellation';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { ExtensionsTree, ExtensionData, ExtensionsGridView, getExtensions } from 'vs/workbench/contrib/extensions/browser/extensionsViewer';
import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update';
import { KeybindingParser } from 'vs/base/common/keybindingParser';
@@ -251,6 +256,9 @@ export class ExtensionEditor extends EditorPane {
const extensionActionBar = this._register(new ActionBar(extensionActions, {
animated: false,
actionViewItemProvider: (action: IAction) => {
if (action instanceof ExtensionDropDownAction) {
return action.createActionViewItem();
}
if (action instanceof ActionWithDropDownAction) {
return new ExtensionActionWithDropdownActionViewItem(action, { icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService);
}
@@ -276,6 +284,7 @@ export class ExtensionEditor extends EditorPane {
const navbar = new NavBar(body);
const content = append(body, $('.content'));
content.id = generateUuid(); // An id is needed for the webview parent flow to
this.template = {
builtin,
@@ -323,8 +332,6 @@ export class ExtensionEditor extends EditorPane {
}
private async updateTemplate(input: ExtensionsInput, template: IExtensionEditorTemplate, preserveFocus: boolean): Promise<void> {
const runningExtensions = await this.extensionService.getExtensions();
this.activeElement = null;
this.editorLoadComplete = false;
const extension = input.extension;
@@ -405,7 +412,6 @@ export class ExtensionEditor extends EditorPane {
const systemDisabledWarningAction = this.instantiationService.createInstance(SystemDisabledWarningAction);
const actions = [
reloadAction,
this.instantiationService.createInstance(SyncIgnoredIconAction),
this.instantiationService.createInstance(StatusLabelAction),
this.instantiationService.createInstance(UpdateAction),
this.instantiationService.createInstance(SetColorThemeAction, await this.workbenchThemeService.getColorThemes()),
@@ -413,13 +419,19 @@ export class ExtensionEditor extends EditorPane {
this.instantiationService.createInstance(SetProductIconThemeAction, await this.workbenchThemeService.getProductIconThemes()),
this.instantiationService.createInstance(EnableDropDownAction),
this.instantiationService.createInstance(DisableDropDownAction, runningExtensions),
this.instantiationService.createInstance(DisableDropDownAction),
this.instantiationService.createInstance(RemoteInstallAction, false),
this.instantiationService.createInstance(LocalInstallAction),
this.instantiationService.createInstance(WebInstallAction),
combinedInstallAction,
this.instantiationService.createInstance(InstallingLabelAction),
this.instantiationService.createInstance(UninstallAction),
this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.uninstall', UninstallAction.UninstallLabel, [
this.instantiationService.createInstance(UninstallAction),
this.instantiationService.createInstance(InstallAnotherVersionAction),
]),
this.instantiationService.createInstance(ToggleSyncExtensionAction),
systemDisabledWarningAction,
this.instantiationService.createInstance(ExtensionEditorManageExtensionAction),
this.instantiationService.createInstance(ExtensionToolTipAction, systemDisabledWarningAction, reloadAction),
this.instantiationService.createInstance(MaliciousStatusLabelAction, true),
];
@@ -432,7 +444,7 @@ export class ExtensionEditor extends EditorPane {
this.transientDisposables.add(disposable);
}
this.setSubText(extension, reloadAction, template);
this.setSubText(extension, template);
template.content.innerText = ''; // Clear content before setting navbar actions.
template.navbar.clear();
@@ -463,56 +475,24 @@ export class ExtensionEditor extends EditorPane {
this.editorLoadComplete = true;
}
private setSubText(extension: IExtension, reloadAction: ReloadAction, template: IExtensionEditorTemplate): void {
private setSubText(extension: IExtension, template: IExtensionEditorTemplate): void {
hide(template.subtextContainer);
const ignoreAction = this.instantiationService.createInstance(IgnoreExtensionRecommendationAction, extension);
const undoIgnoreAction = this.instantiationService.createInstance(UndoIgnoreExtensionRecommendationAction, extension);
ignoreAction.enabled = false;
undoIgnoreAction.enabled = false;
template.ignoreActionbar.clear();
template.ignoreActionbar.push([ignoreAction, undoIgnoreAction], { icon: true, label: true });
this.transientDisposables.add(ignoreAction);
this.transientDisposables.add(undoIgnoreAction);
const updateRecommendationFn = () => {
const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason();
if (extRecommendations[extension.identifier.id.toLowerCase()]) {
ignoreAction.enabled = true;
undoIgnoreAction.enabled = false;
template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText;
show(template.subtextContainer);
} else if (this.extensionIgnoredRecommendationsService.globalIgnoredRecommendations.indexOf(extension.identifier.id.toLowerCase()) !== -1) {
ignoreAction.enabled = false;
undoIgnoreAction.enabled = true;
template.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension.");
show(template.subtextContainer);
} else {
ignoreAction.enabled = false;
undoIgnoreAction.enabled = false;
template.subtext.textContent = '';
hide(template.subtextContainer);
}
};
updateRecommendationFn();
this.transientDisposables.add(this.extensionRecommendationsService.onDidChangeRecommendations(() => updateRecommendationFn()));
this.transientDisposables.add(reloadAction.onDidChange(e => {
if (e.tooltip) {
template.subtext.textContent = reloadAction.tooltip;
show(template.subtextContainer);
ignoreAction.enabled = false;
undoIgnoreAction.enabled = false;
}
if (e.enabled === true) {
show(template.subtextContainer);
}
if (e.enabled === false) {
hide(template.subtextContainer);
}
this.layout();
}));
}
clearInput(): void {
@@ -558,8 +538,13 @@ export class ExtensionEditor extends EditorPane {
template.content.innerText = '';
this.activeElement = null;
if (id) {
this.open(id, extension, template)
const cts = new CancellationTokenSource();
this.contentDisposables.add(toDisposable(() => cts.dispose(true)));
this.open(id, extension, template, cts.token)
.then(activeElement => {
if (cts.token.isCancellationRequested) {
return;
}
this.activeElement = activeElement;
if (focus) {
this.focus();
@@ -568,26 +553,31 @@ export class ExtensionEditor extends EditorPane {
}
}
private open(id: string, extension: IExtension, template: IExtensionEditorTemplate): Promise<IActiveElement | null> {
private open(id: string, extension: IExtension, template: IExtensionEditorTemplate, token: CancellationToken): Promise<IActiveElement | null> {
switch (id) {
case NavbarSection.Readme: return this.openReadme(template);
case NavbarSection.Contributions: return this.openContributions(template);
case NavbarSection.Changelog: return this.openChangelog(template);
case NavbarSection.Dependencies: return this.openDependencies(extension, template);
case NavbarSection.Readme: return this.openReadme(template, token);
case NavbarSection.Contributions: return this.openContributions(template, token);
case NavbarSection.Changelog: return this.openChangelog(template, token);
case NavbarSection.Dependencies: return this.openDependencies(extension, template, token);
}
return Promise.resolve(null);
}
private async openMarkdown(cacheResult: CacheResult<string>, noContentCopy: string, template: IExtensionEditorTemplate): Promise<IActiveElement> {
private async openMarkdown(cacheResult: CacheResult<string>, noContentCopy: string, template: IExtensionEditorTemplate, token: CancellationToken): Promise<IActiveElement | null> {
try {
const body = await this.renderMarkdown(cacheResult, template);
if (token.isCancellationRequested) {
return Promise.resolve(null);
}
const webview = this.contentDisposables.add(this.webviewService.createWebviewOverlay('extensionEditor', {
enableFindWidget: true,
}, {}, undefined));
webview.claim(this);
webview.claim(this, this.scopedContextKeyService);
setParentFlowTo(webview.container, template.content);
webview.layoutWebviewOverElement(template.content);
webview.html = body;
this.contentDisposables.add(webview.onDidFocus(() => this.fireOnDidFocus()));
@@ -828,15 +818,19 @@ export class ExtensionEditor extends EditorPane {
</html>`;
}
private async openReadme(template: IExtensionEditorTemplate): Promise<IActiveElement> {
private async openReadme(template: IExtensionEditorTemplate, token: CancellationToken): Promise<IActiveElement | null> {
const manifest = await this.extensionManifest!.get().promise;
if (manifest && manifest.extensionPack && manifest.extensionPack.length) {
return this.openExtensionPackReadme(manifest, template);
return this.openExtensionPackReadme(manifest, template, token);
}
return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), template);
return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), template, token);
}
private async openExtensionPackReadme(manifest: IExtensionManifest, template: IExtensionEditorTemplate): Promise<IActiveElement> {
private async openExtensionPackReadme(manifest: IExtensionManifest, template: IExtensionEditorTemplate, token: CancellationToken): Promise<IActiveElement | null> {
if (token.isCancellationRequested) {
return Promise.resolve(null);
}
const extensionPackReadme = append(template.content, $('div', { class: 'extension-pack-readme' }));
extensionPackReadme.style.margin = '0 auto';
extensionPackReadme.style.maxWidth = '882px';
@@ -860,21 +854,25 @@ export class ExtensionEditor extends EditorPane {
const readmeContent = append(extensionPackReadme, $('div.readme-content'));
await Promise.all([
this.renderExtensionPack(manifest, extensionPackContent),
this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), { ...template, ...{ content: readmeContent } }),
this.renderExtensionPack(manifest, extensionPackContent, token),
this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), { ...template, ...{ content: readmeContent } }, token),
]);
return { focus: () => extensionPackContent.focus() };
}
private openChangelog(template: IExtensionEditorTemplate): Promise<IActiveElement> {
return this.openMarkdown(this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available."), template);
private openChangelog(template: IExtensionEditorTemplate, token: CancellationToken): Promise<IActiveElement | null> {
return this.openMarkdown(this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available."), template, token);
}
private openContributions(template: IExtensionEditorTemplate): Promise<IActiveElement> {
private openContributions(template: IExtensionEditorTemplate, token: CancellationToken): Promise<IActiveElement | null> {
const content = $('div', { class: 'subcontent', tabindex: '0' });
return this.loadContents(() => this.extensionManifest!.get(), template)
.then(manifest => {
if (token.isCancellationRequested) {
return null;
}
if (!manifest) {
return content;
}
@@ -900,6 +898,7 @@ export class ExtensionEditor extends EditorPane {
this.renderLocalizations(content, manifest, layout),
this.renderCustomEditors(content, manifest, layout),
this.renderAuthentication(content, manifest, layout),
this.renderActivationEvents(content, manifest, layout),
];
scrollableContent.scanDomNode();
@@ -914,13 +913,21 @@ export class ExtensionEditor extends EditorPane {
}
return content;
}, () => {
if (token.isCancellationRequested) {
return null;
}
append(content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions");
append(template.content, content);
return content;
});
}
private openDependencies(extension: IExtension, template: IExtensionEditorTemplate): Promise<IActiveElement> {
private openDependencies(extension: IExtension, template: IExtensionEditorTemplate, token: CancellationToken): Promise<IActiveElement | null> {
if (token.isCancellationRequested) {
return Promise.resolve(null);
}
if (arrays.isFalsyOrEmpty(extension.dependencies)) {
append(template.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies");
return Promise.resolve(template.content);
@@ -949,7 +956,11 @@ export class ExtensionEditor extends EditorPane {
return Promise.resolve({ focus() { dependenciesTree.domFocus(); } });
}
private async renderExtensionPack(manifest: IExtensionManifest, parent: HTMLElement): Promise<void> {
private async renderExtensionPack(manifest: IExtensionManifest, parent: HTMLElement, token: CancellationToken): Promise<void> {
if (token.isCancellationRequested) {
return;
}
const content = $('div', { class: 'subcontent' });
const scrollableContent = new DomScrollableElement(content, { useShadows: false });
append(parent, scrollableContent.getDomNode());
@@ -1414,6 +1425,21 @@ export class ExtensionEditor extends EditorPane {
return true;
}
private renderActivationEvents(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
const activationEvents = manifest.activationEvents || [];
if (!activationEvents.length) {
return false;
}
const details = $('details', { open: true, ontoggle: onDetailsToggle },
$('summary', { tabindex: '0' }, localize('activation events', "Activation Events ({0})", activationEvents.length)),
$('ul', undefined, ...activationEvents.map(activationEvent => $('li', undefined, $('code', undefined, activationEvent))))
);
append(container, details);
return true;
}
private resolveKeybinding(rawKeyBinding: IKeyBinding): ResolvedKeybinding | null {
let key: string | undefined;

View File

@@ -11,14 +11,13 @@ import { isPromiseCanceledError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
import { INotificationHandle, INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { INotificationHandle, INotificationService, IPromptChoice, IPromptChoiceWithMenu, Severity } from 'vs/platform/notification/common/notification';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
@@ -26,14 +25,6 @@ import { ITASExperimentService } from 'vs/workbench/services/experiment/common/e
import { EnablementState, IWorkbenchExtensioManagementService, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
interface IExtensionsConfiguration {
autoUpdate: boolean;
autoCheckUpdates: boolean;
ignoreRecommendations: boolean;
showRecommendationsOnlyOnDemand: boolean;
closeExtensionDetailsOnViewChange: boolean;
}
type ExtensionRecommendationsNotificationClassification = {
userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
extensionId?: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
@@ -142,18 +133,16 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec
@IWorkbenchExtensioManagementService private readonly extensionManagementService: IWorkbenchExtensioManagementService,
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
@IExtensionIgnoredRecommendationsService private readonly extensionIgnoredRecommendationsService: IExtensionIgnoredRecommendationsService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
@IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService,
@IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService,
@optional(ITASExperimentService) tasExperimentService: ITASExperimentService,
) {
storageKeysSyncRegistryService.registerStorageKey({ key: ignoreImportantExtensionRecommendationStorageKey, version: 1 });
this.tasExperimentService = tasExperimentService;
}
hasToIgnoreRecommendationNotifications(): boolean {
const config = this.configurationService.getValue<IExtensionsConfiguration>('extensions');
return config.ignoreRecommendations || config.showRecommendationsOnlyOnDemand;
const config = this.configurationService.getValue<{ ignoreRecommendations: boolean, showRecommendationsOnlyOnDemand?: boolean }>('extensions');
return config.ignoreRecommendations || !!config.showRecommendationsOnlyOnDemand;
}
async promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string, source: RecommendationSource): Promise<RecommendationsNotificationResult> {
@@ -207,7 +196,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec
});
if (result === RecommendationsNotificationResult.Accepted) {
this.storageService.store(donotShowWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE);
this.storageService.store(donotShowWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE, StorageTarget.USER);
}
}
@@ -252,7 +241,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec
{ onDidInstallRecommendedExtensions, onDidShowRecommendedExtensions, onDidCancelRecommendedExtensions, onDidNeverShowRecommendedExtensionsAgain }: RecommendationsNotificationActions): CancelablePromise<RecommendationsNotificationResult> {
return createCancelablePromise<RecommendationsNotificationResult>(async token => {
let accepted = false;
const choices: IPromptChoice[] = [];
const choices: (IPromptChoice | IPromptChoiceWithMenu)[] = [];
const installExtensions = async (isMachineScoped?: boolean) => {
this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue));
onDidInstallRecommendedExtensions(extensions);
@@ -263,14 +252,12 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec
};
choices.push({
label: localize('install', "Install"),
run: () => installExtensions()
});
if (this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncResourceEnablementService.isResourceEnabled(SyncResource.Extensions)) {
choices.push({
run: () => installExtensions(),
menu: this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncResourceEnablementService.isResourceEnabled(SyncResource.Extensions) ? [{
label: localize('install and do no sync', "Install (Do not sync)"),
run: () => installExtensions(true)
});
}
}] : undefined,
});
choices.push(...[{
label: localize('show recommendations', "Show Recommendations"),
run: async () => {
@@ -423,11 +410,11 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec
const importantRecommendationsIgnoreList = [...this.ignoredRecommendations];
if (!importantRecommendationsIgnoreList.includes(id.toLowerCase())) {
importantRecommendationsIgnoreList.push(id.toLowerCase());
this.storageService.store(ignoreImportantExtensionRecommendationStorageKey, JSON.stringify(importantRecommendationsIgnoreList), StorageScope.GLOBAL);
this.storageService.store(ignoreImportantExtensionRecommendationStorageKey, JSON.stringify(importantRecommendationsIgnoreList), StorageScope.GLOBAL, StorageTarget.USER);
}
}
private setIgnoreRecommendationsConfig(configVal: boolean) {
this.configurationService.updateValue('extensions.ignoreRecommendations', configVal, ConfigurationTarget.USER);
this.configurationService.updateValue('extensions.ignoreRecommendations', configVal);
}
}

View File

@@ -7,8 +7,6 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { IExtensionManagementService, IExtensionGalleryService, InstallOperation, DidInstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionRecommendationsService, ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ShowRecommendationsOnlyOnDemandKey } from 'vs/workbench/contrib/extensions/common/extensions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { distinct, shuffle } from 'vs/base/common/arrays';
import { Emitter, Event } from 'vs/base/common/event';
@@ -52,7 +50,6 @@ export class ExtensionRecommendationsService extends Disposable implements IExte
@IInstantiationService instantiationService: IInstantiationService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@@ -92,15 +89,9 @@ export class ExtensionRecommendationsService extends Disposable implements IExte
this.fileBasedRecommendations.activate(),
this.experimentalRecommendations.activate(),
this.keymapRecommendations.activate(),
this.lifecycleService.when(LifecyclePhase.Eventually)
.then(async () => {
if (!this.configurationService.getValue<boolean>(ShowRecommendationsOnlyOnDemandKey)) {
await this.activateProactiveRecommendations();
}
})
]);
this._register(this.extensionRecommendationsManagementService.onDidChangeIgnoredRecommendations(() => this._onDidChangeRecommendations.fire()));
this._register(Event.any(this.workspaceRecommendations.onDidChangeRecommendations, this.configBasedRecommendations.onDidChangeRecommendations, this.extensionRecommendationsManagementService.onDidChangeIgnoredRecommendations)(() => this._onDidChangeRecommendations.fire()));
this._register(this.extensionRecommendationsManagementService.onDidChangeGlobalIgnoredRecommendation(({ extensionId, isRecommended }) => {
if (!isRecommended) {
const reason = this.getAllRecommendationsWithReason()[extensionId];
@@ -111,7 +102,6 @@ export class ExtensionRecommendationsService extends Disposable implements IExte
}));
await this.promptWorkspaceRecommendations();
this._register(Event.any(this.workspaceRecommendations.onDidChangeRecommendations, this.configBasedRecommendations.onDidChangeRecommendations)(() => this.promptWorkspaceRecommendations()));
}
private isEnabled(): boolean {

View File

@@ -6,11 +6,11 @@
import { localize } from 'vs/nls';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import { Registry } from 'vs/platform/registry/common/platform';
import { MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
import { MenuRegistry, MenuId, registerAction2, Action2, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ExtensionsLabel, ExtensionsLocalizedLabel, ExtensionsChannelId, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
@@ -18,7 +18,7 @@ import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer,
import {
OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction,
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction,
EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction, ClearExtensionsSearchResultsAction
EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction, ClearExtensionsSearchResultsAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, RefreshExtensionsAction
} from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor';
@@ -26,7 +26,7 @@ import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContrib
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import * as jsonContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeymapExtensions } from 'vs/workbench/contrib/extensions/common/extensionsUtils';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
@@ -54,7 +54,7 @@ import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions, CATEGORIES } from 'vs/workbench/common/actions';
import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
import { ExtensionRecommendationNotificationService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService';
import { IExtensionService, toExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
@@ -62,6 +62,10 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { ResourceContextKey } from 'vs/workbench/common/resources';
import { IAction } from 'vs/base/common/actions';
import { IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig';
import { Schemas } from 'vs/base/common/network';
import { ShowRuntimeExtensionsAction } from 'vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor';
import { extensionsViewIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons';
// Singletons
registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService);
@@ -100,12 +104,13 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new SyncDescriptor(ExtensionsInput)
]);
Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(
{
id: VIEWLET_ID,
name: localize('extensions', "Extensions"),
ctorDescriptor: new SyncDescriptor(ExtensionsViewPaneContainer),
icon: 'codicon-extensions',
icon: extensionsViewIcon,
order: 4,
rejectAddedViews: true,
alwaysUseContainerInfo: true
@@ -140,7 +145,7 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
},
'extensions.showRecommendationsOnlyOnDemand': {
type: 'boolean',
description: localize('extensionsShowRecommendationsOnlyOnDemand', "When enabled, recommendations will not be fetched or shown unless specifically requested by the user. Some recommendations are fetched from a Microsoft online service."),
deprecationMessage: localize('extensionsShowRecommendationsOnlyOnDemand_Deprecated', "This setting is deprecated. Use extensions.ignoreRecommendations setting to control recommendation notifications. Use Extensions view's visibility actions to hide Recommended view by default."),
default: false,
tags: ['usesOnlineServices']
},
@@ -234,8 +239,8 @@ CommandsRegistry.registerCommand({
.then(async (extensions) => {
for (const extension of extensions) {
const requireReload = !(extension.local && extensionService.canAddExtension(toExtensionDescription(extension.local)));
const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.displayName || extension.name)
: localize('InstallVSIXAction.success', "Completed installing the extension {0}.", extension.displayName || extension.name);
const message = requireReload ? localize('InstallVSIXAction.successReload', "Completed installing {0} extension from VSIX. Please reload Visual Studio Code to enable it.", extension.displayName || extension.name)
: localize('InstallVSIXAction.success', "Completed installing {0} extension from VSIX.", extension.displayName || extension.name);
const actions = requireReload ? [{
label: localize('InstallVSIXAction.reloadNow', "Reload Now"),
run: () => hostService.reload()
@@ -279,7 +284,7 @@ CommandsRegistry.registerCommand({
}
try {
await extensionManagementService.uninstall(extensionToUninstall, true);
await extensionManagementService.uninstall(extensionToUninstall);
} catch (e) {
onUnexpectedError(e);
throw e;
@@ -771,6 +776,22 @@ class ExtensionsContributions implements IWorkbenchContribution {
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: RefreshExtensionsAction.ID,
title: { value: RefreshExtensionsAction.LABEL, original: 'Refresh' },
category: ExtensionsLocalizedLabel,
menu: {
id: MenuId.CommandPalette,
}
});
}
run(accessor: ServicesAccessor) {
return runAction(accessor.get(IInstantiationService).createInstance(RefreshExtensionsAction, RefreshExtensionsAction.ID, RefreshExtensionsAction.LABEL));
}
});
registerAction2(class extends Action2 {
constructor() {
super({
@@ -916,7 +937,7 @@ class ExtensionsContributions implements IWorkbenchContribution {
menu: {
id: MenuId.ExtensionContext,
group: '2_configure',
when: CONTEXT_SYNC_ENABLEMENT
when: ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT, ContextKeyExpr.has('inExtensionEditor').negate())
},
});
}
@@ -929,6 +950,220 @@ class ExtensionsContributions implements IWorkbenchContribution {
}
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.extensions.action.ignoreRecommendation',
title: { value: localize('workbench.extensions.action.ignoreRecommendation', "Ignore Recommendation"), original: `Ignore Recommendation` },
menu: {
id: MenuId.ExtensionContext,
group: '3_recommendations',
when: ContextKeyExpr.has('isExtensionRecommended'),
order: 1
},
});
}
async run(accessor: ServicesAccessor, id: string): Promise<any> {
accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, true);
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.extensions.action.undoIgnoredRecommendation',
title: { value: localize('workbench.extensions.action.undoIgnoredRecommendation', "Undo Ignored Recommendation"), original: `Undo Ignored Recommendation` },
menu: {
id: MenuId.ExtensionContext,
group: '3_recommendations',
when: ContextKeyExpr.has('isUserIgnoredRecommendation'),
order: 1
},
});
}
async run(accessor: ServicesAccessor, id: string): Promise<any> {
accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, false);
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.extensions.action.addExtensionToWorkspaceRecommendations',
title: { value: localize('workbench.extensions.action.addExtensionToWorkspaceRecommendations', "Add to Workspace Recommendations"), original: `Add to Workspace Recommendations` },
menu: {
id: MenuId.ExtensionContext,
group: '3_recommendations',
when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended').negate(), ContextKeyExpr.has('isUserIgnoredRecommendation').negate()),
order: 2
},
});
}
run(accessor: ServicesAccessor, id: string): Promise<any> {
return accessor.get(IWorkpsaceExtensionsConfigService).toggleRecommendation(id);
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.extensions.action.removeExtensionFromWorkspaceRecommendations',
title: { value: localize('workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', "Remove from Workspace Recommendations"), original: `Remove from Workspace Recommendations` },
menu: {
id: MenuId.ExtensionContext,
group: '3_recommendations',
when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended')),
order: 2
},
});
}
run(accessor: ServicesAccessor, id: string): Promise<any> {
return accessor.get(IWorkpsaceExtensionsConfigService).toggleRecommendation(id);
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.extensions.action.addToWorkspaceRecommendations',
title: { value: localize('workbench.extensions.action.addToWorkspaceRecommendations', "Add Extension to Workspace Recommendations"), original: `Add Extension to Workspace Recommendations` },
category: localize('extensions', "Extensions"),
menu: {
id: MenuId.CommandPalette,
when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)),
},
});
}
async run(accessor: ServicesAccessor): Promise<any> {
const editorService = accessor.get(IEditorService);
const workpsaceExtensionsConfigService = accessor.get(IWorkpsaceExtensionsConfigService);
if (!(editorService.activeEditor instanceof ExtensionsInput)) {
return;
}
const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase();
const recommendations = await workpsaceExtensionsConfigService.getRecommendations();
if (recommendations.includes(extensionId)) {
return;
}
await workpsaceExtensionsConfigService.toggleRecommendation(extensionId);
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.extensions.action.addToWorkspaceFolderRecommendations',
title: { value: localize('workbench.extensions.action.addToWorkspaceFolderRecommendations', "Add Extension to Workspace Folder Recommendations"), original: `Add Extension to Workspace Folder Recommendations` },
category: localize('extensions', "Extensions"),
menu: {
id: MenuId.CommandPalette,
when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)),
},
});
}
async run(accessor: ServicesAccessor): Promise<any> {
return accessor.get(ICommandService).executeCommand('workbench.extensions.action.addToWorkspaceRecommendations');
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.extensions.action.addToWorkspaceIgnoredRecommendations',
title: { value: localize('workbench.extensions.action.addToWorkspaceIgnoredRecommendations', "Add Extension to Workspace Ignored Recommendations"), original: `Add Extension to Workspace Ignored Recommendations` },
category: localize('extensions', "Extensions"),
menu: {
id: MenuId.CommandPalette,
when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)),
},
});
}
async run(accessor: ServicesAccessor): Promise<any> {
const editorService = accessor.get(IEditorService);
const workpsaceExtensionsConfigService = accessor.get(IWorkpsaceExtensionsConfigService);
if (!(editorService.activeEditor instanceof ExtensionsInput)) {
return;
}
const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase();
const unwatedRecommendations = await workpsaceExtensionsConfigService.getUnwantedRecommendations();
if (unwatedRecommendations.includes(extensionId)) {
return;
}
await workpsaceExtensionsConfigService.toggleUnwantedRecommendation(extensionId);
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations',
title: { value: localize('workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', "Add Extension to Workspace Folder Ignored Recommendations"), original: `Add Extension to Workspace Folder Ignored Recommendations` },
category: localize('extensions', "Extensions"),
menu: {
id: MenuId.CommandPalette,
when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)),
},
});
}
run(accessor: ServicesAccessor): Promise<any> {
return accessor.get(ICommandService).executeCommand('workbench.extensions.action.addToWorkspaceIgnoredRecommendations');
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: ConfigureWorkspaceRecommendedExtensionsAction.ID,
title: { value: ConfigureWorkspaceRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace)' },
category: localize('extensions', "Extensions"),
menu: {
id: MenuId.CommandPalette,
when: WorkbenchStateContext.isEqualTo('workspace'),
},
});
}
run(accessor: ServicesAccessor): Promise<any> {
return accessor.get(IInstantiationService).createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL).run();
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: ConfigureWorkspaceFolderRecommendedExtensionsAction.ID,
title: { value: ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace Folder)' },
category: localize('extensions', "Extensions"),
menu: {
id: MenuId.CommandPalette,
when: WorkbenchStateContext.notEqualsTo('empty'),
},
});
}
run(accessor: ServicesAccessor): Promise<any> {
return accessor.get(IInstantiationService).createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL).run();
}
});
}
}
@@ -936,9 +1171,12 @@ const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(Workbench
workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Starting);
workbenchRegistry.registerWorkbenchContribution(StatusUpdater, LifecyclePhase.Restored);
workbenchRegistry.registerWorkbenchContribution(MaliciousExtensionChecker, LifecyclePhase.Eventually);
workbenchRegistry.registerWorkbenchContribution(ConfigureRecommendedExtensionsCommandsContributor, LifecyclePhase.Eventually);
workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase.Restored);
workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Starting);
workbenchRegistry.registerWorkbenchContribution(ExtensionActivationProgress, LifecyclePhase.Eventually);
workbenchRegistry.registerWorkbenchContribution(ExtensionDependencyChecker, LifecyclePhase.Eventually);
workbenchRegistry.registerWorkbenchContribution(RemoteExtensionsInstaller, LifecyclePhase.Eventually);
// Running Extensions
const actionRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ShowRuntimeExtensionsAction), 'Show Running Extensions', CATEGORIES.Developer.value);

View File

@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { RuntimeExtensionsEditor } from 'vs/workbench/contrib/extensions/browser/browserRuntimeExtensionsEditor';
import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput';
// Running Extensions
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
EditorDescriptor.create(RuntimeExtensionsEditor, RuntimeExtensionsEditor.ID, localize('runtimeExtension', "Running Extensions")),
[new SyncDescriptor(RuntimeExtensionsInput)]
);

View File

@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Codicon } from 'vs/base/common/codicons';
import { localize } from 'vs/nls';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
export const extensionsViewIcon = registerIcon('extensions-view-icon', Codicon.extensions, localize('extensionsViewIcon', 'View icon of the extensions view.'));
export const manageExtensionIcon = registerIcon('extensions-manage', Codicon.gear, localize('manageExtensionIcon', 'Icon for the \'Manage\' action in the extensions view.'));
export const clearSearchResultsIcon = registerIcon('extensions-clear-search-results', Codicon.clearAll, localize('clearSearchResultsIcon', 'Icon for the \'Clear Search Result\' action in the extensions view.'));
export const refreshIcon = registerIcon('extensions-refresh', Codicon.refresh, localize('refreshIcon', 'Icon for the \'Refresh\' action in the extensions view.'));
export const filterIcon = registerIcon('extensions-filter', Codicon.filter, localize('filterIcon', 'Icon for the \'Filter\' action in the extensions view.'));
export const installLocalInRemoteIcon = registerIcon('extensions-install-local-in-remote', Codicon.cloudDownload, localize('installLocalInRemoteIcon', 'Icon for the \'Install Local Extension in Remote\' action in the extensions view.'));
export const installWorkspaceRecommendedIcon = registerIcon('extensions-install-workspace-recommended', Codicon.cloudDownload, localize('installWorkspaceRecommendedIcon', 'Icon for the \'Install Workspace Recommended Extensions\' action in the extensions view.'));
export const configureRecommendedIcon = registerIcon('extensions-configure-recommended', Codicon.pencil, localize('configureRecommendedIcon', 'Icon for the \'Configure Recommended Extensions\' action in the extensions view.'));
export const syncEnabledIcon = registerIcon('extensions-sync-enabled', Codicon.sync, localize('syncEnabledIcon', 'Icon to indicate that an extension is synced.'));
export const syncIgnoredIcon = registerIcon('extensions-sync-ignored', Codicon.syncIgnored, localize('syncIgnoredIcon', 'Icon to indicate that an extension is ignored when syncing.'));
export const remoteIcon = registerIcon('extensions-remote', Codicon.remote, localize('remoteIcon', 'Icon to indicate that an extension is remote in the extensions view and editor.'));
export const installCountIcon = registerIcon('extensions-install-count', Codicon.cloudDownload, localize('installCountIcon', 'Icon shown along with the install count in the extensions view and editor.'));
export const ratingIcon = registerIcon('extensions-rating', Codicon.star, localize('ratingIcon', 'Icon shown along with the rating in the extensions view and editor.'));
export const starFullIcon = registerIcon('extensions-star-full', Codicon.starFull, localize('starFullIcon', 'Full star icon used for the rating in the extensions editor.'));
export const starHalfIcon = registerIcon('extensions-star-half', Codicon.starHalf, localize('starHalfIcon', 'Half star icon used for the rating in the extensions editor.'));
export const starEmptyIcon = registerIcon('extensions-star-empty', Codicon.starEmpty, localize('starEmptyIcon', 'Empty star icon used for the rating in the extensions editor.'));
export const warningIcon = registerIcon('extensions-warning-message', Codicon.warning, localize('warningIcon', 'Icon shown with a warning message in the extensions editor.'));
export const infoIcon = registerIcon('extensions-info-message', Codicon.info, localize('infoIcon', 'Icon shown with an info message in the extensions editor.'));

Some files were not shown because too many files have changed in this diff Show More