From 5d60b7fdc4d1e22a951083ad1870153431265334 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 7 Nov 2019 11:31:01 +0100 Subject: [PATCH 001/246] wip: git fs provider --- extensions/git/src/commands.ts | 16 +- extensions/git/src/contentProvider.ts | 6 +- extensions/git/src/fileSystemProvider.ts | 193 +++++++++++++++++++++++ extensions/git/src/main.ts | 2 + extensions/git/src/repository.ts | 4 +- extensions/git/src/uri.ts | 14 +- 6 files changed, 219 insertions(+), 16 deletions(-) create mode 100644 extensions/git/src/fileSystemProvider.ts diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index e58f8c42292..8ba25c0009d 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -14,7 +14,7 @@ import { CommitOptions, ForcePushMode, Git, Stash } from './git'; import { Model } from './model'; import { Repository, Resource, ResourceGroupType } from './repository'; import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging'; -import { fromGitUri, toGitUri } from './uri'; +import { fromGitUri, toGitFSUri } from './uri'; import { grep, isDescendant, pathEquals } from './util'; const localize = nls.loadMessageBundle(); @@ -291,7 +291,7 @@ export class CommandCenter { const repository = this.model.getRepositoryForSubmodule(resource.resourceUri); if (repository) { - right = toGitUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: repository.root }); + right = toGitFSUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: repository.root }); } } else { if (resource.type !== Status.DELETED_BY_THEM) { @@ -334,7 +334,7 @@ export class CommandCenter { const repository = this.model.getRepository(uri); if (!repository) { - return toGitUri(uri, ref); + return toGitFSUri(uri, ref); } try { @@ -350,7 +350,7 @@ export class CommandCenter { const { mimetype } = await repository.detectObjectType(object); if (mimetype === 'text/plain') { - return toGitUri(uri, ref); + return toGitFSUri(uri, ref); } if (size > 1000000) { // 1 MB @@ -365,7 +365,7 @@ export class CommandCenter { return Uri.parse(`data:;label:${path.basename(uri.fsPath)};description:${gitRef},`); } catch (err) { - return toGitUri(uri, ref); + return toGitFSUri(uri, ref); } } @@ -1012,7 +1012,7 @@ export class CommandCenter { return; } - const originalUri = toGitUri(modifiedUri, '~'); + const originalUri = toGitFSUri(modifiedUri, '~'); const originalDocument = await workspace.openTextDocument(originalUri); const result = applyLineChanges(originalDocument, modifiedDocument, changes); @@ -1060,7 +1060,7 @@ export class CommandCenter { return; } - const originalUri = toGitUri(modifiedUri, '~'); + const originalUri = toGitFSUri(modifiedUri, '~'); const originalDocument = await workspace.openTextDocument(originalUri); const selectionsBeforeRevert = textEditor.selections; const visibleRangesBeforeRevert = textEditor.visibleRanges; @@ -1127,7 +1127,7 @@ export class CommandCenter { return; } - const originalUri = toGitUri(modifiedUri, 'HEAD'); + const originalUri = toGitFSUri(modifiedUri, 'HEAD'); const originalDocument = await workspace.openTextDocument(originalUri); const selectedLines = toLineRanges(textEditor.selections, modifiedDocument); const selectedDiffs = diffs diff --git a/extensions/git/src/contentProvider.ts b/extensions/git/src/contentProvider.ts index 1b7c08493ed..e6da7b50f87 100644 --- a/extensions/git/src/contentProvider.ts +++ b/extensions/git/src/contentProvider.ts @@ -5,7 +5,7 @@ import { workspace, Uri, Disposable, Event, EventEmitter, window } from 'vscode'; import { debounce, throttle } from './decorators'; -import { fromGitUri, toGitUri } from './uri'; +import { fromGitUri, toGitFSUri } from './uri'; import { Model, ModelChangeEvent, OriginalResourceChangeEvent } from './model'; import { filterEvent, eventToPromise, isDescendant, pathEquals } from './util'; @@ -50,7 +50,7 @@ export class GitContentProvider { return; } - this._onDidChange.fire(toGitUri(uri, '', { replaceFileExtension: true })); + this._onDidChange.fire(toGitFSUri(uri, '', { replaceFileExtension: true })); } @debounce(1100) @@ -147,4 +147,4 @@ export class GitContentProvider { dispose(): void { this.disposables.forEach(d => d.dispose()); } -} \ No newline at end of file +} diff --git a/extensions/git/src/fileSystemProvider.ts b/extensions/git/src/fileSystemProvider.ts new file mode 100644 index 00000000000..1042a8d966c --- /dev/null +++ b/extensions/git/src/fileSystemProvider.ts @@ -0,0 +1,193 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { workspace, Uri, Disposable, Event, EventEmitter, window, FileSystemProvider, FileChangeEvent, FileStat, FileType, FileChangeType, FileSystemError } from 'vscode'; +import { debounce, throttle } from './decorators'; +import { fromGitUri, toGitFSUri } from './uri'; +import { Model, ModelChangeEvent, OriginalResourceChangeEvent } from './model'; +import { filterEvent, eventToPromise, isDescendant, pathEquals } from './util'; + +interface CacheRow { + uri: Uri; + timestamp: number; +} + +const THREE_MINUTES = 1000 * 60 * 3; +const FIVE_MINUTES = 1000 * 60 * 5; + +export class GitFileSystemProvider implements FileSystemProvider { + + private _onDidChangeFile = new EventEmitter(); + readonly onDidChangeFile: Event = this._onDidChangeFile.event; + + private changedRepositoryRoots = new Set(); + private cache = new Map(); + private disposables: Disposable[] = []; + + constructor(private model: Model) { + this.disposables.push( + model.onDidChangeRepository(this.onDidChangeRepository, this), + model.onDidChangeOriginalResource(this.onDidChangeOriginalResource, this), + workspace.registerFileSystemProvider('gitfs', this, { isReadonly: true, isCaseSensitive: true }) + ); + + setInterval(() => this.cleanup(), FIVE_MINUTES); + } + + private onDidChangeRepository({ repository }: ModelChangeEvent): void { + this.changedRepositoryRoots.add(repository.root); + this.eventuallyFireChangeEvents(); + } + + private onDidChangeOriginalResource({ uri }: OriginalResourceChangeEvent): void { + if (uri.scheme !== 'file') { + return; + } + + const gitUri = toGitFSUri(uri, '', { replaceFileExtension: true }); + this._onDidChangeFile.fire([{ type: FileChangeType.Changed, uri: gitUri }]); + } + + @debounce(1100) + private eventuallyFireChangeEvents(): void { + this.fireChangeEvents(); + } + + @throttle + private async fireChangeEvents(): Promise { + if (!window.state.focused) { + const onDidFocusWindow = filterEvent(window.onDidChangeWindowState, e => e.focused); + await eventToPromise(onDidFocusWindow); + } + + const events: FileChangeEvent[] = []; + + for (const { uri } of this.cache.values()) { + const fsPath = uri.fsPath; + + for (const root of this.changedRepositoryRoots) { + if (isDescendant(root, fsPath)) { + events.push({ type: FileChangeType.Changed, uri }); + break; + } + } + } + + if (events.length > 0) { + this._onDidChangeFile.fire(events); + } + + this.changedRepositoryRoots.clear(); + } + + private cleanup(): void { + const now = new Date().getTime(); + const cache = new Map(); + + for (const row of this.cache.values()) { + const { path } = fromGitUri(row.uri); + const isOpen = workspace.textDocuments + .filter(d => d.uri.scheme === 'file') + .some(d => pathEquals(d.uri.fsPath, path)); + + if (isOpen || now - row.timestamp < THREE_MINUTES) { + cache.set(row.uri.toString(), row); + } else { + // TODO: should fire delete events? + } + } + + this.cache = cache; + } + + //#region File System Provider + + watch(): Disposable { + throw new Error('Method not implemented.'); + } + + stat(uri: Uri): FileStat { + const { submoduleOf } = fromGitUri(uri); + const repository = submoduleOf ? this.model.getRepository(submoduleOf) : this.model.getRepository(uri); + + if (!repository) { + throw FileSystemError.FileNotFound(); + } + + return { type: FileType.File, size: 0, mtime: 0, ctime: 0 }; + } + + readDirectory(): Thenable<[string, FileType][]> { + throw new Error('Method not implemented.'); + } + + createDirectory(): void { + throw new Error('Method not implemented.'); + } + + async readFile(uri: Uri): Promise { + let { path, ref, submoduleOf } = fromGitUri(uri); + + if (submoduleOf) { + const repository = this.model.getRepository(submoduleOf); + + if (!repository) { + throw FileSystemError.FileNotFound(); + } + + const encoder = new TextEncoder(); + + if (ref === 'index') { + return encoder.encode(await repository.diffIndexWithHEAD(path)); + } else { + return encoder.encode(await repository.diffWithHEAD(path)); + } + } + + const repository = this.model.getRepository(uri); + + if (!repository) { + throw FileSystemError.FileNotFound(); + } + + const timestamp = new Date().getTime(); + const cacheValue: CacheRow = { uri, timestamp }; + + this.cache.set(uri.toString(), cacheValue); + + if (ref === '~') { + const fileUri = Uri.file(path); + const uriString = fileUri.toString(); + const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); + ref = indexStatus ? '' : 'HEAD'; + } else if (/^~\d$/.test(ref)) { + ref = `:${ref[1]}`; + } + + try { + return await repository.buffer(ref, path); + } catch (err) { + return new Uint8Array(0); + } + } + + writeFile(): void { + throw new Error('Method not implemented.'); + } + + delete(): void { + throw new Error('Method not implemented.'); + } + + rename(): void { + throw new Error('Method not implemented.'); + } + + //#endregion File System Provider + + dispose(): void { + this.disposables.forEach(d => d.dispose()); + } +} diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 52b7c3c106a..74b26975b20 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -11,6 +11,7 @@ import { findGit, Git, IGit } from './git'; import { Model } from './model'; import { CommandCenter } from './commands'; import { GitContentProvider } from './contentProvider'; +import { GitFileSystemProvider } from './fileSystemProvider'; import { GitDecorations } from './decorationProvider'; import { Askpass } from './askpass'; import { toDisposable, filterEvent, eventToPromise } from './util'; @@ -62,6 +63,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann disposables.push( new CommandCenter(git, model, outputChannel, telemetryReporter), new GitContentProvider(model), + new GitFileSystemProvider(model), new GitDecorations(model), new GitProtocolHandler() ); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 949c97bb8a3..825b7c486cb 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -12,7 +12,7 @@ import { AutoFetcher } from './autofetch'; import { debounce, memoize, throttle } from './decorators'; import { Commit, CommitOptions, ForcePushMode, GitError, Repository as BaseRepository, Stash, Submodule } from './git'; import { StatusBarCommands } from './statusbar'; -import { toGitUri } from './uri'; +import { toGitFSUri } from './uri'; import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent } from './util'; import { IFileWatcher, watch } from './watch'; @@ -834,7 +834,7 @@ export class Repository implements Disposable { return; } - return toGitUri(uri, '', { replaceFileExtension: true }); + return toGitFSUri(uri, '', { replaceFileExtension: true }); } async getInputTemplate(): Promise { diff --git a/extensions/git/src/uri.ts b/extensions/git/src/uri.ts index 2eb21adfd6f..5a808fecc0e 100644 --- a/extensions/git/src/uri.ts +++ b/extensions/git/src/uri.ts @@ -23,7 +23,7 @@ export interface GitUriOptions { // As a mitigation for extensions like ESLint showing warnings and errors // for git URIs, let's change the file extension of these uris to .git, // when `replaceFileExtension` is true. -export function toGitUri(uri: Uri, ref: string, options: GitUriOptions = {}): Uri { +function _toGitUri(scheme: string, uri: Uri, ref: string, options: GitUriOptions = {}): Uri { const params: GitUriParams = { path: uri.fsPath, ref @@ -42,8 +42,16 @@ export function toGitUri(uri: Uri, ref: string, options: GitUriOptions = {}): Ur } return uri.with({ - scheme: 'git', + scheme, path, query: JSON.stringify(params) }); -} \ No newline at end of file +} + +export function toGitUri(uri: Uri, ref: string, options: GitUriOptions = {}): Uri { + return _toGitUri('git', uri, ref, options); +} + +export function toGitFSUri(uri: Uri, ref: string, options: GitUriOptions = {}): Uri { + return _toGitUri('gitfs', uri, ref, options); +} From 15bfc4022232afb4de238334b02e03919dcd73ea Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 7 Nov 2019 14:05:21 +0100 Subject: [PATCH 002/246] smaller footprint --- extensions/git/src/commands.ts | 16 ++++++++-------- extensions/git/src/contentProvider.ts | 4 ++-- extensions/git/src/fileSystemProvider.ts | 12 ++++-------- extensions/git/src/repository.ts | 4 ++-- extensions/git/src/uri.ts | 12 ++---------- 5 files changed, 18 insertions(+), 30 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 8ba25c0009d..e58f8c42292 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -14,7 +14,7 @@ import { CommitOptions, ForcePushMode, Git, Stash } from './git'; import { Model } from './model'; import { Repository, Resource, ResourceGroupType } from './repository'; import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging'; -import { fromGitUri, toGitFSUri } from './uri'; +import { fromGitUri, toGitUri } from './uri'; import { grep, isDescendant, pathEquals } from './util'; const localize = nls.loadMessageBundle(); @@ -291,7 +291,7 @@ export class CommandCenter { const repository = this.model.getRepositoryForSubmodule(resource.resourceUri); if (repository) { - right = toGitFSUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: repository.root }); + right = toGitUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: repository.root }); } } else { if (resource.type !== Status.DELETED_BY_THEM) { @@ -334,7 +334,7 @@ export class CommandCenter { const repository = this.model.getRepository(uri); if (!repository) { - return toGitFSUri(uri, ref); + return toGitUri(uri, ref); } try { @@ -350,7 +350,7 @@ export class CommandCenter { const { mimetype } = await repository.detectObjectType(object); if (mimetype === 'text/plain') { - return toGitFSUri(uri, ref); + return toGitUri(uri, ref); } if (size > 1000000) { // 1 MB @@ -365,7 +365,7 @@ export class CommandCenter { return Uri.parse(`data:;label:${path.basename(uri.fsPath)};description:${gitRef},`); } catch (err) { - return toGitFSUri(uri, ref); + return toGitUri(uri, ref); } } @@ -1012,7 +1012,7 @@ export class CommandCenter { return; } - const originalUri = toGitFSUri(modifiedUri, '~'); + const originalUri = toGitUri(modifiedUri, '~'); const originalDocument = await workspace.openTextDocument(originalUri); const result = applyLineChanges(originalDocument, modifiedDocument, changes); @@ -1060,7 +1060,7 @@ export class CommandCenter { return; } - const originalUri = toGitFSUri(modifiedUri, '~'); + const originalUri = toGitUri(modifiedUri, '~'); const originalDocument = await workspace.openTextDocument(originalUri); const selectionsBeforeRevert = textEditor.selections; const visibleRangesBeforeRevert = textEditor.visibleRanges; @@ -1127,7 +1127,7 @@ export class CommandCenter { return; } - const originalUri = toGitFSUri(modifiedUri, 'HEAD'); + const originalUri = toGitUri(modifiedUri, 'HEAD'); const originalDocument = await workspace.openTextDocument(originalUri); const selectedLines = toLineRanges(textEditor.selections, modifiedDocument); const selectedDiffs = diffs diff --git a/extensions/git/src/contentProvider.ts b/extensions/git/src/contentProvider.ts index e6da7b50f87..ad380e70ecf 100644 --- a/extensions/git/src/contentProvider.ts +++ b/extensions/git/src/contentProvider.ts @@ -5,7 +5,7 @@ import { workspace, Uri, Disposable, Event, EventEmitter, window } from 'vscode'; import { debounce, throttle } from './decorators'; -import { fromGitUri, toGitFSUri } from './uri'; +import { fromGitUri, toGitUri } from './uri'; import { Model, ModelChangeEvent, OriginalResourceChangeEvent } from './model'; import { filterEvent, eventToPromise, isDescendant, pathEquals } from './util'; @@ -50,7 +50,7 @@ export class GitContentProvider { return; } - this._onDidChange.fire(toGitFSUri(uri, '', { replaceFileExtension: true })); + this._onDidChange.fire(toGitUri(uri, '', { replaceFileExtension: true })); } @debounce(1100) diff --git a/extensions/git/src/fileSystemProvider.ts b/extensions/git/src/fileSystemProvider.ts index 1042a8d966c..131bb531267 100644 --- a/extensions/git/src/fileSystemProvider.ts +++ b/extensions/git/src/fileSystemProvider.ts @@ -5,9 +5,9 @@ import { workspace, Uri, Disposable, Event, EventEmitter, window, FileSystemProvider, FileChangeEvent, FileStat, FileType, FileChangeType, FileSystemError } from 'vscode'; import { debounce, throttle } from './decorators'; -import { fromGitUri, toGitFSUri } from './uri'; +import { fromGitUri, toGitUri } from './uri'; import { Model, ModelChangeEvent, OriginalResourceChangeEvent } from './model'; -import { filterEvent, eventToPromise, isDescendant, pathEquals } from './util'; +import { filterEvent, eventToPromise, isDescendant, pathEquals, EmptyDisposable } from './util'; interface CacheRow { uri: Uri; @@ -46,7 +46,7 @@ export class GitFileSystemProvider implements FileSystemProvider { return; } - const gitUri = toGitFSUri(uri, '', { replaceFileExtension: true }); + const gitUri = toGitUri(uri, '', { replaceFileExtension: true }); this._onDidChangeFile.fire([{ type: FileChangeType.Changed, uri: gitUri }]); } @@ -102,10 +102,8 @@ export class GitFileSystemProvider implements FileSystemProvider { this.cache = cache; } - //#region File System Provider - watch(): Disposable { - throw new Error('Method not implemented.'); + return EmptyDisposable; } stat(uri: Uri): FileStat { @@ -185,8 +183,6 @@ export class GitFileSystemProvider implements FileSystemProvider { throw new Error('Method not implemented.'); } - //#endregion File System Provider - dispose(): void { this.disposables.forEach(d => d.dispose()); } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 825b7c486cb..949c97bb8a3 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -12,7 +12,7 @@ import { AutoFetcher } from './autofetch'; import { debounce, memoize, throttle } from './decorators'; import { Commit, CommitOptions, ForcePushMode, GitError, Repository as BaseRepository, Stash, Submodule } from './git'; import { StatusBarCommands } from './statusbar'; -import { toGitFSUri } from './uri'; +import { toGitUri } from './uri'; import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent } from './util'; import { IFileWatcher, watch } from './watch'; @@ -834,7 +834,7 @@ export class Repository implements Disposable { return; } - return toGitFSUri(uri, '', { replaceFileExtension: true }); + return toGitUri(uri, '', { replaceFileExtension: true }); } async getInputTemplate(): Promise { diff --git a/extensions/git/src/uri.ts b/extensions/git/src/uri.ts index 5a808fecc0e..9b40f428853 100644 --- a/extensions/git/src/uri.ts +++ b/extensions/git/src/uri.ts @@ -23,7 +23,7 @@ export interface GitUriOptions { // As a mitigation for extensions like ESLint showing warnings and errors // for git URIs, let's change the file extension of these uris to .git, // when `replaceFileExtension` is true. -function _toGitUri(scheme: string, uri: Uri, ref: string, options: GitUriOptions = {}): Uri { +export function toGitUri(uri: Uri, ref: string, options: GitUriOptions = {}): Uri { const params: GitUriParams = { path: uri.fsPath, ref @@ -42,16 +42,8 @@ function _toGitUri(scheme: string, uri: Uri, ref: string, options: GitUriOptions } return uri.with({ - scheme, + scheme: 'gitfs', path, query: JSON.stringify(params) }); } - -export function toGitUri(uri: Uri, ref: string, options: GitUriOptions = {}): Uri { - return _toGitUri('git', uri, ref, options); -} - -export function toGitFSUri(uri: Uri, ref: string, options: GitUriOptions = {}): Uri { - return _toGitUri('gitfs', uri, ref, options); -} From c60c6c1c57e746bc46c28b335340ae8007e9f475 Mon Sep 17 00:00:00 2001 From: Konstantin Solomatov Date: Thu, 7 Nov 2019 17:41:31 -0800 Subject: [PATCH 003/246] Fix bug with hanged pseudoterminal --- src/vs/workbench/api/common/extHostTerminalService.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 40ecebf643b..3c7effe75cd 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -456,14 +456,18 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ // Processes should be initialized here for normal virtual process terminals, however for // tasks they are responsible for attaching the virtual process to a terminal so this // function may be called before tasks is able to attach to the terminal. - let retries = 5; + let retries = 8; + let currentTimeout = 50; while (retries-- > 0) { if (this._terminalProcesses[id]) { (this._terminalProcesses[id] as ExtHostPseudoterminal).startSendingEvents(initialDimensions); return; } - await timeout(50); + await timeout(currentTimeout); + currentTimeout *= 2; } + + throw new Error('Wasn\'t able to start extension terminal'); } protected _setupExtHostProcessListeners(id: number, p: ITerminalChildProcess): void { From bde2d42430da6ce46ac2b5cefb4aeca728df2abb Mon Sep 17 00:00:00 2001 From: Konstantin Solomatov Date: Fri, 8 Nov 2019 10:12:41 -0800 Subject: [PATCH 004/246] Code review comments --- .../api/common/extHostTerminalService.ts | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 3c7effe75cd..60e3badcb10 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -271,6 +271,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ protected _activeTerminal: ExtHostTerminal | undefined; protected _terminals: ExtHostTerminal[] = []; protected _terminalProcesses: { [id: number]: ITerminalChildProcess } = {}; + protected _initialDimensions: { [id: number]: ITerminalDimensionsDto } = {}; protected _getTerminalPromises: { [id: number]: Promise } = {}; public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; } @@ -453,21 +454,9 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ } await openPromise; - // Processes should be initialized here for normal virtual process terminals, however for - // tasks they are responsible for attaching the virtual process to a terminal so this - // function may be called before tasks is able to attach to the terminal. - let retries = 8; - let currentTimeout = 50; - while (retries-- > 0) { - if (this._terminalProcesses[id]) { - (this._terminalProcesses[id] as ExtHostPseudoterminal).startSendingEvents(initialDimensions); - return; - } - await timeout(currentTimeout); - currentTimeout *= 2; + if (initialDimensions) { + this._initialDimensions[id] = initialDimensions; } - - throw new Error('Wasn\'t able to start extension terminal'); } protected _setupExtHostProcessListeners(id: number, p: ITerminalChildProcess): void { @@ -482,6 +471,12 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ p.onProcessOverrideDimensions(e => this._proxy.$sendOverrideDimensions(id, e)); } this._terminalProcesses[id] = p; + + + if (p instanceof ExtHostPseudoterminal) { + p.startSendingEvents(this._initialDimensions[id]); + delete this._initialDimensions[id]; + } } public $acceptProcessInput(id: number, data: string): void { From ed52435fe5d878fbccada2e4998fc89e9865d7de Mon Sep 17 00:00:00 2001 From: Konstantin Solomatov Date: Fri, 8 Nov 2019 11:29:09 -0800 Subject: [PATCH 005/246] Fixing tests --- src/vs/workbench/api/common/extHostTerminalService.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 60e3badcb10..addaa879b71 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -454,9 +454,14 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ } await openPromise; - if (initialDimensions) { - this._initialDimensions[id] = initialDimensions; + if (this._terminalProcesses[id]) { + (this._terminalProcesses[id] as ExtHostPseudoterminal).startSendingEvents(initialDimensions); + } else { + if (initialDimensions) { + this._initialDimensions[id] = initialDimensions; + } } + } protected _setupExtHostProcessListeners(id: number, p: ITerminalChildProcess): void { @@ -472,7 +477,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ } this._terminalProcesses[id] = p; - if (p instanceof ExtHostPseudoterminal) { p.startSendingEvents(this._initialDimensions[id]); delete this._initialDimensions[id]; From 794ed4ff1c0d4e538749511964e89e5ece060c9b Mon Sep 17 00:00:00 2001 From: Konstantin Solomatov Date: Fri, 8 Nov 2019 11:55:23 -0800 Subject: [PATCH 006/246] Fixed test --- src/vs/workbench/api/common/extHostTerminalService.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index addaa879b71..69c102e518f 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -272,6 +272,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ protected _terminals: ExtHostTerminal[] = []; protected _terminalProcesses: { [id: number]: ITerminalChildProcess } = {}; protected _initialDimensions: { [id: number]: ITerminalDimensionsDto } = {}; + protected _startedExtensionTerminal: { [id: number]: boolean } = {}; protected _getTerminalPromises: { [id: number]: Promise } = {}; public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; } @@ -432,6 +433,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise { // Make sure the ExtHostTerminal exists so onDidOpenTerminal has fired before we call // Pseudoterminal.start + const terminal = await this._getTerminalByIdEventually(id); if (!terminal) { return; @@ -454,6 +456,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ } await openPromise; + this._startedExtensionTerminal[id] = true; if (this._terminalProcesses[id]) { (this._terminalProcesses[id] as ExtHostPseudoterminal).startSendingEvents(initialDimensions); } else { @@ -477,7 +480,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ } this._terminalProcesses[id] = p; - if (p instanceof ExtHostPseudoterminal) { + if (p instanceof ExtHostPseudoterminal && this._startedExtensionTerminal[id]) { p.startSendingEvents(this._initialDimensions[id]); delete this._initialDimensions[id]; } @@ -519,6 +522,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ // Remove process reference delete this._terminalProcesses[id]; + delete this._startedExtensionTerminal[id]; // Send exit event to main side this._proxy.$sendProcessExit(id, exitCode); From 9f2592e2614d8427eb97363632c47dd9789ad1ac Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 14 Nov 2019 10:31:18 +0100 Subject: [PATCH 007/246] gitfs: fix mtime --- extensions/git/src/fileSystemProvider.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/fileSystemProvider.ts b/extensions/git/src/fileSystemProvider.ts index 131bb531267..55ea6867fa9 100644 --- a/extensions/git/src/fileSystemProvider.ts +++ b/extensions/git/src/fileSystemProvider.ts @@ -24,6 +24,7 @@ export class GitFileSystemProvider implements FileSystemProvider { private changedRepositoryRoots = new Set(); private cache = new Map(); + private mtime = new Date().getTime(); private disposables: Disposable[] = []; constructor(private model: Model) { @@ -47,6 +48,7 @@ export class GitFileSystemProvider implements FileSystemProvider { } const gitUri = toGitUri(uri, '', { replaceFileExtension: true }); + this.mtime = new Date().getTime(); this._onDidChangeFile.fire([{ type: FileChangeType.Changed, uri: gitUri }]); } @@ -76,6 +78,7 @@ export class GitFileSystemProvider implements FileSystemProvider { } if (events.length > 0) { + this.mtime = new Date().getTime(); this._onDidChangeFile.fire(events); } @@ -114,7 +117,7 @@ export class GitFileSystemProvider implements FileSystemProvider { throw FileSystemError.FileNotFound(); } - return { type: FileType.File, size: 0, mtime: 0, ctime: 0 }; + return { type: FileType.File, size: 0, mtime: this.mtime, ctime: 0 }; } readDirectory(): Thenable<[string, FileType][]> { From 6d7f763548b1e161cf12727cb637bd25423ffcdd Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 17 Nov 2019 12:52:17 -0800 Subject: [PATCH 008/246] Only set _extensionTerminalAwaitingStart when deferred start is needed --- .../api/common/extHostTerminalService.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 94b1782e588..b26797bdef4 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -288,8 +288,8 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ protected _activeTerminal: ExtHostTerminal | undefined; protected _terminals: ExtHostTerminal[] = []; protected _terminalProcesses: { [id: number]: ITerminalChildProcess } = {}; - protected _initialDimensions: { [id: number]: ITerminalDimensionsDto } = {}; - protected _startedExtensionTerminal: { [id: number]: boolean } = {}; + protected _initialDimensions: { [id: number]: ITerminalDimensionsDto | undefined } = {}; + protected _extensionTerminalAwaitingStart: { [id: number]: boolean | undefined } = {}; protected _getTerminalPromises: { [id: number]: Promise } = {}; public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; } @@ -465,13 +465,12 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ } await openPromise; - this._startedExtensionTerminal[id] = true; if (this._terminalProcesses[id]) { (this._terminalProcesses[id] as ExtHostPseudoterminal).startSendingEvents(initialDimensions); } else { - if (initialDimensions) { - this._initialDimensions[id] = initialDimensions; - } + // Defer startSendingEvents call to when _setupExtHostProcessListeners is called + this._extensionTerminalAwaitingStart[id] = true; + this._initialDimensions[id] = initialDimensions; } } @@ -489,7 +488,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ } this._terminalProcesses[id] = p; - if (p instanceof ExtHostPseudoterminal && this._startedExtensionTerminal[id]) { + if (this._extensionTerminalAwaitingStart[id] && p instanceof ExtHostPseudoterminal) { p.startSendingEvents(this._initialDimensions[id]); delete this._initialDimensions[id]; } @@ -531,7 +530,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ // Remove process reference delete this._terminalProcesses[id]; - delete this._startedExtensionTerminal[id]; + delete this._extensionTerminalAwaitingStart[id]; // Send exit event to main side this._proxy.$sendProcessExit(id, exitCode); From f329ef375319595ef6a9468784a2cce0ffcfb601 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 17 Nov 2019 12:54:38 -0800 Subject: [PATCH 009/246] Merge dimensions into awaiting object --- .../workbench/api/common/extHostTerminalService.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index b26797bdef4..02589872e0a 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -288,8 +288,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ protected _activeTerminal: ExtHostTerminal | undefined; protected _terminals: ExtHostTerminal[] = []; protected _terminalProcesses: { [id: number]: ITerminalChildProcess } = {}; - protected _initialDimensions: { [id: number]: ITerminalDimensionsDto | undefined } = {}; - protected _extensionTerminalAwaitingStart: { [id: number]: boolean | undefined } = {}; + protected _extensionTerminalAwaitingStart: { [id: number]: { initialDimensions: ITerminalDimensionsDto | undefined } | undefined } = {}; protected _getTerminalPromises: { [id: number]: Promise } = {}; public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; } @@ -469,8 +468,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ (this._terminalProcesses[id] as ExtHostPseudoterminal).startSendingEvents(initialDimensions); } else { // Defer startSendingEvents call to when _setupExtHostProcessListeners is called - this._extensionTerminalAwaitingStart[id] = true; - this._initialDimensions[id] = initialDimensions; + this._extensionTerminalAwaitingStart[id] = { initialDimensions }; } } @@ -488,9 +486,10 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ } this._terminalProcesses[id] = p; - if (this._extensionTerminalAwaitingStart[id] && p instanceof ExtHostPseudoterminal) { - p.startSendingEvents(this._initialDimensions[id]); - delete this._initialDimensions[id]; + const awaitingStart = this._extensionTerminalAwaitingStart[id]; + if (awaitingStart && p instanceof ExtHostPseudoterminal) { + p.startSendingEvents(awaitingStart.initialDimensions); + delete this._extensionTerminalAwaitingStart[id]; } } From 649ef05dfb1a7914f87b455e4308022686946271 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 17 Nov 2019 12:55:10 -0800 Subject: [PATCH 010/246] Fix ws --- src/vs/workbench/api/common/extHostTerminalService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 02589872e0a..43e4f6bf753 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -441,7 +441,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise { // Make sure the ExtHostTerminal exists so onDidOpenTerminal has fired before we call // Pseudoterminal.start - const terminal = await this._getTerminalByIdEventually(id); if (!terminal) { return; From a1bb403adff7a90bd7bbbb18db1fe301d7fc7b93 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 18 Nov 2019 09:47:50 -0800 Subject: [PATCH 011/246] Update editor action icons to use icon font --- .../parts/editor/editor.contribution.ts | 49 ++++++------------- .../parts/editor/media/close-all-dark.svg | 4 -- .../parts/editor/media/close-all-light.svg | 4 -- .../parts/editor/media/close-dark-alt.svg | 3 -- .../editor/media/close-dirty-dark-alt.svg | 3 -- .../editor/media/close-dirty-light-alt.svg | 3 -- .../parts/editor/media/close-light-alt.svg | 3 -- .../parts/editor/media/next-diff-dark.svg | 3 -- .../parts/editor/media/next-diff-light.svg | 3 -- .../parts/editor/media/paragraph-dark.svg | 3 -- .../editor/media/paragraph-disabled-dark.svg | 5 -- .../editor/media/paragraph-disabled-light.svg | 5 -- .../parts/editor/media/paragraph-light.svg | 3 -- .../parts/editor/media/previous-diff-dark.svg | 3 -- .../editor/media/previous-diff-light.svg | 3 -- .../media/split-editor-horizontal-dark.svg | 3 -- .../media/split-editor-horizontal-hc.svg | 3 -- .../media/split-editor-horizontal-light.svg | 7 --- .../media/split-editor-vertical-dark.svg | 3 -- .../editor/media/split-editor-vertical-hc.svg | 3 -- .../media/split-editor-vertical-light.svg | 7 --- .../parts/editor/media/titlecontrol.css | 10 +++- 22 files changed, 23 insertions(+), 110 deletions(-) delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-all-dark.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-all-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-dark-alt.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-dirty-dark-alt.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-dirty-light-alt.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-light-alt.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/next-diff-dark.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/next-diff-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/paragraph-dark.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/paragraph-disabled-dark.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/paragraph-disabled-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/paragraph-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/previous-diff-dark.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/previous-diff-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-dark.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-hc.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-vertical-dark.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-vertical-hc.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-vertical-light.svg diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 7dee5c30a3a..db6df41d3ca 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -52,7 +52,6 @@ import { toLocalResource } from 'vs/base/common/resources'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; // Register String Editor @@ -452,13 +451,14 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands. MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20, when: ContextKeyExpr.has('config.workbench.editor.showTabs') }); -interface IEditorToolItem { id: string; title: string; iconDark: URI; iconLight: URI; } +interface IEditorToolItem { id: string; title: string; iconDark?: URI; iconLight?: URI; iconClassName?: string; } function appendEditorToolItem(primary: IEditorToolItem, when: ContextKeyExpr | undefined, order: number, alternative?: IEditorToolItem): void { const item: IMenuItem = { command: { id: primary.id, title: primary.title, + iconClassName: primary.iconClassName, iconLocation: { dark: primary.iconDark, light: primary.iconLight @@ -473,6 +473,7 @@ function appendEditorToolItem(primary: IEditorToolItem, when: ContextKeyExpr | u item.alt = { id: alternative.id, title: alternative.title, + iconClassName: alternative.iconClassName, iconLocation: { dark: alternative.iconDark, light: alternative.iconLight @@ -483,26 +484,19 @@ function appendEditorToolItem(primary: IEditorToolItem, when: ContextKeyExpr | u MenuRegistry.appendMenuItem(MenuId.EditorTitle, item); } -const SPLIT_EDITOR_HORIZONTAL_DARK_ICON = URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/split-editor-horizontal-dark.svg')); -const SPLIT_EDITOR_HORIZONTAL_LIGHT_ICON = URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/split-editor-horizontal-light.svg')); -const SPLIT_EDITOR_VERTICAL_DARK_ICON = URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/split-editor-vertical-dark.svg')); -const SPLIT_EDITOR_VERTICAL_LIGHT_ICON = URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/split-editor-vertical-light.svg')); - // Editor Title Menu: Split Editor appendEditorToolItem( { id: SplitEditorAction.ID, title: nls.localize('splitEditorRight', "Split Editor Right"), - iconDark: SPLIT_EDITOR_HORIZONTAL_DARK_ICON, - iconLight: SPLIT_EDITOR_HORIZONTAL_LIGHT_ICON + iconClassName: 'codicon-split-horizontal' }, ContextKeyExpr.not('splitEditorsVertically'), 100000, // towards the end { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitEditorDown', "Split Editor Down"), - iconDark: SPLIT_EDITOR_VERTICAL_DARK_ICON, - iconLight: SPLIT_EDITOR_VERTICAL_LIGHT_ICON + iconClassName: 'codicon-split-vertical' } ); @@ -510,37 +504,30 @@ appendEditorToolItem( { id: SplitEditorAction.ID, title: nls.localize('splitEditorDown', "Split Editor Down"), - iconDark: SPLIT_EDITOR_VERTICAL_DARK_ICON, - iconLight: SPLIT_EDITOR_VERTICAL_LIGHT_ICON + iconClassName: 'codicon-split-vertical' }, ContextKeyExpr.has('splitEditorsVertically'), 100000, // towards the end { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitEditorRight', "Split Editor Right"), - iconDark: SPLIT_EDITOR_HORIZONTAL_DARK_ICON, - iconLight: SPLIT_EDITOR_HORIZONTAL_LIGHT_ICON + iconClassName: 'codicon-split-horizontal' } ); -const CLOSE_ALL_DARK_ICON = URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-all-dark.svg')); -const CLOSE_ALL_LIGHT_ICON = URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-all-light.svg')); - // Editor Title Menu: Close Group (tabs disabled) appendEditorToolItem( { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close"), - iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-dark-alt.svg')), - iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-light-alt.svg')) + iconClassName: 'codicon-close' }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.not('groupActiveEditorDirty')), 1000000, // towards the far end { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All"), - iconDark: CLOSE_ALL_DARK_ICON, - iconLight: CLOSE_ALL_LIGHT_ICON + iconClassName: 'codicon-close-all' } ); @@ -548,16 +535,14 @@ appendEditorToolItem( { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close"), - iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-dirty-dark-alt.svg')), - iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/close-dirty-light-alt.svg')) + iconClassName: 'codicon-close-dirty' }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.has('groupActiveEditorDirty')), 1000000, // towards the far end { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All"), - iconDark: CLOSE_ALL_DARK_ICON, - iconLight: CLOSE_ALL_LIGHT_ICON + iconClassName: 'codicon-close-all' } ); @@ -566,8 +551,7 @@ appendEditorToolItem( { id: editorCommands.GOTO_PREVIOUS_CHANGE, title: nls.localize('navigate.prev.label', "Previous Change"), - iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/previous-diff-dark.svg')), - iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/previous-diff-light.svg')) + iconClassName: 'codicon-arrow-up' }, TextCompareEditorActiveContext, 10 @@ -578,8 +562,7 @@ appendEditorToolItem( { id: editorCommands.GOTO_NEXT_CHANGE, title: nls.localize('navigate.next.label', "Next Change"), - iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/next-diff-dark.svg')), - iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/next-diff-light.svg')) + iconClassName: 'codicon-arrow-down' }, TextCompareEditorActiveContext, 11 @@ -590,8 +573,7 @@ appendEditorToolItem( { id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, title: nls.localize('ignoreTrimWhitespace.label', "Ignore Leading/Trailing Whitespace Differences"), - iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/paragraph-dark.svg')), - iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/paragraph-light.svg')) + iconClassName: 'codicon-whitespace' }, ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', true)), 20 @@ -602,8 +584,7 @@ appendEditorToolItem( { id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, title: nls.localize('showTrimWhitespace.label', "Show Leading/Trailing Whitespace Differences"), - iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/paragraph-disabled-dark.svg')), - iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/paragraph-disabled-light.svg')) + iconClassName: 'codicon-whitespace disabled' }, ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', false)), 20 diff --git a/src/vs/workbench/browser/parts/editor/media/close-all-dark.svg b/src/vs/workbench/browser/parts/editor/media/close-all-dark.svg deleted file mode 100644 index d69cc9c8351..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/close-all-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/browser/parts/editor/media/close-all-light.svg b/src/vs/workbench/browser/parts/editor/media/close-all-light.svg deleted file mode 100644 index 9fcf77fe72e..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/close-all-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/browser/parts/editor/media/close-dark-alt.svg b/src/vs/workbench/browser/parts/editor/media/close-dark-alt.svg deleted file mode 100644 index 44ece771f45..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/close-dark-alt.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/close-dirty-dark-alt.svg b/src/vs/workbench/browser/parts/editor/media/close-dirty-dark-alt.svg deleted file mode 100644 index 51946be5bb7..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/close-dirty-dark-alt.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/close-dirty-light-alt.svg b/src/vs/workbench/browser/parts/editor/media/close-dirty-light-alt.svg deleted file mode 100644 index fb91225b968..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/close-dirty-light-alt.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/close-light-alt.svg b/src/vs/workbench/browser/parts/editor/media/close-light-alt.svg deleted file mode 100644 index 742fcae4ae7..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/close-light-alt.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/next-diff-dark.svg b/src/vs/workbench/browser/parts/editor/media/next-diff-dark.svg deleted file mode 100644 index 455532ddb7a..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/next-diff-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/next-diff-light.svg b/src/vs/workbench/browser/parts/editor/media/next-diff-light.svg deleted file mode 100644 index a443086f358..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/next-diff-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/paragraph-dark.svg b/src/vs/workbench/browser/parts/editor/media/paragraph-dark.svg deleted file mode 100644 index 24708ab31f9..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/paragraph-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/paragraph-disabled-dark.svg b/src/vs/workbench/browser/parts/editor/media/paragraph-disabled-dark.svg deleted file mode 100644 index 3c174668fdc..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/paragraph-disabled-dark.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/vs/workbench/browser/parts/editor/media/paragraph-disabled-light.svg b/src/vs/workbench/browser/parts/editor/media/paragraph-disabled-light.svg deleted file mode 100644 index 5edb9e63eb7..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/paragraph-disabled-light.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/vs/workbench/browser/parts/editor/media/paragraph-light.svg b/src/vs/workbench/browser/parts/editor/media/paragraph-light.svg deleted file mode 100644 index 734fe619208..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/paragraph-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/previous-diff-dark.svg b/src/vs/workbench/browser/parts/editor/media/previous-diff-dark.svg deleted file mode 100644 index 5ca3526019f..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/previous-diff-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/previous-diff-light.svg b/src/vs/workbench/browser/parts/editor/media/previous-diff-light.svg deleted file mode 100644 index 87e179a7f3c..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/previous-diff-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-dark.svg b/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-dark.svg deleted file mode 100644 index 8c22a7c5bfe..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-hc.svg b/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-hc.svg deleted file mode 100644 index 82c19d0c8fc..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-light.svg b/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-light.svg deleted file mode 100644 index 400952cdda8..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-light.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/browser/parts/editor/media/split-editor-vertical-dark.svg b/src/vs/workbench/browser/parts/editor/media/split-editor-vertical-dark.svg deleted file mode 100644 index 419c21be4f6..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/split-editor-vertical-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/split-editor-vertical-hc.svg b/src/vs/workbench/browser/parts/editor/media/split-editor-vertical-hc.svg deleted file mode 100644 index 7565fd3c168..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/split-editor-vertical-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/media/split-editor-vertical-light.svg b/src/vs/workbench/browser/parts/editor/media/split-editor-vertical-light.svg deleted file mode 100644 index 405291c14dd..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/split-editor-vertical-light.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index 2c77ffb461d..9d825abeb16 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -30,10 +30,11 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions .action-label, .monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-label { - display: block; + display: flex; height: 35px; - line-height: 35px; min-width: 28px; + align-items: center; + justify-content: center; background-size: 16px; background-position: center center; background-repeat: no-repeat; @@ -53,6 +54,11 @@ color: inherit; } +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions .action-label.disabled, +.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-label.disabled { + opacity: 0.4; +} + /* Drag Cursor */ .monaco-workbench .part.editor > .content .editor-group-container > .title { cursor: grab; From a406fd6b07604eabc718666ec7d42f6360307b93 Mon Sep 17 00:00:00 2001 From: OneQuid Date: Tue, 19 Nov 2019 03:47:36 +0800 Subject: [PATCH 012/246] Fixes webview responding shortcuts twice on linux (#84967) --- .../webview/browser/baseWebviewElement.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts index 466e046411a..892944dadea 100644 --- a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts @@ -14,6 +14,7 @@ import { URI } from 'vs/base/common/uri'; import { areWebviewInputOptionsEqual } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { isLinux } from 'vs/base/common/platform'; export const enum WebviewMessageChannels { onmessage = 'onmessage', @@ -122,12 +123,15 @@ export abstract class BaseWebview extends Disposable { this.handleFocusChange(false); })); - this._register(this.on('did-keydown', (data: KeyboardEvent) => { - // Electron: workaround for https://github.com/electron/electron/issues/14258 - // We have to detect keyboard events in the and dispatch them to our - // keybinding service because these events do not bubble to the parent window anymore. - this.handleKeyDown(data); - })); + if (!isLinux) { + // Fixes #82670 webview responding shortcuts twice on linux. + this._register(this.on('did-keydown', (data: KeyboardEvent) => { + // Electron: workaround for https://github.com/electron/electron/issues/14258 + // We have to detect keyboard events in the and dispatch them to our + // keybinding service because these events do not bubble to the parent window anymore. + this.handleKeyDown(data); + })); + } this.style(); this._register(webviewThemeDataProvider.onThemeDataChanged(this.style, this)); From 1f69cfa2aabf09cf3d105f73defc188c4b737623 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 18 Nov 2019 11:49:37 -0800 Subject: [PATCH 013/246] Re #84715. adopt panel background in comment list. --- .../workbench/contrib/comments/browser/commentsTreeViewer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index c499a52051e..ba8510c41f4 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -20,6 +20,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; export const COMMENTS_PANEL_ID = 'workbench.panel.comments'; export const COMMENTS_PANEL_TITLE = 'Comments'; @@ -200,6 +201,9 @@ export class CommentsList extends WorkbenchAsyncDataTree { }, collapseByDefault: () => { return false; + }, + overrideStyles: { + listBackground: PANEL_BACKGROUND } }, contextKeyService, From 642659c47dc12d70fe8eff1068557f72166b8db9 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 18 Nov 2019 11:54:11 -0800 Subject: [PATCH 014/246] strict function work, #81574 --- src/vs/code/electron-browser/issue/issueReporterMain.ts | 8 ++++---- .../contrib/comments/browser/commentThreadWidget.ts | 4 ++-- .../contrib/comments/browser/simpleCommentEditor.ts | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index 32eaef78ac4..cbb2e5c5269 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -345,8 +345,8 @@ export class IssueReporter extends Disposable { const showInfoElements = document.getElementsByClassName('showInfo'); for (let i = 0; i < showInfoElements.length; i++) { - const showInfo = showInfoElements.item(i); - showInfo!.addEventListener('click', (e: MouseEvent) => { + const showInfo = showInfoElements.item(i)!; + (showInfo as HTMLAnchorElement).addEventListener('click', (e: MouseEvent) => { e.preventDefault(); const label = (e.target); if (label) { @@ -432,9 +432,9 @@ export class IssueReporter extends Disposable { sendWorkbenchCommand('workbench.action.reloadWindowWithExtensionsDisabled'); }); - this.addEventListener('disableExtensions', 'keydown', (e: KeyboardEvent) => { + this.addEventListener('disableExtensions', 'keydown', (e: Event) => { e.stopPropagation(); - if (e.keyCode === 13 || e.keyCode === 32) { + if ((e as KeyboardEvent).keyCode === 13 || (e as KeyboardEvent).keyCode === 32) { sendWorkbenchCommand('workbench.extensions.action.disableAll'); sendWorkbenchCommand('workbench.action.reloadWindow'); } diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 72eb83fb5b8..e9f233496aa 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -27,7 +27,7 @@ import { peekViewBorder } from 'vs/editor/contrib/peekView/peekView'; import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget'; import * as nls from 'vs/nls'; import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenu, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { IMenu, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -260,7 +260,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } private setActionBarActions(menu: IMenu): void { - const groups = menu.getActions({ shouldForwardArgs: true }).reduce((r, [, actions]) => [...r, ...actions], []); + const groups = menu.getActions({ shouldForwardArgs: true }).reduce((r, [, actions]) => [...r, ...actions], <(MenuItemAction | SubmenuItemAction)[]>[]); this._actionbarWidget.clear(); this._actionbarWidget.push([...groups, this._collapseAction], { label: false, icon: true }); } diff --git a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts index 7cd39297216..d57b989bede 100644 --- a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts +++ b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { EditorAction, EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; +import { EditorAction, EditorExtensionsRegistry, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -46,9 +46,9 @@ export class SimpleCommentEditor extends CodeEditorWidget { @INotificationService notificationService: INotificationService, @IAccessibilityService accessibilityService: IAccessibilityService ) { - const codeEditorWidgetOptions = { + const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { isSimpleWidget: true, - contributions: [ + contributions: [ { id: MenuPreventer.ID, ctor: MenuPreventer }, { id: ContextMenuController.ID, ctor: ContextMenuController }, { id: SuggestController.ID, ctor: SuggestController }, From 94116da8faa06716652b1bdf3c31c64d39759f1b Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 18 Nov 2019 11:54:42 -0800 Subject: [PATCH 015/246] add port to vscode launch.json for pwa debugger --- .vscode/launch.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index 25279b09fd3..7e112fc94e1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -131,6 +131,7 @@ "linux": { "runtimeExecutable": "${workspaceFolder}/scripts/code.sh" }, + "port": 9222, "timeout": 20000, "env": { "VSCODE_EXTHOST_WILL_SEND_SOCKET": null From 7a89ac693bcc70ac2407b16be6eb1f87a01a0df3 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Mon, 18 Nov 2019 22:59:33 +0100 Subject: [PATCH 016/246] Make debugging independent from node APIs; fixes #85076 --- .../api/common/extHostDebugService.ts | 1087 ++++++++++++++++- .../workbench/api/node/extHostDebugService.ts | 1035 +--------------- .../extensions/worker/extHost.services.ts | 4 +- 3 files changed, 1124 insertions(+), 1002 deletions(-) diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index a80504d6c0f..5050a8dc1df 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -3,11 +3,36 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from 'vs/base/common/event'; -import { ExtHostDebugServiceShape } from 'vs/workbench/api/common/extHost.protocol'; + +import * as path from 'vs/base/common/path'; +import { Schemas } from 'vs/base/common/network'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { Event, Emitter } from 'vs/base/common/event'; +import { asPromise } from 'vs/base/common/async'; +import { + MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID, + IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto +} from 'vs/workbench/api/common/extHost.protocol'; +import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DataBreakpoint, DebugConsoleMode } from 'vs/workbench/api/common/extHostTypes'; +import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; +import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; +import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; +import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IAdapterDescriptor, IDebugAdapterImplementation } from 'vs/workbench/contrib/debug/common/debug'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver'; +import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostConfiguration'; +import { convertToVSCPaths, convertToDAPaths, isDebuggerMainContribution } from 'vs/workbench/contrib/debug/common/debugUtils'; +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; +import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; +import { ISignService } from 'vs/platform/sign/common/sign'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import * as vscode from 'vscode'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { withNullAsUndefined } from 'vs/base/common/types'; export const IExtHostDebugService = createDecorator('IExtHostDebugService'); @@ -33,3 +58,1061 @@ export interface IExtHostDebugService extends ExtHostDebugServiceShape { asDebugSourceUri(source: vscode.DebugSource, session?: vscode.DebugSession): vscode.Uri; } +export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDebugServiceShape { + + readonly _serviceBrand: undefined; + + private _configProviderHandleCounter: number; + private _configProviders: ConfigProviderTuple[]; + + private _adapterFactoryHandleCounter: number; + private _adapterFactories: DescriptorFactoryTuple[]; + + private _trackerFactoryHandleCounter: number; + private _trackerFactories: TrackerFactoryTuple[]; + + private _debugServiceProxy: MainThreadDebugServiceShape; + private _debugSessions: Map = new Map(); + + private readonly _onDidStartDebugSession: Emitter; + get onDidStartDebugSession(): Event { return this._onDidStartDebugSession.event; } + + private readonly _onDidTerminateDebugSession: Emitter; + get onDidTerminateDebugSession(): Event { return this._onDidTerminateDebugSession.event; } + + private readonly _onDidChangeActiveDebugSession: Emitter; + get onDidChangeActiveDebugSession(): Event { return this._onDidChangeActiveDebugSession.event; } + + private _activeDebugSession: ExtHostDebugSession | undefined; + get activeDebugSession(): ExtHostDebugSession | undefined { return this._activeDebugSession; } + + private readonly _onDidReceiveDebugSessionCustomEvent: Emitter; + get onDidReceiveDebugSessionCustomEvent(): Event { return this._onDidReceiveDebugSessionCustomEvent.event; } + + private _activeDebugConsole: ExtHostDebugConsole; + get activeDebugConsole(): ExtHostDebugConsole { return this._activeDebugConsole; } + + private _breakpoints: Map; + private _breakpointEventsActive: boolean; + + private readonly _onDidChangeBreakpoints: Emitter; + + private _aexCommands: Map; + private _debugAdapters: Map; + private _debugAdaptersTrackers: Map; + + private _variableResolver: IConfigurationResolverService | undefined; + + private _signService: ISignService | undefined; + + + constructor( + @IExtHostRpcService extHostRpcService: IExtHostRpcService, + @IExtHostWorkspace private _workspaceService: IExtHostWorkspace, + @IExtHostExtensionService private _extensionService: IExtHostExtensionService, + @IExtHostDocumentsAndEditors private _editorsService: IExtHostDocumentsAndEditors, + @IExtHostConfiguration protected _configurationService: IExtHostConfiguration, + @IExtHostCommands private _commandService: IExtHostCommands + ) { + this._configProviderHandleCounter = 0; + this._configProviders = []; + + this._adapterFactoryHandleCounter = 0; + this._adapterFactories = []; + + this._trackerFactoryHandleCounter = 0; + this._trackerFactories = []; + + this._aexCommands = new Map(); + this._debugAdapters = new Map(); + this._debugAdaptersTrackers = new Map(); + + this._onDidStartDebugSession = new Emitter(); + this._onDidTerminateDebugSession = new Emitter(); + this._onDidChangeActiveDebugSession = new Emitter(); + this._onDidReceiveDebugSessionCustomEvent = new Emitter(); + + this._debugServiceProxy = extHostRpcService.getProxy(MainContext.MainThreadDebugService); + + this._onDidChangeBreakpoints = new Emitter({ + onFirstListenerAdd: () => { + this.startBreakpoints(); + } + }); + + this._activeDebugConsole = new ExtHostDebugConsole(this._debugServiceProxy); + + this._breakpoints = new Map(); + this._breakpointEventsActive = false; + + this._extensionService.getExtensionRegistry().then((extensionRegistry: ExtensionDescriptionRegistry) => { + extensionRegistry.onDidChange(_ => { + this.registerAllDebugTypes(extensionRegistry); + }); + this.registerAllDebugTypes(extensionRegistry); + }); + } + + public asDebugSourceUri(src: vscode.DebugSource, session?: vscode.DebugSession): URI { + + const source = src; + + if (typeof source.sourceReference === 'number') { + // src can be retrieved via DAP's "source" request + + let debug = `debug:${encodeURIComponent(source.path || '')}`; + let sep = '?'; + + if (session) { + debug += `${sep}session=${encodeURIComponent(session.id)}`; + sep = '&'; + } + + debug += `${sep}ref=${source.sourceReference}`; + + return URI.parse(debug); + } else if (source.path) { + // src is just a local file path + return URI.file(source.path); + } else { + throw new Error(`cannot create uri from DAP 'source' object; properties 'path' and 'sourceReference' are both missing.`); + } + } + + private registerAllDebugTypes(extensionRegistry: ExtensionDescriptionRegistry) { + + const debugTypes: string[] = []; + this._aexCommands.clear(); + + for (const ed of extensionRegistry.getAllExtensionDescriptions()) { + if (ed.contributes) { + const debuggers = ed.contributes['debuggers']; + if (debuggers && debuggers.length > 0) { + for (const dbg of debuggers) { + if (isDebuggerMainContribution(dbg)) { + debugTypes.push(dbg.type); + if (dbg.adapterExecutableCommand) { + this._aexCommands.set(dbg.type, dbg.adapterExecutableCommand); + } + } + } + } + } + } + + this._debugServiceProxy.$registerDebugTypes(debugTypes); + } + + // extension debug API + + get onDidChangeBreakpoints(): Event { + return this._onDidChangeBreakpoints.event; + } + + get breakpoints(): vscode.Breakpoint[] { + + this.startBreakpoints(); + + const result: vscode.Breakpoint[] = []; + this._breakpoints.forEach(bp => result.push(bp)); + return result; + } + + public addBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise { + + this.startBreakpoints(); + + // filter only new breakpoints + const breakpoints = breakpoints0.filter(bp => { + const id = bp.id; + if (!this._breakpoints.has(id)) { + this._breakpoints.set(id, bp); + return true; + } + return false; + }); + + // send notification for added breakpoints + this.fireBreakpointChanges(breakpoints, [], []); + + // convert added breakpoints to DTOs + const dtos: Array = []; + const map = new Map(); + for (const bp of breakpoints) { + if (bp instanceof SourceBreakpoint) { + let dto = map.get(bp.location.uri.toString()); + if (!dto) { + dto = { + type: 'sourceMulti', + uri: bp.location.uri, + lines: [] + }; + map.set(bp.location.uri.toString(), dto); + dtos.push(dto); + } + dto.lines.push({ + id: bp.id, + enabled: bp.enabled, + condition: bp.condition, + hitCondition: bp.hitCondition, + logMessage: bp.logMessage, + line: bp.location.range.start.line, + character: bp.location.range.start.character + }); + } else if (bp instanceof FunctionBreakpoint) { + dtos.push({ + type: 'function', + id: bp.id, + enabled: bp.enabled, + hitCondition: bp.hitCondition, + logMessage: bp.logMessage, + condition: bp.condition, + functionName: bp.functionName + }); + } + } + + // send DTOs to VS Code + return this._debugServiceProxy.$registerBreakpoints(dtos); + } + + public removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise { + + this.startBreakpoints(); + + // remove from array + const breakpoints = breakpoints0.filter(b => this._breakpoints.delete(b.id)); + + // send notification + this.fireBreakpointChanges([], breakpoints, []); + + // unregister with VS Code + const ids = breakpoints.filter(bp => bp instanceof SourceBreakpoint).map(bp => bp.id); + const fids = breakpoints.filter(bp => bp instanceof FunctionBreakpoint).map(bp => bp.id); + const dids = breakpoints.filter(bp => bp instanceof DataBreakpoint).map(bp => bp.id); + return this._debugServiceProxy.$unregisterBreakpoints(ids, fids, dids); + } + + public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise { + return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig, { + parentSessionID: options.parentSession ? options.parentSession.id : undefined, + repl: options.consoleMode === DebugConsoleMode.MergeWithParent ? 'mergeWithParent' : 'separate' + }); + } + + public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable { + + if (!provider) { + return new Disposable(() => { }); + } + + if (provider.debugAdapterExecutable) { + console.error('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.'); + } + + const handle = this._configProviderHandleCounter++; + this._configProviders.push({ type, handle, provider }); + + this._debugServiceProxy.$registerDebugConfigurationProvider(type, + !!provider.provideDebugConfigurations, + !!provider.resolveDebugConfiguration, + !!provider.debugAdapterExecutable, // TODO@AW: deprecated + handle); + + return new Disposable(() => { + this._configProviders = this._configProviders.filter(p => p.provider !== provider); // remove + this._debugServiceProxy.$unregisterDebugConfigurationProvider(handle); + }); + } + + public registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable { + + if (!factory) { + return new Disposable(() => { }); + } + + // a DebugAdapterDescriptorFactory can only be registered in the extension that contributes the debugger + if (!this.definesDebugType(extension, type)) { + throw new Error(`a DebugAdapterDescriptorFactory can only be registered from the extension that defines the '${type}' debugger.`); + } + + // make sure that only one factory for this type is registered + if (this.getAdapterFactoryByType(type)) { + throw new Error(`a DebugAdapterDescriptorFactory can only be registered once per a type.`); + } + + const handle = this._adapterFactoryHandleCounter++; + this._adapterFactories.push({ type, handle, factory }); + + this._debugServiceProxy.$registerDebugAdapterDescriptorFactory(type, handle); + + return new Disposable(() => { + this._adapterFactories = this._adapterFactories.filter(p => p.factory !== factory); // remove + this._debugServiceProxy.$unregisterDebugAdapterDescriptorFactory(handle); + }); + } + + public registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable { + + if (!factory) { + return new Disposable(() => { }); + } + + const handle = this._trackerFactoryHandleCounter++; + this._trackerFactories.push({ type, handle, factory }); + + return new Disposable(() => { + this._trackerFactories = this._trackerFactories.filter(p => p.factory !== factory); // remove + }); + } + + // RPC methods (ExtHostDebugServiceShape) + + public async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise { + return Promise.resolve(undefined); + } + + public async $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise { + if (!this._variableResolver) { + const [workspaceFolders, configProvider] = await Promise.all([this._workspaceService.getWorkspaceFolders2(), this._configurationService.getConfigProvider()]); + this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._editorsService, configProvider!); + } + let ws: IWorkspaceFolder | undefined; + const folder = await this.getFolder(folderUri); + if (folder) { + ws = { + uri: folder.uri, + name: folder.name, + index: folder.index, + toResource: () => { + throw new Error('Not implemented'); + } + }; + } + return this._variableResolver.resolveAny(ws, config); + } + + protected createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined { + if (adapter.type === 'implementation') { + return new DirectDebugAdapter(adapter.implementation); + } + return undefined; + } + + protected createSignService(): ISignService | undefined { + return undefined; + } + + public async $startDASession(debugAdapterHandle: number, sessionDto: IDebugSessionDto): Promise { + const mythis = this; + + const session = await this.getSession(sessionDto); + + return this.getAdapterDescriptor(this.getAdapterFactoryByType(session.type), session).then(daDescriptor => { + + const adapter = this.convertToDto(daDescriptor); + + const da: AbstractDebugAdapter | undefined = this.createDebugAdapter(adapter, session); + + const debugAdapter = da; + + if (debugAdapter) { + this._debugAdapters.set(debugAdapterHandle, debugAdapter); + + return this.getDebugAdapterTrackers(session).then(tracker => { + + if (tracker) { + this._debugAdaptersTrackers.set(debugAdapterHandle, tracker); + } + + debugAdapter.onMessage(async message => { + + if (message.type === 'request' && (message).command === 'handshake') { + + const request = message; + + const response: DebugProtocol.Response = { + type: 'response', + seq: 0, + command: request.command, + request_seq: request.seq, + success: true + }; + + if (!this._signService) { + this._signService = this.createSignService(); + } + + try { + if (this._signService) { + const signature = await this._signService.sign(request.arguments.value); + response.body = { + signature: signature + }; + debugAdapter.sendResponse(response); + } else { + throw new Error('no signer'); + } + } catch (e) { + response.success = false; + response.message = e.message; + debugAdapter.sendResponse(response); + } + } else { + if (tracker && tracker.onDidSendMessage) { + tracker.onDidSendMessage(message); + } + + // DA -> VS Code + message = convertToVSCPaths(message, true); + + mythis._debugServiceProxy.$acceptDAMessage(debugAdapterHandle, message); + } + }); + debugAdapter.onError(err => { + if (tracker && tracker.onError) { + tracker.onError(err); + } + this._debugServiceProxy.$acceptDAError(debugAdapterHandle, err.name, err.message, err.stack); + }); + debugAdapter.onExit((code: number | null) => { + if (tracker && tracker.onExit) { + tracker.onExit(withNullAsUndefined(code), undefined); + } + this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, withNullAsUndefined(code), undefined); + }); + + if (tracker && tracker.onWillStartSession) { + tracker.onWillStartSession(); + } + + return debugAdapter.startSession(); + }); + + } + return undefined; + }); + } + + public $sendDAMessage(debugAdapterHandle: number, message: DebugProtocol.ProtocolMessage): void { + + // VS Code -> DA + message = convertToDAPaths(message, false); + + const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle); // TODO@AW: same handle? + if (tracker && tracker.onWillReceiveMessage) { + tracker.onWillReceiveMessage(message); + } + + const da = this._debugAdapters.get(debugAdapterHandle); + if (da) { + da.sendMessage(message); + } + } + + public $stopDASession(debugAdapterHandle: number): Promise { + + const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle); + this._debugAdaptersTrackers.delete(debugAdapterHandle); + if (tracker && tracker.onWillStopSession) { + tracker.onWillStopSession(); + } + + const da = this._debugAdapters.get(debugAdapterHandle); + this._debugAdapters.delete(debugAdapterHandle); + if (da) { + return da.stopSession(); + } else { + return Promise.resolve(void 0); + } + } + + public $acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void { + + const a: vscode.Breakpoint[] = []; + const r: vscode.Breakpoint[] = []; + const c: vscode.Breakpoint[] = []; + + if (delta.added) { + for (const bpd of delta.added) { + const id = bpd.id; + if (id && !this._breakpoints.has(id)) { + let bp: vscode.Breakpoint; + if (bpd.type === 'function') { + bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); + } else if (bpd.type === 'data') { + bp = new DataBreakpoint(bpd.label, bpd.dataId, bpd.canPersist, bpd.enabled, bpd.hitCondition, bpd.condition, bpd.logMessage); + } else { + const uri = URI.revive(bpd.uri); + bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); + } + (bp as any)._id = id; + this._breakpoints.set(id, bp); + a.push(bp); + } + } + } + + if (delta.removed) { + for (const id of delta.removed) { + const bp = this._breakpoints.get(id); + if (bp) { + this._breakpoints.delete(id); + r.push(bp); + } + } + } + + if (delta.changed) { + for (const bpd of delta.changed) { + if (bpd.id) { + const bp = this._breakpoints.get(bpd.id); + if (bp) { + if (bp instanceof FunctionBreakpoint && bpd.type === 'function') { + const fbp = bp; + fbp.enabled = bpd.enabled; + fbp.condition = bpd.condition; + fbp.hitCondition = bpd.hitCondition; + fbp.logMessage = bpd.logMessage; + fbp.functionName = bpd.functionName; + } else if (bp instanceof SourceBreakpoint && bpd.type === 'source') { + const sbp = bp; + sbp.enabled = bpd.enabled; + sbp.condition = bpd.condition; + sbp.hitCondition = bpd.hitCondition; + sbp.logMessage = bpd.logMessage; + sbp.location = new Location(URI.revive(bpd.uri), new Position(bpd.line, bpd.character)); + } + c.push(bp); + } + } + } + } + + this.fireBreakpointChanges(a, r, c); + } + + public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined, token: CancellationToken): Promise { + return asPromise(async () => { + const provider = this.getConfigProviderByHandle(configProviderHandle); + if (!provider) { + throw new Error('no DebugConfigurationProvider found'); + } + if (!provider.provideDebugConfigurations) { + throw new Error('DebugConfigurationProvider has no method provideDebugConfigurations'); + } + const folder = await this.getFolder(folderUri); + return provider.provideDebugConfigurations(folder, token); + }).then(debugConfigurations => { + if (!debugConfigurations) { + throw new Error('nothing returned from DebugConfigurationProvider.provideDebugConfigurations'); + } + return debugConfigurations; + }); + } + + public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration, token: CancellationToken): Promise { + return asPromise(async () => { + const provider = this.getConfigProviderByHandle(configProviderHandle); + if (!provider) { + throw new Error('no DebugConfigurationProvider found'); + } + if (!provider.resolveDebugConfiguration) { + throw new Error('DebugConfigurationProvider has no method resolveDebugConfiguration'); + } + const folder = await this.getFolder(folderUri); + return provider.resolveDebugConfiguration(folder, debugConfiguration, token); + }); + } + + // TODO@AW deprecated and legacy + public $legacyDebugAdapterExecutable(configProviderHandle: number, folderUri: UriComponents | undefined): Promise { + return asPromise(async () => { + const provider = this.getConfigProviderByHandle(configProviderHandle); + if (!provider) { + throw new Error('no DebugConfigurationProvider found'); + } + if (!provider.debugAdapterExecutable) { + throw new Error('DebugConfigurationProvider has no method debugAdapterExecutable'); + } + const folder = await this.getFolder(folderUri); + return provider.debugAdapterExecutable(folder, CancellationToken.None); + }).then(executable => { + if (!executable) { + throw new Error('nothing returned from DebugConfigurationProvider.debugAdapterExecutable'); + } + return this.convertToDto(executable); + }); + } + + public async $provideDebugAdapter(adapterProviderHandle: number, sessionDto: IDebugSessionDto): Promise { + const adapterProvider = this.getAdapterProviderByHandle(adapterProviderHandle); + if (!adapterProvider) { + return Promise.reject(new Error('no handler found')); + } + const session = await this.getSession(sessionDto); + return this.getAdapterDescriptor(adapterProvider, session).then(x => this.convertToDto(x)); + } + + public async $acceptDebugSessionStarted(sessionDto: IDebugSessionDto): Promise { + const session = await this.getSession(sessionDto); + this._onDidStartDebugSession.fire(session); + } + + public async $acceptDebugSessionTerminated(sessionDto: IDebugSessionDto): Promise { + const session = await this.getSession(sessionDto); + if (session) { + this._onDidTerminateDebugSession.fire(session); + this._debugSessions.delete(session.id); + } + } + + public async $acceptDebugSessionActiveChanged(sessionDto: IDebugSessionDto | undefined): Promise { + this._activeDebugSession = sessionDto ? await this.getSession(sessionDto) : undefined; + this._onDidChangeActiveDebugSession.fire(this._activeDebugSession); + } + + public async $acceptDebugSessionNameChanged(sessionDto: IDebugSessionDto, name: string): Promise { + const session = await this.getSession(sessionDto); + if (session) { + session._acceptNameChanged(name); + } + } + + public async $acceptDebugSessionCustomEvent(sessionDto: IDebugSessionDto, event: any): Promise { + const session = await this.getSession(sessionDto); + const ee: vscode.DebugSessionCustomEvent = { + session: session, + event: event.event, + body: event.body + }; + this._onDidReceiveDebugSessionCustomEvent.fire(ee); + } + + // private & dto helpers + + private convertToDto(x: vscode.DebugAdapterDescriptor | undefined): IAdapterDescriptor { + + if (x instanceof DebugAdapterExecutable) { + + const a = x; + if (a['implementation']) { + return { + type: 'implementation', + implementation: a['implementation'] + }; + } + + return { + type: 'executable', + command: x.command, + args: x.args, + options: x.options + }; + } else if (x instanceof DebugAdapterServer) { + return { + type: 'server', + port: x.port, + host: x.host + }; + } else /* if (x instanceof DebugAdapterImplementation) { + return { + type: 'implementation', + implementation: x.implementation + }; + } else */ { + throw new Error('convertToDto unexpected type'); + } + } + + private getAdapterFactoryByType(type: string): vscode.DebugAdapterDescriptorFactory | undefined { + const results = this._adapterFactories.filter(p => p.type === type); + if (results.length > 0) { + return results[0].factory; + } + return undefined; + } + + private getAdapterProviderByHandle(handle: number): vscode.DebugAdapterDescriptorFactory | undefined { + const results = this._adapterFactories.filter(p => p.handle === handle); + if (results.length > 0) { + return results[0].factory; + } + return undefined; + } + + private getConfigProviderByHandle(handle: number): vscode.DebugConfigurationProvider | undefined { + const results = this._configProviders.filter(p => p.handle === handle); + if (results.length > 0) { + return results[0].provider; + } + return undefined; + } + + private definesDebugType(ed: IExtensionDescription, type: string) { + if (ed.contributes) { + const debuggers = ed.contributes['debuggers']; + if (debuggers && debuggers.length > 0) { + for (const dbg of debuggers) { + // only debugger contributions with a "label" are considered a "defining" debugger contribution + if (dbg.label && dbg.type) { + if (dbg.type === type) { + return true; + } + } + } + } + } + return false; + } + + private getDebugAdapterTrackers(session: ExtHostDebugSession): Promise { + + const config = session.configuration; + const type = config.type; + + const promises = this._trackerFactories + .filter(tuple => tuple.type === type || tuple.type === '*') + .map(tuple => asPromise>(() => tuple.factory.createDebugAdapterTracker(session)).then(p => p, err => null)); + + return Promise.race([ + Promise.all(promises).then(result => { + const trackers = result.filter(t => !!t); // filter null + if (trackers.length > 0) { + return new MultiTracker(trackers); + } + return undefined; + }), + new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + clearTimeout(timeout); + reject(new Error('timeout')); + }, 1000); + }) + ]).catch(err => { + // ignore errors + return undefined; + }); + } + + private async getAdapterDescriptor(adapterProvider: vscode.DebugAdapterDescriptorFactory | undefined, session: ExtHostDebugSession): Promise { + + // a "debugServer" attribute in the launch config takes precedence + const serverPort = session.configuration.debugServer; + if (typeof serverPort === 'number') { + return Promise.resolve(new DebugAdapterServer(serverPort)); + } + + // TODO@AW legacy + const pair = this._configProviders.filter(p => p.type === session.type).pop(); + if (pair && pair.provider.debugAdapterExecutable) { + const func = pair.provider.debugAdapterExecutable; + return asPromise(() => func(session.workspaceFolder, CancellationToken.None)).then(executable => { + if (executable) { + return executable; + } + return undefined; + }); + } + + if (adapterProvider) { + const extensionRegistry = await this._extensionService.getExtensionRegistry(); + return asPromise(() => adapterProvider.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry))).then(daDescriptor => { + if (daDescriptor) { + return daDescriptor; + } + return undefined; + }); + } + + // try deprecated command based extension API "adapterExecutableCommand" to determine the executable + // TODO@AW legacy + const aex = this._aexCommands.get(session.type); + if (aex) { + const folder = session.workspaceFolder; + const rootFolder = folder ? folder.uri.toString() : undefined; + return this._commandService.executeCommand(aex, rootFolder).then((ae: { command: string, args: string[] }) => { + return new DebugAdapterExecutable(ae.command, ae.args || []); + }); + } + + // fallback: use executable information from package.json + const extensionRegistry = await this._extensionService.getExtensionRegistry(); + return Promise.resolve(this.daExecutableFromPackage(session, extensionRegistry)); + } + + protected daExecutableFromPackage(session: ExtHostDebugSession, extensionRegistry: ExtensionDescriptionRegistry): DebugAdapterExecutable | undefined { + return undefined; + } + + private startBreakpoints() { + if (!this._breakpointEventsActive) { + this._breakpointEventsActive = true; + this._debugServiceProxy.$startBreakpointEvents(); + } + } + + private fireBreakpointChanges(added: vscode.Breakpoint[], removed: vscode.Breakpoint[], changed: vscode.Breakpoint[]) { + if (added.length > 0 || removed.length > 0 || changed.length > 0) { + this._onDidChangeBreakpoints.fire(Object.freeze({ + added, + removed, + changed, + })); + } + } + + private async getSession(dto: IDebugSessionDto): Promise { + if (dto) { + if (typeof dto === 'string') { + const ds = this._debugSessions.get(dto); + if (ds) { + return ds; + } + } else { + let ds = this._debugSessions.get(dto.id); + if (!ds) { + const folder = await this.getFolder(dto.folderUri); + ds = new ExtHostDebugSession(this._debugServiceProxy, dto.id, dto.type, dto.name, folder, dto.configuration); + this._debugSessions.set(ds.id, ds); + this._debugServiceProxy.$sessionCached(ds.id); + } + return ds; + } + } + throw new Error('cannot find session'); + } + + private getFolder(_folderUri: UriComponents | undefined): Promise { + if (_folderUri) { + const folderURI = URI.revive(_folderUri); + return this._workspaceService.resolveWorkspaceFolder(folderURI); + } + return Promise.resolve(undefined); + } +} + +export class ExtHostDebugSession implements vscode.DebugSession { + + constructor( + private _debugServiceProxy: MainThreadDebugServiceShape, + private _id: DebugSessionUUID, + private _type: string, + private _name: string, + private _workspaceFolder: vscode.WorkspaceFolder | undefined, + private _configuration: vscode.DebugConfiguration) { + } + + public get id(): string { + return this._id; + } + + public get type(): string { + return this._type; + } + + public get name(): string { + return this._name; + } + + public set name(name: string) { + this._name = name; + this._debugServiceProxy.$setDebugSessionName(this._id, name); + } + + _acceptNameChanged(name: string) { + this._name = name; + } + + public get workspaceFolder(): vscode.WorkspaceFolder | undefined { + return this._workspaceFolder; + } + + public get configuration(): vscode.DebugConfiguration { + return this._configuration; + } + + public customRequest(command: string, args: any): Promise { + return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args); + } +} + +export class ExtHostDebugConsole implements vscode.DebugConsole { + + private _debugServiceProxy: MainThreadDebugServiceShape; + + constructor(proxy: MainThreadDebugServiceShape) { + this._debugServiceProxy = proxy; + } + + append(value: string): void { + this._debugServiceProxy.$appendDebugConsole(value); + } + + appendLine(value: string): void { + this.append(value + '\n'); + } +} + +class ExtHostVariableResolverService extends AbstractVariableResolverService { + + constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider) { + super({ + getFolderUri: (folderName: string): URI | undefined => { + const found = folders.filter(f => f.name === folderName); + if (found && found.length > 0) { + return found[0].uri; + } + return undefined; + }, + getWorkspaceFolderCount: (): number => { + return folders.length; + }, + getConfigurationValue: (folderUri: URI, section: string): string | undefined => { + return configurationService.getConfiguration(undefined, folderUri).get(section); + }, + getExecPath: (): string | undefined => { + return undefined; + }, + getFilePath: (): string | undefined => { + const activeEditor = editorService.activeEditor(); + if (activeEditor) { + const resource = activeEditor.document.uri; + if (resource.scheme === Schemas.file) { + return path.normalize(resource.fsPath); + } + } + return undefined; + }, + getSelectedText: (): string | undefined => { + const activeEditor = editorService.activeEditor(); + if (activeEditor && !activeEditor.selection.isEmpty) { + return activeEditor.document.getText(activeEditor.selection); + } + return undefined; + }, + getLineNumber: (): string | undefined => { + const activeEditor = editorService.activeEditor(); + if (activeEditor) { + return String(activeEditor.selection.end.line + 1); + } + return undefined; + } + }, {} /* process.env as IProcessEnvironment */); + } +} + +interface ConfigProviderTuple { + type: string; + handle: number; + provider: vscode.DebugConfigurationProvider; +} + +interface DescriptorFactoryTuple { + type: string; + handle: number; + factory: vscode.DebugAdapterDescriptorFactory; +} + +interface TrackerFactoryTuple { + type: string; + handle: number; + factory: vscode.DebugAdapterTrackerFactory; +} + +class MultiTracker implements vscode.DebugAdapterTracker { + + constructor(private trackers: vscode.DebugAdapterTracker[]) { + } + + onWillStartSession(): void { + this.trackers.forEach(t => t.onWillStartSession ? t.onWillStartSession() : undefined); + } + + onWillReceiveMessage(message: any): void { + this.trackers.forEach(t => t.onWillReceiveMessage ? t.onWillReceiveMessage(message) : undefined); + } + + onDidSendMessage(message: any): void { + this.trackers.forEach(t => t.onDidSendMessage ? t.onDidSendMessage(message) : undefined); + } + + onWillStopSession(): void { + this.trackers.forEach(t => t.onWillStopSession ? t.onWillStopSession() : undefined); + } + + onError(error: Error): void { + this.trackers.forEach(t => t.onError ? t.onError(error) : undefined); + } + + onExit(code: number, signal: string): void { + this.trackers.forEach(t => t.onExit ? t.onExit(code, signal) : undefined); + } +} + +interface IDapTransport { + start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void): void; + send(message: DebugProtocol.ProtocolMessage): void; + stop(): void; +} + +class DirectDebugAdapter extends AbstractDebugAdapter implements IDapTransport { + + + private _sendUp!: (msg: DebugProtocol.ProtocolMessage) => void; + + constructor(implementation: any) { + super(); + if (implementation.__setTransport) { + implementation.__setTransport(this); + } + } + + // IDapTransport + start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void) { + this._sendUp = cb; + } + + // AbstractDebugAdapter + startSession(): Promise { + return Promise.resolve(undefined); + } + + // AbstractDebugAdapter + // VSCode -> DA + sendMessage(message: DebugProtocol.ProtocolMessage): void { + this._sendUp(message); + } + + // AbstractDebugAdapter + stopSession(): Promise { + this.stop(); + return Promise.resolve(undefined); + } + + // IDapTransport + // DA -> VSCode + send(message: DebugProtocol.ProtocolMessage) { + this.acceptMessage(message); + } + + // IDapTransport + stop(): void { + throw new Error('Method not implemented.'); + } +} + + +export class WorkerExtHostDebugService extends ExtHostDebugServiceBase { + constructor( + @IExtHostRpcService extHostRpcService: IExtHostRpcService, + @IExtHostWorkspace workspaceService: IExtHostWorkspace, + @IExtHostExtensionService extensionService: IExtHostExtensionService, + @IExtHostDocumentsAndEditors editorsService: IExtHostDocumentsAndEditors, + @IExtHostConfiguration configurationService: IExtHostConfiguration, + @IExtHostCommands commandService: IExtHostCommands + ) { + super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, commandService); + } +} + diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 81e40b7f893..3f62931e053 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -3,357 +3,73 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'vs/base/common/path'; -import { Schemas } from 'vs/base/common/network'; -import { URI, UriComponents } from 'vs/base/common/uri'; -import { Event, Emitter } from 'vs/base/common/event'; -import { asPromise } from 'vs/base/common/async'; import * as nls from 'vs/nls'; -import { - MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID, - IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto -} from 'vs/workbench/api/common/extHost.protocol'; import * as vscode from 'vscode'; -import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DataBreakpoint, DebugConsoleMode } from 'vs/workbench/api/common/extHostTypes'; +import * as path from 'vs/base/common/path'; +import { DebugAdapterExecutable } from 'vs/workbench/api/common/extHostTypes'; import { ExecutableDebugAdapter, SocketDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; -import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; -import { IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IAdapterDescriptor } from 'vs/workbench/contrib/debug/common/debug'; -import { hasChildProcesses, prepareCommand, runInExternalTerminal } from 'vs/workbench/contrib/debug/node/terminals'; -import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver'; -import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostConfiguration'; -import { convertToVSCPaths, convertToDAPaths, isDebuggerMainContribution } from 'vs/workbench/contrib/debug/common/debugUtils'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { IAdapterDescriptor } from 'vs/workbench/contrib/debug/common/debug'; +import { IExtHostConfiguration, ExtHostConfigProvider } from '../common/extHostConfiguration'; import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; -import { IProcessEnvironment } from 'vs/base/common/platform'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { SignService } from 'vs/platform/sign/node/signService'; -import { ISignService } from 'vs/platform/sign/common/sign'; import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService'; -import { withNullAsUndefined } from 'vs/base/common/types'; +import { ExtHostDebugServiceBase, ExtHostDebugSession } from 'vs/workbench/api/common/extHostDebugService'; +import { ISignService } from 'vs/platform/sign/common/sign'; +import { SignService } from 'vs/platform/sign/node/signService'; +import { hasChildProcesses, prepareCommand, runInExternalTerminal } from 'vs/workbench/contrib/debug/node/terminals'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver'; +import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import { IProcessEnvironment } from 'vs/base/common/platform'; -export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugServiceShape { + +export class ExtHostDebugService extends ExtHostDebugServiceBase { readonly _serviceBrand: undefined; - private _configProviderHandleCounter: number; - private _configProviders: ConfigProviderTuple[]; - - private _adapterFactoryHandleCounter: number; - private _adapterFactories: DescriptorFactoryTuple[]; - - private _trackerFactoryHandleCounter: number; - private _trackerFactories: TrackerFactoryTuple[]; - - private _debugServiceProxy: MainThreadDebugServiceShape; - private _debugSessions: Map = new Map(); - - private readonly _onDidStartDebugSession: Emitter; - get onDidStartDebugSession(): Event { return this._onDidStartDebugSession.event; } - - private readonly _onDidTerminateDebugSession: Emitter; - get onDidTerminateDebugSession(): Event { return this._onDidTerminateDebugSession.event; } - - private readonly _onDidChangeActiveDebugSession: Emitter; - get onDidChangeActiveDebugSession(): Event { return this._onDidChangeActiveDebugSession.event; } - - private _activeDebugSession: ExtHostDebugSession | undefined; - get activeDebugSession(): ExtHostDebugSession | undefined { return this._activeDebugSession; } - - private readonly _onDidReceiveDebugSessionCustomEvent: Emitter; - get onDidReceiveDebugSessionCustomEvent(): Event { return this._onDidReceiveDebugSessionCustomEvent.event; } - - private _activeDebugConsole: ExtHostDebugConsole; - get activeDebugConsole(): ExtHostDebugConsole { return this._activeDebugConsole; } - - private _breakpoints: Map; - private _breakpointEventsActive: boolean; - - private readonly _onDidChangeBreakpoints: Emitter; - - private _aexCommands: Map; - private _debugAdapters: Map; - private _debugAdaptersTrackers: Map; - - private _variableResolver: IConfigurationResolverService | undefined; - private _integratedTerminalInstance?: vscode.Terminal; private _terminalDisposedListener: IDisposable | undefined; - private _signService: ISignService | undefined; - - constructor( @IExtHostRpcService extHostRpcService: IExtHostRpcService, - @IExtHostWorkspace private _workspaceService: IExtHostWorkspace, - @IExtHostExtensionService private _extensionService: IExtHostExtensionService, - @IExtHostDocumentsAndEditors private _editorsService: IExtHostDocumentsAndEditors, - @IExtHostConfiguration private _configurationService: IExtHostConfiguration, + @IExtHostWorkspace workspaceService: IExtHostWorkspace, + @IExtHostExtensionService extensionService: IExtHostExtensionService, + @IExtHostDocumentsAndEditors editorsService: IExtHostDocumentsAndEditors, + @IExtHostConfiguration configurationService: IExtHostConfiguration, @IExtHostTerminalService private _terminalService: IExtHostTerminalService, - @IExtHostCommands private _commandService: IExtHostCommands + @IExtHostCommands commandService: IExtHostCommands ) { - this._configProviderHandleCounter = 0; - this._configProviders = []; - - this._adapterFactoryHandleCounter = 0; - this._adapterFactories = []; - - this._trackerFactoryHandleCounter = 0; - this._trackerFactories = []; - - this._aexCommands = new Map(); - this._debugAdapters = new Map(); - this._debugAdaptersTrackers = new Map(); - - this._onDidStartDebugSession = new Emitter(); - this._onDidTerminateDebugSession = new Emitter(); - this._onDidChangeActiveDebugSession = new Emitter(); - this._onDidReceiveDebugSessionCustomEvent = new Emitter(); - - this._debugServiceProxy = extHostRpcService.getProxy(MainContext.MainThreadDebugService); - - this._onDidChangeBreakpoints = new Emitter({ - onFirstListenerAdd: () => { - this.startBreakpoints(); - } - }); - - this._activeDebugConsole = new ExtHostDebugConsole(this._debugServiceProxy); - - this._breakpoints = new Map(); - this._breakpointEventsActive = false; - - this._extensionService.getExtensionRegistry().then((extensionRegistry: ExtensionDescriptionRegistry) => { - extensionRegistry.onDidChange(_ => { - this.registerAllDebugTypes(extensionRegistry); - }); - this.registerAllDebugTypes(extensionRegistry); - }); + super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, commandService); } - public asDebugSourceUri(src: vscode.DebugSource, session?: vscode.DebugSession): URI { - - const source = src; - - if (typeof source.sourceReference === 'number') { - // src can be retrieved via DAP's "source" request - - let debug = `debug:${encodeURIComponent(source.path || '')}`; - let sep = '?'; - - if (session) { - debug += `${sep}session=${encodeURIComponent(session.id)}`; - sep = '&'; - } - - debug += `${sep}ref=${source.sourceReference}`; - - return URI.parse(debug); - } else if (source.path) { - // src is just a local file path - return URI.file(source.path); - } else { - throw new Error(`cannot create uri from DAP 'source' object; properties 'path' and 'sourceReference' are both missing.`); + protected createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined { + switch (adapter.type) { + case 'server': + return new SocketDebugAdapter(adapter); + case 'executable': + return new ExecutableDebugAdapter(adapter, session.type); } + return super.createDebugAdapter(adapter, session); } - private registerAllDebugTypes(extensionRegistry: ExtensionDescriptionRegistry) { - - const debugTypes: string[] = []; - this._aexCommands.clear(); - - for (const ed of extensionRegistry.getAllExtensionDescriptions()) { - if (ed.contributes) { - const debuggers = ed.contributes['debuggers']; - if (debuggers && debuggers.length > 0) { - for (const dbg of debuggers) { - if (isDebuggerMainContribution(dbg)) { - debugTypes.push(dbg.type); - if (dbg.adapterExecutableCommand) { - this._aexCommands.set(dbg.type, dbg.adapterExecutableCommand); - } - } - } - } - } + protected daExecutableFromPackage(session: ExtHostDebugSession, extensionRegistry: ExtensionDescriptionRegistry): DebugAdapterExecutable | undefined { + const dae = ExecutableDebugAdapter.platformAdapterExecutable(extensionRegistry.getAllExtensionDescriptions(), session.type); + if (dae) { + return new DebugAdapterExecutable(dae.command, dae.args, dae.options); } - - this._debugServiceProxy.$registerDebugTypes(debugTypes); + return undefined; } - // extension debug API - - get onDidChangeBreakpoints(): Event { - return this._onDidChangeBreakpoints.event; + protected createSignService(): ISignService | undefined { + return new SignService(); } - get breakpoints(): vscode.Breakpoint[] { - - this.startBreakpoints(); - - const result: vscode.Breakpoint[] = []; - this._breakpoints.forEach(bp => result.push(bp)); - return result; - } - - public addBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise { - - this.startBreakpoints(); - - // filter only new breakpoints - const breakpoints = breakpoints0.filter(bp => { - const id = bp.id; - if (!this._breakpoints.has(id)) { - this._breakpoints.set(id, bp); - return true; - } - return false; - }); - - // send notification for added breakpoints - this.fireBreakpointChanges(breakpoints, [], []); - - // convert added breakpoints to DTOs - const dtos: Array = []; - const map = new Map(); - for (const bp of breakpoints) { - if (bp instanceof SourceBreakpoint) { - let dto = map.get(bp.location.uri.toString()); - if (!dto) { - dto = { - type: 'sourceMulti', - uri: bp.location.uri, - lines: [] - }; - map.set(bp.location.uri.toString(), dto); - dtos.push(dto); - } - dto.lines.push({ - id: bp.id, - enabled: bp.enabled, - condition: bp.condition, - hitCondition: bp.hitCondition, - logMessage: bp.logMessage, - line: bp.location.range.start.line, - character: bp.location.range.start.character - }); - } else if (bp instanceof FunctionBreakpoint) { - dtos.push({ - type: 'function', - id: bp.id, - enabled: bp.enabled, - hitCondition: bp.hitCondition, - logMessage: bp.logMessage, - condition: bp.condition, - functionName: bp.functionName - }); - } - } - - // send DTOs to VS Code - return this._debugServiceProxy.$registerBreakpoints(dtos); - } - - public removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise { - - this.startBreakpoints(); - - // remove from array - const breakpoints = breakpoints0.filter(b => this._breakpoints.delete(b.id)); - - // send notification - this.fireBreakpointChanges([], breakpoints, []); - - // unregister with VS Code - const ids = breakpoints.filter(bp => bp instanceof SourceBreakpoint).map(bp => bp.id); - const fids = breakpoints.filter(bp => bp instanceof FunctionBreakpoint).map(bp => bp.id); - const dids = breakpoints.filter(bp => bp instanceof DataBreakpoint).map(bp => bp.id); - return this._debugServiceProxy.$unregisterBreakpoints(ids, fids, dids); - } - - public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise { - return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig, { - parentSessionID: options.parentSession ? options.parentSession.id : undefined, - repl: options.consoleMode === DebugConsoleMode.MergeWithParent ? 'mergeWithParent' : 'separate' - }); - } - - public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable { - - if (!provider) { - return new Disposable(() => { }); - } - - if (provider.debugAdapterExecutable) { - console.error('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.'); - } - - const handle = this._configProviderHandleCounter++; - this._configProviders.push({ type, handle, provider }); - - this._debugServiceProxy.$registerDebugConfigurationProvider(type, - !!provider.provideDebugConfigurations, - !!provider.resolveDebugConfiguration, - !!provider.debugAdapterExecutable, // TODO@AW: deprecated - handle); - - return new Disposable(() => { - this._configProviders = this._configProviders.filter(p => p.provider !== provider); // remove - this._debugServiceProxy.$unregisterDebugConfigurationProvider(handle); - }); - } - - public registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable { - - if (!factory) { - return new Disposable(() => { }); - } - - // a DebugAdapterDescriptorFactory can only be registered in the extension that contributes the debugger - if (!this.definesDebugType(extension, type)) { - throw new Error(`a DebugAdapterDescriptorFactory can only be registered from the extension that defines the '${type}' debugger.`); - } - - // make sure that only one factory for this type is registered - if (this.getAdapterFactoryByType(type)) { - throw new Error(`a DebugAdapterDescriptorFactory can only be registered once per a type.`); - } - - const handle = this._adapterFactoryHandleCounter++; - this._adapterFactories.push({ type, handle, factory }); - - this._debugServiceProxy.$registerDebugAdapterDescriptorFactory(type, handle); - - return new Disposable(() => { - this._adapterFactories = this._adapterFactories.filter(p => p.factory !== factory); // remove - this._debugServiceProxy.$unregisterDebugAdapterDescriptorFactory(handle); - }); - } - - public registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable { - - if (!factory) { - return new Disposable(() => { }); - } - - const handle = this._trackerFactoryHandleCounter++; - this._trackerFactories.push({ type, handle, factory }); - - return new Disposable(() => { - this._trackerFactories = this._trackerFactories.filter(p => p.factory !== factory); // remove - }); - } - - // RPC methods (ExtHostDebugServiceShape) - public async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise { if (args.kind === 'integrated') { @@ -404,584 +120,7 @@ export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugSe runInExternalTerminal(args, await this._configurationService.getConfigProvider()); } - return Promise.resolve(undefined); - } - - public async $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise { - if (!this._variableResolver) { - const [workspaceFolders, configProvider] = await Promise.all([this._workspaceService.getWorkspaceFolders2(), this._configurationService.getConfigProvider()]); - this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._editorsService, configProvider!); - } - let ws: IWorkspaceFolder | undefined; - const folder = await this.getFolder(folderUri); - if (folder) { - ws = { - uri: folder.uri, - name: folder.name, - index: folder.index, - toResource: () => { - throw new Error('Not implemented'); - } - }; - } - return this._variableResolver.resolveAny(ws, config); - } - - public async $startDASession(debugAdapterHandle: number, sessionDto: IDebugSessionDto): Promise { - const mythis = this; - - const session = await this.getSession(sessionDto); - - return this.getAdapterDescriptor(this.getAdapterFactoryByType(session.type), session).then(daDescriptor => { - - const adapter = this.convertToDto(daDescriptor); - let da: AbstractDebugAdapter | undefined = undefined; - - switch (adapter.type) { - - case 'server': - da = new SocketDebugAdapter(adapter); - break; - - case 'executable': - da = new ExecutableDebugAdapter(adapter, session.type); - break; - - case 'implementation': - da = new DirectDebugAdapter(adapter.implementation); - break; - - default: - break; - } - - const debugAdapter = da; - - if (debugAdapter) { - this._debugAdapters.set(debugAdapterHandle, debugAdapter); - - return this.getDebugAdapterTrackers(session).then(tracker => { - - if (tracker) { - this._debugAdaptersTrackers.set(debugAdapterHandle, tracker); - } - - debugAdapter.onMessage(async message => { - - if (message.type === 'request' && (message).command === 'handshake') { - - const request = message; - - const response: DebugProtocol.Response = { - type: 'response', - seq: 0, - command: request.command, - request_seq: request.seq, - success: true - }; - - if (!this._signService) { - this._signService = new SignService(); - } - - try { - const signature = await this._signService.sign(request.arguments.value); - response.body = { - signature: signature - }; - debugAdapter.sendResponse(response); - } catch (e) { - response.success = false; - response.message = e.message; - debugAdapter.sendResponse(response); - } - } else { - if (tracker && tracker.onDidSendMessage) { - tracker.onDidSendMessage(message); - } - - // DA -> VS Code - message = convertToVSCPaths(message, true); - - mythis._debugServiceProxy.$acceptDAMessage(debugAdapterHandle, message); - } - }); - debugAdapter.onError(err => { - if (tracker && tracker.onError) { - tracker.onError(err); - } - this._debugServiceProxy.$acceptDAError(debugAdapterHandle, err.name, err.message, err.stack); - }); - debugAdapter.onExit((code: number | null) => { - if (tracker && tracker.onExit) { - tracker.onExit(withNullAsUndefined(code), undefined); - } - this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, withNullAsUndefined(code), undefined); - }); - - if (tracker && tracker.onWillStartSession) { - tracker.onWillStartSession(); - } - - return debugAdapter.startSession(); - }); - - } - return undefined; - }); - } - - public $sendDAMessage(debugAdapterHandle: number, message: DebugProtocol.ProtocolMessage): void { - - // VS Code -> DA - message = convertToDAPaths(message, false); - - const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle); // TODO@AW: same handle? - if (tracker && tracker.onWillReceiveMessage) { - tracker.onWillReceiveMessage(message); - } - - const da = this._debugAdapters.get(debugAdapterHandle); - if (da) { - da.sendMessage(message); - } - } - - public $stopDASession(debugAdapterHandle: number): Promise { - - const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle); - this._debugAdaptersTrackers.delete(debugAdapterHandle); - if (tracker && tracker.onWillStopSession) { - tracker.onWillStopSession(); - } - - const da = this._debugAdapters.get(debugAdapterHandle); - this._debugAdapters.delete(debugAdapterHandle); - if (da) { - return da.stopSession(); - } else { - return Promise.resolve(void 0); - } - } - - public $acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void { - - const a: vscode.Breakpoint[] = []; - const r: vscode.Breakpoint[] = []; - const c: vscode.Breakpoint[] = []; - - if (delta.added) { - for (const bpd of delta.added) { - const id = bpd.id; - if (id && !this._breakpoints.has(id)) { - let bp: vscode.Breakpoint; - if (bpd.type === 'function') { - bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); - } else if (bpd.type === 'data') { - bp = new DataBreakpoint(bpd.label, bpd.dataId, bpd.canPersist, bpd.enabled, bpd.hitCondition, bpd.condition, bpd.logMessage); - } else { - const uri = URI.revive(bpd.uri); - bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); - } - (bp as any)._id = id; - this._breakpoints.set(id, bp); - a.push(bp); - } - } - } - - if (delta.removed) { - for (const id of delta.removed) { - const bp = this._breakpoints.get(id); - if (bp) { - this._breakpoints.delete(id); - r.push(bp); - } - } - } - - if (delta.changed) { - for (const bpd of delta.changed) { - if (bpd.id) { - const bp = this._breakpoints.get(bpd.id); - if (bp) { - if (bp instanceof FunctionBreakpoint && bpd.type === 'function') { - const fbp = bp; - fbp.enabled = bpd.enabled; - fbp.condition = bpd.condition; - fbp.hitCondition = bpd.hitCondition; - fbp.logMessage = bpd.logMessage; - fbp.functionName = bpd.functionName; - } else if (bp instanceof SourceBreakpoint && bpd.type === 'source') { - const sbp = bp; - sbp.enabled = bpd.enabled; - sbp.condition = bpd.condition; - sbp.hitCondition = bpd.hitCondition; - sbp.logMessage = bpd.logMessage; - sbp.location = new Location(URI.revive(bpd.uri), new Position(bpd.line, bpd.character)); - } - c.push(bp); - } - } - } - } - - this.fireBreakpointChanges(a, r, c); - } - - public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined, token: CancellationToken): Promise { - return asPromise(async () => { - const provider = this.getConfigProviderByHandle(configProviderHandle); - if (!provider) { - throw new Error('no DebugConfigurationProvider found'); - } - if (!provider.provideDebugConfigurations) { - throw new Error('DebugConfigurationProvider has no method provideDebugConfigurations'); - } - const folder = await this.getFolder(folderUri); - return provider.provideDebugConfigurations(folder, token); - }).then(debugConfigurations => { - if (!debugConfigurations) { - throw new Error('nothing returned from DebugConfigurationProvider.provideDebugConfigurations'); - } - return debugConfigurations; - }); - } - - public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration, token: CancellationToken): Promise { - return asPromise(async () => { - const provider = this.getConfigProviderByHandle(configProviderHandle); - if (!provider) { - throw new Error('no DebugConfigurationProvider found'); - } - if (!provider.resolveDebugConfiguration) { - throw new Error('DebugConfigurationProvider has no method resolveDebugConfiguration'); - } - const folder = await this.getFolder(folderUri); - return provider.resolveDebugConfiguration(folder, debugConfiguration, token); - }); - } - - // TODO@AW deprecated and legacy - public $legacyDebugAdapterExecutable(configProviderHandle: number, folderUri: UriComponents | undefined): Promise { - return asPromise(async () => { - const provider = this.getConfigProviderByHandle(configProviderHandle); - if (!provider) { - throw new Error('no DebugConfigurationProvider found'); - } - if (!provider.debugAdapterExecutable) { - throw new Error('DebugConfigurationProvider has no method debugAdapterExecutable'); - } - const folder = await this.getFolder(folderUri); - return provider.debugAdapterExecutable(folder, CancellationToken.None); - }).then(executable => { - if (!executable) { - throw new Error('nothing returned from DebugConfigurationProvider.debugAdapterExecutable'); - } - return this.convertToDto(executable); - }); - } - - public async $provideDebugAdapter(adapterProviderHandle: number, sessionDto: IDebugSessionDto): Promise { - const adapterProvider = this.getAdapterProviderByHandle(adapterProviderHandle); - if (!adapterProvider) { - return Promise.reject(new Error('no handler found')); - } - const session = await this.getSession(sessionDto); - return this.getAdapterDescriptor(adapterProvider, session).then(x => this.convertToDto(x)); - } - - public async $acceptDebugSessionStarted(sessionDto: IDebugSessionDto): Promise { - const session = await this.getSession(sessionDto); - this._onDidStartDebugSession.fire(session); - } - - public async $acceptDebugSessionTerminated(sessionDto: IDebugSessionDto): Promise { - const session = await this.getSession(sessionDto); - if (session) { - this._onDidTerminateDebugSession.fire(session); - this._debugSessions.delete(session.id); - } - } - - public async $acceptDebugSessionActiveChanged(sessionDto: IDebugSessionDto | undefined): Promise { - this._activeDebugSession = sessionDto ? await this.getSession(sessionDto) : undefined; - this._onDidChangeActiveDebugSession.fire(this._activeDebugSession); - } - - public async $acceptDebugSessionNameChanged(sessionDto: IDebugSessionDto, name: string): Promise { - const session = await this.getSession(sessionDto); - if (session) { - session._acceptNameChanged(name); - } - } - - public async $acceptDebugSessionCustomEvent(sessionDto: IDebugSessionDto, event: any): Promise { - const session = await this.getSession(sessionDto); - const ee: vscode.DebugSessionCustomEvent = { - session: session, - event: event.event, - body: event.body - }; - this._onDidReceiveDebugSessionCustomEvent.fire(ee); - } - - // private & dto helpers - - private convertToDto(x: vscode.DebugAdapterDescriptor | undefined): IAdapterDescriptor { - if (x instanceof DebugAdapterExecutable) { - return { - type: 'executable', - command: x.command, - args: x.args, - options: x.options - }; - } else if (x instanceof DebugAdapterServer) { - return { - type: 'server', - port: x.port, - host: x.host - }; - } else /* if (x instanceof DebugAdapterImplementation) { - return { - type: 'implementation', - implementation: x.implementation - }; - } else */ { - throw new Error('convertToDto unexpected type'); - } - } - - private getAdapterFactoryByType(type: string): vscode.DebugAdapterDescriptorFactory | undefined { - const results = this._adapterFactories.filter(p => p.type === type); - if (results.length > 0) { - return results[0].factory; - } - return undefined; - } - - private getAdapterProviderByHandle(handle: number): vscode.DebugAdapterDescriptorFactory | undefined { - const results = this._adapterFactories.filter(p => p.handle === handle); - if (results.length > 0) { - return results[0].factory; - } - return undefined; - } - - private getConfigProviderByHandle(handle: number): vscode.DebugConfigurationProvider | undefined { - const results = this._configProviders.filter(p => p.handle === handle); - if (results.length > 0) { - return results[0].provider; - } - return undefined; - } - - private definesDebugType(ed: IExtensionDescription, type: string) { - if (ed.contributes) { - const debuggers = ed.contributes['debuggers']; - if (debuggers && debuggers.length > 0) { - for (const dbg of debuggers) { - // only debugger contributions with a "label" are considered a "defining" debugger contribution - if (dbg.label && dbg.type) { - if (dbg.type === type) { - return true; - } - } - } - } - } - return false; - } - - private getDebugAdapterTrackers(session: ExtHostDebugSession): Promise { - - const config = session.configuration; - const type = config.type; - - const promises = this._trackerFactories - .filter(tuple => tuple.type === type || tuple.type === '*') - .map(tuple => asPromise>(() => tuple.factory.createDebugAdapterTracker(session)).then(p => p, err => null)); - - return Promise.race([ - Promise.all(promises).then(result => { - const trackers = result.filter(t => !!t); // filter null - if (trackers.length > 0) { - return new MultiTracker(trackers); - } - return undefined; - }), - new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - clearTimeout(timeout); - reject(new Error('timeout')); - }, 1000); - }) - ]).catch(err => { - // ignore errors - return undefined; - }); - } - - private async getAdapterDescriptor(adapterProvider: vscode.DebugAdapterDescriptorFactory | undefined, session: ExtHostDebugSession): Promise { - - // a "debugServer" attribute in the launch config takes precedence - const serverPort = session.configuration.debugServer; - if (typeof serverPort === 'number') { - return Promise.resolve(new DebugAdapterServer(serverPort)); - } - - // TODO@AW legacy - const pair = this._configProviders.filter(p => p.type === session.type).pop(); - if (pair && pair.provider.debugAdapterExecutable) { - const func = pair.provider.debugAdapterExecutable; - return asPromise(() => func(session.workspaceFolder, CancellationToken.None)).then(executable => { - if (executable) { - return executable; - } - return undefined; - }); - } - - if (adapterProvider) { - const extensionRegistry = await this._extensionService.getExtensionRegistry(); - return asPromise(() => adapterProvider.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry))).then(daDescriptor => { - if (daDescriptor) { - return daDescriptor; - } - return undefined; - }); - } - - // try deprecated command based extension API "adapterExecutableCommand" to determine the executable - // TODO@AW legacy - const aex = this._aexCommands.get(session.type); - if (aex) { - const folder = session.workspaceFolder; - const rootFolder = folder ? folder.uri.toString() : undefined; - return this._commandService.executeCommand(aex, rootFolder).then((ae: { command: string, args: string[] }) => { - return new DebugAdapterExecutable(ae.command, ae.args || []); - }); - } - - // fallback: use executable information from package.json - const extensionRegistry = await this._extensionService.getExtensionRegistry(); - return Promise.resolve(this.daExecutableFromPackage(session, extensionRegistry)); - } - - private daExecutableFromPackage(session: ExtHostDebugSession, extensionRegistry: ExtensionDescriptionRegistry): DebugAdapterExecutable | undefined { - const dae = ExecutableDebugAdapter.platformAdapterExecutable(extensionRegistry.getAllExtensionDescriptions(), session.type); - if (dae) { - return new DebugAdapterExecutable(dae.command, dae.args, dae.options); - } - return undefined; - } - - private startBreakpoints() { - if (!this._breakpointEventsActive) { - this._breakpointEventsActive = true; - this._debugServiceProxy.$startBreakpointEvents(); - } - } - - private fireBreakpointChanges(added: vscode.Breakpoint[], removed: vscode.Breakpoint[], changed: vscode.Breakpoint[]) { - if (added.length > 0 || removed.length > 0 || changed.length > 0) { - this._onDidChangeBreakpoints.fire(Object.freeze({ - added, - removed, - changed, - })); - } - } - - private async getSession(dto: IDebugSessionDto): Promise { - if (dto) { - if (typeof dto === 'string') { - const ds = this._debugSessions.get(dto); - if (ds) { - return ds; - } - } else { - let ds = this._debugSessions.get(dto.id); - if (!ds) { - const folder = await this.getFolder(dto.folderUri); - ds = new ExtHostDebugSession(this._debugServiceProxy, dto.id, dto.type, dto.name, folder, dto.configuration); - this._debugSessions.set(ds.id, ds); - this._debugServiceProxy.$sessionCached(ds.id); - } - return ds; - } - } - throw new Error('cannot find session'); - } - - private getFolder(_folderUri: UriComponents | undefined): Promise { - if (_folderUri) { - const folderURI = URI.revive(_folderUri); - return this._workspaceService.resolveWorkspaceFolder(folderURI); - } - return Promise.resolve(undefined); - } -} - -export class ExtHostDebugSession implements vscode.DebugSession { - - constructor( - private _debugServiceProxy: MainThreadDebugServiceShape, - private _id: DebugSessionUUID, - private _type: string, - private _name: string, - private _workspaceFolder: vscode.WorkspaceFolder | undefined, - private _configuration: vscode.DebugConfiguration) { - } - - public get id(): string { - return this._id; - } - - public get type(): string { - return this._type; - } - - public get name(): string { - return this._name; - } - - public set name(name: string) { - this._name = name; - this._debugServiceProxy.$setDebugSessionName(this._id, name); - } - - _acceptNameChanged(name: string) { - this._name = name; - } - - public get workspaceFolder(): vscode.WorkspaceFolder | undefined { - return this._workspaceFolder; - } - - public get configuration(): vscode.DebugConfiguration { - return this._configuration; - } - - public customRequest(command: string, args: any): Promise { - return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args); - } -} - -export class ExtHostDebugConsole implements vscode.DebugConsole { - - private _debugServiceProxy: MainThreadDebugServiceShape; - - constructor(proxy: MainThreadDebugServiceShape) { - this._debugServiceProxy = proxy; - } - - append(value: string): void { - this._debugServiceProxy.$appendDebugConsole(value); - } - - appendLine(value: string): void { - this.append(value + '\n'); + return super.$runInTerminal(args); } } @@ -1032,103 +171,3 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ }, process.env as IProcessEnvironment); } } - -interface ConfigProviderTuple { - type: string; - handle: number; - provider: vscode.DebugConfigurationProvider; -} - -interface DescriptorFactoryTuple { - type: string; - handle: number; - factory: vscode.DebugAdapterDescriptorFactory; -} - -interface TrackerFactoryTuple { - type: string; - handle: number; - factory: vscode.DebugAdapterTrackerFactory; -} - -class MultiTracker implements vscode.DebugAdapterTracker { - - constructor(private trackers: vscode.DebugAdapterTracker[]) { - } - - onWillStartSession(): void { - this.trackers.forEach(t => t.onWillStartSession ? t.onWillStartSession() : undefined); - } - - onWillReceiveMessage(message: any): void { - this.trackers.forEach(t => t.onWillReceiveMessage ? t.onWillReceiveMessage(message) : undefined); - } - - onDidSendMessage(message: any): void { - this.trackers.forEach(t => t.onDidSendMessage ? t.onDidSendMessage(message) : undefined); - } - - onWillStopSession(): void { - this.trackers.forEach(t => t.onWillStopSession ? t.onWillStopSession() : undefined); - } - - onError(error: Error): void { - this.trackers.forEach(t => t.onError ? t.onError(error) : undefined); - } - - onExit(code: number, signal: string): void { - this.trackers.forEach(t => t.onExit ? t.onExit(code, signal) : undefined); - } -} - -interface IDapTransport { - start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void): void; - send(message: DebugProtocol.ProtocolMessage): void; - stop(): void; -} - -class DirectDebugAdapter extends AbstractDebugAdapter implements IDapTransport { - - - private _sendUp!: (msg: DebugProtocol.ProtocolMessage) => void; - - constructor(implementation: any) { - super(); - if (implementation.__setTransport) { - implementation.__setTransport(this); - } - } - - // IDapTransport - start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void) { - this._sendUp = cb; - } - - // AbstractDebugAdapter - startSession(): Promise { - return Promise.resolve(undefined); - } - - // AbstractDebugAdapter - // VSCode -> DA - sendMessage(message: DebugProtocol.ProtocolMessage): void { - this._sendUp(message); - } - - // AbstractDebugAdapter - stopSession(): Promise { - this.stop(); - return Promise.resolve(undefined); - } - - // IDapTransport - // DA -> VSCode - send(message: DebugProtocol.ProtocolMessage) { - this.acceptMessage(message); - } - - // IDapTransport - stop(): void { - throw new Error('Method not implemented.'); - } -} diff --git a/src/vs/workbench/services/extensions/worker/extHost.services.ts b/src/vs/workbench/services/extensions/worker/extHost.services.ts index ed2e8767cf1..8a65101aa4e 100644 --- a/src/vs/workbench/services/extensions/worker/extHost.services.ts +++ b/src/vs/workbench/services/extensions/worker/extHost.services.ts @@ -12,7 +12,7 @@ import { IExtHostCommands, ExtHostCommands } from 'vs/workbench/api/common/extHo import { IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtHostTerminalService, WorkerExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; import { IExtHostTask, WorkerExtHostTask } from 'vs/workbench/api/common/extHostTask'; -import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService'; +import { IExtHostDebugService, WorkerExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService'; import { IExtHostSearch, ExtHostSearch } from 'vs/workbench/api/common/extHostSearch'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; @@ -51,7 +51,7 @@ function NotImplementedProxy(name: ServiceIdentifier): { new(): T } { } registerSingleton(IExtHostTerminalService, WorkerExtHostTerminalService); registerSingleton(IExtHostTask, WorkerExtHostTask); -registerSingleton(IExtHostDebugService, class extends NotImplementedProxy(IExtHostDebugService) { }); +registerSingleton(IExtHostDebugService, WorkerExtHostDebugService); registerSingleton(IExtensionStoragePaths, class extends NotImplementedProxy(IExtensionStoragePaths) { whenReady = Promise.resolve(); }); From e6abf471d86d6f4779015d90c2b010da2738ab81 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 18 Nov 2019 12:08:27 -0800 Subject: [PATCH 017/246] Update js/ts grammars --- extensions/javascript/syntaxes/JavaScript.tmLanguage.json | 8 ++++---- .../javascript/syntaxes/JavaScriptReact.tmLanguage.json | 8 ++++---- extensions/typescript-basics/cgmanifest.json | 2 +- .../typescript-basics/syntaxes/TypeScript.tmLanguage.json | 8 ++++---- .../syntaxes/TypeScriptReact.tmLanguage.json | 8 ++++---- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 47894898ab3..c8377485559 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/477c1b17e273b64af13040c064c9ed62c8b32fba", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/4daff7b8904bc549dfbee8df1e2f7c82194b9f45", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -2451,7 +2451,7 @@ }, { "name": "string.regexp.js", - "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.js" @@ -4572,7 +4572,7 @@ "patterns": [ { "name": "string.regexp.js", - "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.js" @@ -4595,7 +4595,7 @@ }, { "name": "string.regexp.js", - "begin": "((?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.js.jsx" @@ -4595,7 +4595,7 @@ }, { "name": "string.regexp.js.jsx", - "begin": "((?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.ts" @@ -4644,7 +4644,7 @@ }, { "name": "string.regexp.ts", - "begin": "((?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.tsx" @@ -4595,7 +4595,7 @@ }, { "name": "string.regexp.tsx", - "begin": "((? Date: Mon, 18 Nov 2019 12:09:39 -0800 Subject: [PATCH 018/246] Fixing a few more strict function type errors For #81574 --- src/vs/base/node/watcher.ts | 4 ++-- src/vs/base/test/common/event.test.ts | 2 +- .../extensionManagement/common/extensionGalleryService.ts | 4 ++-- src/vs/platform/storage/node/storageIpc.ts | 4 ++-- src/vs/workbench/api/common/extHostQuickOpen.ts | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/base/node/watcher.ts b/src/vs/base/node/watcher.ts index b51f73c6eaa..b71a84a63f1 100644 --- a/src/vs/base/node/watcher.ts +++ b/src/vs/base/node/watcher.ts @@ -10,7 +10,7 @@ import { normalizeNFC } from 'vs/base/common/normalization'; import { toDisposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { exists, readdir } from 'vs/base/node/pfs'; -export function watchFile(path: string, onChange: (type: 'changed' | 'deleted', path: string) => void, onError: (error: string) => void): IDisposable { +export function watchFile(path: string, onChange: (type: 'added' | 'changed' | 'deleted', path: string) => void, onError: (error: string) => void): IDisposable { return doWatchNonRecursive({ path, isDirectory: false }, onChange, onError); } @@ -189,4 +189,4 @@ function doWatchNonRecursive(file: { path: string, isDirectory: boolean }, onCha watcherDisposables = dispose(watcherDisposables); }); -} \ No newline at end of file +} diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 25d47dc6e00..4c05cad2447 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -174,7 +174,7 @@ suite('Event', function () { test('Debounce Event', function (done: () => void) { let doc = new Samples.Document3(); - let onDocDidChange = Event.debounce(doc.onDidChange, (prev: string[], cur) => { + let onDocDidChange = Event.debounce(doc.onDidChange, (prev: string[] | undefined, cur) => { if (!prev) { prev = [cur]; } else if (prev.indexOf(cur) < 0) { diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index a3c9d9b927d..f3fb5ecca84 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -580,7 +580,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { if (extension.assets.manifest) { return this.getAsset(extension.assets.manifest, {}, token) .then(asText) - .then(JSON.parse); + .then(text => text ? JSON.parse(text) : null); } return Promise.resolve(null); } @@ -590,7 +590,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { if (asset) { return this.getAsset(asset[1]) .then(asText) - .then(JSON.parse); + .then(text => text ? JSON.parse(text) : null); } return Promise.resolve(null); } diff --git a/src/vs/platform/storage/node/storageIpc.ts b/src/vs/platform/storage/node/storageIpc.ts index b0a2b3752a5..2d04f0a77f7 100644 --- a/src/vs/platform/storage/node/storageIpc.ts +++ b/src/vs/platform/storage/node/storageIpc.ts @@ -83,7 +83,7 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC // Listen for changes in global storage to send to listeners // that are listening. Use a debouncer to reduce IPC traffic. - this._register(Event.debounce(this.storageMainService.onDidChangeStorage, (prev: IStorageChangeEvent[], cur: IStorageChangeEvent) => { + this._register(Event.debounce(this.storageMainService.onDidChangeStorage, (prev: IStorageChangeEvent[] | undefined, cur: IStorageChangeEvent) => { if (!prev) { prev = [cur]; } else { @@ -161,7 +161,7 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS } private registerListeners(): void { - this.onDidChangeItemsOnMainListener = this.channel.listen('onDidChangeItems')((e: ISerializableItemsChangeEvent) => this.onDidChangeItemsOnMain(e)); + this.onDidChangeItemsOnMainListener = this.channel.listen('onDidChangeItems')((e: ISerializableItemsChangeEvent) => this.onDidChangeItemsOnMain(e)); } private onDidChangeItemsOnMain(e: ISerializableItemsChangeEvent): void { diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index 3a85192f04a..8d089783698 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -160,7 +160,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { // ---- workspace folder picker showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions, token = CancellationToken.None): Promise { - return this._commands.executeCommand('_workbench.pickWorkspaceFolder', [options]).then(async (selectedFolder: WorkspaceFolder) => { + return this._commands.executeCommand('_workbench.pickWorkspaceFolder', [options]).then(async (selectedFolder: WorkspaceFolder) => { if (!selectedFolder) { return undefined; } From 050a44265ce2fa7c766c79e94a1282c3b13dc1a9 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 18 Nov 2019 15:30:08 -0800 Subject: [PATCH 019/246] Mark param readonly for strict function types --- .../test/electron-browser/api/extHostDiagnostics.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts index 43e7087b3be..374f21e78d0 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts @@ -97,10 +97,10 @@ suite('ExtHostDiagnostics', () => { assert.throws(() => array.pop()); assert.throws(() => array[0] = new Diagnostic(new Range(0, 0, 0, 0), 'evil')); - collection.forEach((uri, array: Diagnostic[]) => { - assert.throws(() => array.length = 0); - assert.throws(() => array.pop()); - assert.throws(() => array[0] = new Diagnostic(new Range(0, 0, 0, 0), 'evil')); + collection.forEach((uri, array: readonly Diagnostic[]) => { + assert.throws(() => (array as Diagnostic[]).length = 0); + assert.throws(() => (array as Diagnostic[]).pop()); + assert.throws(() => (array as Diagnostic[])[0] = new Diagnostic(new Range(0, 0, 0, 0), 'evil')); }); array = collection.get(URI.parse('foo:bar')) as Diagnostic[]; From 525b43fc009f0a27d70e3eeb80efb6694dbe225f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 18 Nov 2019 15:36:00 -0800 Subject: [PATCH 020/246] Pick up new TS insiders --- extensions/package.json | 2 +- extensions/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index 688a5c05063..8d6065560c0 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.7.2" + "typescript": "3.7.3-insiders.20191118" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 6cae6ac12de..f1a8a66e077 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" - integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== +typescript@3.7.3-insiders.20191118: + version "3.7.3-insiders.20191118" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3-insiders.20191118.tgz#86d3b1de1859d70b0a081b708a223f8f3c6a4d46" + integrity sha512-r06UsJwJzLwgRkf3Or2T5tBnUmp8RD5gOHkC6ax9mLHu5r7voo1WAUpvG09R92GvdEQsgZXxOUhdEJWEoc/5sA== From 1c9fd8a59787487ca71d8c5ce140569a0e036a15 Mon Sep 17 00:00:00 2001 From: chrisdias Date: Mon, 18 Nov 2019 18:38:21 -0500 Subject: [PATCH 021/246] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b6f0986df88..826068852db 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.41.0", - "distro": "b2b71bcd54560577429f8ee10aa270a41036a09c", + "distro": "2640e642579d97a7fca354ccb76784787f8ff50d", "author": { "name": "Microsoft Corporation" }, From 4a42a6436e227b266c0826e92e387517e896886e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Nov 2019 07:40:22 +0100 Subject: [PATCH 022/246] editor - add and adopt revert() --- .../browser/parts/editor/editorActions.ts | 2 +- src/vs/workbench/common/editor.ts | 3 +++ .../common/editor/untitledTextEditorInput.ts | 4 ++-- .../contrib/files/browser/fileCommands.ts | 22 +++++------------ .../test/browser/fileEditorInput.test.ts | 6 ++++- .../services/editor/browser/editorService.ts | 24 +++++++++++++++---- .../services/editor/common/editorService.ts | 13 ++++++++-- .../common/editor/untitledTextEditor.test.ts | 6 ++++- .../workbench/test/workbenchTestServices.ts | 8 +++++-- 9 files changed, 59 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 8dc609269f7..fc8f42b2a5a 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -646,7 +646,7 @@ export abstract class BaseCloseAllAction extends Action { let saveOrRevert: boolean; if (confirm === ConfirmResult.DONT_SAVE) { - saveOrRevert = await this.editorService.revertAll({ soft: true }); + saveOrRevert = await this.editorService.revertAll({ soft: true, includeUntitled: true }); } else { saveOrRevert = await this.editorService.saveAll({ reason: SaveReason.EXPLICIT, includeUntitled: true }); } diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 0fe32907c4e..1d6a263a79b 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -334,6 +334,9 @@ export interface IRevertOptions { /** * A soft revert will clear dirty state of a working copy * but will not attempt to load it from its persisted state. + * + * This option may be used in scenarios where an editor is + * closed and where we do not require to load the contents. */ soft?: boolean; } diff --git a/src/vs/workbench/common/editor/untitledTextEditorInput.ts b/src/vs/workbench/common/editor/untitledTextEditorInput.ts index 8be428a4931..cc44661e542 100644 --- a/src/vs/workbench/common/editor/untitledTextEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledTextEditorInput.ts @@ -185,14 +185,14 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin return this.doSaveAs(group, () => this.textFileService.saveAs(this.resource, undefined, options), true /* replace editor across all groups */); } - revert(options?: IRevertOptions): Promise { + async revert(options?: IRevertOptions): Promise { if (this.cachedModel) { this.cachedModel.revert(); } this.dispose(); // a reverted untitled text editor is no longer valid, so we dispose it - return Promise.resolve(true); + return true; } suggestFileName(): string { diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 962a8183061..ca9a702ee87 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -412,23 +412,13 @@ CommandsRegistry.registerCommand({ const notificationService = accessor.get(INotificationService); const listService = accessor.get(IListService); const editorGroupsService = accessor.get(IEditorGroupsService); + const editorService = accessor.get(IEditorService); - const editors = getMultiSelectedEditors(listService, editorGroupsService); - if (editors.length) { - try { - await Promise.all(editors.map(async ({ groupId, editor }) => { - if (editor.isUntitled()) { - return; // we do not allow to revert untitled editors - } - - // Use revert as a hint to pin the editor - editorGroupsService.getGroup(groupId)?.pinEditor(editor); - - return editor.revert({ force: true }); - })); - } catch (error) { - notificationService.error(nls.localize('genericRevertError', "Failed to revert '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); - } + const editors = getMultiSelectedEditors(listService, editorGroupsService).filter(({ editor }) => !editor.isUntitled() /* all except untitled */); + try { + await editorService.revert(editors, { force: true }); + } catch (error) { + notificationService.error(nls.localize('genericRevertError', "Failed to revert '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); } } }); diff --git a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts index 35e56e70a5f..932c54fa018 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts @@ -153,8 +153,12 @@ suite('Files - FileEditorInput', () => { resolved.textEditorModel!.setValue('changed'); assert.ok(input.isDirty()); - await input.revert(); + assert.ok(await input.revert()); assert.ok(!input.isDirty()); + + input.dispose(); + assert.ok(input.isDisposed()); + resolved.dispose(); }); diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 4562afe63e5..6fdebe6ca2c 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -19,7 +19,7 @@ import { basename, isEqual } from 'vs/base/common/resources'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { localize } from 'vs/nls'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, GroupChangeKind, preferredSideBySideGroupDirection, EditorsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IResourceEditor, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IVisibleEditor, IEditorService, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, ISaveEditorsOptions, ISaveAllEditorsOptions } from 'vs/workbench/services/editor/common/editorService'; +import { IResourceEditor, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IVisibleEditor, IEditorService, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, ISaveEditorsOptions, ISaveAllEditorsOptions, IRevertAllEditorsOptions } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { coalesce } from 'vs/base/common/arrays'; @@ -658,7 +658,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#endregion - //#region save + //#region save/revert async save(editors: IEditorIdentifier | IEditorIdentifier[], options?: ISaveEditorsOptions): Promise { @@ -715,12 +715,28 @@ export class EditorService extends Disposable implements EditorServiceImpl { return this.save(this.getAllDirtyEditors(!!options?.includeUntitled), options); } - async revertAll(options?: IRevertOptions): Promise { - const result = await Promise.all(this.getAllDirtyEditors(true /* include untitled */).map(async ({ editor }) => editor.revert(options))); + async revert(editors: IEditorIdentifier | IEditorIdentifier[], options?: IRevertOptions): Promise { + + // Convert to array + if (!Array.isArray(editors)) { + editors = [editors]; + } + + const result = await Promise.all(editors.map(async ({ groupId, editor }) => { + + // Use revert as a hint to pin the editor + this.editorGroupService.getGroup(groupId)?.pinEditor(editor); + + return editor.revert(options); + })); return result.every(success => !!success); } + async revertAll(options?: IRevertAllEditorsOptions): Promise { + return this.revert(this.getAllDirtyEditors(!!options?.includeUntitled), options); + } + private getAllDirtyEditors(includeUntitled: boolean): IEditorIdentifier[] { const editors: IEditorIdentifier[] = []; diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index 6bb7691e0b6..af64d9b7a1a 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -52,7 +52,7 @@ export interface ISaveEditorsOptions extends ISaveOptions { saveAs?: boolean; } -export interface ISaveAllEditorsOptions extends ISaveEditorsOptions { +export interface IBaseSaveRevertAllEditorOptions { /** * Wether to include untitled editors as well. @@ -60,6 +60,10 @@ export interface ISaveAllEditorsOptions extends ISaveEditorsOptions { includeUntitled?: boolean; } +export interface ISaveAllEditorsOptions extends ISaveEditorsOptions, IBaseSaveRevertAllEditorOptions { } + +export interface IRevertAllEditorsOptions extends IRevertOptions, IBaseSaveRevertAllEditorOptions { } + export interface IEditorService { _serviceBrand: undefined; @@ -212,8 +216,13 @@ export interface IEditorService { */ saveAll(options?: ISaveAllEditorsOptions): Promise; + /** + * Reverts the provided list of editors. + */ + revert(editors: IEditorIdentifier | IEditorIdentifier[], options?: IRevertOptions): Promise; + /** * Reverts all editors. */ - revertAll(options?: IRevertOptions): Promise; + revertAll(options?: IRevertAllEditorsOptions): Promise; } diff --git a/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts b/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts index 78cb84d606e..f9f9f5eb20e 100644 --- a/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts +++ b/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts @@ -98,9 +98,13 @@ suite('Workbench untitled text editors', () => { assert.ok(!workingCopyService.isDirty(input2.getResource())); assert.equal(workingCopyService.dirtyCount, 0); - input2.dispose(); + assert.ok(input1.revert()); + assert.ok(input1.isDisposed()); + assert.ok(!service.exists(input1.getResource())); + input2.dispose(); assert.ok(!service.exists(input2.getResource())); + done(); }); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 861d7bc9a7c..5b0e3cdadef 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -57,7 +57,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IDecorationsService, IResourceDecorationChangeEvent, IDecoration, IDecorationData, IDecorationsProvider } from 'vs/workbench/services/decorations/browser/decorations'; import { IDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IMoveEditorOptions, ICopyEditorOptions, IEditorReplacement, IGroupChangeEvent, EditorsOrder, IFindGroupScope, EditorGroupLayout, ICloseEditorOptions } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IEditorService, IOpenEditorOverrideHandler, IVisibleEditor, ISaveEditorsOptions } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService, IOpenEditorOverrideHandler, IVisibleEditor, ISaveEditorsOptions, IRevertAllEditorsOptions } from 'vs/workbench/services/editor/common/editorService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; @@ -930,7 +930,11 @@ export class TestEditorService implements EditorServiceImpl { throw new Error('Method not implemented.'); } - revertAll(options?: IRevertOptions): Promise { + revert(editors: IEditorIdentifier[], options?: IRevertOptions): Promise { + throw new Error('Method not implemented.'); + } + + revertAll(options?: IRevertAllEditorsOptions): Promise { throw new Error('Method not implemented.'); } } From f5e791a6c3702e34ccdc860ab463463341b1c6e1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Nov 2019 07:49:21 +0100 Subject: [PATCH 023/246] editors - reuse options --- .../workbench/services/editor/browser/editorService.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 6fdebe6ca2c..a24a906db1f 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -19,7 +19,7 @@ import { basename, isEqual } from 'vs/base/common/resources'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { localize } from 'vs/nls'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, GroupChangeKind, preferredSideBySideGroupDirection, EditorsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IResourceEditor, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IVisibleEditor, IEditorService, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, ISaveEditorsOptions, ISaveAllEditorsOptions, IRevertAllEditorsOptions } from 'vs/workbench/services/editor/common/editorService'; +import { IResourceEditor, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IVisibleEditor, IEditorService, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, ISaveEditorsOptions, ISaveAllEditorsOptions, IRevertAllEditorsOptions, IBaseSaveRevertAllEditorOptions } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { coalesce } from 'vs/base/common/arrays'; @@ -712,7 +712,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { } saveAll(options?: ISaveAllEditorsOptions): Promise { - return this.save(this.getAllDirtyEditors(!!options?.includeUntitled), options); + return this.save(this.getAllDirtyEditors(options), options); } async revert(editors: IEditorIdentifier | IEditorIdentifier[], options?: IRevertOptions): Promise { @@ -734,15 +734,15 @@ export class EditorService extends Disposable implements EditorServiceImpl { } async revertAll(options?: IRevertAllEditorsOptions): Promise { - return this.revert(this.getAllDirtyEditors(!!options?.includeUntitled), options); + return this.revert(this.getAllDirtyEditors(options), options); } - private getAllDirtyEditors(includeUntitled: boolean): IEditorIdentifier[] { + private getAllDirtyEditors(options?: IBaseSaveRevertAllEditorOptions): IEditorIdentifier[] { const editors: IEditorIdentifier[] = []; for (const group of this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE)) { for (const editor of group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)) { - if (editor.isDirty() && (!editor.isUntitled() || includeUntitled)) { + if (editor.isDirty() && (!editor.isUntitled() || !!options?.includeUntitled)) { editors.push({ groupId: group.id, editor }); } } From 027e16ff6999d3c0d3891c34942825633de4e3bb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Nov 2019 07:56:46 +0100 Subject: [PATCH 024/246] notifications - fix broken animation --- .../browser/parts/notifications/media/notificationsToasts.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css b/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css index 3f07c0123ab..f1ab3c2cf1c 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css @@ -27,10 +27,9 @@ .monaco-workbench > .notifications-toasts .notification-toast-container > .notification-toast { margin: 5px; /* enables separation and drop shadows around toasts */ - transform: translateY(100%); /* move the notification 50px to the bottom (to prevent bleed through) */ + transform: translate3d(0px, 100%, 0px); /* move the notification 50px to the bottom (to prevent bleed through) */ opacity: 0; /* fade the toast in */ transition: transform 300ms ease-out, opacity 300ms ease-out; - transform: translate3d(0px, 0px, 0px); /* force a separate layer for the toast to speed things up */ } .monaco-workbench > .notifications-toasts .notification-toast-container > .notification-toast.notification-fade-in { From 782f1592de7733cfbc6675300371ae8fd88bdd57 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Nov 2019 08:33:26 +0100 Subject: [PATCH 025/246] fix #84973 --- src/vs/base/node/encoding.ts | 4 ++-- .../base/test/node/encoding/encoding.test.ts | 4 ++-- .../electron-browser/nativeTextFileService.ts | 10 +++----- .../textfile/test/textFileService.io.test.ts | 24 +++++++++---------- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index c4cd563ef8f..3fbae109f00 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -167,7 +167,7 @@ function toNodeEncoding(enc: string | null): string { return enc; } -export function detectEncodingByBOMFromBuffer(buffer: Buffer | VSBuffer | null, bytesRead: number): string | null { +export function detectEncodingByBOMFromBuffer(buffer: Buffer | VSBuffer | null, bytesRead: number): typeof UTF8_with_bom | typeof UTF16le | typeof UTF16be | null { if (!buffer || bytesRead < UTF16be_BOM.length) { return null; } @@ -193,7 +193,7 @@ export function detectEncodingByBOMFromBuffer(buffer: Buffer | VSBuffer | null, // UTF-8 if (b0 === UTF8_BOM[0] && b1 === UTF8_BOM[1] && b2 === UTF8_BOM[2]) { - return UTF8; + return UTF8_with_bom; } return null; diff --git a/src/vs/base/test/node/encoding/encoding.test.ts b/src/vs/base/test/node/encoding/encoding.test.ts index eef17fbddb6..a78f2ed6b46 100644 --- a/src/vs/base/test/node/encoding/encoding.test.ts +++ b/src/vs/base/test/node/encoding/encoding.test.ts @@ -9,7 +9,7 @@ import * as encoding from 'vs/base/node/encoding'; import { Readable } from 'stream'; import { getPathFromAmdModule } from 'vs/base/common/amd'; -export async function detectEncodingByBOM(file: string): Promise { +export async function detectEncodingByBOM(file: string): Promise { try { const { buffer, bytesRead } = await readExactlyByFile(file, 3); @@ -86,7 +86,7 @@ suite('Encoding', () => { const file = getPathFromAmdModule(require, './fixtures/some_utf8.css'); const detectedEncoding = await detectEncodingByBOM(file); - assert.equal(detectedEncoding, 'utf8'); + assert.equal(detectedEncoding, 'utf8bom'); }); test('detectBOM UTF-16 LE', async () => { diff --git a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts index e105588731a..9b748aef08b 100644 --- a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts @@ -375,7 +375,7 @@ export class EncodingOracle extends Disposable implements IResourceEncodings { if (!overwriteEncoding && encoding === UTF8) { try { const buffer = (await this.fileService.readFile(resource, { length: UTF8_BOM.length })).value; - if (detectEncodingByBOMFromBuffer(buffer, buffer.byteLength) === UTF8) { + if (detectEncodingByBOMFromBuffer(buffer, buffer.byteLength) === UTF8_with_bom) { return { encoding, addBOM: true }; } } catch (error) { @@ -400,7 +400,7 @@ export class EncodingOracle extends Disposable implements IResourceEncodings { // Encoding passed in as option if (options?.encoding) { - if (detectedEncoding === UTF8 && options.encoding === UTF8) { + if (detectedEncoding === UTF8_with_bom && options.encoding === UTF8) { preferredEncoding = UTF8_with_bom; // indicate the file has BOM if we are to resolve with UTF 8 } else { preferredEncoding = options.encoding; // give passed in encoding highest priority @@ -409,11 +409,7 @@ export class EncodingOracle extends Disposable implements IResourceEncodings { // Encoding detected else if (detectedEncoding) { - if (detectedEncoding === UTF8) { - preferredEncoding = UTF8_with_bom; // if we detected UTF-8, it can only be because of a BOM - } else { - preferredEncoding = detectedEncoding; - } + preferredEncoding = detectedEncoding; } // Encoding configured diff --git a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts index 79eedaa0e67..eae7fcd7217 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts @@ -172,7 +172,7 @@ suite('Files - TextFileService i/o', () => { assert.equal(await exists(resource.fsPath), true); const detectedEncoding = await detectEncodingByBOM(resource.fsPath); - assert.equal(detectedEncoding, UTF8); + assert.equal(detectedEncoding, UTF8_with_bom); }); test('create - UTF 8 BOM - content provided', async () => { @@ -183,7 +183,7 @@ suite('Files - TextFileService i/o', () => { assert.equal(await exists(resource.fsPath), true); const detectedEncoding = await detectEncodingByBOM(resource.fsPath); - assert.equal(detectedEncoding, UTF8); + assert.equal(detectedEncoding, UTF8_with_bom); }); test('create - UTF 8 BOM - empty content - snapshot', async () => { @@ -194,7 +194,7 @@ suite('Files - TextFileService i/o', () => { assert.equal(await exists(resource.fsPath), true); const detectedEncoding = await detectEncodingByBOM(resource.fsPath); - assert.equal(detectedEncoding, UTF8); + assert.equal(detectedEncoding, UTF8_with_bom); }); test('create - UTF 8 BOM - content provided - snapshot', async () => { @@ -205,7 +205,7 @@ suite('Files - TextFileService i/o', () => { assert.equal(await exists(resource.fsPath), true); const detectedEncoding = await detectEncodingByBOM(resource.fsPath); - assert.equal(detectedEncoding, UTF8); + assert.equal(detectedEncoding, UTF8_with_bom); }); test('write - use encoding (UTF 16 BE) - small content as string', async () => { @@ -325,12 +325,12 @@ suite('Files - TextFileService i/o', () => { await service.write(resource, content, { encoding: UTF8_with_bom }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); - assert.equal(detectedEncoding, UTF8); + assert.equal(detectedEncoding, UTF8_with_bom); // ensure BOM preserved await service.write(resource, content, { encoding: UTF8 }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); - assert.equal(detectedEncoding, UTF8); + assert.equal(detectedEncoding, UTF8_with_bom); // allow to remove BOM await service.write(resource, content, { encoding: UTF8, overwriteEncoding: true }); @@ -353,12 +353,12 @@ suite('Files - TextFileService i/o', () => { await service.write(resource, model.createSnapshot(), { encoding: UTF8_with_bom }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); - assert.equal(detectedEncoding, UTF8); + assert.equal(detectedEncoding, UTF8_with_bom); // ensure BOM preserved await service.write(resource, model.createSnapshot(), { encoding: UTF8 }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); - assert.equal(detectedEncoding, UTF8); + assert.equal(detectedEncoding, UTF8_with_bom); // allow to remove BOM await service.write(resource, model.createSnapshot(), { encoding: UTF8, overwriteEncoding: true }); @@ -375,11 +375,11 @@ suite('Files - TextFileService i/o', () => { const resource = URI.file(join(testDir, 'some_utf8_bom.txt')); let detectedEncoding = await detectEncodingByBOM(resource.fsPath); - assert.equal(detectedEncoding, UTF8); + assert.equal(detectedEncoding, UTF8_with_bom); await service.write(resource, 'Hello World'); detectedEncoding = await detectEncodingByBOM(resource.fsPath); - assert.equal(detectedEncoding, UTF8); + assert.equal(detectedEncoding, UTF8_with_bom); }); test('write - ensure BOM in empty file - content as string', async () => { @@ -388,7 +388,7 @@ suite('Files - TextFileService i/o', () => { await service.write(resource, '', { encoding: UTF8_with_bom }); let detectedEncoding = await detectEncodingByBOM(resource.fsPath); - assert.equal(detectedEncoding, UTF8); + assert.equal(detectedEncoding, UTF8_with_bom); }); test('write - ensure BOM in empty file - content as snapshot', async () => { @@ -397,7 +397,7 @@ suite('Files - TextFileService i/o', () => { await service.write(resource, TextModel.createFromString('').createSnapshot(), { encoding: UTF8_with_bom }); let detectedEncoding = await detectEncodingByBOM(resource.fsPath); - assert.equal(detectedEncoding, UTF8); + assert.equal(detectedEncoding, UTF8_with_bom); }); test('readStream - small text', async () => { From 2079fd13e6377cdaa9a236046bd1a9077467376b Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Tue, 19 Nov 2019 02:57:36 -0500 Subject: [PATCH 026/246] Force blur when disabling input element. Fixes #85033 --- src/vs/base/browser/ui/inputbox/inputBox.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 28d20f9f371..38161148cd9 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -305,6 +305,7 @@ export class InputBox extends Widget { } public disable(): void { + this.blur(); this.input.disabled = true; this._hideMessage(); } From 144111485b55bc424d380a6fec2edff59293e7e8 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Nov 2019 10:10:59 +0100 Subject: [PATCH 027/246] add tests #79519 --- .../test/browser/parts/views/views.test.ts | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/test/browser/parts/views/views.test.ts b/src/vs/workbench/test/browser/parts/views/views.test.ts index 9ae0966307c..54121d9571a 100644 --- a/src/vs/workbench/test/browser/parts/views/views.test.ts +++ b/src/vs/workbench/test/browser/parts/views/views.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ContributableViewsModel, ViewsService } from 'vs/workbench/browser/parts/views/views'; +import { ContributableViewsModel, ViewsService, IViewState } from 'vs/workbench/browser/parts/views/views'; import { IViewsRegistry, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewsService } from 'vs/workbench/common/views'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { move } from 'vs/base/common/arrays'; @@ -244,4 +244,54 @@ suite('ContributableViewsModel', () => { assert.deepEqual(model.visibleViewDescriptors, [view1, view2, view3], 'view2 should go to the middle'); assert.deepEqual(seq.elements, [view1, view2, view3]); }); + + test('view states', async function () { + const viewStates = new Map(); + viewStates.set('view1', { visibleGlobal: false, collapsed: false, visibleWorkspace: undefined }); + const model = new ContributableViewsModel(container, viewsService, viewStates); + const seq = new ViewDescriptorSequence(model); + + assert.equal(model.visibleViewDescriptors.length, 0); + assert.equal(seq.elements.length, 0); + + const viewDescriptor: IViewDescriptor = { + id: 'view1', + ctorDescriptor: null!, + name: 'Test View 1' + }; + + ViewsRegistry.registerViews([viewDescriptor], container); + assert.equal(model.visibleViewDescriptors.length, 0, 'view should not appear since it was set not visible in view state'); + assert.equal(seq.elements.length, 0); + }); + + test('view states and when contexts', async function () { + const viewStates = new Map(); + viewStates.set('view1', { visibleGlobal: false, collapsed: false, visibleWorkspace: undefined }); + const model = new ContributableViewsModel(container, viewsService, viewStates); + const seq = new ViewDescriptorSequence(model); + + assert.equal(model.visibleViewDescriptors.length, 0); + assert.equal(seq.elements.length, 0); + + const viewDescriptor: IViewDescriptor = { + id: 'view1', + ctorDescriptor: null!, + name: 'Test View 1', + when: ContextKeyExpr.equals('showview1', true) + }; + + ViewsRegistry.registerViews([viewDescriptor], container); + assert.equal(model.visibleViewDescriptors.length, 0, 'view should not appear since context isnt in'); + assert.equal(seq.elements.length, 0); + + const key = contextKeyService.createKey('showview1', false); + assert.equal(model.visibleViewDescriptors.length, 0, 'view should still not appear since showview1 isnt true'); + assert.equal(seq.elements.length, 0); + + key.set(true); + await new Promise(c => setTimeout(c, 30)); + assert.equal(model.visibleViewDescriptors.length, 0, 'view should still not appear since it was set not visible in view state'); + assert.equal(seq.elements.length, 0); + }); }); From bacf3ba1630fab98c8b166dd99dbf629813af479 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Nov 2019 10:12:24 +0100 Subject: [PATCH 028/246] #79519 filter non visible views from add and remove events --- src/vs/workbench/browser/parts/views/views.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index abab26cccde..8eceaffcb90 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -436,14 +436,15 @@ export class ContributableViewsModel extends Disposable { this._viewDescriptors = viewDescriptors.sort(this.compareViewDescriptors.bind(this)); for (let index = 0; index < previous.length; index++) { - if (this._viewDescriptors.every(viewDescriptor => viewDescriptor.id !== previous[index].id)) { - toRemove.push({ index, viewDescriptor: previous[index] }); + const previousViewDescriptor = previous[index]; + if (this.isViewDescriptorVisible(previousViewDescriptor) && this._viewDescriptors.every(viewDescriptor => viewDescriptor.id !== previousViewDescriptor.id)) { + toRemove.push({ index, viewDescriptor: previousViewDescriptor }); } } for (let index = 0; index < this._viewDescriptors.length; index++) { const viewDescriptor = this._viewDescriptors[index]; - if (previous.every(previousViewDescriptor => previousViewDescriptor.id !== viewDescriptor.id)) { + if (this.isViewDescriptorVisible(viewDescriptor) && previous.every(previousViewDescriptor => previousViewDescriptor.id !== viewDescriptor.id)) { const state = this.viewStates.get(viewDescriptor.id)!; toAdd.push({ index, viewDescriptor, size: state.size, collapsed: !!state.collapsed }); } From 69ffc7f593723fdc2abfe67c80e674f577d9060e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Nov 2019 09:40:26 +0100 Subject: [PATCH 029/246] remove console.warn for macOS --- src/vs/base/browser/ui/list/listWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 5bc68faa7f1..30dde3e1f90 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -715,7 +715,7 @@ export class DefaultStyleController implements IStyleController { if (styles.listBackground) { if (styles.listBackground.isOpaque()) { content.push(`.monaco-list${suffix} .monaco-list-rows { background: ${styles.listBackground}; }`); - } else { + } else if (!platform.isMacintosh) { // subpixel AA doesn't exist in macOS console.warn(`List with id '${this.selectorSuffix}' was styled with a non-opaque background color. This will break sub-pixel antialiasing.`); } } From 4a581d25d4115677cbc015d812ec2eda1a22f099 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Nov 2019 10:15:18 +0100 Subject: [PATCH 030/246] scm: fix subpixel aa related to #84715 --- src/vs/workbench/contrib/scm/browser/mainPanel.ts | 6 +++++- .../workbench/contrib/scm/browser/repositoryPanel.ts | 12 ++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/mainPanel.ts b/src/vs/workbench/contrib/scm/browser/mainPanel.ts index d45bdb1b57c..c8903522901 100644 --- a/src/vs/workbench/contrib/scm/browser/mainPanel.ts +++ b/src/vs/workbench/contrib/scm/browser/mainPanel.ts @@ -29,6 +29,7 @@ import { renderCodicons } from 'vs/base/browser/ui/codiconLabel/codiconLabel'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewDescriptor } from 'vs/workbench/common/views'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; export interface ISpliceEvent { index: number; @@ -195,7 +196,10 @@ export class MainPanel extends ViewletPanel { this.list = this.instantiationService.createInstance>(WorkbenchList, `SCM Main`, container, delegate, [renderer], { identityProvider, - horizontalScrolling: false + horizontalScrolling: false, + overrideStyles: { + listBackground: SIDE_BAR_BACKGROUND + } }); this._register(renderer.onDidRenderElement(e => this.list.updateWidth(this.viewModel.repositories.indexOf(e)), null)); diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts b/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts index 3c026cf10a3..894717d716f 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts @@ -38,7 +38,7 @@ import * as platform from 'vs/base/common/platform'; import { ITreeNode, ITreeFilter, ITreeSorter, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { ResourceTree, IResourceNode } from 'vs/base/common/resourceTree'; import { ISequence, ISplice } from 'vs/base/common/sequence'; -import { ObjectTree, ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/tree/objectTree'; +import { ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/tree/objectTree'; import { Iterator } from 'vs/base/common/iterator'; import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; import { URI } from 'vs/base/common/uri'; @@ -52,6 +52,7 @@ import { memoize } from 'vs/base/common/decorators'; import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; type TreeElement = ISCMResourceGroup | IResourceNode | ISCMResource; @@ -428,7 +429,7 @@ class ViewModel { constructor( private groups: ISequence, - private tree: ObjectTree, + private tree: WorkbenchCompressibleObjectTree, private _mode: ViewModelMode, @IEditorService protected editorService: IEditorService, @IConfigurationService protected configurationService: IConfigurationService, @@ -591,7 +592,7 @@ export class RepositoryPanel extends ViewletPanel { private inputBoxContainer!: HTMLElement; private inputBox!: InputBox; private listContainer!: HTMLElement; - private tree!: ObjectTree; + private tree!: WorkbenchCompressibleObjectTree; private viewModel!: ViewModel; private listLabels!: ResourceLabels; private menus: SCMMenus; @@ -744,7 +745,10 @@ export class RepositoryPanel extends ViewletPanel { horizontalScrolling: false, filter, sorter, - keyboardNavigationLabelProvider + keyboardNavigationLabelProvider, + overrideStyles: { + listBackground: SIDE_BAR_BACKGROUND + } }); this._register(Event.chain(this.tree.onDidOpen) From ae0d5c909f402400887343028cde696b463026f8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Nov 2019 10:19:04 +0100 Subject: [PATCH 031/246] check for disposed lenses before updating them, #85093 --- src/vs/editor/contrib/codelens/codelensController.ts | 2 +- src/vs/editor/contrib/codelens/codelensWidget.ts | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 852571ac070..40fca0ab3bd 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -367,7 +367,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { }); return Promise.all(promises).then(() => { - if (!token.isCancellationRequested) { + if (!token.isCancellationRequested && !lenses[i].isDisposed()) { lenses[i].updateCommands(resolvedSymbols); } }); diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index c046279ce16..7470178f85d 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -68,7 +68,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { line: number, ) { this._editor = editor; - this._id = (CodeLensContentWidget._idPool++).toString(); + this._id = `codelens.widget-${(CodeLensContentWidget._idPool++)}`; this.updatePosition(line); @@ -188,6 +188,7 @@ export class CodeLensWidget { private _contentWidget?: CodeLensContentWidget; private _decorationIds: string[]; private _data: CodeLensItem[]; + private _isDisposed: boolean = false; constructor( data: CodeLensItem[], @@ -250,7 +251,13 @@ export class CodeLensWidget { } if (this._contentWidget) { this._editor.removeContentWidget(this._contentWidget); + this._contentWidget = undefined; } + this._isDisposed = true; + } + + isDisposed(): boolean { + return this._isDisposed; } isValid(): boolean { @@ -315,7 +322,7 @@ export class CodeLensWidget { } update(viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { - if (this.isValid() && this._editor.hasModel()) { + if (this.isValid()) { const range = this._editor.getModel().getDecorationRange(this._decorationIds[0]); if (range) { this._viewZone.afterLineNumber = range.startLineNumber - 1; From db597dff739d46d487499c93fb765f6d98447ef4 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Nov 2019 10:21:49 +0100 Subject: [PATCH 032/246] search viewlet: fix subpixel aa --- src/vs/workbench/contrib/search/browser/searchView.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index bd2d3c226e4..8d9eb83dc94 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -61,6 +61,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; const $ = dom.$; @@ -651,7 +652,10 @@ export class SearchView extends ViewletPanel { identityProvider, accessibilityProvider: this.instantiationService.createInstance(SearchAccessibilityProvider, this.viewModel), dnd: this.instantiationService.createInstance(SearchDND), - multipleSelectionSupport: false + multipleSelectionSupport: false, + overrideStyles: { + listBackground: SIDE_BAR_BACKGROUND + } })); this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); From d4126a6f2f15e3246a0f5fd1ef191721d79b5931 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Nov 2019 10:33:06 +0100 Subject: [PATCH 033/246] settings: fix subpixel aa --- .../preferences/browser/settingsTree.ts | 21 ++++++++++--------- .../contrib/preferences/browser/tocTree.ts | 1 + 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index f732f7672dd..592b3194e54 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -36,7 +36,7 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; +import { errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ITOCEntry } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; @@ -1517,20 +1517,21 @@ export class SettingsTree extends ObjectTree { })); this.disposables.add(attachStyler(themeService, { - listActiveSelectionBackground: transparent(Color.white, 0), + listBackground: editorBackground, + listActiveSelectionBackground: editorBackground, listActiveSelectionForeground: foreground, - listFocusAndSelectionBackground: transparent(Color.white, 0), + listFocusAndSelectionBackground: editorBackground, listFocusAndSelectionForeground: foreground, - listFocusBackground: transparent(Color.white, 0), + listFocusBackground: editorBackground, listFocusForeground: foreground, listHoverForeground: foreground, - listHoverBackground: transparent(Color.white, 0), - listHoverOutline: transparent(Color.white, 0), - listFocusOutline: transparent(Color.white, 0), - listInactiveSelectionBackground: transparent(Color.white, 0), + listHoverBackground: editorBackground, + listHoverOutline: editorBackground, + listFocusOutline: editorBackground, + listInactiveSelectionBackground: editorBackground, listInactiveSelectionForeground: foreground, - listInactiveFocusBackground: transparent(Color.white, 0), - listInactiveFocusOutline: transparent(Color.white, 0) + listInactiveFocusBackground: editorBackground, + listInactiveFocusOutline: editorBackground }, colors => { this.style(colors); })); diff --git a/src/vs/workbench/contrib/preferences/browser/tocTree.ts b/src/vs/workbench/contrib/preferences/browser/tocTree.ts index 301eaf966c5..cb7b19f19f2 100644 --- a/src/vs/workbench/contrib/preferences/browser/tocTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/tocTree.ts @@ -209,6 +209,7 @@ export class TOCTree extends ObjectTree { options); this.disposables.add(attachStyler(themeService, { + listBackground: editorBackground, listActiveSelectionBackground: editorBackground, listActiveSelectionForeground: settingsHeaderForeground, listFocusAndSelectionBackground: editorBackground, From 7ddd0c0faa3182017df7bd2090d4b7332ae6cb23 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Nov 2019 10:37:07 +0100 Subject: [PATCH 034/246] custom trees: fix subpixel aa related to #84715 --- src/vs/workbench/browser/parts/views/customView.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index c48f38e7118..9a47283f370 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -41,6 +41,7 @@ import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } fro import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; +import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; export class CustomTreeViewPanel extends ViewletPanel { @@ -403,6 +404,9 @@ export class CustomTreeView extends Disposable implements ITreeView { return e.collapsibleState !== TreeItemCollapsibleState.Expanded; }, multipleSelectionSupport: this.canSelectMany, + overrideStyles: { + listBackground: SIDE_BAR_BACKGROUND + } }) as WorkbenchAsyncDataTree); aligner.tree = this.tree; const actionRunner = new MultipleSelectionActionRunner(this.notificationService, () => this.tree!.getSelection()); From f5aa62a37d195a60175958112276c137a5ec8fd3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Nov 2019 10:43:52 +0100 Subject: [PATCH 035/246] explorer: fix right click --- .../workbench/contrib/files/browser/views/explorerView.ts | 8 ++++++-- .../contrib/files/browser/views/explorerViewer.ts | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 010f7e545d3..9d585d6d276 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -29,7 +29,7 @@ import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; -import { ExplorerDelegate, ExplorerAccessibilityProvider, ExplorerDataSource, FilesRenderer, ICompressedNavigationController, FilesFilter, FileSorter, FileDragAndDrop, ExplorerCompressionDelegate } from 'vs/workbench/contrib/files/browser/views/explorerViewer'; +import { ExplorerDelegate, ExplorerAccessibilityProvider, ExplorerDataSource, FilesRenderer, ICompressedNavigationController, FilesFilter, FileSorter, FileDragAndDrop, ExplorerCompressionDelegate, isCompressedFolderName } from 'vs/workbench/contrib/files/browser/views/explorerViewer'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; @@ -475,7 +475,11 @@ export class ExplorerView extends ViewletPanel { const controller = this.renderer.getCompressedNavigationController(stat); if (controller) { - anchor = controller.labels[controller.index]; + if (isCompressedFolderName(e.browserEvent.target)) { + anchor = controller.labels[controller.index]; + } else { + controller.last(); + } } } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 8bb84ec2795..7e1d56a9a1e 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -1096,6 +1096,10 @@ function getIconLabelNameFromHTMLElement(target: HTMLElement | EventTarget | Ele return null; } +export function isCompressedFolderName(target: HTMLElement | EventTarget | Element | null): boolean { + return !!getIconLabelNameFromHTMLElement(target); +} + export class ExplorerCompressionDelegate implements ITreeCompressionDelegate { isIncompressible(stat: ExplorerItem): boolean { From e0a43e6f6d9cac62b8f791bee8fb109123be1c37 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 19 Nov 2019 10:46:14 +0100 Subject: [PATCH 036/246] fixes #85080 --- src/vs/platform/files/common/fileService.ts | 6 +++--- src/vs/workbench/contrib/files/browser/fileActions.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index d914b9c5810..b8250ae4267 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -683,7 +683,7 @@ export class FileService extends Disposable implements IFileService { } if (!isSameResourceWithDifferentPathCase && isEqualOrParent(target, source, !isPathCaseSensitive)) { - throw new Error(localize('unableToMoveCopyError2', "Unable to move/copy when source is parent of target")); + throw new Error(localize('unableToMoveCopyError2', "Unable to move/copy when source is parent of target.")); } } @@ -693,7 +693,7 @@ export class FileService extends Disposable implements IFileService { // Bail out if target exists and we are not about to overwrite if (!overwrite) { - throw new FileOperationError(localize('unableToMoveCopyError3', "Unable to move/copy. File already exists at destination."), FileOperationResult.FILE_MOVE_CONFLICT); + throw new FileOperationError(localize('unableToMoveCopyError3', "Unable to move/copy since a file already exists at destination."), FileOperationResult.FILE_MOVE_CONFLICT); } // Special case: if the target is a parent of the source, we cannot delete @@ -701,7 +701,7 @@ export class FileService extends Disposable implements IFileService { if (sourceProvider === targetProvider) { const isPathCaseSensitive = !!(sourceProvider.capabilities & FileSystemProviderCapabilities.PathCaseSensitive); if (isEqualOrParent(source, target, !isPathCaseSensitive)) { - throw new Error(localize('unableToMoveCopyError4', "Unable to move/copy. File would replace folder it is contained in.")); + throw new Error(localize('unableToMoveCopyError4', "Unable to move/copy since a file would replace the folder it is contained in.")); } } } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 72d5dc4669a..b21409c7e63 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -1046,7 +1046,7 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => { return await fileService.copy(fileToPaste, targetFile); } } catch (e) { - onError(notificationService, new Error(nls.localize('fileDeleted', "File to paste was deleted or moved meanwhile. {0}", getErrorMessage(e)))); + onError(notificationService, new Error(nls.localize('fileDeleted', "The file to paste has been deleted or moved since you copied it. {0}", getErrorMessage(e)))); return undefined; } })); From 9cfb2fd998901fbf07cf2d5ece7b7d1fdda6f57f Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 19 Nov 2019 10:46:11 +0100 Subject: [PATCH 037/246] Reduce memory usage: Do not cache TextDocument.lineAt (#84940) --- .../api/common/extHostDocumentData.ts | 68 ++++++++++++------- 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/api/common/extHostDocumentData.ts b/src/vs/workbench/api/common/extHostDocumentData.ts index 064cbbb0426..28e76f2a267 100644 --- a/src/vs/workbench/api/common/extHostDocumentData.ts +++ b/src/vs/workbench/api/common/extHostDocumentData.ts @@ -28,7 +28,6 @@ export class ExtHostDocumentData extends MirrorTextModel { private _languageId: string; private _isDirty: boolean; private _document?: vscode.TextDocument; - private _textLines: vscode.TextLine[] = []; private _isDisposed: boolean = false; constructor(proxy: MainThreadDocumentsShape, uri: URI, lines: string[], eol: string, @@ -130,33 +129,11 @@ export class ExtHostDocumentData extends MirrorTextModel { line = lineOrPosition; } - if (typeof line !== 'number' || line < 0 || line >= this._lines.length) { + if (typeof line !== 'number' || line < 0 || line >= this._lines.length || Math.floor(line) !== line) { throw new Error('Illegal value for `line`'); } - let result = this._textLines[line]; - if (!result || result.lineNumber !== line || result.text !== this._lines[line]) { - - const text = this._lines[line]; - const firstNonWhitespaceCharacterIndex = /^(\s*)/.exec(text)![1].length; - const range = new Range(line, 0, line, text.length); - const rangeIncludingLineBreak = line < this._lines.length - 1 - ? new Range(line, 0, line + 1, 0) - : range; - - result = Object.freeze({ - lineNumber: line, - range, - rangeIncludingLineBreak, - text, - firstNonWhitespaceCharacterIndex, //TODO@api, rename to 'leadingWhitespaceLength' - isEmptyOrWhitespace: firstNonWhitespaceCharacterIndex === text.length - }); - - this._textLines[line] = result; - } - - return result; + return new ExtHostDocumentLine(line, this._lines[line], line === this._lines.length - 1); } private _offsetAt(position: vscode.Position): number { @@ -255,3 +232,44 @@ export class ExtHostDocumentData extends MirrorTextModel { return undefined; } } + +class ExtHostDocumentLine implements vscode.TextLine { + + private readonly _line: number; + private readonly _text: string; + private readonly _isLastLine: boolean; + + constructor(line: number, text: string, isLastLine: boolean) { + this._line = line; + this._text = text; + this._isLastLine = isLastLine; + } + + public get lineNumber(): number { + return this._line; + } + + public get text(): string { + return this._text; + } + + public get range(): Range { + return new Range(this._line, 0, this._line, this._text.length); + } + + public get rangeIncludingLineBreak(): Range { + if (this._isLastLine) { + return this.range; + } + return new Range(this._line, 0, this._line + 1, 0); + } + + public get firstNonWhitespaceCharacterIndex(): number { + //TODO@api, rename to 'leadingWhitespaceLength' + return /^(\s*)/.exec(this._text)![1].length; + } + + public get isEmptyOrWhitespace(): boolean { + return this.firstNonWhitespaceCharacterIndex === this._text.length; + } +} From 29b99f85e54aa5af6cd4edf918a67d4f70a10aea Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Nov 2019 10:55:45 +0100 Subject: [PATCH 038/246] improve build resubmission perf fixes #85107 --- build/azure-pipelines/product-compile.yml | 35 ++++++++++++----------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 87c70f4f805..8029f8a5661 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -12,23 +12,24 @@ steps: vstsFeed: 'npm-vscode' platformIndependent: true alias: 'Compilation' + dryRun: true - task: NodeTool@0 inputs: versionSpec: "12.13.0" - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.x" - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - task: AzureKeyVault@1 displayName: 'Azure Key Vault: Get Secrets' inputs: azureSubscription: 'vscode-builds-subscription' KeyVaultName: vscode - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e @@ -41,7 +42,7 @@ steps: git config user.email "vscode@microsoft.com" git config user.name "VSCode" displayName: Prepare tooling - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e @@ -49,33 +50,33 @@ steps: git fetch distro git merge $(node -p "require('./package.json').distro") displayName: Merge distro - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 inputs: keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e CHILD_CONCURRENCY=1 yarn --frozen-lockfile displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) - script: | set -e yarn postinstall displayName: Run postinstall scripts - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), eq(variables['CacheRestored'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['CacheRestored'], 'true')) # Mixin must run before optimize, because the CSS loader will # inline small SVGs @@ -83,7 +84,7 @@ steps: set -e node build/azure-pipelines/mixin displayName: Mix in quality - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e @@ -91,20 +92,20 @@ steps: yarn gulp tslint yarn monaco-compile-check displayName: Run hygiene, tslint and monaco compile checks - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set - ./build/azure-pipelines/common/extract-telemetry.sh displayName: Extract Telemetry - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e AZURE_WEBVIEW_STORAGE_ACCESS_KEY="$(vscode-webview-storage-key)" \ ./build/azure-pipelines/common/publish-webview.sh displayName: Publish Webview - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e @@ -114,14 +115,14 @@ steps: yarn gulp minify-vscode-reh yarn gulp minify-vscode-reh-web displayName: Compile - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ node build/azure-pipelines/upload-sourcemaps displayName: Upload sourcemaps - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e @@ -129,7 +130,7 @@ steps: AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ node build/azure-pipelines/common/createBuild.js $VERSION displayName: Create build - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: @@ -138,4 +139,4 @@ steps: vstsFeed: 'npm-vscode' platformIndependent: true alias: 'Compilation' - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) From 3e0dab8653325561bcf104b57c37b955e3a62341 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Nov 2019 10:59:00 +0100 Subject: [PATCH 039/246] more jsdoc, clarify that deleting/renaming a folder sends only 1 event, #43768 --- src/vs/vscode.proposed.d.ts | 52 ++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 2b5b8549455..64ef7736f55 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -888,7 +888,7 @@ declare module 'vscode' { /** * An event that is emitted when files are being created. * - * *Note* that this event is triggered by user gestures, like creating a file from the + * *Note:* This event is triggered by user gestures, like creating a file from the * explorer, or from the [`workspace.applyEdit`](#workspace.applyEdit)-api. This event is *not* fired when * files change on disk, e.g triggered by another application, or when using the * [`workspace.fs`](#FileSystem)-api. @@ -896,27 +896,61 @@ declare module 'vscode' { export const onWillCreateFiles: Event; /** - * An event that is emitted when files are being deleted. + * An event that is emitted when files have been created. * - * *Note* that this event is triggered by user gestures, like deleting a file from the - * explorer, or from the [`workspace.applyEdit`](#workspace.applyEdit)-api. This event is *not* fired when + * *Note:* This event is triggered by user gestures, like creating a file from the + * explorer, or from the [`workspace.applyEdit`](#workspace.applyEdit)-api, but this event is *not* fired when * files change on disk, e.g triggered by another application, or when using the * [`workspace.fs`](#FileSystem)-api. */ + export const onDidCreateFiles: Event; + + /** + * An event that is emitted when files are being deleted. + * + * *Note 1:* This event is triggered by user gestures, like deleting a file from the + * explorer, or from the [`workspace.applyEdit`](#workspace.applyEdit)-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * [`workspace.fs`](#FileSystem)-api. + * + * *Note 2:* When deleting a folder with children only one event is fired. + */ export const onWillDeleteFiles: Event; /** - * An event that is emitted when files are being renamed. + * An event that is emitted when files have been deleted. * - * *Note* that this event is triggered by user gestures, like renaming a file from the - * explorer, and from the [`workspace.applyEdit`](#workspace.applyEdit)-api. This event is *not* fired when + * *Note 1:* This event is triggered by user gestures, like deleting a file from the + * explorer, or from the [`workspace.applyEdit`](#workspace.applyEdit)-api, but this event is *not* fired when * files change on disk, e.g triggered by another application, or when using the * [`workspace.fs`](#FileSystem)-api. + * + * *Note 2:* When deleting a folder with children only one event is fired. + */ + export const onDidDeleteFiles: Event; + + /** + * An event that is emitted when files are being renamed. + * + * *Note 1:* This event is triggered by user gestures, like renaming a file from the + * explorer, and from the [`workspace.applyEdit`](#workspace.applyEdit)-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * [`workspace.fs`](#FileSystem)-api. + * + * *Note 2:* When renaming a folder with children only one event is fired. */ export const onWillRenameFiles: Event; - export const onDidCreateFiles: Event; - export const onDidDeleteFiles: Event; + /** + * An event that is emitted when files have been renamed. + * + * *Note 1:* This event is triggered by user gestures, like renaming a file from the + * explorer, and from the [`workspace.applyEdit`](#workspace.applyEdit)-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * [`workspace.fs`](#FileSystem)-api. + * + * *Note 2:* When renaming a folder with children only one event is fired. + */ export const onDidRenameFiles: Event; } //#endregion From cf624b01fcdb2130b6a27190b469925735636931 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 19 Nov 2019 11:13:18 +0100 Subject: [PATCH 040/246] Fix memory leak caused by listeners to nulToken (#84940) --- .../typescript-language-features/src/utils/cancellation.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/utils/cancellation.ts b/extensions/typescript-language-features/src/utils/cancellation.ts index 10933baa939..72672663ad2 100644 --- a/extensions/typescript-language-features/src/utils/cancellation.ts +++ b/extensions/typescript-language-features/src/utils/cancellation.ts @@ -5,6 +5,9 @@ import * as vscode from 'vscode'; -const nulTokenSource = new vscode.CancellationTokenSource(); +const noopDisposable = vscode.Disposable.from(); -export const nulToken = nulTokenSource.token; \ No newline at end of file +export const nulToken: vscode.CancellationToken = { + isCancellationRequested: false, + onCancellationRequested: () => noopDisposable +}; From 16d898488127dbb998ed100251d40ad6f01bc633 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 19 Nov 2019 02:27:50 -0800 Subject: [PATCH 041/246] Use icon font in remote explorer (#85053) --- .../browser/help-documentation-dark.svg | 11 ---- .../remote/browser/help-documentation-hc.svg | 11 ---- .../browser/help-documentation-light.svg | 11 ---- .../remote/browser/help-feedback-dark.svg | 4 -- .../remote/browser/help-feedback-hc.svg | 4 -- .../remote/browser/help-feedback-light.svg | 4 -- .../browser/help-getting-started-dark.svg | 4 -- .../browser/help-getting-started-hc.svg | 4 -- .../browser/help-getting-started-light.svg | 4 -- .../remote/browser/help-report-issue-dark.svg | 4 -- .../remote/browser/help-report-issue-hc.svg | 4 -- .../browser/help-report-issue-light.svg | 4 -- .../browser/help-review-issues-dark.svg | 4 -- .../remote/browser/help-review-issues-hc.svg | 4 -- .../browser/help-review-issues-light.svg | 4 -- .../contrib/remote/browser/remote.ts | 10 ++-- .../contrib/remote/browser/remoteViewlet.css | 60 ------------------- 17 files changed, 5 insertions(+), 146 deletions(-) delete mode 100644 src/vs/workbench/contrib/remote/browser/help-documentation-dark.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-documentation-hc.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-documentation-light.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-feedback-dark.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-feedback-hc.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-feedback-light.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-getting-started-dark.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-getting-started-hc.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-getting-started-light.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-report-issue-dark.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-report-issue-hc.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-report-issue-light.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-review-issues-dark.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-review-issues-hc.svg delete mode 100644 src/vs/workbench/contrib/remote/browser/help-review-issues-light.svg diff --git a/src/vs/workbench/contrib/remote/browser/help-documentation-dark.svg b/src/vs/workbench/contrib/remote/browser/help-documentation-dark.svg deleted file mode 100644 index 2673902c684..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-documentation-dark.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-documentation-hc.svg b/src/vs/workbench/contrib/remote/browser/help-documentation-hc.svg deleted file mode 100644 index e8dc8205bab..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-documentation-hc.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-documentation-light.svg b/src/vs/workbench/contrib/remote/browser/help-documentation-light.svg deleted file mode 100644 index 4a3009baeee..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-documentation-light.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-feedback-dark.svg b/src/vs/workbench/contrib/remote/browser/help-feedback-dark.svg deleted file mode 100644 index 5d99408934e..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-feedback-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-feedback-hc.svg b/src/vs/workbench/contrib/remote/browser/help-feedback-hc.svg deleted file mode 100644 index 941430e9dd6..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-feedback-hc.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-feedback-light.svg b/src/vs/workbench/contrib/remote/browser/help-feedback-light.svg deleted file mode 100644 index 72437202b72..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-feedback-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-getting-started-dark.svg b/src/vs/workbench/contrib/remote/browser/help-getting-started-dark.svg deleted file mode 100644 index 0ea65d83198..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-getting-started-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-getting-started-hc.svg b/src/vs/workbench/contrib/remote/browser/help-getting-started-hc.svg deleted file mode 100644 index 5bb05d3d8c5..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-getting-started-hc.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-getting-started-light.svg b/src/vs/workbench/contrib/remote/browser/help-getting-started-light.svg deleted file mode 100644 index 46cde7f7cc0..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-getting-started-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-report-issue-dark.svg b/src/vs/workbench/contrib/remote/browser/help-report-issue-dark.svg deleted file mode 100644 index 0117ceb7ded..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-report-issue-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-report-issue-hc.svg b/src/vs/workbench/contrib/remote/browser/help-report-issue-hc.svg deleted file mode 100644 index b0c521b7dc6..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-report-issue-hc.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-report-issue-light.svg b/src/vs/workbench/contrib/remote/browser/help-report-issue-light.svg deleted file mode 100644 index 5da9322b6a9..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-report-issue-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-review-issues-dark.svg b/src/vs/workbench/contrib/remote/browser/help-review-issues-dark.svg deleted file mode 100644 index 21eec9cbcb8..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-review-issues-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-review-issues-hc.svg b/src/vs/workbench/contrib/remote/browser/help-review-issues-hc.svg deleted file mode 100644 index 94013ea52ae..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-review-issues-hc.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/remote/browser/help-review-issues-light.svg b/src/vs/workbench/contrib/remote/browser/help-review-issues-light.svg deleted file mode 100644 index 826d0eefbf4..00000000000 --- a/src/vs/workbench/contrib/remote/browser/help-review-issues-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index e4d5e233034..312efc0e9e3 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -58,7 +58,7 @@ class HelpModel { if (getStarted.length) { helpItems.push(new HelpItem( ['getStarted'], - nls.localize('remote.help.getStarted', "Get Started"), + nls.localize('remote.help.getStarted', "$(star) Get Started"), getStarted.map((info: HelpInformation) => ({ extensionDescription: info.extensionDescription, url: info.getStarted! @@ -73,7 +73,7 @@ class HelpModel { if (documentation.length) { helpItems.push(new HelpItem( ['documentation'], - nls.localize('remote.help.documentation', "Read Documentation"), + nls.localize('remote.help.documentation', "$(book) Read Documentation"), documentation.map((info: HelpInformation) => ({ extensionDescription: info.extensionDescription, url: info.documentation! @@ -88,7 +88,7 @@ class HelpModel { if (feedback.length) { helpItems.push(new HelpItem( ['feedback'], - nls.localize('remote.help.feedback', "Provide Feedback"), + nls.localize('remote.help.feedback', "$(twitter) Provide Feedback"), feedback.map((info: HelpInformation) => ({ extensionDescription: info.extensionDescription, url: info.feedback! @@ -103,7 +103,7 @@ class HelpModel { if (issues.length) { helpItems.push(new HelpItem( ['issues'], - nls.localize('remote.help.issues', "Review Issues"), + nls.localize('remote.help.issues', "$(issues) Review Issues"), issues.map((info: HelpInformation) => ({ extensionDescription: info.extensionDescription, url: info.issues! @@ -116,7 +116,7 @@ class HelpModel { if (helpItems.length) { helpItems.push(new IssueReporterItem( ['issueReporter'], - nls.localize('remote.help.report', "Report Issue"), + nls.localize('remote.help.report', "$(comment) Report Issue"), remoteExplorerService.helpInformation.map(info => info.extensionDescription), quickInputService, commandService diff --git a/src/vs/workbench/contrib/remote/browser/remoteViewlet.css b/src/vs/workbench/contrib/remote/browser/remoteViewlet.css index d95c32bbf0d..39b5cdec500 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteViewlet.css +++ b/src/vs/workbench/contrib/remote/browser/remoteViewlet.css @@ -19,66 +19,6 @@ width: 0px !important; } -.vs .remote-help-tree-node-item-icon.getStarted { - background-image: url('help-getting-started-light.svg') -} - -.vs .remote-help-tree-node-item-icon.documentation { - background-image: url('help-documentation-light.svg') -} - -.vs .remote-help-tree-node-item-icon.feedback { - background-image: url('help-feedback-light.svg') -} - -.vs .remote-help-tree-node-item-icon.issues { - background-image: url('help-review-issues-light.svg') -} - -.vs .remote-help-tree-node-item-icon.issueReporter { - background-image: url('help-report-issue-light.svg') -} - -.vs-dark .remote-help-tree-node-item-icon.getStarted { - background-image: url('help-getting-started-dark.svg') -} - -.vs-dark .remote-help-tree-node-item-icon.documentation { - background-image: url('help-documentation-dark.svg') -} - -.vs-dark .remote-help-tree-node-item-icon.feedback { - background-image: url('help-feedback-dark.svg') -} - -.vs-dark .remote-help-tree-node-item-icon.issues { - background-image: url('help-review-issues-dark.svg') -} - -.vs-dark .remote-help-tree-node-item-icon.issueReporter { - background-image: url('help-report-issue-dark.svg') -} - -.hc-black .remote-help-tree-node-item-icon.getStarted { - background-image: url('help-getting-started-hc.svg') -} - -.hc-black .remote-help-tree-node-item-icon.documentation { - background-image: url('help-documentation-hc.svg') -} - -.hc-black .remote-help-tree-node-item-icon.feedback { - background-image: url('help-feedback-hc.svg') -} - -.hc-black .remote-help-tree-node-item-icon.issues { - background-image: url('help-review-issues-hc.svg') -} - -.hc-black .remote-help-tree-node-item-icon.issueReporter { - background-image: url('help-report-issue-hc.svg') -} - .monaco-workbench .part > .title > .title-actions .switch-remote { display: flex; align-items: center; From 515a0cb200a1404a7e052ec0bcb0458a2ffaa719 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Nov 2019 11:28:11 +0100 Subject: [PATCH 042/246] search viewlet: fix subpixel aa in panel --- .../contrib/search/browser/search.contribution.ts | 4 ++-- .../workbench/contrib/search/browser/searchPanel.ts | 4 ++-- .../workbench/contrib/search/browser/searchView.ts | 12 +++++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 22f4f3467de..7cf58147ab2 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -43,7 +43,7 @@ import { OpenSymbolHandler } from 'vs/workbench/contrib/search/browser/openSymbo import { registerContributions as replaceContributions } from 'vs/workbench/contrib/search/browser/replaceContributions'; import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction } from 'vs/workbench/contrib/search/browser/searchActions'; import { SearchPanel } from 'vs/workbench/contrib/search/browser/searchPanel'; -import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; +import { SearchView, SearchViewPosition } from 'vs/workbench/contrib/search/browser/searchView'; import { SearchViewlet } from 'vs/workbench/contrib/search/browser/searchViewlet'; import { registerContributions as searchWidgetContributions } from 'vs/workbench/contrib/search/browser/searchWidget'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; @@ -542,7 +542,7 @@ class RegisterSearchViewContribution implements IWorkbenchContribution { } } else { Registry.as(PanelExtensions.Panels).deregisterPanel(PANEL_ID); - viewsRegistry.registerViews([{ id: VIEW_ID, name: nls.localize('search', "Search"), ctorDescriptor: { ctor: SearchView }, canToggleVisibility: false }], VIEW_CONTAINER); + viewsRegistry.registerViews([{ id: VIEW_ID, name: nls.localize('search', "Search"), ctorDescriptor: { ctor: SearchView, arguments: [SearchViewPosition.SideBar] }, canToggleVisibility: false }], VIEW_CONTAINER); if (open) { viewletService.openViewlet(VIEWLET_ID); } diff --git a/src/vs/workbench/contrib/search/browser/searchPanel.ts b/src/vs/workbench/contrib/search/browser/searchPanel.ts index 0c50a100a55..54cf8e628d1 100644 --- a/src/vs/workbench/contrib/search/browser/searchPanel.ts +++ b/src/vs/workbench/contrib/search/browser/searchPanel.ts @@ -7,7 +7,7 @@ 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 { PANEL_ID } from 'vs/workbench/services/search/common/search'; -import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; +import { SearchView, SearchViewPosition } from 'vs/workbench/contrib/search/browser/searchView'; import { Panel } from 'vs/workbench/browser/panel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { localize } from 'vs/nls'; @@ -25,7 +25,7 @@ export class SearchPanel extends Panel { @IInstantiationService instantiationService: IInstantiationService, ) { super(PANEL_ID, telemetryService, themeService, storageService); - this.searchView = this._register(instantiationService.createInstance(SearchView, { id: PANEL_ID, title: localize('search', "Search"), actionRunner: this.getActionRunner() })); + this.searchView = this._register(instantiationService.createInstance(SearchView, SearchViewPosition.Panel, { id: PANEL_ID, title: localize('search', "Search"), actionRunner: this.getActionRunner() })); this._register(this.searchView.onDidChangeTitleArea(() => this.updateTitleArea())); this._register(this.onDidChangeVisibility(visible => this.searchView.setVisible(visible))); } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 8d9eb83dc94..26df3d5d2db 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -61,7 +61,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; const $ = dom.$; @@ -71,6 +71,11 @@ enum SearchUIState { SlowSearch } +export enum SearchViewPosition { + SideBar, + Panel +} + const SEARCH_CANCELLED_MESSAGE = nls.localize('searchCanceled', "Search was canceled before any results could be found - "); export class SearchView extends ViewletPanel { @@ -133,6 +138,7 @@ export class SearchView extends ViewletPanel { private addToSearchHistoryDelayer: Delayer; constructor( + private position: SearchViewPosition, options: IViewletPanelOptions, @IFileService private readonly fileService: IFileService, @IEditorService private readonly editorService: IEditorService, @@ -157,7 +163,7 @@ export class SearchView extends ViewletPanel { @IStorageService storageService: IStorageService, @IOpenerService private readonly openerService: IOpenerService ) { - super({ ...(options as IViewletPanelOptions), id: VIEW_ID, ariaHeaderLabel: nls.localize('searchView', "Search") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...options, id: VIEW_ID, ariaHeaderLabel: nls.localize('searchView', "Search") }, keybindingService, contextMenuService, configurationService, contextKeyService); this.viewletVisible = Constants.SearchViewVisibleKey.bindTo(contextKeyService); this.viewletFocused = Constants.SearchViewFocusedKey.bindTo(contextKeyService); @@ -654,7 +660,7 @@ export class SearchView extends ViewletPanel { dnd: this.instantiationService.createInstance(SearchDND), multipleSelectionSupport: false, overrideStyles: { - listBackground: SIDE_BAR_BACKGROUND + listBackground: this.position === SearchViewPosition.SideBar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND } })); this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); From 094fd80e20b811644a56686a64e22041e9b3c508 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Nov 2019 11:37:25 +0100 Subject: [PATCH 043/246] adding configurable timeout, #43768 --- src/vs/base/common/event.ts | 5 +- src/vs/base/test/common/event.test.ts | 11 +++-- .../mainThreadFileSystemEventService.ts | 46 +++++++++++++++++-- .../workbench/api/common/extHost.protocol.ts | 2 +- .../common/extHostFileSystemEventService.ts | 33 +++++++------ .../textfile/browser/textFileService.ts | 7 +-- 6 files changed, 73 insertions(+), 31 deletions(-) diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index b85073b6786..1f397703d16 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -7,6 +7,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { once as onceFn } from 'vs/base/common/functional'; import { Disposable, IDisposable, toDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; +import { CancellationToken } from 'vs/base/common/cancellation'; /** * To an event a function with one or zero parameters @@ -655,7 +656,7 @@ export class AsyncEmitter extends Emitter { private _asyncDeliveryQueue?: [Listener, T, Promise[]][]; - async fireAsync(eventFn: (thenables: Promise[], listener: Function) => T): Promise { + async fireAsync(eventFn: (thenables: Promise[], listener: Function) => T, token: CancellationToken): Promise { if (!this._listeners) { return; } @@ -672,7 +673,7 @@ export class AsyncEmitter extends Emitter { this._asyncDeliveryQueue.push([e.value, eventFn(thenables, typeof e.value === 'function' ? e.value : e.value[0]), thenables]); } - while (this._asyncDeliveryQueue.length > 0) { + while (this._asyncDeliveryQueue.length > 0 && !token.isCancellationRequested) { const [listener, event, thenables] = this._asyncDeliveryQueue.shift()!; try { if (typeof listener === 'function') { diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 4c05cad2447..5937f8ecc73 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -7,6 +7,7 @@ import { Event, Emitter, EventBufferer, EventMultiplexer, AsyncEmitter, IWaitUnt import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import * as Errors from 'vs/base/common/errors'; import { timeout } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; namespace Samples { @@ -276,7 +277,7 @@ suite('AsyncEmitter', function () { foo: true, bar: 1, waitUntil(t: Promise) { thenables.push(t); } - })); + }), CancellationToken.None); emitter.dispose(); }); @@ -308,7 +309,7 @@ suite('AsyncEmitter', function () { waitUntil(t) { thenables.push(t); } - })); + }), CancellationToken.None); assert.equal(globalState, 2); }); @@ -329,7 +330,7 @@ suite('AsyncEmitter', function () { waitUntil(t) { thenables.push(t); } - })); + }), CancellationToken.None); assert.deepEqual(events, [1, 2]); done = true; } @@ -347,7 +348,7 @@ suite('AsyncEmitter', function () { waitUntil(t) { thenables.push(t); } - })); + }), CancellationToken.None); assert.ok(done); }); @@ -377,7 +378,7 @@ suite('AsyncEmitter', function () { waitUntil(t) { thenables.push(t); } - })).then(() => { + }), CancellationToken.None).then(() => { assert.equal(globalState, 2); }).catch(e => { console.log(e); diff --git a/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts b/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts index 295bd0d8c9e..da387c35747 100644 --- a/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts +++ b/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts @@ -10,6 +10,11 @@ import { ExtHostContext, FileSystemEvents, IExtHostContext } from '../common/ext import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { localize } from 'vs/nls'; +import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ILogService } from 'vs/platform/log/common/log'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; @extHostCustomer export class MainThreadFileSystemEventService { @@ -20,7 +25,9 @@ export class MainThreadFileSystemEventService { extHostContext: IExtHostContext, @IFileService fileService: IFileService, @ITextFileService textFileService: ITextFileService, - @IProgressService progressService: IProgressService + @IProgressService progressService: IProgressService, + @IConfigurationService configService: IConfigurationService, + @ILogService logService: ILogService, ) { const proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemEventService); @@ -59,16 +66,33 @@ export class MainThreadFileSystemEventService { messages.set(FileOperation.DELETE, localize('msg-delete', "Running 'File Delete' participants...")); messages.set(FileOperation.MOVE, localize('msg-rename', "Running 'File Rename' participants...")); + this._listener.add(textFileService.onWillRunOperation(e => { + + const timeout = configService.getValue('files.participants.timeout'); + if (timeout <= 0) { + return; // disabled + } + const p = progressService.withProgress({ location: ProgressLocation.Window }, progress => { progress.report({ message: messages.get(e.operation) }); - const p1 = proxy.$onWillRunFileOperation(e.operation, e.target, e.source); - const p2 = new Promise((_resolve, reject) => { - setTimeout(() => reject(new Error('timeout')), 5000); + return new Promise((resolve, reject) => { + + const cts = new CancellationTokenSource(); + + const timeoutHandle = setTimeout(() => { + logService.trace('CANCELLED file participants because of timeout', timeout, e.target, e.operation); + cts.cancel(); + reject(new Error('timeout')); + }, timeout); + + proxy.$onWillRunFileOperation(e.operation, e.target, e.source, cts.token) + .then(resolve, reject) + .finally(() => clearTimeout(timeoutHandle)); }); - return Promise.race([p1, p2]); + }); e.waitUntil(p); @@ -82,3 +106,15 @@ export class MainThreadFileSystemEventService { this._listener.dispose(); } } + + +Registry.as(Extensions.Configuration).registerConfiguration({ + id: 'files', + properties: { + 'files.participants.timeout': { + type: 'number', + default: 5000, + markdownDescription: localize('files.participants.timeout', "Timeout in milliseconds after which file participants for create, rename, and delete are cancelled. Use `0` to disable participants."), + } + } +}); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 35dbdf8eafe..ec42255a44d 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -923,7 +923,7 @@ export interface FileSystemEvents { export interface ExtHostFileSystemEventServiceShape { $onFileEvent(events: FileSystemEvents): void; - $onWillRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined): Promise; + $onWillRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined, token: CancellationToken): Promise; $onDidRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined): void; } diff --git a/src/vs/workbench/api/common/extHostFileSystemEventService.ts b/src/vs/workbench/api/common/extHostFileSystemEventService.ts index f39a22d203e..2b38f88a5a3 100644 --- a/src/vs/workbench/api/common/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/common/extHostFileSystemEventService.ts @@ -14,6 +14,7 @@ import { Disposable, WorkspaceEdit } from './extHostTypes'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { FileOperation } from 'vs/platform/files/common/files'; import { flatten } from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; class FileSystemWatcher implements vscode.FileSystemWatcher { @@ -176,23 +177,23 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ }; } - async $onWillRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined): Promise { + async $onWillRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined, token: CancellationToken): Promise { switch (operation) { case FileOperation.MOVE: - await this._fireWillEvent(this._onWillRenameFile, { files: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }], }); + await this._fireWillEvent(this._onWillRenameFile, { files: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }] }, token); break; case FileOperation.DELETE: - await this._fireWillEvent(this._onWillDeleteFile, { files: [URI.revive(target)] }); + await this._fireWillEvent(this._onWillDeleteFile, { files: [URI.revive(target)] }, token); break; case FileOperation.CREATE: - await this._fireWillEvent(this._onWillCreateFile, { files: [URI.revive(target)] }); + await this._fireWillEvent(this._onWillCreateFile, { files: [URI.revive(target)] }, token); break; default: //ignore, dont send } } - private async _fireWillEvent(emitter: AsyncEmitter, data: Omit): Promise { + private async _fireWillEvent(emitter: AsyncEmitter, data: Omit, token: CancellationToken): Promise { const edits: WorkspaceEdit[] = []; await Promise.resolve(emitter.fireAsync(bucket => { @@ -214,19 +215,21 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ } } }; - })); + }, token)); - if (edits.length === 0) { - return undefined; + if (token.isCancellationRequested) { + return; } - // flatten all WorkspaceEdits collected via waitUntil-call - // and apply them in one go. - const allEdits = new Array>(); - for (let edit of edits) { - let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors); - allEdits.push(edits); + if (edits.length > 0) { + // flatten all WorkspaceEdits collected via waitUntil-call + // and apply them in one go. + const allEdits = new Array>(); + for (let edit of edits) { + let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors); + allEdits.push(edits); + } + return this._mainThreadTextEditors.$tryApplyWorkspaceEdit({ edits: flatten(allEdits) }); } - return this._mainThreadTextEditors.$tryApplyWorkspaceEdit({ edits: flatten(allEdits) }); } } diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 400006f193f..55e7fded9ba 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -36,6 +36,7 @@ import { ITextSnapshot } from 'vs/editor/common/model'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { CancellationToken } from 'vs/base/common/cancellation'; /** * The workbench file service implementation implements the raw file service spec and adds additional methods on top. @@ -344,7 +345,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex async create(resource: URI, value?: string | ITextSnapshot, options?: ICreateFileOptions): Promise { // before event - await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.CREATE, resource)); + await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.CREATE, resource), CancellationToken.None); const stat = await this.doCreate(resource, value, options); @@ -374,7 +375,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex async delete(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise { // before event - await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.DELETE, resource)); + await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.DELETE, resource), CancellationToken.None); const dirtyFiles = this.getDirty().filter(dirty => isEqualOrParent(dirty, resource)); await this.revertAll(dirtyFiles, { soft: true }); @@ -388,7 +389,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex async move(source: URI, target: URI, overwrite?: boolean): Promise { // before event - await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.MOVE, target, source)); + await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.MOVE, target, source), CancellationToken.None); // find all models that related to either source or target (can be many if resource is a folder) const sourceModels: ITextFileEditorModel[] = []; From a8e3e0da535326dc433ab67cec240190645ba65c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Nov 2019 11:38:33 +0100 Subject: [PATCH 044/246] do not listen when not enabled --- .../platform/userDataSync/common/userDataSyncService.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 85b8a9ee152..bebcedaf122 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -43,9 +43,13 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.extensionsSynchroniser = this._register(this.instantiationService.createInstance(ExtensionsSynchroniser)); this.synchronisers = [this.settingsSynchroniser, this.extensionsSynchroniser]; this.updateStatus(); - this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeStatus, () => undefined)))(() => this.updateStatus())); + + if (this.userDataSyncStoreService.enabled) { + this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeStatus, () => undefined)))(() => this.updateStatus())); + this._register(authTokenService.onDidChangeStatus(() => this.onDidChangeAuthTokenStatus())); + } + this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal)); - this._register(authTokenService.onDidChangeStatus(() => this.onDidChangeAuthTokenStatus())); } async sync(_continue?: boolean): Promise { From 3f69d88abd98d22033103f50093bef1c67b52982 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 19 Nov 2019 12:03:04 +0100 Subject: [PATCH 045/246] Make the extensionKind in product.json have bigger precedence than the extensionKind in the manifest --- .../services/extensions/common/extensionsUtil.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts index 83fdf37fb67..6a232c9616b 100644 --- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -32,14 +32,14 @@ export function getExtensionKind(manifest: IExtensionManifest, productService: I return toArray(result); } - // check the manifest itself - result = manifest.extensionKind; + // check product.json + result = getProductExtensionKind(manifest, productService); if (typeof result !== 'undefined') { return toArray(result); } - // check product.json - result = getProductExtensionKind(manifest, productService); + // check the manifest itself + result = manifest.extensionKind; if (typeof result !== 'undefined') { return toArray(result); } From 839719d489dd69453fa9834df4cae52f0c084345 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Nov 2019 12:11:12 +0100 Subject: [PATCH 046/246] simplify AsyncEmitter usage --- src/vs/base/common/event.ts | 32 +++++++++++------ src/vs/base/test/common/event.test.ts | 36 ++++--------------- .../common/extHostFileSystemEventService.ts | 28 +++++---------- .../textfile/browser/textFileService.ts | 6 ++-- .../services/textfile/common/textfiles.ts | 19 +++------- 5 files changed, 43 insertions(+), 78 deletions(-) diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 1f397703d16..e6c16f43df6 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -654,27 +654,39 @@ export interface IWaitUntil { export class AsyncEmitter extends Emitter { - private _asyncDeliveryQueue?: [Listener, T, Promise[]][]; + private _asyncDeliveryQueue?: LinkedList<[Listener, Omit]>; - async fireAsync(eventFn: (thenables: Promise[], listener: Function) => T, token: CancellationToken): Promise { + async fireAsync(data: Omit, token: CancellationToken, promiseJoin?: (p: Promise) => Promise): Promise { if (!this._listeners) { return; } - // put all [listener,event]-pairs into delivery queue - // then emit all event. an inner/nested event might be - // the driver of this if (!this._asyncDeliveryQueue) { - this._asyncDeliveryQueue = []; + this._asyncDeliveryQueue = new LinkedList(); } for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) { - const thenables: Promise[] = []; - this._asyncDeliveryQueue.push([e.value, eventFn(thenables, typeof e.value === 'function' ? e.value : e.value[0]), thenables]); + this._asyncDeliveryQueue.push([e.value, data]); } - while (this._asyncDeliveryQueue.length > 0 && !token.isCancellationRequested) { - const [listener, event, thenables] = this._asyncDeliveryQueue.shift()!; + while (this._asyncDeliveryQueue.size > 0 && !token.isCancellationRequested) { + + const [listener, data] = this._asyncDeliveryQueue.shift()!; + const thenables: Promise[] = []; + + const event = { + ...data, + waitUntil: (p: Promise): void => { + if (Object.isFrozen(thenables)) { + throw new Error('waitUntil can NOT be called asynchronous'); + } + if (promiseJoin) { + p = promiseJoin(p); + } + thenables.push(p); + } + }; + try { if (typeof listener === 'function') { listener.call(undefined, event); diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 5937f8ecc73..a0e8ca9fcfa 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Event, Emitter, EventBufferer, EventMultiplexer, AsyncEmitter, IWaitUntil, PauseableEmitter } from 'vs/base/common/event'; +import { Event, Emitter, EventBufferer, EventMultiplexer, IWaitUntil, PauseableEmitter, AsyncEmitter } from 'vs/base/common/event'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import * as Errors from 'vs/base/common/errors'; import { timeout } from 'vs/base/common/async'; @@ -273,11 +273,7 @@ suite('AsyncEmitter', function () { assert.equal(typeof e.waitUntil, 'function'); }); - emitter.fireAsync(thenables => ({ - foo: true, - bar: 1, - waitUntil(t: Promise) { thenables.push(t); } - }), CancellationToken.None); + emitter.fireAsync({ foo: true, bar: 1, }, CancellationToken.None); emitter.dispose(); }); @@ -304,12 +300,7 @@ suite('AsyncEmitter', function () { })); }); - await emitter.fireAsync(thenables => ({ - foo: true, - waitUntil(t) { - thenables.push(t); - } - }), CancellationToken.None); + await emitter.fireAsync({ foo: true }, CancellationToken.None); assert.equal(globalState, 2); }); @@ -325,12 +316,7 @@ suite('AsyncEmitter', function () { emitter.event(e => { e.waitUntil(timeout(10).then(async _ => { if (e.foo === 1) { - await emitter.fireAsync(thenables => ({ - foo: 2, - waitUntil(t) { - thenables.push(t); - } - }), CancellationToken.None); + await emitter.fireAsync({ foo: 2 }, CancellationToken.None); assert.deepEqual(events, [1, 2]); done = true; } @@ -343,12 +329,7 @@ suite('AsyncEmitter', function () { e.waitUntil(timeout(7)); }); - await emitter.fireAsync(thenables => ({ - foo: 1, - waitUntil(t) { - thenables.push(t); - } - }), CancellationToken.None); + await emitter.fireAsync({ foo: 1 }, CancellationToken.None); assert.ok(done); }); @@ -373,12 +354,7 @@ suite('AsyncEmitter', function () { e.waitUntil(timeout(10)); }); - await emitter.fireAsync(thenables => ({ - foo: true, - waitUntil(t) { - thenables.push(t); - } - }), CancellationToken.None).then(() => { + await emitter.fireAsync({ foo: true }, CancellationToken.None).then(() => { assert.equal(globalState, 2); }).catch(e => { console.log(e); diff --git a/src/vs/workbench/api/common/extHostFileSystemEventService.ts b/src/vs/workbench/api/common/extHostFileSystemEventService.ts index 2b38f88a5a3..d289fc249fe 100644 --- a/src/vs/workbench/api/common/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/common/extHostFileSystemEventService.ts @@ -196,26 +196,14 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ private async _fireWillEvent(emitter: AsyncEmitter, data: Omit, token: CancellationToken): Promise { const edits: WorkspaceEdit[] = []; - await Promise.resolve(emitter.fireAsync(bucket => { - return { - ...data, - ...{ - waitUntil: (thenable: Promise): void => { - if (Object.isFrozen(bucket)) { - throw new TypeError('waitUntil cannot be called async'); - } - const promise = Promise.resolve(thenable).then(result => { - // ignore all results except for WorkspaceEdits. Those - // are stored in a spare array - if (result instanceof WorkspaceEdit) { - edits.push(result); - } - }); - bucket.push(promise); - } - } - }; - }, token)); + + await emitter.fireAsync(data, token, async p => { + // ignore all results except for WorkspaceEdits. Those are stored in an array. + const result = await Promise.resolve(p); + if (result instanceof WorkspaceEdit) { + edits.push(result); + } + }); if (token.isCancellationRequested) { return; diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 55e7fded9ba..6228f117813 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -345,7 +345,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex async create(resource: URI, value?: string | ITextSnapshot, options?: ICreateFileOptions): Promise { // before event - await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.CREATE, resource), CancellationToken.None); + await this._onWillRunOperation.fireAsync({ operation: FileOperation.CREATE, target: resource }, CancellationToken.None); const stat = await this.doCreate(resource, value, options); @@ -375,7 +375,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex async delete(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise { // before event - await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.DELETE, resource), CancellationToken.None); + await this._onWillRunOperation.fireAsync({ operation: FileOperation.DELETE, target: resource }, CancellationToken.None); const dirtyFiles = this.getDirty().filter(dirty => isEqualOrParent(dirty, resource)); await this.revertAll(dirtyFiles, { soft: true }); @@ -389,7 +389,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex async move(source: URI, target: URI, overwrite?: boolean): Promise { // before event - await this._onWillRunOperation.fireAsync(promises => new FileOperationWillRunEvent(promises, FileOperation.MOVE, target, source), CancellationToken.None); + await this._onWillRunOperation.fireAsync({ operation: FileOperation.MOVE, target, source }, CancellationToken.None); // find all models that related to either source or target (can be many if resource is a folder) const sourceModels: ITextFileEditorModel[] = []; diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 51338221b40..a1383b93901 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -131,21 +131,10 @@ export interface ITextFileService extends IDisposable { move(source: URI, target: URI, overwrite?: boolean): Promise; } -export class FileOperationWillRunEvent implements IWaitUntil { - - constructor( - private _thenables: Promise[], - readonly operation: FileOperation, - readonly target: URI, - readonly source?: URI | undefined - ) { } - - waitUntil(thenable: Promise): void { - if (Object.isFrozen(this._thenables)) { - throw new Error('waitUntil cannot be used aync'); - } - this._thenables.push(thenable); - } +export interface FileOperationWillRunEvent extends IWaitUntil { + operation: FileOperation; + target: URI; + source?: URI; } export class FileOperationDidRunEvent { From 5c372215d57a412db0df9e206b53d9d67331f250 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Tue, 19 Nov 2019 12:11:55 +0100 Subject: [PATCH 047/246] add mock debug to web sample --- extensions/vscode-api-tests/package.json | 73 +- extensions/vscode-api-tests/src/extension.ts | 3752 +++++++++++++++++- 2 files changed, 3809 insertions(+), 16 deletions(-) diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 6ed66a3932b..d80a384c6fd 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -46,23 +46,66 @@ } }, "taskDefinitions": [ - { - "type": "custombuildscript", - "required": [ - "flavor" - ], - "properties": { - "flavor": { - "type": "string", - "description": "The build flavor. Should be either '32' or '64'." - }, - "flags": { - "type": "array", - "description": "Additional build flags." - } + { + "type": "custombuildscript", + "required": [ + "flavor" + ], + "properties": { + "flavor": { + "type": "string", + "description": "The build flavor. Should be either '32' or '64'." + }, + "flags": { + "type": "array", + "description": "Additional build flags." } } - ] + } + ], + "breakpoints": [ + { + "language": "markdown" + } + ], + "debuggers": [ + { + "type": "mock", + "label": "Mock Debug", + "configurationAttributes": { + "launch": { + "required": [ + "program" + ], + "properties": { + "program": { + "type": "string", + "description": "Absolute path to a text file.", + "default": "${workspaceFolder}/file.md" + }, + "stopOnEntry": { + "type": "boolean", + "description": "Automatically stop after launch.", + "default": true + }, + "trace": { + "type": "boolean", + "description": "Enable logging of the Debug Adapter Protocol.", + "default": true + } + } + } + }, + "initialConfigurations": [ + { + "type": "mock", + "request": "launch", + "name": "Debug file.md", + "program": "${workspaceFolder}/file.md" + } + ] + } + ] }, "scripts": { "compile": "node ./node_modules/vscode/bin/compile -watch -p ./", diff --git a/extensions/vscode-api-tests/src/extension.ts b/extensions/vscode-api-tests/src/extension.ts index 7d345d9ae95..3ed712c26f3 100644 --- a/extensions/vscode-api-tests/src/extension.ts +++ b/extensions/vscode-api-tests/src/extension.ts @@ -24,6 +24,8 @@ export function activate(context: vscode.ExtensionContext) { enableTasks(); vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`memfs:/sample-folder/large.ts`)); + + context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('mock', new MockDebugAdapterDescriptorFactory(memFs))); } function enableFs(context: vscode.ExtensionContext): MemFS { @@ -40,7 +42,7 @@ function enableFs(context: vscode.ExtensionContext): MemFS { memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.json`), textEncoder.encode('{ "json": true }'), { create: true, overwrite: true }); memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.ts`), textEncoder.encode('console.log("TypeScript")'), { create: true, overwrite: true }); memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.css`), textEncoder.encode('* { color: green; }'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.md`), textEncoder.encode('Hello _World_'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.md`), textEncoder.encode(getDebuggableFile()), { create: true, overwrite: true }); memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.xml`), textEncoder.encode(''), { create: true, overwrite: true }); memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.py`), textEncoder.encode('import base64, sys; base64.decode(open(sys.argv[1], "rb"), open(sys.argv[2], "wb"))'), { create: true, overwrite: true }); memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.php`), textEncoder.encode('&1\'); ?>'), { create: true, overwrite: true }); @@ -316,6 +318,50 @@ module Mankala { `; } + function getDebuggableFile(): string { + return `# VS Code Mock Debug + +This is a starter sample for developing VS Code debug adapters. + +**Mock Debug** simulates a debug adapter for Visual Studio Code. +It supports *step*, *continue*, *breakpoints*, *exceptions*, and +*variable access* but it is not connected to any real debugger. + +The sample is meant as an educational piece showing how to implement a debug +adapter for VS Code. It can be used as a starting point for developing a real adapter. + +More information about how to develop a new debug adapter can be found +[here](https://code.visualstudio.com/docs/extensions/example-debuggers). +Or discuss debug adapters on Gitter: +[![Gitter Chat](https://img.shields.io/badge/chat-online-brightgreen.svg)](https://gitter.im/Microsoft/vscode) + +## Using Mock Debug + +* Install the **Mock Debug** extension in VS Code. +* Create a new 'program' file 'readme.md' and enter several lines of arbitrary text. +* Switch to the debug viewlet and press the gear dropdown. +* Select the debug environment "Mock Debug". +* Press the green 'play' button to start debugging. + +You can now 'step through' the 'readme.md' file, set and hit breakpoints, and run into exceptions (if the word exception appears in a line). + +![Mock Debug](images/mock-debug.gif) + +## Build and Run + +[![build status](https://travis-ci.org/Microsoft/vscode-mock-debug.svg?branch=master)](https://travis-ci.org/Microsoft/vscode-mock-debug) +[![build status](https://ci.appveyor.com/api/projects/status/empmw5q1tk6h1fly/branch/master?svg=true)](https://ci.appveyor.com/project/weinand/vscode-mock-debug) + + +* Clone the project [https://github.com/Microsoft/vscode-mock-debug.git](https://github.com/Microsoft/vscode-mock-debug.git) +* Open the project folder in VS Code. +* Press 'F5' to build and launch Mock Debug in another VS Code window. In that window: + * Open a new workspace, create a new 'program' file 'readme.md' and enter several lines of arbitrary text. + * Switch to the debug viewlet and press the gear dropdown. + * Select the debug environment "Mock Debug". + * Press 'F5' to start debugging.`; + } + return memFs; } @@ -845,3 +891,3707 @@ export class MemFS implements vscode.FileSystemProvider, vscode.FileSearchProvid return result; } } + +//--------------------------------------------------------------------------- + +/** Declaration module describing the VS Code debug protocol. + Auto-generated from json schema. Do not edit manually. +*/ +declare module DebugProtocol { + + /** Base class of requests, responses, and events. */ + export interface ProtocolMessage { + /** Sequence number (also known as message ID). For protocol messages of type 'request' this ID can be used to cancel the request. */ + seq: number; + /** Message type. + Values: 'request', 'response', 'event', etc. + */ + type: string; + } + + /** A client or debug adapter initiated request. */ + export interface Request extends ProtocolMessage { + // type: 'request'; + /** The command to execute. */ + command: string; + /** Object containing arguments for the command. */ + arguments?: any; + } + + /** A debug adapter initiated event. */ + export interface Event extends ProtocolMessage { + // type: 'event'; + /** Type of event. */ + event: string; + /** Event-specific information. */ + body?: any; + } + + /** Response for a request. */ + export interface Response extends ProtocolMessage { + // type: 'response'; + /** Sequence number of the corresponding request. */ + request_seq: number; + /** Outcome of the request. + If true, the request was successful and the 'body' attribute may contain the result of the request. + If the value is false, the attribute 'message' contains the error in short form and the 'body' may contain additional information (see 'ErrorResponse.body.error'). + */ + success: boolean; + /** The command requested. */ + command: string; + /** Contains the raw error in short form if 'success' is false. + This raw error might be interpreted by the frontend and is not shown in the UI. + Some predefined values exist. + Values: + 'cancelled': request was cancelled. + etc. + */ + message?: string; + /** Contains request result if success is true and optional error details if success is false. */ + body?: any; + } + + /** On error (whenever 'success' is false), the body can provide more details. */ + export interface ErrorResponse extends Response { + body: { + /** An optional, structured error message. */ + error?: Message; + }; + } + + /** Cancel request; value of command field is 'cancel'. + The 'cancel' request is used by the frontend to indicate that it is no longer interested in the result produced by a specific request issued earlier. + This request has a hint characteristic: a debug adapter can only be expected to make a 'best effort' in honouring this request but there are no guarantees. + The 'cancel' request may return an error if it could not cancel an operation but a frontend should refrain from presenting this error to end users. + A frontend client should only call this request if the capability 'supportsCancelRequest' is true. + The request that got canceled still needs to send a response back. + This can either be a normal result ('success' attribute true) or an error response ('success' attribute false and the 'message' set to 'cancelled'). + Returning partial results from a cancelled request is possible but please note that a frontend client has no generic way for detecting that a response is partial or not. + */ + export interface CancelRequest extends Request { + // command: 'cancel'; + arguments?: CancelArguments; + } + + /** Arguments for 'cancel' request. */ + export interface CancelArguments { + /** The ID (attribute 'seq') of the request to cancel. */ + requestId?: number; + } + + /** Response to 'cancel' request. This is just an acknowledgement, so no body field is required. */ + export interface CancelResponse extends Response { + } + + /** Event message for 'initialized' event type. + This event indicates that the debug adapter is ready to accept configuration requests (e.g. SetBreakpointsRequest, SetExceptionBreakpointsRequest). + A debug adapter is expected to send this event when it is ready to accept configuration requests (but not before the 'initialize' request has finished). + The sequence of events/requests is as follows: + - adapters sends 'initialized' event (after the 'initialize' request has returned) + - frontend sends zero or more 'setBreakpoints' requests + - frontend sends one 'setFunctionBreakpoints' request + - frontend sends a 'setExceptionBreakpoints' request if one or more 'exceptionBreakpointFilters' have been defined (or if 'supportsConfigurationDoneRequest' is not defined or false) + - frontend sends other future configuration requests + - frontend sends one 'configurationDone' request to indicate the end of the configuration. + */ + export interface InitializedEvent extends Event { + // event: 'initialized'; + } + + /** Event message for 'stopped' event type. + The event indicates that the execution of the debuggee has stopped due to some condition. + This can be caused by a break point previously set, a stepping action has completed, by executing a debugger statement etc. + */ + export interface StoppedEvent extends Event { + // event: 'stopped'; + body: { + /** The reason for the event. + 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', etc. + */ + reason: 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. */ + threadId?: number; + /** A value of true hints to the frontend that this event should not change the focus. */ + preserveFocusHint?: boolean; + /** Additional information. E.g. if reason is 'exception', text contains the exception name. This string is shown in the UI. */ + text?: string; + /** If 'allThreadsStopped' is true, a debug adapter can announce that all threads have stopped. + - The client should use this information to enable that all threads can be expanded to access their stacktraces. + - If the attribute is missing or false, only the thread with the given threadId can be expanded. + */ + allThreadsStopped?: boolean; + }; + } + + /** Event message for 'continued' event type. + The event indicates that the execution of the debuggee has continued. + Please note: a debug adapter is not expected to send this event in response to a request that implies that execution continues, e.g. 'launch' or 'continue'. + It is only necessary to send a 'continued' event if there was no previous request that implied this. + */ + export interface ContinuedEvent extends Event { + // event: 'continued'; + body: { + /** The thread which was continued. */ + threadId: number; + /** If 'allThreadsContinued' is true, a debug adapter can announce that all threads have continued. */ + allThreadsContinued?: boolean; + }; + } + + /** Event message for 'exited' event type. + The event indicates that the debuggee has exited and returns its exit code. + */ + export interface ExitedEvent extends Event { + // event: 'exited'; + body: { + /** The exit code returned from the debuggee. */ + exitCode: number; + }; + } + + /** Event message for 'terminated' event type. + The event indicates that debugging of the debuggee has terminated. This does **not** mean that the debuggee itself has exited. + */ + export interface TerminatedEvent extends Event { + // event: 'terminated'; + body?: { + /** A debug adapter may set 'restart' to true (or to an arbitrary object) to request that the front end restarts the session. + The value is not interpreted by the client and passed unmodified as an attribute '__restart' to the 'launch' and 'attach' requests. + */ + restart?: any; + }; + } + + /** Event message for 'thread' event type. + The event indicates that a thread has started or exited. + */ + export interface ThreadEvent extends Event { + // event: 'thread'; + body: { + /** The reason for the event. + Values: 'started', 'exited', etc. + */ + reason: string; + /** The identifier of the thread. */ + threadId: number; + }; + } + + /** Event message for 'output' event type. + The event indicates that the target has produced some output. + */ + export interface OutputEvent extends Event { + // event: 'output'; + body: { + /** The output category. If not specified, 'console' is assumed. + Values: 'console', 'stdout', 'stderr', 'telemetry', etc. + */ + category?: string; + /** The output to report. */ + output: string; + /** 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; + /** An optional source location line where the output was produced. */ + line?: number; + /** An optional source location column where the output was produced. */ + column?: number; + /** Optional data to report. For the 'telemetry' category the data will be sent to telemetry, for the other categories the data is shown in JSON format. */ + data?: any; + }; + } + + /** Event message for 'breakpoint' event type. + The event indicates that some information about a breakpoint has changed. + */ + export interface BreakpointEvent extends Event { + // event: 'breakpoint'; + body: { + /** The reason for the event. + Values: 'changed', 'new', 'removed', etc. + */ + reason: string; + /** The 'id' attribute is used to find the target breakpoint and the other attributes are used as the new values. */ + breakpoint: Breakpoint; + }; + } + + /** Event message for 'module' event type. + The event indicates that some information about a module has changed. + */ + export interface ModuleEvent extends Event { + // event: 'module'; + body: { + /** The reason for the event. */ + reason: 'new' | 'changed' | 'removed'; + /** The new, changed, or removed module. In case of 'removed' only the module id is used. */ + module: Module; + }; + } + + /** Event message for 'loadedSource' event type. + The event indicates that some source has been added, changed, or removed from the set of all loaded sources. + */ + export interface LoadedSourceEvent extends Event { + // event: 'loadedSource'; + body: { + /** The reason for the event. */ + reason: 'new' | 'changed' | 'removed'; + /** The new, changed, or removed source. */ + source: Source; + }; + } + + /** Event message for 'process' event type. + The event indicates that the debugger has begun debugging a new process. Either one that it has launched, or one that it has attached to. + */ + export interface ProcessEvent extends Event { + // event: 'process'; + body: { + /** The logical name of the process. This is usually the full path to process's executable file. Example: /home/example/myproj/program.js. */ + name: string; + /** The system process id of the debugged process. This property will be missing for non-system processes. */ + systemProcessId?: number; + /** If true, the process is running on the same computer as the debug adapter. */ + isLocalProcess?: boolean; + /** Describes how the debug engine started debugging this process. + 'launch': Process was launched under the debugger. + 'attach': Debugger attached to an existing process. + 'attachForSuspendedLaunch': A project launcher component has launched a new process in a suspended state and then asked the debugger to attach. + */ + startMethod?: 'launch' | 'attach' | 'attachForSuspendedLaunch'; + /** The size of a pointer or address for this process, in bits. This value may be used by clients when formatting addresses for display. */ + pointerSize?: number; + }; + } + + /** Event message for 'capabilities' event type. + The event indicates that one or more capabilities have changed. + Since the capabilities are dependent on the frontend and its UI, it might not be possible to change that at random times (or too late). + Consequently this event has a hint characteristic: a frontend can only be expected to make a 'best effort' in honouring individual capabilities but there are no guarantees. + Only changed capabilities need to be included, all other capabilities keep their values. + */ + export interface CapabilitiesEvent extends Event { + // event: 'capabilities'; + body: { + /** The set of updated capabilities. */ + capabilities: Capabilities; + }; + } + + /** RunInTerminal request; value of command field is 'runInTerminal'. + This request is sent from the debug adapter to the client to run a command in a terminal. This is typically used to launch the debuggee in a terminal provided by the client. + */ + export interface RunInTerminalRequest extends Request { + // command: 'runInTerminal'; + arguments: RunInTerminalRequestArguments; + } + + /** Arguments for 'runInTerminal' request. */ + export interface RunInTerminalRequestArguments { + /** What kind of terminal to launch. */ + kind?: 'integrated' | 'external'; + /** Optional title of the terminal. */ + title?: string; + /** Working directory of the command. */ + cwd: string; + /** List of arguments. The first argument is the command to run. */ + args: string[]; + /** Environment key-value pairs that are added to or removed from the default environment. */ + env?: { [key: string]: string | null; }; + } + + /** 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). */ + processId?: number; + /** The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31 - 1). */ + shellProcessId?: number; + }; + } + + /** Initialize request; value of command field is 'initialize'. + The 'initialize' request is sent as the first request from the client to the debug adapter in order to configure it with client capabilities and to retrieve capabilities from the debug adapter. + Until the debug adapter has responded to with an 'initialize' response, the client must not send any additional requests or events to the debug adapter. In addition the debug adapter is not allowed to send any requests or events to the client until it has responded with an 'initialize' response. + The 'initialize' request may only be sent once. + */ + export interface InitializeRequest extends Request { + // command: 'initialize'; + arguments: InitializeRequestArguments; + } + + /** Arguments for 'initialize' request. */ + export interface InitializeRequestArguments { + /** The ID of the (frontend) client using this adapter. */ + clientID?: string; + /** The human readable name of the (frontend) client using this adapter. */ + clientName?: string; + /** The ID of the debug adapter. */ + adapterID: string; + /** The ISO-639 locale of the (frontend) client using this adapter, e.g. en-US or de-CH. */ + locale?: string; + /** If true all line numbers are 1-based (default). */ + linesStartAt1?: boolean; + /** If true all column numbers are 1-based (default). */ + columnsStartAt1?: boolean; + /** Determines in what format paths are specified. The default is 'path', which is the native format. + Values: 'path', 'uri', etc. + */ + pathFormat?: string; + /** Client supports the optional type attribute for variables. */ + supportsVariableType?: boolean; + /** Client supports the paging of variables. */ + supportsVariablePaging?: boolean; + /** Client supports the runInTerminal request. */ + supportsRunInTerminalRequest?: boolean; + /** Client supports memory references. */ + supportsMemoryReferences?: boolean; + } + + /** Response to 'initialize' request. */ + export interface InitializeResponse extends Response { + /** The capabilities of this debug adapter. */ + body?: Capabilities; + } + + /** ConfigurationDone request; value of command field is 'configurationDone'. + The client of the debug protocol must send this request at the end of the sequence of configuration requests (which was started by the 'initialized' event). + */ + export interface ConfigurationDoneRequest extends Request { + // command: 'configurationDone'; + arguments?: ConfigurationDoneArguments; + } + + /** Arguments for 'configurationDone' request. */ + export interface ConfigurationDoneArguments { + } + + /** Response to 'configurationDone' request. This is just an acknowledgement, so no body field is required. */ + export interface ConfigurationDoneResponse extends Response { + } + + /** Launch request; value of command field is 'launch'. + The launch request is sent from the client to the debug adapter to start the debuggee with or without debugging (if 'noDebug' is true). Since launching is debugger/runtime specific, the arguments for this request are not part of this specification. + */ + export interface LaunchRequest extends Request { + // command: 'launch'; + arguments: LaunchRequestArguments; + } + + /** Arguments for 'launch' request. Additional attributes are implementation specific. */ + export interface LaunchRequestArguments { + /** If noDebug is true the launch request should launch the program without enabling debugging. */ + noDebug?: boolean; + /** Optional data from the previous, restarted session. + The data is sent as the 'restart' attribute of the 'terminated' event. + The client should leave the data intact. + */ + __restart?: any; + } + + /** Response to 'launch' request. This is just an acknowledgement, so no body field is required. */ + export interface LaunchResponse extends Response { + } + + /** Attach request; value of command field is 'attach'. + The attach request is sent from the client to the debug adapter to attach to a debuggee that is already running. Since attaching is debugger/runtime specific, the arguments for this request are not part of this specification. + */ + export interface AttachRequest extends Request { + // command: 'attach'; + arguments: AttachRequestArguments; + } + + /** Arguments for 'attach' request. Additional attributes are implementation specific. */ + export interface AttachRequestArguments { + /** Optional data from the previous, restarted session. + The data is sent as the 'restart' attribute of the 'terminated' event. + The client should leave the data intact. + */ + __restart?: any; + } + + /** Response to 'attach' request. This is just an acknowledgement, so no body field is required. */ + export interface AttachResponse extends Response { + } + + /** Restart request; value of command field is 'restart'. + Restarts a debug session. If the capability 'supportsRestartRequest' is missing or has the value false, + the client will implement 'restart' by terminating the debug adapter first and then launching it anew. + A debug adapter can override this default behaviour by implementing a restart request + and setting the capability 'supportsRestartRequest' to true. + */ + export interface RestartRequest extends Request { + // command: 'restart'; + arguments?: RestartArguments; + } + + /** Arguments for 'restart' request. */ + export interface RestartArguments { + } + + /** Response to 'restart' request. This is just an acknowledgement, so no body field is required. */ + export interface RestartResponse extends Response { + } + + /** Disconnect request; value of command field is 'disconnect'. + The 'disconnect' request is sent from the client to the debug adapter in order to stop debugging. It asks the debug adapter to disconnect from the debuggee and to terminate the debug adapter. If the debuggee has been started with the 'launch' request, the 'disconnect' request terminates the debuggee. If the 'attach' request was used to connect to the debuggee, 'disconnect' does not terminate the debuggee. This behavior can be controlled with the 'terminateDebuggee' argument (if supported by the debug adapter). + */ + export interface DisconnectRequest extends Request { + // command: 'disconnect'; + arguments?: DisconnectArguments; + } + + /** Arguments for 'disconnect' request. */ + export interface DisconnectArguments { + /** A value of true indicates that this 'disconnect' request is part of a restart sequence. */ + restart?: boolean; + /** Indicates whether the debuggee should be terminated when the debugger is disconnected. + If unspecified, the debug adapter is free to do whatever it thinks is best. + A client can only rely on this attribute being properly honored if a debug adapter returns true for the 'supportTerminateDebuggee' capability. + */ + terminateDebuggee?: boolean; + } + + /** Response to 'disconnect' request. This is just an acknowledgement, so no body field is required. */ + export interface DisconnectResponse extends Response { + } + + /** Terminate request; value of command field is 'terminate'. + The 'terminate' request is sent from the client to the debug adapter in order to give the debuggee a chance for terminating itself. + */ + export interface TerminateRequest extends Request { + // command: 'terminate'; + arguments?: TerminateArguments; + } + + /** Arguments for 'terminate' request. */ + export interface TerminateArguments { + /** A value of true indicates that this 'terminate' request is part of a restart sequence. */ + restart?: boolean; + } + + /** Response to 'terminate' request. This is just an acknowledgement, so no body field is required. */ + export interface TerminateResponse extends Response { + } + + /** BreakpointLocations request; value of command field is 'breakpointLocations'. + The 'breakpointLocations' request returns all possible locations for source breakpoints in a given range. + */ + export interface BreakpointLocationsRequest extends Request { + // command: 'breakpointLocations'; + arguments?: BreakpointLocationsArguments; + } + + /** Arguments for 'breakpointLocations' request. */ + export interface BreakpointLocationsArguments { + /** The source location of the breakpoints; either 'source.path' or 'source.reference' must be specified. */ + source: Source; + /** Start line of range to search possible breakpoint locations in. If only the line is specified, the request returns all possible locations in that line. */ + line: number; + /** Optional start column of range to search possible breakpoint locations in. If no start column is given, the first column in the start line is assumed. */ + column?: number; + /** Optional end line of range to search possible breakpoint locations in. If no end line is given, then the end line is assumed to be the start line. */ + endLine?: number; + /** Optional end column of range to search possible breakpoint locations in. If no end column is given, then it is assumed to be in the last column of the end line. */ + endColumn?: number; + } + + /** Response to 'breakpointLocations' request. + Contains possible locations for source breakpoints. + */ + export interface BreakpointLocationsResponse extends Response { + body: { + /** Sorted set of possible breakpoint locations. */ + breakpoints: BreakpointLocation[]; + }; + } + + /** SetBreakpoints request; value of command field is 'setBreakpoints'. + Sets multiple breakpoints for a single source and clears all previous breakpoints in that source. + To clear all breakpoint for a source, specify an empty array. + When a breakpoint is hit, a 'stopped' event (with reason 'breakpoint') is generated. + */ + export interface SetBreakpointsRequest extends Request { + // command: 'setBreakpoints'; + arguments: SetBreakpointsArguments; + } + + /** Arguments for 'setBreakpoints' request. */ + export interface SetBreakpointsArguments { + /** The source location of the breakpoints; either 'source.path' or 'source.reference' must be specified. */ + source: Source; + /** The code locations of the breakpoints. */ + breakpoints?: SourceBreakpoint[]; + /** Deprecated: The code locations of the breakpoints. */ + lines?: number[]; + /** A value of true indicates that the underlying source has been modified which results in new breakpoint locations. */ + sourceModified?: boolean; + } + + /** Response to 'setBreakpoints' request. + Returned is information about each breakpoint created by this request. + This includes the actual code location and whether the breakpoint could be verified. + The breakpoints returned are in the same order as the elements of the 'breakpoints' + (or the deprecated 'lines') array in the arguments. + */ + export interface SetBreakpointsResponse extends Response { + body: { + /** Information about the breakpoints. The array elements are in the same order as the elements of the 'breakpoints' (or the deprecated 'lines') array in the arguments. */ + breakpoints: Breakpoint[]; + }; + } + + /** SetFunctionBreakpoints request; value of command field is 'setFunctionBreakpoints'. + Replaces all existing function breakpoints with new function breakpoints. + To clear all function breakpoints, specify an empty array. + When a function breakpoint is hit, a 'stopped' event (with reason 'function breakpoint') is generated. + */ + export interface SetFunctionBreakpointsRequest extends Request { + // command: 'setFunctionBreakpoints'; + arguments: SetFunctionBreakpointsArguments; + } + + /** Arguments for 'setFunctionBreakpoints' request. */ + export interface SetFunctionBreakpointsArguments { + /** The function names of the breakpoints. */ + breakpoints: FunctionBreakpoint[]; + } + + /** Response to 'setFunctionBreakpoints' request. + Returned is information about each breakpoint created by this request. + */ + export interface SetFunctionBreakpointsResponse extends Response { + body: { + /** Information about the breakpoints. The array elements correspond to the elements of the 'breakpoints' array. */ + breakpoints: Breakpoint[]; + }; + } + + /** SetExceptionBreakpoints request; value of command field is 'setExceptionBreakpoints'. + The request configures the debuggers response to thrown exceptions. If an exception is configured to break, a 'stopped' event is fired (with reason 'exception'). + */ + export interface SetExceptionBreakpointsRequest extends Request { + // command: 'setExceptionBreakpoints'; + arguments: SetExceptionBreakpointsArguments; + } + + /** Arguments for 'setExceptionBreakpoints' request. */ + export interface SetExceptionBreakpointsArguments { + /** IDs of checked exception options. The set of IDs is returned via the 'exceptionBreakpointFilters' capability. */ + filters: string[]; + /** Configuration options for selected exceptions. */ + exceptionOptions?: ExceptionOptions[]; + } + + /** Response to 'setExceptionBreakpoints' request. This is just an acknowledgement, so no body field is required. */ + export interface SetExceptionBreakpointsResponse extends Response { + } + + /** DataBreakpointInfo request; value of command field is 'dataBreakpointInfo'. + Obtains information on a possible data breakpoint that could be set on an expression or variable. + */ + export interface DataBreakpointInfoRequest extends Request { + // command: 'dataBreakpointInfo'; + arguments: DataBreakpointInfoArguments; + } + + /** Arguments for 'dataBreakpointInfo' request. */ + export interface DataBreakpointInfoArguments { + /** Reference to the Variable container if the data breakpoint is requested for a child of the container. */ + variablesReference?: number; + /** The name of the Variable's child to obtain data breakpoint information for. If variableReference isn’t provided, this can be an expression. */ + name: string; + } + + /** Response to 'dataBreakpointInfo' request. */ + export interface DataBreakpointInfoResponse extends Response { + body: { + /** An identifier for the data on which a data breakpoint can be registered with the setDataBreakpoints request or null if no data breakpoint is available. */ + dataId: string | null; + /** UI string that describes on what data the breakpoint is set on or why a data breakpoint is not available. */ + description: string; + /** Optional attribute listing the available access types for a potential data breakpoint. A UI frontend could surface this information. */ + accessTypes?: DataBreakpointAccessType[]; + /** Optional attribute indicating that a potential data breakpoint could be persisted across sessions. */ + canPersist?: boolean; + }; + } + + /** SetDataBreakpoints request; value of command field is 'setDataBreakpoints'. + Replaces all existing data breakpoints with new data breakpoints. + To clear all data breakpoints, specify an empty array. + When a data breakpoint is hit, a 'stopped' event (with reason 'data breakpoint') is generated. + */ + export interface SetDataBreakpointsRequest extends Request { + // command: 'setDataBreakpoints'; + arguments: SetDataBreakpointsArguments; + } + + /** Arguments for 'setDataBreakpoints' request. */ + export interface SetDataBreakpointsArguments { + /** The contents of this array replaces all existing data breakpoints. An empty array clears all data breakpoints. */ + breakpoints: DataBreakpoint[]; + } + + /** Response to 'setDataBreakpoints' request. + Returned is information about each breakpoint created by this request. + */ + export interface SetDataBreakpointsResponse extends Response { + body: { + /** Information about the data breakpoints. The array elements correspond to the elements of the input argument 'breakpoints' array. */ + breakpoints: Breakpoint[]; + }; + } + + /** Continue request; value of command field is 'continue'. + The request starts the debuggee to run again. + */ + export interface ContinueRequest extends Request { + // command: 'continue'; + arguments: ContinueArguments; + } + + /** Arguments for 'continue' request. */ + export interface ContinueArguments { + /** Continue execution for the specified thread (if possible). If the backend cannot continue on a single thread but will continue on all threads, it should set the 'allThreadsContinued' attribute in the response to true. */ + threadId: number; + } + + /** Response to 'continue' request. */ + export interface ContinueResponse extends Response { + body: { + /** If true, the 'continue' request has ignored the specified thread and continued all threads instead. If this attribute is missing a value of 'true' is assumed for backward compatibility. */ + allThreadsContinued?: boolean; + }; + } + + /** Next request; value of command field is 'next'. + The request starts the debuggee to run again for one step. + The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed. + */ + export interface NextRequest extends Request { + // command: 'next'; + arguments: NextArguments; + } + + /** Arguments for 'next' request. */ + export interface NextArguments { + /** Execute 'next' for this thread. */ + threadId: number; + } + + /** Response to 'next' request. This is just an acknowledgement, so no body field is required. */ + export interface NextResponse extends Response { + } + + /** StepIn request; value of command field is 'stepIn'. + The request starts the debuggee to step into a function/method if possible. + If it cannot step into a target, 'stepIn' behaves like 'next'. + The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed. + If there are multiple function/method calls (or other targets) on the source line, + the optional argument 'targetId' can be used to control into which target the 'stepIn' should occur. + The list of possible targets for a given source line can be retrieved via the 'stepInTargets' request. + */ + export interface StepInRequest extends Request { + // command: 'stepIn'; + arguments: StepInArguments; + } + + /** Arguments for 'stepIn' request. */ + export interface StepInArguments { + /** Execute 'stepIn' for this thread. */ + threadId: number; + /** Optional id of the target to step into. */ + targetId?: number; + } + + /** Response to 'stepIn' request. This is just an acknowledgement, so no body field is required. */ + export interface StepInResponse extends Response { + } + + /** StepOut request; value of command field is 'stepOut'. + The request starts the debuggee to run again for one step. + The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed. + */ + export interface StepOutRequest extends Request { + // command: 'stepOut'; + arguments: StepOutArguments; + } + + /** Arguments for 'stepOut' request. */ + export interface StepOutArguments { + /** Execute 'stepOut' for this thread. */ + threadId: number; + } + + /** Response to 'stepOut' request. This is just an acknowledgement, so no body field is required. */ + export interface StepOutResponse extends Response { + } + + /** StepBack request; value of command field is 'stepBack'. + The request starts the debuggee to run one step backwards. + The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed. Clients should only call this request if the capability 'supportsStepBack' is true. + */ + export interface StepBackRequest extends Request { + // command: 'stepBack'; + arguments: StepBackArguments; + } + + /** Arguments for 'stepBack' request. */ + export interface StepBackArguments { + /** Execute 'stepBack' for this thread. */ + threadId: number; + } + + /** Response to 'stepBack' request. This is just an acknowledgement, so no body field is required. */ + export interface StepBackResponse extends Response { + } + + /** ReverseContinue request; value of command field is 'reverseContinue'. + The request starts the debuggee to run backward. Clients should only call this request if the capability 'supportsStepBack' is true. + */ + export interface ReverseContinueRequest extends Request { + // command: 'reverseContinue'; + arguments: ReverseContinueArguments; + } + + /** Arguments for 'reverseContinue' request. */ + export interface ReverseContinueArguments { + /** Execute 'reverseContinue' for this thread. */ + threadId: number; + } + + /** Response to 'reverseContinue' request. This is just an acknowledgement, so no body field is required. */ + export interface ReverseContinueResponse extends Response { + } + + /** RestartFrame request; value of command field is 'restartFrame'. + The request restarts execution of the specified stackframe. + The debug adapter first sends the response and then a 'stopped' event (with reason 'restart') after the restart has completed. + */ + export interface RestartFrameRequest extends Request { + // command: 'restartFrame'; + arguments: RestartFrameArguments; + } + + /** Arguments for 'restartFrame' request. */ + export interface RestartFrameArguments { + /** Restart this stackframe. */ + frameId: number; + } + + /** Response to 'restartFrame' request. This is just an acknowledgement, so no body field is required. */ + export interface RestartFrameResponse extends Response { + } + + /** Goto request; value of command field is 'goto'. + The request sets the location where the debuggee will continue to run. + This makes it possible to skip the execution of code or to executed code again. + The code between the current location and the goto target is not executed but skipped. + The debug adapter first sends the response and then a 'stopped' event with reason 'goto'. + */ + export interface GotoRequest extends Request { + // command: 'goto'; + arguments: GotoArguments; + } + + /** Arguments for 'goto' request. */ + export interface GotoArguments { + /** Set the goto target for this thread. */ + threadId: number; + /** The location where the debuggee will continue to run. */ + targetId: number; + } + + /** Response to 'goto' request. This is just an acknowledgement, so no body field is required. */ + export interface GotoResponse extends Response { + } + + /** Pause request; value of command field is 'pause'. + The request suspends the debuggee. + The debug adapter first sends the response and then a 'stopped' event (with reason 'pause') after the thread has been paused successfully. + */ + export interface PauseRequest extends Request { + // command: 'pause'; + arguments: PauseArguments; + } + + /** Arguments for 'pause' request. */ + export interface PauseArguments { + /** Pause execution for this thread. */ + threadId: number; + } + + /** Response to 'pause' request. This is just an acknowledgement, so no body field is required. */ + export interface PauseResponse extends Response { + } + + /** StackTrace request; value of command field is 'stackTrace'. + The request returns a stacktrace from the current execution state. + */ + export interface StackTraceRequest extends Request { + // command: 'stackTrace'; + arguments: StackTraceArguments; + } + + /** Arguments for 'stackTrace' request. */ + export interface StackTraceArguments { + /** Retrieve the stacktrace for this thread. */ + threadId: number; + /** The index of the first frame to return; if omitted frames start at 0. */ + startFrame?: number; + /** The maximum number of frames to return. If levels is not specified or 0, all frames are returned. */ + levels?: number; + /** Specifies details on how to format the stack frames. */ + format?: StackFrameFormat; + } + + /** Response to 'stackTrace' request. */ + export interface StackTraceResponse extends Response { + body: { + /** The frames of the stackframe. If the array has length zero, there are no stackframes available. + This means that there is no location information available. + */ + stackFrames: StackFrame[]; + /** The total number of frames available. */ + totalFrames?: number; + }; + } + + /** Scopes request; value of command field is 'scopes'. + The request returns the variable scopes for a given stackframe ID. + */ + export interface ScopesRequest extends Request { + // command: 'scopes'; + arguments: ScopesArguments; + } + + /** Arguments for 'scopes' request. */ + export interface ScopesArguments { + /** Retrieve the scopes for this stackframe. */ + frameId: number; + } + + /** Response to 'scopes' request. */ + export interface ScopesResponse extends Response { + body: { + /** The scopes of the stackframe. If the array has length zero, there are no scopes available. */ + scopes: Scope[]; + }; + } + + /** Variables request; value of command field is 'variables'. + Retrieves all child variables for the given variable reference. + An optional filter can be used to limit the fetched children to either named or indexed children. + */ + export interface VariablesRequest extends Request { + // command: 'variables'; + arguments: VariablesArguments; + } + + /** Arguments for 'variables' request. */ + export interface VariablesArguments { + /** The Variable reference. */ + variablesReference: number; + /** Optional filter to limit the child variables to either named or indexed. If omitted, both types are fetched. */ + filter?: 'indexed' | 'named'; + /** The index of the first variable to return; if omitted children start at 0. */ + start?: number; + /** The number of variables to return. If count is missing or 0, all variables are returned. */ + count?: number; + /** Specifies details on how to format the Variable values. */ + format?: ValueFormat; + } + + /** Response to 'variables' request. */ + export interface VariablesResponse extends Response { + body: { + /** All (or a range) of variables for the given variable reference. */ + variables: Variable[]; + }; + } + + /** SetVariable request; value of command field is 'setVariable'. + Set the variable with the given name in the variable container to a new value. + */ + export interface SetVariableRequest extends Request { + // command: 'setVariable'; + arguments: SetVariableArguments; + } + + /** Arguments for 'setVariable' request. */ + export interface SetVariableArguments { + /** The reference of the variable container. */ + variablesReference: number; + /** The name of the variable in the container. */ + name: string; + /** The value of the variable. */ + value: string; + /** Specifies details on how to format the response value. */ + format?: ValueFormat; + } + + /** Response to 'setVariable' request. */ + export interface SetVariableResponse extends Response { + body: { + /** The new value of the variable. */ + value: string; + /** 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). */ + 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). + */ + 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). + */ + indexedVariables?: number; + }; + } + + /** Source request; value of command field is 'source'. + The request retrieves the source code for a given source reference. + */ + export interface SourceRequest extends Request { + // command: 'source'; + arguments: SourceArguments; + } + + /** Arguments for 'source' request. */ + export interface SourceArguments { + /** Specifies the source content to load. Either source.path or source.sourceReference must be specified. */ + source?: Source; + /** The reference to the source. This is the same as source.sourceReference. This is provided for backward compatibility since old backends do not understand the 'source' attribute. */ + sourceReference: number; + } + + /** Response to 'source' request. */ + export interface SourceResponse extends Response { + body: { + /** Content of the source reference. */ + content: string; + /** Optional content type (mime type) of the source. */ + mimeType?: string; + }; + } + + /** Threads request; value of command field is 'threads'. + The request retrieves a list of all threads. + */ + export interface ThreadsRequest extends Request { + // command: 'threads'; + } + + /** Response to 'threads' request. */ + export interface ThreadsResponse extends Response { + body: { + /** All threads. */ + threads: Thread[]; + }; + } + + /** TerminateThreads request; value of command field is 'terminateThreads'. + The request terminates the threads with the given ids. + */ + export interface TerminateThreadsRequest extends Request { + // command: 'terminateThreads'; + arguments: TerminateThreadsArguments; + } + + /** Arguments for 'terminateThreads' request. */ + export interface TerminateThreadsArguments { + /** Ids of threads to be terminated. */ + threadIds?: number[]; + } + + /** Response to 'terminateThreads' request. This is just an acknowledgement, so no body field is required. */ + export interface TerminateThreadsResponse extends Response { + } + + /** Modules request; value of command field is 'modules'. + Modules can be retrieved from the debug adapter with the ModulesRequest which can either return all modules or a range of modules to support paging. + */ + export interface ModulesRequest extends Request { + // command: 'modules'; + arguments: ModulesArguments; + } + + /** Arguments for 'modules' request. */ + export interface ModulesArguments { + /** The index of the first module to return; if omitted modules start at 0. */ + startModule?: number; + /** The number of modules to return. If moduleCount is not specified or 0, all modules are returned. */ + moduleCount?: number; + } + + /** Response to 'modules' request. */ + export interface ModulesResponse extends Response { + body: { + /** All modules or range of modules. */ + modules: Module[]; + /** The total number of modules available. */ + totalModules?: number; + }; + } + + /** LoadedSources request; value of command field is 'loadedSources'. + Retrieves the set of all sources currently loaded by the debugged process. + */ + export interface LoadedSourcesRequest extends Request { + // command: 'loadedSources'; + arguments?: LoadedSourcesArguments; + } + + /** Arguments for 'loadedSources' request. */ + export interface LoadedSourcesArguments { + } + + /** Response to 'loadedSources' request. */ + export interface LoadedSourcesResponse extends Response { + body: { + /** Set of loaded sources. */ + sources: Source[]; + }; + } + + /** Evaluate request; value of command field is 'evaluate'. + Evaluates the given expression in the context of the top most stack frame. + The expression has access to any variables and arguments that are in scope. + */ + export interface EvaluateRequest extends Request { + // command: 'evaluate'; + arguments: EvaluateArguments; + } + + /** Arguments for 'evaluate' request. */ + export interface EvaluateArguments { + /** The expression to evaluate. */ + expression: string; + /** Evaluate the expression in the scope of this stack frame. If not specified, the expression is evaluated in the global scope. */ + frameId?: number; + /** The context in which the evaluate request is run. + Values: + 'watch': evaluate is run in a watch. + 'repl': evaluate is run from REPL console. + 'hover': evaluate is run from a data hover. + etc. + */ + context?: string; + /** Specifies details on how to format the Evaluate result. */ + format?: ValueFormat; + } + + /** Response to 'evaluate' request. */ + export interface EvaluateResponse extends Response { + body: { + /** The result of the evaluate request. */ + result: string; + /** The optional type of the evaluate result. */ + type?: string; + /** 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). */ + 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). + */ + 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). + */ + indexedVariables?: number; + /** Memory reference to a location appropriate for this result. For pointer type eval results, this is generally a reference to the memory address contained in the pointer. */ + memoryReference?: string; + }; + } + + /** SetExpression request; value of command field is 'setExpression'. + Evaluates the given 'value' expression and assigns it to the 'expression' which must be a modifiable l-value. + The expressions have access to any variables and arguments that are in scope of the specified frame. + */ + export interface SetExpressionRequest extends Request { + // command: 'setExpression'; + arguments: SetExpressionArguments; + } + + /** Arguments for 'setExpression' request. */ + export interface SetExpressionArguments { + /** The l-value expression to assign to. */ + expression: string; + /** The value expression to assign to the l-value expression. */ + value: string; + /** Evaluate the expressions in the scope of this stack frame. If not specified, the expressions are evaluated in the global scope. */ + frameId?: number; + /** Specifies how the resulting value should be formatted. */ + format?: ValueFormat; + } + + /** Response to 'setExpression' request. */ + export interface SetExpressionResponse extends Response { + body: { + /** The new value of the expression. */ + value: string; + /** The optional type of the value. */ + type?: string; + /** 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). */ + 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). + */ + 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). + */ + indexedVariables?: number; + }; + } + + /** StepInTargets request; value of command field is 'stepInTargets'. + This request retrieves the possible stepIn targets for the specified stack frame. + These targets can be used in the 'stepIn' request. + The StepInTargets may only be called if the 'supportsStepInTargetsRequest' capability exists and is true. + */ + export interface StepInTargetsRequest extends Request { + // command: 'stepInTargets'; + arguments: StepInTargetsArguments; + } + + /** Arguments for 'stepInTargets' request. */ + export interface StepInTargetsArguments { + /** The stack frame for which to retrieve the possible stepIn targets. */ + frameId: number; + } + + /** Response to 'stepInTargets' request. */ + export interface StepInTargetsResponse extends Response { + body: { + /** The possible stepIn targets of the specified source location. */ + targets: StepInTarget[]; + }; + } + + /** GotoTargets request; value of command field is 'gotoTargets'. + This request retrieves the possible goto targets for the specified source location. + These targets can be used in the 'goto' request. + The GotoTargets request may only be called if the 'supportsGotoTargetsRequest' capability exists and is true. + */ + export interface GotoTargetsRequest extends Request { + // command: 'gotoTargets'; + arguments: GotoTargetsArguments; + } + + /** Arguments for 'gotoTargets' request. */ + export interface GotoTargetsArguments { + /** The source location for which the goto targets are determined. */ + source: Source; + /** The line location for which the goto targets are determined. */ + line: number; + /** An optional column location for which the goto targets are determined. */ + column?: number; + } + + /** Response to 'gotoTargets' request. */ + export interface GotoTargetsResponse extends Response { + body: { + /** The possible goto targets of the specified location. */ + targets: GotoTarget[]; + }; + } + + /** Completions request; value of command field is 'completions'. + Returns a list of possible completions for a given caret position and text. + The CompletionsRequest may only be called if the 'supportsCompletionsRequest' capability exists and is true. + */ + export interface CompletionsRequest extends Request { + // command: 'completions'; + arguments: CompletionsArguments; + } + + /** Arguments for 'completions' request. */ + export interface CompletionsArguments { + /** Returns completions in the scope of this stack frame. If not specified, the completions are returned for the global scope. */ + frameId?: number; + /** One or more source lines. Typically this is the text a user has typed into the debug console before he asked for completion. */ + text: string; + /** The character position for which to determine the completion proposals. */ + column: number; + /** An optional line for which to determine the completion proposals. If missing the first line of the text is assumed. */ + line?: number; + } + + /** Response to 'completions' request. */ + export interface CompletionsResponse extends Response { + body: { + /** The possible completions for . */ + targets: CompletionItem[]; + }; + } + + /** ExceptionInfo request; value of command field is 'exceptionInfo'. + Retrieves the details of the exception that caused this event to be raised. + */ + export interface ExceptionInfoRequest extends Request { + // command: 'exceptionInfo'; + arguments: ExceptionInfoArguments; + } + + /** Arguments for 'exceptionInfo' request. */ + export interface ExceptionInfoArguments { + /** Thread for which exception information should be retrieved. */ + threadId: number; + } + + /** Response to 'exceptionInfo' request. */ + export interface ExceptionInfoResponse extends Response { + body: { + /** ID of the exception that was thrown. */ + exceptionId: string; + /** Descriptive text for the exception provided by the debug adapter. */ + description?: string; + /** Mode that caused the exception notification to be raised. */ + breakMode: ExceptionBreakMode; + /** Detailed information about the exception. */ + details?: ExceptionDetails; + }; + } + + /** ReadMemory request; value of command field is 'readMemory'. + Reads bytes from memory at the provided location. + */ + export interface ReadMemoryRequest extends Request { + // command: 'readMemory'; + arguments: ReadMemoryArguments; + } + + /** Arguments for 'readMemory' request. */ + export interface ReadMemoryArguments { + /** Memory reference to the base location from which data should be read. */ + memoryReference: string; + /** Optional offset (in bytes) to be applied to the reference location before reading data. Can be negative. */ + offset?: number; + /** Number of bytes to read at the specified location and offset. */ + count: number; + } + + /** Response to 'readMemory' request. */ + export interface ReadMemoryResponse extends Response { + body?: { + /** The address of the first byte of data returned. Treated as a hex value if prefixed with '0x', or as a decimal value otherwise. */ + address: string; + /** The number of unreadable bytes encountered after the last successfully read byte. This can be used to determine the number of bytes that must be skipped before a subsequent 'readMemory' request will succeed. */ + unreadableBytes?: number; + /** The bytes read from memory, encoded using base64. */ + data?: string; + }; + } + + /** Disassemble request; value of command field is 'disassemble'. + Disassembles code stored at the provided location. + */ + export interface DisassembleRequest extends Request { + // command: 'disassemble'; + arguments: DisassembleArguments; + } + + /** Arguments for 'disassemble' request. */ + export interface DisassembleArguments { + /** Memory reference to the base location containing the instructions to disassemble. */ + memoryReference: string; + /** Optional offset (in bytes) to be applied to the reference location before disassembling. Can be negative. */ + offset?: number; + /** Optional offset (in instructions) to be applied after the byte offset (if any) before disassembling. Can be negative. */ + instructionOffset?: number; + /** Number of instructions to disassemble starting at the specified location and offset. An adapter must return exactly this number of instructions - any unavailable instructions should be replaced with an implementation-defined 'invalid instruction' value. */ + instructionCount: number; + /** If true, the adapter should attempt to resolve memory addresses and other values to symbolic names. */ + resolveSymbols?: boolean; + } + + /** Response to 'disassemble' request. */ + export interface DisassembleResponse extends Response { + body?: { + /** The list of disassembled instructions. */ + instructions: DisassembledInstruction[]; + }; + } + + /** Information about the capabilities of a debug adapter. */ + export interface Capabilities { + /** The debug adapter supports the 'configurationDone' request. */ + supportsConfigurationDoneRequest?: boolean; + /** The debug adapter supports function breakpoints. */ + supportsFunctionBreakpoints?: boolean; + /** The debug adapter supports conditional breakpoints. */ + supportsConditionalBreakpoints?: boolean; + /** The debug adapter supports breakpoints that break execution after a specified number of hits. */ + 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. */ + exceptionBreakpointFilters?: ExceptionBreakpointsFilter[]; + /** The debug adapter supports stepping back via the 'stepBack' and 'reverseContinue' requests. */ + supportsStepBack?: boolean; + /** The debug adapter supports setting a variable to a value. */ + supportsSetVariable?: boolean; + /** The debug adapter supports restarting a frame. */ + supportsRestartFrame?: boolean; + /** The debug adapter supports the 'gotoTargets' request. */ + supportsGotoTargetsRequest?: boolean; + /** The debug adapter supports the 'stepInTargets' request. */ + supportsStepInTargetsRequest?: boolean; + /** The debug adapter supports the 'completions' request. */ + supportsCompletionsRequest?: boolean; + /** The set of characters that should trigger completion in a REPL. If not specified, the UI should assume the '.' character. */ + completionTriggerCharacters?: string[]; + /** The debug adapter supports the 'modules' request. */ + supportsModulesRequest?: boolean; + /** The set of additional module information exposed by the debug adapter. */ + additionalModuleColumns?: ColumnDescriptor[]; + /** Checksum algorithms supported by the debug adapter. */ + supportedChecksumAlgorithms?: ChecksumAlgorithm[]; + /** The debug adapter supports the 'restart' request. In this case a client should not implement 'restart' by terminating and relaunching the adapter but by calling the RestartRequest. */ + supportsRestartRequest?: boolean; + /** The debug adapter supports 'exceptionOptions' on the setExceptionBreakpoints request. */ + supportsExceptionOptions?: boolean; + /** The debug adapter supports a 'format' attribute on the stackTraceRequest, variablesRequest, and evaluateRequest. */ + supportsValueFormattingOptions?: boolean; + /** The debug adapter supports the 'exceptionInfo' request. */ + supportsExceptionInfoRequest?: boolean; + /** The debug adapter supports the 'terminateDebuggee' attribute on the 'disconnect' request. */ + supportTerminateDebuggee?: boolean; + /** The debug adapter supports the delayed loading of parts of the stack, which requires that both the 'startFrame' and 'levels' arguments and the 'totalFrames' result of the 'StackTrace' request are supported. */ + supportsDelayedStackTraceLoading?: boolean; + /** The debug adapter supports the 'loadedSources' request. */ + supportsLoadedSourcesRequest?: boolean; + /** The debug adapter supports logpoints by interpreting the 'logMessage' attribute of the SourceBreakpoint. */ + supportsLogPoints?: boolean; + /** The debug adapter supports the 'terminateThreads' request. */ + supportsTerminateThreadsRequest?: boolean; + /** The debug adapter supports the 'setExpression' request. */ + supportsSetExpression?: boolean; + /** The debug adapter supports the 'terminate' request. */ + supportsTerminateRequest?: boolean; + /** The debug adapter supports data breakpoints. */ + supportsDataBreakpoints?: boolean; + /** The debug adapter supports the 'readMemory' request. */ + supportsReadMemoryRequest?: boolean; + /** The debug adapter supports the 'disassemble' request. */ + supportsDisassembleRequest?: boolean; + /** The debug adapter supports the 'cancel' request. */ + supportsCancelRequest?: boolean; + /** The debug adapter supports the 'breakpointLocations' request. */ + supportsBreakpointLocationsRequest?: boolean; + } + + /** An ExceptionBreakpointsFilter is shown in the UI as an 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. */ + filter: string; + /** The name of the filter. This will be shown in the UI. */ + label: string; + /** Initial value of the filter. If not specified a value 'false' is assumed. */ + default?: boolean; + } + + /** A structured message object. Used to return errors from requests. */ + export interface Message { + /** Unique identifier for the message. */ + id: number; + /** A format string for the message. Embedded variables have the form '{name}'. + If variable name starts with an underscore character, the variable does not contain user data (PII) and can be safely used for telemetry purposes. + */ + format: string; + /** An object used as a dictionary for looking up the variables in the format string. */ + variables?: { [key: string]: string; }; + /** If true send to telemetry. */ + sendTelemetry?: boolean; + /** If true show user. */ + showUser?: boolean; + /** An optional url where additional information about this message can be found. */ + url?: string; + /** An optional label that is presented to the user as the UI for opening the url. */ + urlLabel?: string; + } + + /** A Module object represents a row in the modules view. + Two attributes are mandatory: an id identifies a module in the modules view and is used in a ModuleEvent for identifying a module for adding, updating or deleting. + The name is used to minimally render the module in the UI. + + Additional attributes can be added to the module. They will show up in the module View if they have a corresponding ColumnDescriptor. + + To avoid an unnecessary proliferation of additional attributes with similar semantics but different names + we recommend to re-use attributes from the 'recommended' list below first, and only introduce new attributes if nothing appropriate could be found. + */ + export interface Module { + /** Unique identifier for the module. */ + id: number | string; + /** A name of the module. */ + name: string; + /** optional but recommended attributes. + always try to use these first before introducing additional attributes. + + Logical full path to the module. The exact definition is implementation defined, but usually this would be a full path to the on-disk file for the module. + */ + path?: string; + /** True if the module is optimized. */ + isOptimized?: boolean; + /** True if the module is considered 'user code' by a debugger that supports 'Just My Code'. */ + isUserCode?: boolean; + /** Version of Module. */ + version?: string; + /** User understandable description of if symbols were found for the module (ex: 'Symbols Loaded', 'Symbols not found', etc. */ + symbolStatus?: string; + /** Logical full path to the symbol file. The exact definition is implementation defined. */ + symbolFilePath?: string; + /** Module created or modified. */ + dateTimeStamp?: string; + /** Address range covered by this module. */ + addressRange?: string; + } + + /** A ColumnDescriptor specifies what module attribute to show in a column of the ModulesView, how to format it, and what the column's label should be. + It is only used if the underlying UI actually supports this level of customization. + */ + export interface ColumnDescriptor { + /** Name of the attribute rendered in this column. */ + attributeName: string; + /** Header UI label of column. */ + label: string; + /** Format to use for the rendered values in this column. TBD how the format strings looks like. */ + format?: string; + /** Datatype of values in this column. Defaults to 'string' if not specified. */ + type?: 'string' | 'number' | 'boolean' | 'unixTimestampUTC'; + /** Width of this column in characters (hint only). */ + width?: number; + } + + /** The ModulesViewDescriptor is the container for all declarative configuration options of a ModuleView. + For now it only specifies the columns to be shown in the modules view. + */ + export interface ModulesViewDescriptor { + columns: ColumnDescriptor[]; + } + + /** A Thread */ + export interface Thread { + /** Unique identifier for the thread. */ + id: number; + /** A name of the thread. */ + name: string; + } + + /** A Source is a descriptor for source code. It is returned from the debug adapter as part of a StackFrame and it is used by clients when specifying breakpoints. */ + export interface Source { + /** The short name of the source. Every source returned from the debug adapter has a name. When sending a source to the debug adapter this name is optional. */ + name?: string; + /** The path of the source to be shown in the UI. It is only used to locate and load the content of the source if no sourceReference is specified (or its value is 0). */ + 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). */ + sourceReference?: number; + /** An optional hint for how to present the source in the UI. A value of 'deemphasize' can be used to indicate that the source is not available or that it is skipped on stepping. */ + presentationHint?: 'normal' | 'emphasize' | 'deemphasize'; + /** The (optional) origin of this source: possible values 'internal module', 'inlined content from source map', etc. */ + origin?: string; + /** An optional list of sources that are related to this source. These may be the source that generated this source. */ + sources?: Source[]; + /** Optional data that a debug adapter might want to loop through the client. The client should leave the data intact and persist it across sessions. The client should not interpret the data. */ + adapterData?: any; + /** The checksums associated with this file. */ + checksums?: Checksum[]; + } + + /** A Stackframe contains the source location. */ + export interface StackFrame { + /** An identifier for the stack frame. It must be unique across all threads. This id can be used to retrieve the scopes of the frame with the 'scopesRequest' or to restart the execution of a stackframe. */ + id: number; + /** The name of the stack frame, typically a method name. */ + name: string; + /** The optional source of the frame. */ + source?: Source; + /** The line within the file of the frame. If source is null or doesn't exist, line is 0 and must be ignored. */ + line: number; + /** The column within the line. If source is null or doesn't exist, column is 0 and must be ignored. */ + column: number; + /** An optional end line of the range covered by the stack frame. */ + endLine?: number; + /** An optional end column of the range covered by the stack frame. */ + endColumn?: number; + /** Optional memory reference for the current instruction pointer in this frame. */ + instructionPointerReference?: string; + /** The module associated with this frame, if any. */ + moduleId?: number | string; + /** An optional hint for how to present this frame in the UI. A value of 'label' can be used to indicate that the frame is an artificial frame that is used as a visual label or separator. A value of 'subtle' can be used to change the appearance of a frame in a 'subtle' way. */ + presentationHint?: 'normal' | 'label' | 'subtle'; + } + + /** A Scope is a named container for variables. Optionally a scope can map to a source or a range within a source. */ + export interface Scope { + /** Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This string is shown in the UI as is and can be translated. */ + name: string; + /** An optional hint for how to present this scope in the UI. If this attribute is missing, the scope is shown with a generic UI. + Values: + 'arguments': Scope contains method arguments. + 'locals': Scope contains local variables. + 'registers': Scope contains registers. Only a single 'registers' scope should be returned from a 'scopes' request. + etc. + */ + presentationHint?: 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. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + */ + namedVariables?: number; + /** The number of indexed variables in this scope. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + */ + indexedVariables?: number; + /** If true, the number of variables in this scope is large or expensive to retrieve. */ + expensive: boolean; + /** Optional source for this scope. */ + source?: Source; + /** Optional start line of the range covered by this scope. */ + line?: number; + /** Optional start column of the range covered by this scope. */ + column?: number; + /** Optional end line of the range covered by this scope. */ + endLine?: number; + /** Optional end column of the range covered by this scope. */ + endColumn?: number; + } + + /** A Variable is a name/value pair. + Optionally a variable can have a 'type' that is shown if space permits or when hovering over the variable's name. + An optional 'kind' is used to render additional properties of the variable, e.g. different icons can be used to indicate that a variable is public or private. + If the value is structured (has children), a handle is provided to retrieve the children with the VariablesRequest. + If the number of named or indexed children is large, the numbers should be returned via the optional 'namedVariables' and 'indexedVariables' attributes. + The client can use this optional information to present the children in a paged UI and fetch them in chunks. + */ + export interface Variable { + /** The variable's name. */ + name: string; + /** The variable's value. This can be a multi-line text, e.g. for a function the body of a function. */ + value: string; + /** The type of the variable's value. Typically shown in the UI when hovering over the value. */ + type?: string; + /** Properties of a variable that can be used to determine how to render the variable in the UI. */ + presentationHint?: VariablePresentationHint; + /** Optional evaluatable name of this variable which can be passed to the 'EvaluateRequest' to fetch the variable's value. */ + evaluateName?: string; + /** If variablesReference is > 0, the variable is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */ + variablesReference: number; + /** The number of named child variables. + The client can use this optional information to present the children in a paged UI and fetch them in chunks. + */ + namedVariables?: number; + /** The number of indexed child variables. + The client can use this optional information to present the children in a paged UI and fetch them in chunks. + */ + indexedVariables?: number; + /** Optional memory reference for the variable if the variable represents executable code, such as a function pointer. */ + memoryReference?: string; + } + + /** Optional properties of a variable that can be used to determine how to render the variable in the UI. */ + export interface VariablePresentationHint { + /** The kind of variable. Before introducing additional values, try to use the listed values. + Values: + 'property': Indicates that the object is a property. + 'method': Indicates that the object is a method. + 'class': Indicates that the object is a class. + 'data': Indicates that the object is data. + 'event': Indicates that the object is an event. + 'baseClass': Indicates that the object is a base class. + 'innerClass': Indicates that the object is an inner class. + 'interface': Indicates that the object is an interface. + 'mostDerivedClass': Indicates that the object is the most derived class. + 'virtual': Indicates that the object is virtual, that means it is a synthetic object introduced by the adapter for rendering purposes, e.g. an index range for large arrays. + 'dataBreakpoint': Indicates that a data breakpoint is registered for the object. + etc. + */ + kind?: 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. + 'constant': Indicates that the object is a constant. + 'readOnly': Indicates that the object is read only. + 'rawString': Indicates that the object is a raw string. + 'hasObjectId': Indicates that the object can have an Object ID created for it. + 'canHaveObjectId': Indicates that the object has an Object ID associated with it. + 'hasSideEffects': Indicates that the evaluation had side effects. + etc. + */ + attributes?: string[]; + /** Visibility of variable. Before introducing additional values, try to use the listed values. + Values: 'public', 'private', 'protected', 'internal', 'final', etc. + */ + visibility?: string; + } + + /** Properties of a breakpoint location returned from the 'breakpointLocations' request. */ + export interface BreakpointLocation { + /** Start line of breakpoint location. */ + line: number; + /** Optional start column of breakpoint location. */ + column?: number; + /** Optional end line of breakpoint location if the location covers a range. */ + endLine?: number; + /** Optional end column of breakpoint location if the location covers a range. */ + endColumn?: number; + } + + /** Properties of a breakpoint or logpoint passed to the setBreakpoints request. */ + export interface SourceBreakpoint { + /** The source line of the breakpoint or logpoint. */ + line: number; + /** An optional source column of the breakpoint. */ + column?: number; + /** An optional expression for conditional breakpoints. */ + condition?: string; + /** An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. */ + hitCondition?: string; + /** If this attribute exists and is non-empty, the backend must not 'break' (stop) but log the message instead. Expressions within {} are interpolated. */ + logMessage?: string; + } + + /** Properties of a breakpoint passed to the setFunctionBreakpoints request. */ + export interface FunctionBreakpoint { + /** The name of the function. */ + name: string; + /** An optional expression for conditional breakpoints. */ + condition?: string; + /** An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. */ + hitCondition?: string; + } + + /** This enumeration defines all possible access types for data breakpoints. */ + export type DataBreakpointAccessType = 'read' | 'write' | 'readWrite'; + + /** Properties of a data breakpoint passed to the setDataBreakpoints request. */ + export interface DataBreakpoint { + /** An id representing the data. This id is returned from the dataBreakpointInfo request. */ + dataId: string; + /** The access type of the data. */ + accessType?: DataBreakpointAccessType; + /** An optional expression for conditional breakpoints. */ + condition?: string; + /** An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. */ + hitCondition?: string; + } + + /** Information about a Breakpoint created in setBreakpoints or setFunctionBreakpoints. */ + export interface Breakpoint { + /** An optional identifier for the breakpoint. It is needed if breakpoint events are used to update or remove breakpoints. */ + id?: number; + /** If true breakpoint could be set (but not necessarily at the desired location). */ + verified: boolean; + /** An optional message about the state of the breakpoint. This is shown to the user and can be used to explain why a breakpoint could not be verified. */ + message?: string; + /** The source where the breakpoint is located. */ + source?: Source; + /** The start line of the actual range covered by the breakpoint. */ + line?: number; + /** An optional start column of the actual range covered by the breakpoint. */ + column?: number; + /** An optional end line of the actual range covered by the breakpoint. */ + endLine?: number; + /** An optional end column of the actual range covered by the breakpoint. If no end line is given, then the end column is assumed to be in the start line. */ + endColumn?: number; + } + + /** A StepInTarget can be used in the 'stepIn' request and determines into which single target the stepIn request should step. */ + export interface StepInTarget { + /** Unique identifier for a stepIn target. */ + id: number; + /** The name of the stepIn target (shown in the UI). */ + label: string; + } + + /** A GotoTarget describes a code location that can be used as a target in the 'goto' request. + The possible goto targets can be determined via the 'gotoTargets' request. + */ + export interface GotoTarget { + /** Unique identifier for a goto target. This is used in the goto request. */ + id: number; + /** The name of the goto target (shown in the UI). */ + label: string; + /** The line of the goto target. */ + line: number; + /** An optional column of the goto target. */ + column?: number; + /** An optional end line of the range covered by the goto target. */ + endLine?: number; + /** An optional end column of the range covered by the goto target. */ + endColumn?: number; + /** Optional memory reference for the instruction pointer value represented by this target. */ + instructionPointerReference?: string; + } + + /** CompletionItems are the suggestions returned from the CompletionsRequest. */ + export interface CompletionItem { + /** The label of this completion item. By default this is also the text that is inserted when selecting this completion. */ + label: string; + /** If text is not falsy then it is inserted instead of the label. */ + text?: string; + /** A string that should be used when comparing this item with other items. When `falsy` the label is used. */ + sortText?: string; + /** The item's type. Typically the client uses this information to render the item in the UI with an icon. */ + type?: CompletionItemType; + /** This value determines the location (in the CompletionsRequest's 'text' attribute) where the completion text is added. + If missing the text is added at the location specified by the CompletionsRequest's 'column' attribute. + */ + start?: number; + /** This value determines how many characters are overwritten by the completion text. + If missing the value 0 is assumed which results in the completion text being inserted. + */ + length?: number; + } + + /** Some predefined types for the CompletionItem. Please note that not all clients have specific icons for all of them. */ + export type CompletionItemType = 'method' | 'function' | 'constructor' | 'field' | 'variable' | 'class' | 'interface' | 'module' | 'property' | 'unit' | 'value' | 'enum' | 'keyword' | 'snippet' | 'text' | 'color' | 'file' | 'reference' | 'customcolor'; + + /** Names of checksum algorithms that may be supported by a debug adapter. */ + export type ChecksumAlgorithm = 'MD5' | 'SHA1' | 'SHA256' | 'timestamp'; + + /** The checksum of an item calculated by the specified algorithm. */ + export interface Checksum { + /** The algorithm used to calculate this checksum. */ + algorithm: ChecksumAlgorithm; + /** Value of the checksum. */ + checksum: string; + } + + /** Provides formatting information for a value. */ + export interface ValueFormat { + /** Display the value in hex. */ + hex?: boolean; + } + + /** Provides formatting information for a stack frame. */ + export interface StackFrameFormat extends ValueFormat { + /** Displays parameters for the stack frame. */ + parameters?: boolean; + /** Displays the types of parameters for the stack frame. */ + parameterTypes?: boolean; + /** Displays the names of parameters for the stack frame. */ + parameterNames?: boolean; + /** Displays the values of parameters for the stack frame. */ + parameterValues?: boolean; + /** Displays the line number of the stack frame. */ + line?: boolean; + /** Displays the module of the stack frame. */ + module?: boolean; + /** Includes all stack frames, including those the debug adapter might otherwise hide. */ + includeAll?: boolean; + } + + /** 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. By convention the first segment of the path is a category that is used to group exceptions in the UI. */ + path?: ExceptionPathSegment[]; + /** Condition when a thrown exception should result in a break. */ + breakMode: ExceptionBreakMode; + } + + /** This enumeration defines all possible conditions when a thrown exception should result in a break. + never: never breaks, + always: always breaks, + unhandled: breaks when exception unhandled, + userUnhandled: breaks if the exception is not handled by user code. + */ + export type ExceptionBreakMode = 'never' | 'always' | 'unhandled' | 'userUnhandled'; + + /** An ExceptionPathSegment represents a segment in a path that is used to match leafs or nodes in a tree of exceptions. If a segment consists of more than one name, it matches the names provided if 'negate' is false or missing or it matches anything except the names provided if 'negate' is true. */ + export interface ExceptionPathSegment { + /** If false or missing this segment matches the names provided, otherwise it matches anything except the names provided. */ + negate?: boolean; + /** Depending on the value of 'negate' the names that should match or not match. */ + names: string[]; + } + + /** Detailed information about an exception that has occurred. */ + export interface ExceptionDetails { + /** Message contained in the exception. */ + message?: string; + /** Short type name of the exception object. */ + typeName?: string; + /** Fully-qualified type name of the exception object. */ + fullTypeName?: string; + /** Optional expression that can be evaluated in the current scope to obtain the exception object. */ + evaluateName?: string; + /** Stack trace at the time the exception was thrown. */ + stackTrace?: string; + /** Details of the exception contained by this exception, if any. */ + innerException?: ExceptionDetails[]; + } + + /** Represents a single disassembled instruction. */ + export interface DisassembledInstruction { + /** The address of the instruction. Treated as a hex value if prefixed with '0x', or as a decimal value otherwise. */ + address: string; + /** Optional raw bytes representing the instruction and its operands, in an implementation-defined format. */ + instructionBytes?: string; + /** Text representing the instruction and its operands, in an implementation-defined format. */ + instruction: string; + /** Name of the symbol that corresponds with the location of this instruction, if any. */ + symbol?: string; + /** Source location that corresponds to this instruction, if any. Should always be set (if available) on the first instruction returned, but can be omitted afterwards if this instruction maps to the same source file as the previous instruction. */ + location?: Source; + /** The line within the source location that corresponds to this instruction, if any. */ + line?: number; + /** The column within the line that corresponds to this instruction, if any. */ + column?: number; + /** The end line of the range that corresponds to this instruction, if any. */ + endLine?: number; + /** The end column of the range that corresponds to this instruction, if any. */ + endColumn?: number; + } +} + +//------------------------------------------------------------------------------------------------------------------------------ + +export class Message implements DebugProtocol.ProtocolMessage { + seq: number; + type: string; + + public constructor(type: string) { + this.seq = 0; + this.type = type; + } +} + +export class Response extends Message implements DebugProtocol.Response { + request_seq: number; + success: boolean; + command: string; + + public constructor(request: DebugProtocol.Request, message?: string) { + super('response'); + this.request_seq = request.seq; + this.command = request.command; + if (message) { + this.success = false; + (this).message = message; + } else { + this.success = true; + } + } +} + +export class Event extends Message implements DebugProtocol.Event { + event: string; + + public constructor(event: string, body?: any) { + super('event'); + this.event = event; + if (body) { + (this).body = body; + } + } +} + +//-------------------------------------------------------------------------------------------------------------------------------- + +export interface IDaTransport { + start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void): void; + send(message: DebugProtocol.ProtocolMessage): void; + stop(): void; +} + +export class ProtocolServer { + + private close = new vscode.EventEmitter(); + onClose: vscode.Event = this.close.event; + + private error = new vscode.EventEmitter(); + onError: vscode.Event = this.error.event; + + + private _sequence: number = 1; + private _pendingRequests = new Map void>(); + + private _transport?: IDaTransport; + + constructor() { + } + + public __setTransport(transport: IDaTransport): void { + this._sequence = 1; + this._transport = transport; + this._transport.start(msg => { this.dispatch(msg); }, event => { this.error.fire(event); }); + } + + public stop(): void { + if (this._transport) { + this._transport.stop(); + } + } + + public sendEvent(event: DebugProtocol.Event): void { + this._send('event', event); + } + + public sendResponse(response: DebugProtocol.Response): void { + if (response.seq > 0) { + console.error(`attempt to send more than one response for command ${response.command}`); + } else { + this._send('response', response); + } + } + + public sendRequest(command: string, args: any, timeout: number, cb: (response: DebugProtocol.Response) => void): void { + + const request: any = { + command: command + }; + if (args && Object.keys(args).length > 0) { + request.arguments = args; + } + + if (!this._transport) { + this.error.fire(new Event('error', 'sendRequest: no transport')); + return; + } + + this._send('request', request); + + if (cb) { + this._pendingRequests.set(request.seq, cb); + + const timer = setTimeout(() => { + clearTimeout(timer); + const clb = this._pendingRequests.get(request.seq); + if (clb) { + this._pendingRequests.delete(request.seq); + clb(new Response(request, 'timeout')); + } + }, timeout); + } + } + + // ---- protected ---------------------------------------------------------- + + protected dispatchRequest(_request: DebugProtocol.Request): void { + } + + // ---- private ------------------------------------------------------------ + + private dispatch(msg: DebugProtocol.ProtocolMessage) { + if (msg.type === 'request') { + this.dispatchRequest(msg); + } else if (msg.type === 'response') { + const response = msg; + const clb = this._pendingRequests.get(response.request_seq); + if (clb) { + this._pendingRequests.delete(response.request_seq); + clb(response); + } + } + } + + private _send(typ: 'request' | 'response' | 'event', message: DebugProtocol.ProtocolMessage): void { + + message.type = typ; + message.seq = this._sequence++; + + if (this._transport) { + this._transport.send(message); + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------- + +export class Source implements DebugProtocol.Source { + name: string; + path?: string; + sourceReference: number; + + public constructor(name: string, path?: string, id: number = 0, origin?: string, data?: any) { + this.name = name; + this.path = path; + this.sourceReference = id; + if (origin) { + (this).origin = origin; + } + if (data) { + (this).adapterData = data; + } + } +} + +export class Scope implements DebugProtocol.Scope { + name: string; + variablesReference: number; + expensive: boolean; + + public constructor(name: string, reference: number, expensive: boolean = false) { + this.name = name; + this.variablesReference = reference; + this.expensive = expensive; + } +} + +export class StackFrame implements DebugProtocol.StackFrame { + id: number; + source?: Source; + line: number; + column: number; + name: string; + + public constructor(i: number, nm: string, src?: Source, ln: number = 0, col: number = 0) { + this.id = i; + this.source = src; + this.line = ln; + this.column = col; + this.name = nm; + } +} + +export class Thread implements DebugProtocol.Thread { + id: number; + name: string; + + public constructor(id: number, name: string) { + this.id = id; + if (name) { + this.name = name; + } else { + this.name = 'Thread #' + id; + } + } +} + +export class Variable implements DebugProtocol.Variable { + name: string; + value: string; + variablesReference: number; + + public constructor(name: string, value: string, ref: number = 0, indexedVariables?: number, namedVariables?: number) { + this.name = name; + this.value = value; + this.variablesReference = ref; + if (typeof namedVariables === 'number') { + (this).namedVariables = namedVariables; + } + if (typeof indexedVariables === 'number') { + (this).indexedVariables = indexedVariables; + } + } +} + +export class Breakpoint implements DebugProtocol.Breakpoint { + verified: boolean; + + public constructor(verified: boolean, line?: number, column?: number, source?: Source) { + this.verified = verified; + const e: DebugProtocol.Breakpoint = this; + if (typeof line === 'number') { + e.line = line; + } + if (typeof column === 'number') { + e.column = column; + } + if (source) { + e.source = source; + } + } +} + +export class Module implements DebugProtocol.Module { + id: number | string; + name: string; + + public constructor(id: number | string, name: string) { + this.id = id; + this.name = name; + } +} + +export class CompletionItem implements DebugProtocol.CompletionItem { + label: string; + start: number; + length: number; + + public constructor(label: string, start: number, length: number = 0) { + this.label = label; + this.start = start; + this.length = length; + } +} + +export class StoppedEvent extends Event implements DebugProtocol.StoppedEvent { + body: { + reason: string; + }; + + public constructor(reason: string, threadId?: number, exceptionText?: string) { + super('stopped'); + this.body = { + reason: reason + }; + if (typeof threadId === 'number') { + (this as DebugProtocol.StoppedEvent).body.threadId = threadId; + } + if (typeof exceptionText === 'string') { + (this as DebugProtocol.StoppedEvent).body.text = exceptionText; + } + } +} + +export class ContinuedEvent extends Event implements DebugProtocol.ContinuedEvent { + body: { + threadId: number; + }; + + public constructor(threadId: number, allThreadsContinued?: boolean) { + super('continued'); + this.body = { + threadId: threadId + }; + + if (typeof allThreadsContinued === 'boolean') { + (this).body.allThreadsContinued = allThreadsContinued; + } + } +} + +export class InitializedEvent extends Event implements DebugProtocol.InitializedEvent { + public constructor() { + super('initialized'); + } +} + +export class TerminatedEvent extends Event implements DebugProtocol.TerminatedEvent { + public constructor(restart?: any) { + super('terminated'); + if (typeof restart === 'boolean' || restart) { + const e: DebugProtocol.TerminatedEvent = this; + e.body = { + restart: restart + }; + } + } +} + +export class OutputEvent extends Event implements DebugProtocol.OutputEvent { + body: { + category: string, + output: string, + data?: any + }; + + public constructor(output: string, category: string = 'console', data?: any) { + super('output'); + this.body = { + category: category, + output: output + }; + if (data !== undefined) { + this.body.data = data; + } + } +} + +export class ThreadEvent extends Event implements DebugProtocol.ThreadEvent { + body: { + reason: string, + threadId: number + }; + + public constructor(reason: string, threadId: number) { + super('thread'); + this.body = { + reason: reason, + threadId: threadId + }; + } +} + +export class BreakpointEvent extends Event implements DebugProtocol.BreakpointEvent { + body: { + reason: string, + breakpoint: Breakpoint + }; + + public constructor(reason: string, breakpoint: Breakpoint) { + super('breakpoint'); + this.body = { + reason: reason, + breakpoint: breakpoint + }; + } +} + +export class ModuleEvent extends Event implements DebugProtocol.ModuleEvent { + body: { + reason: 'new' | 'changed' | 'removed', + module: Module + }; + + public constructor(reason: 'new' | 'changed' | 'removed', module: Module) { + super('module'); + this.body = { + reason: reason, + module: module + }; + } +} + +export class LoadedSourceEvent extends Event implements DebugProtocol.LoadedSourceEvent { + body: { + reason: 'new' | 'changed' | 'removed', + source: Source + }; + + public constructor(reason: 'new' | 'changed' | 'removed', source: Source) { + super('loadedSource'); + this.body = { + reason: reason, + source: source + }; + } +} + +export class CapabilitiesEvent extends Event implements DebugProtocol.CapabilitiesEvent { + body: { + capabilities: DebugProtocol.Capabilities + }; + + public constructor(capabilities: DebugProtocol.Capabilities) { + super('capabilities'); + this.body = { + capabilities: capabilities + }; + } +} + +export enum ErrorDestination { + User = 1, + Telemetry = 2 +} + +export class DebugSession extends ProtocolServer { + + private _debuggerLinesStartAt1: boolean; + private _debuggerColumnsStartAt1: boolean; + private _debuggerPathsAreURIs: boolean; + + private _clientLinesStartAt1: boolean; + private _clientColumnsStartAt1: boolean; + private _clientPathsAreURIs: boolean; + + protected _isServer: boolean; + + public constructor(obsolete_debuggerLinesAndColumnsStartAt1?: boolean, obsolete_isServer?: boolean) { + super(); + + const linesAndColumnsStartAt1 = typeof obsolete_debuggerLinesAndColumnsStartAt1 === 'boolean' ? obsolete_debuggerLinesAndColumnsStartAt1 : false; + this._debuggerLinesStartAt1 = linesAndColumnsStartAt1; + this._debuggerColumnsStartAt1 = linesAndColumnsStartAt1; + this._debuggerPathsAreURIs = false; + + this._clientLinesStartAt1 = true; + this._clientColumnsStartAt1 = true; + this._clientPathsAreURIs = false; + + this._isServer = typeof obsolete_isServer === 'boolean' ? obsolete_isServer : false; + + this.onClose(() => { + this.shutdown(); + }); + this.onError((_error) => { + this.shutdown(); + }); + } + + public setDebuggerPathFormat(format: string) { + this._debuggerPathsAreURIs = format !== 'path'; + } + + public setDebuggerLinesStartAt1(enable: boolean) { + this._debuggerLinesStartAt1 = enable; + } + + public setDebuggerColumnsStartAt1(enable: boolean) { + this._debuggerColumnsStartAt1 = enable; + } + + public setRunAsServer(enable: boolean) { + this._isServer = enable; + } + + public shutdown(): void { + if (this._isServer) { + // shutdown ignored in server mode + } else { + // TODO@AW + /* + // wait a bit before shutting down + setTimeout(() => { + process.exit(0); + }, 100); + */ + } + } + + protected sendErrorResponse(response: DebugProtocol.Response, codeOrMessage: number | DebugProtocol.Message, format?: string, variables?: any, dest: ErrorDestination = ErrorDestination.User): void { + + let msg: DebugProtocol.Message; + if (typeof codeOrMessage === 'number') { + msg = { + id: codeOrMessage, + format: format + }; + if (variables) { + msg.variables = variables; + } + if (dest & ErrorDestination.User) { + msg.showUser = true; + } + if (dest & ErrorDestination.Telemetry) { + msg.sendTelemetry = true; + } + } else { + msg = codeOrMessage; + } + + response.success = false; + response.message = DebugSession.formatPII(msg.format, true, msg.variables); + if (!response.body) { + response.body = {}; + } + response.body.error = msg; + + this.sendResponse(response); + } + + public runInTerminalRequest(args: DebugProtocol.RunInTerminalRequestArguments, timeout: number, cb: (response: DebugProtocol.Response) => void) { + this.sendRequest('runInTerminal', args, timeout, cb); + } + + protected dispatchRequest(request: DebugProtocol.Request): void { + + const response = new Response(request); + + try { + if (request.command === 'initialize') { + const args = request.arguments; + + if (typeof args.linesStartAt1 === 'boolean') { + this._clientLinesStartAt1 = args.linesStartAt1; + } + if (typeof args.columnsStartAt1 === 'boolean') { + this._clientColumnsStartAt1 = args.columnsStartAt1; + } + + if (args.pathFormat !== 'path') { + this.sendErrorResponse(response, 2018, 'debug adapter only supports native paths', null, ErrorDestination.Telemetry); + } else { + const initializeResponse = response; + initializeResponse.body = {}; + this.initializeRequest(initializeResponse, args); + } + + } else if (request.command === 'launch') { + this.launchRequest(response, request.arguments, request); + + } else if (request.command === 'attach') { + this.attachRequest(response, request.arguments, request); + + } else if (request.command === 'disconnect') { + this.disconnectRequest(response, request.arguments, request); + + } else if (request.command === 'terminate') { + this.terminateRequest(response, request.arguments, request); + + } else if (request.command === 'restart') { + this.restartRequest(response, request.arguments, request); + + } else if (request.command === 'setBreakpoints') { + this.setBreakPointsRequest(response, request.arguments, request); + + } else if (request.command === 'setFunctionBreakpoints') { + this.setFunctionBreakPointsRequest(response, request.arguments, request); + + } else if (request.command === 'setExceptionBreakpoints') { + this.setExceptionBreakPointsRequest(response, request.arguments, request); + + } else if (request.command === 'configurationDone') { + this.configurationDoneRequest(response, request.arguments, request); + + } else if (request.command === 'continue') { + this.continueRequest(response, request.arguments, request); + + } else if (request.command === 'next') { + this.nextRequest(response, request.arguments, request); + + } else if (request.command === 'stepIn') { + this.stepInRequest(response, request.arguments, request); + + } else if (request.command === 'stepOut') { + this.stepOutRequest(response, request.arguments, request); + + } else if (request.command === 'stepBack') { + this.stepBackRequest(response, request.arguments, request); + + } else if (request.command === 'reverseContinue') { + this.reverseContinueRequest(response, request.arguments, request); + + } else if (request.command === 'restartFrame') { + this.restartFrameRequest(response, request.arguments, request); + + } else if (request.command === 'goto') { + this.gotoRequest(response, request.arguments, request); + + } else if (request.command === 'pause') { + this.pauseRequest(response, request.arguments, request); + + } else if (request.command === 'stackTrace') { + this.stackTraceRequest(response, request.arguments, request); + + } else if (request.command === 'scopes') { + this.scopesRequest(response, request.arguments, request); + + } else if (request.command === 'variables') { + this.variablesRequest(response, request.arguments, request); + + } else if (request.command === 'setVariable') { + this.setVariableRequest(response, request.arguments, request); + + } else if (request.command === 'setExpression') { + this.setExpressionRequest(response, request.arguments, request); + + } else if (request.command === 'source') { + this.sourceRequest(response, request.arguments, request); + + } else if (request.command === 'threads') { + this.threadsRequest(response, request); + + } else if (request.command === 'terminateThreads') { + this.terminateThreadsRequest(response, request.arguments, request); + + } else if (request.command === 'evaluate') { + this.evaluateRequest(response, request.arguments, request); + + } else if (request.command === 'stepInTargets') { + this.stepInTargetsRequest(response, request.arguments, request); + + } else if (request.command === 'gotoTargets') { + this.gotoTargetsRequest(response, request.arguments, request); + + } else if (request.command === 'completions') { + this.completionsRequest(response, request.arguments, request); + + } else if (request.command === 'exceptionInfo') { + this.exceptionInfoRequest(response, request.arguments, request); + + } else if (request.command === 'loadedSources') { + this.loadedSourcesRequest(response, request.arguments, request); + + } else if (request.command === 'dataBreakpointInfo') { + this.dataBreakpointInfoRequest(response, request.arguments, request); + + } else if (request.command === 'setDataBreakpoints') { + this.setDataBreakpointsRequest(response, request.arguments, request); + + } else if (request.command === 'readMemory') { + this.readMemoryRequest(response, request.arguments, request); + + } else if (request.command === 'disassemble') { + this.disassembleRequest(response, request.arguments, request); + + } else if (request.command === 'cancel') { + this.cancelRequest(response, request.arguments, request); + + } else if (request.command === 'breakpointLocations') { + this.breakpointLocationsRequest(response, request.arguments, request); + + } else { + this.customRequest(request.command, response, request.arguments, request); + } + } catch (e) { + this.sendErrorResponse(response, 1104, '{_stack}', { _exception: e.message, _stack: e.stack }, ErrorDestination.Telemetry); + } + } + + protected initializeRequest(response: DebugProtocol.InitializeResponse, _args: DebugProtocol.InitializeRequestArguments): void { + + response.body = response.body || {}; + + // This default debug adapter does not support conditional breakpoints. + response.body.supportsConditionalBreakpoints = false; + + // This default debug adapter does not support hit conditional breakpoints. + response.body.supportsHitConditionalBreakpoints = false; + + // This default debug adapter does not support function breakpoints. + response.body.supportsFunctionBreakpoints = false; + + // This default debug adapter implements the 'configurationDone' request. + response.body.supportsConfigurationDoneRequest = true; + + // This default debug adapter does not support hovers based on the 'evaluate' request. + response.body.supportsEvaluateForHovers = false; + + // This default debug adapter does not support the 'stepBack' request. + response.body.supportsStepBack = false; + + // This default debug adapter does not support the 'setVariable' request. + response.body.supportsSetVariable = false; + + // This default debug adapter does not support the 'restartFrame' request. + response.body.supportsRestartFrame = false; + + // This default debug adapter does not support the 'stepInTargets' request. + response.body.supportsStepInTargetsRequest = false; + + // This default debug adapter does not support the 'gotoTargets' request. + response.body.supportsGotoTargetsRequest = false; + + // This default debug adapter does not support the 'completions' request. + response.body.supportsCompletionsRequest = false; + + // This default debug adapter does not support the 'restart' request. + response.body.supportsRestartRequest = false; + + // This default debug adapter does not support the 'exceptionOptions' attribute on the 'setExceptionBreakpoints' request. + response.body.supportsExceptionOptions = false; + + // This default debug adapter does not support the 'format' attribute on the 'variables', 'evaluate', and 'stackTrace' request. + response.body.supportsValueFormattingOptions = false; + + // This debug adapter does not support the 'exceptionInfo' request. + response.body.supportsExceptionInfoRequest = false; + + // This debug adapter does not support the 'TerminateDebuggee' attribute on the 'disconnect' request. + response.body.supportTerminateDebuggee = false; + + // This debug adapter does not support delayed loading of stack frames. + response.body.supportsDelayedStackTraceLoading = false; + + // This debug adapter does not support the 'loadedSources' request. + response.body.supportsLoadedSourcesRequest = false; + + // This debug adapter does not support the 'logMessage' attribute of the SourceBreakpoint. + response.body.supportsLogPoints = false; + + // This debug adapter does not support the 'terminateThreads' request. + response.body.supportsTerminateThreadsRequest = false; + + // This debug adapter does not support the 'setExpression' request. + response.body.supportsSetExpression = false; + + // This debug adapter does not support the 'terminate' request. + response.body.supportsTerminateRequest = false; + + // This debug adapter does not support data breakpoints. + response.body.supportsDataBreakpoints = false; + + /** This debug adapter does not support the 'readMemory' request. */ + response.body.supportsReadMemoryRequest = false; + + /** The debug adapter does not support the 'disassemble' request. */ + response.body.supportsDisassembleRequest = false; + + /** The debug adapter does not support the 'cancel' request. */ + response.body.supportsCancelRequest = false; + + /** The debug adapter does not support the 'breakpointLocations' request. */ + response.body.supportsBreakpointLocationsRequest = false; + + this.sendResponse(response); + } + + protected disconnectRequest(response: DebugProtocol.DisconnectResponse, _args: DebugProtocol.DisconnectArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + this.shutdown(); + } + + protected launchRequest(response: DebugProtocol.LaunchResponse, _args: DebugProtocol.LaunchRequestArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected attachRequest(response: DebugProtocol.AttachResponse, _args: DebugProtocol.AttachRequestArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected terminateRequest(response: DebugProtocol.TerminateResponse, _args: DebugProtocol.TerminateArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected restartRequest(response: DebugProtocol.RestartResponse, _args: DebugProtocol.RestartArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, _args: DebugProtocol.SetBreakpointsArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected setFunctionBreakPointsRequest(response: DebugProtocol.SetFunctionBreakpointsResponse, _args: DebugProtocol.SetFunctionBreakpointsArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected setExceptionBreakPointsRequest(response: DebugProtocol.SetExceptionBreakpointsResponse, _args: DebugProtocol.SetExceptionBreakpointsArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse, _args: DebugProtocol.ConfigurationDoneArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected continueRequest(response: DebugProtocol.ContinueResponse, _args: DebugProtocol.ContinueArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected nextRequest(response: DebugProtocol.NextResponse, _args: DebugProtocol.NextArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected stepInRequest(response: DebugProtocol.StepInResponse, _args: DebugProtocol.StepInArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected stepOutRequest(response: DebugProtocol.StepOutResponse, _args: DebugProtocol.StepOutArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected stepBackRequest(response: DebugProtocol.StepBackResponse, _args: DebugProtocol.StepBackArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, _args: DebugProtocol.ReverseContinueArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected restartFrameRequest(response: DebugProtocol.RestartFrameResponse, _args: DebugProtocol.RestartFrameArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected gotoRequest(response: DebugProtocol.GotoResponse, _args: DebugProtocol.GotoArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected pauseRequest(response: DebugProtocol.PauseResponse, _args: DebugProtocol.PauseArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected sourceRequest(response: DebugProtocol.SourceResponse, _args: DebugProtocol.SourceArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected threadsRequest(response: DebugProtocol.ThreadsResponse, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected terminateThreadsRequest(response: DebugProtocol.TerminateThreadsResponse, _args: DebugProtocol.TerminateThreadsArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected stackTraceRequest(response: DebugProtocol.StackTraceResponse, _args: DebugProtocol.StackTraceArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected scopesRequest(response: DebugProtocol.ScopesResponse, _args: DebugProtocol.ScopesArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected variablesRequest(response: DebugProtocol.VariablesResponse, _args: DebugProtocol.VariablesArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected setVariableRequest(response: DebugProtocol.SetVariableResponse, _args: DebugProtocol.SetVariableArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected setExpressionRequest(response: DebugProtocol.SetExpressionResponse, _args: DebugProtocol.SetExpressionArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected evaluateRequest(response: DebugProtocol.EvaluateResponse, _args: DebugProtocol.EvaluateArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected stepInTargetsRequest(response: DebugProtocol.StepInTargetsResponse, _args: DebugProtocol.StepInTargetsArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected gotoTargetsRequest(response: DebugProtocol.GotoTargetsResponse, _args: DebugProtocol.GotoTargetsArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected completionsRequest(response: DebugProtocol.CompletionsResponse, _args: DebugProtocol.CompletionsArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected exceptionInfoRequest(response: DebugProtocol.ExceptionInfoResponse, _args: DebugProtocol.ExceptionInfoArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected loadedSourcesRequest(response: DebugProtocol.LoadedSourcesResponse, _args: DebugProtocol.LoadedSourcesArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected dataBreakpointInfoRequest(response: DebugProtocol.DataBreakpointInfoResponse, _args: DebugProtocol.DataBreakpointInfoArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected setDataBreakpointsRequest(response: DebugProtocol.SetDataBreakpointsResponse, _args: DebugProtocol.SetDataBreakpointsArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected readMemoryRequest(response: DebugProtocol.ReadMemoryResponse, _args: DebugProtocol.ReadMemoryArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected disassembleRequest(response: DebugProtocol.DisassembleResponse, _args: DebugProtocol.DisassembleArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected cancelRequest(response: DebugProtocol.CancelResponse, _args: DebugProtocol.CancelArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + protected breakpointLocationsRequest(response: DebugProtocol.BreakpointLocationsResponse, _args: DebugProtocol.BreakpointLocationsArguments, _request?: DebugProtocol.Request): void { + this.sendResponse(response); + } + + /** + * Override this hook to implement custom requests. + */ + protected customRequest(_command: string, response: DebugProtocol.Response, _args: any, _request?: DebugProtocol.Request): void { + this.sendErrorResponse(response, 1014, 'unrecognized request', null, ErrorDestination.Telemetry); + } + + //---- protected ------------------------------------------------------------------------------------------------- + + protected convertClientLineToDebugger(line: number): number { + if (this._debuggerLinesStartAt1) { + return this._clientLinesStartAt1 ? line : line + 1; + } + return this._clientLinesStartAt1 ? line - 1 : line; + } + + protected convertDebuggerLineToClient(line: number): number { + if (this._debuggerLinesStartAt1) { + return this._clientLinesStartAt1 ? line : line - 1; + } + return this._clientLinesStartAt1 ? line + 1 : line; + } + + protected convertClientColumnToDebugger(column: number): number { + if (this._debuggerColumnsStartAt1) { + return this._clientColumnsStartAt1 ? column : column + 1; + } + return this._clientColumnsStartAt1 ? column - 1 : column; + } + + protected convertDebuggerColumnToClient(column: number): number { + if (this._debuggerColumnsStartAt1) { + return this._clientColumnsStartAt1 ? column : column - 1; + } + return this._clientColumnsStartAt1 ? column + 1 : column; + } + + protected convertClientPathToDebugger(clientPath: string): string { + if (this._clientPathsAreURIs !== this._debuggerPathsAreURIs) { + if (this._clientPathsAreURIs) { + return DebugSession.uri2path(clientPath); + } else { + return DebugSession.path2uri(clientPath); + } + } + return clientPath; + } + + protected convertDebuggerPathToClient(debuggerPath: string): string { + if (this._debuggerPathsAreURIs !== this._clientPathsAreURIs) { + if (this._debuggerPathsAreURIs) { + return DebugSession.uri2path(debuggerPath); + } else { + return DebugSession.path2uri(debuggerPath); + } + } + return debuggerPath; + } + + //---- private ------------------------------------------------------------------------------- + + private static path2uri(path: string): string { + + path = encodeURI(path); + + let uri = new URL(`file:`); // ignore 'path' for now + uri.pathname = path; // now use 'path' to get the correct percent encoding (see https://url.spec.whatwg.org) + return uri.toString(); + } + + private static uri2path(sourceUri: string): string { + + let uri = new URL(sourceUri); + let s = decodeURIComponent(uri.pathname); + return s; + } + + private static _formatPIIRegexp = /{([^}]+)}/g; + + /* + * If argument starts with '_' it is OK to send its value to telemetry. + */ + private static formatPII(format: string, excludePII: boolean, args?: { [key: string]: string }): string { + return format.replace(DebugSession._formatPIIRegexp, function (match, paramName) { + if (excludePII && paramName.length > 0 && paramName[0] !== '_') { + return match; + } + return args && args[paramName] && args.hasOwnProperty(paramName) ? + args[paramName] : + match; + }); + } +} + +//--------------------------------------------------------------------------- + +export class Handles { + + private START_HANDLE = 1000; + + private _nextHandle: number; + private _handleMap = new Map(); + + public constructor(startHandle?: number) { + this._nextHandle = typeof startHandle === 'number' ? startHandle : this.START_HANDLE; + } + + public reset(): void { + this._nextHandle = this.START_HANDLE; + this._handleMap = new Map(); + } + + public create(value: T): number { + const handle = this._nextHandle++; + this._handleMap.set(handle, value); + return handle; + } + + public get(handle: number, dflt?: T): T | undefined { + return this._handleMap.get(handle) || dflt; + } +} + +//--------------------------------------------------------------------------- + +function basename(path: string): string { + const pos = path.lastIndexOf('/'); + if (pos >= 0) { + return path.substring(pos + 1); + } + return path; +} + +export class MockDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory { + + constructor(private memfs: MemFS) { + } + + createDebugAdapterDescriptor(_session: vscode.DebugSession, _executable: vscode.DebugAdapterExecutable | undefined): vscode.ProviderResult { + // TODO@AW: we need some extension API for connecting a DA implementation with a vscode.DebugAdapterDescriptor + const descriptor = new vscode.DebugAdapterExecutable('dummy'); + const a = descriptor; + a['implementation'] = new MockDebugSession(this.memfs); + return descriptor; + } +} + +function timeout(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +/** + * This interface describes the mock-debug specific launch attributes + * (which are not part of the Debug Adapter Protocol). + * The schema for these attributes lives in the package.json of the mock-debug extension. + * The interface should always match this schema. + */ +interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments { + /** An absolute path to the "program" to debug. */ + program: string; + /** Automatically stop target after launch. If not specified, target does not stop. */ + stopOnEntry?: boolean; + /** enable logging the Debug Adapter Protocol */ + trace?: boolean; +} + +export class MockDebugSession extends DebugSession { + + // we don't support multiple threads, so we can use a hardcoded ID for the default thread + private static THREAD_ID = 1; + + // a Mock runtime (or debugger) + private _runtime: MockRuntime; + + private _variableHandles = new Handles(); + + //private _configurationDone = new Subject(); + + private promiseResolve?: () => void; + private _configurationDone = new Promise((r, _e) => { + this.promiseResolve = r; + setTimeout(r, 1000); + }); + + private _cancelationTokens = new Map(); + private _isLongrunning = new Map(); + + /** + * Creates a new debug adapter that is used for one debug session. + * We configure the default implementation of a debug adapter here. + */ + public constructor(memfs: MemFS) { + + super(); + + // this debugger uses zero-based lines and columns + this.setDebuggerLinesStartAt1(false); + this.setDebuggerColumnsStartAt1(false); + + this._runtime = new MockRuntime(memfs); + + // setup event handlers + this._runtime.onStopOnEntry(() => { + this.sendEvent(new StoppedEvent('entry', MockDebugSession.THREAD_ID)); + }); + this._runtime.onStopOnStep(() => { + this.sendEvent(new StoppedEvent('step', MockDebugSession.THREAD_ID)); + }); + this._runtime.onStopOnBreakpoint(() => { + this.sendEvent(new StoppedEvent('breakpoint', MockDebugSession.THREAD_ID)); + }); + this._runtime.onStopOnDataBreakpoint(() => { + this.sendEvent(new StoppedEvent('data breakpoint', MockDebugSession.THREAD_ID)); + }); + this._runtime.onStopOnException(() => { + this.sendEvent(new StoppedEvent('exception', MockDebugSession.THREAD_ID)); + }); + this._runtime.onBreakpointValidated((bp: MockBreakpoint) => { + this.sendEvent(new BreakpointEvent('changed', { verified: bp.verified, id: bp.id })); + }); + this._runtime.onOutput(oe => { + const e: DebugProtocol.OutputEvent = new OutputEvent(`${oe.text}\n`); + e.body.source = this.createSource(oe.filePath); + e.body.line = this.convertDebuggerLineToClient(oe.line); + e.body.column = this.convertDebuggerColumnToClient(oe.column); + this.sendEvent(e); + }); + this._runtime.onEnd(() => { + this.sendEvent(new TerminatedEvent()); + }); + } + + /** + * The 'initialize' request is the first request called by the frontend + * to interrogate the features the debug adapter provides. + */ + protected initializeRequest(response: DebugProtocol.InitializeResponse, _args: DebugProtocol.InitializeRequestArguments): void { + + // build and return the capabilities of this debug adapter: + response.body = response.body || {}; + + // the adapter implements the configurationDoneRequest. + response.body.supportsConfigurationDoneRequest = true; + + // make VS Code to use 'evaluate' when hovering over source + response.body.supportsEvaluateForHovers = true; + + // make VS Code to show a 'step back' button + response.body.supportsStepBack = true; + + // make VS Code to support data breakpoints + response.body.supportsDataBreakpoints = true; + + // make VS Code to support completion in REPL + response.body.supportsCompletionsRequest = true; + response.body.completionTriggerCharacters = ['.', '[']; + + // make VS Code to send cancelRequests + response.body.supportsCancelRequest = true; + + // make VS Code send the breakpointLocations request + response.body.supportsBreakpointLocationsRequest = true; + + this.sendResponse(response); + + // since this debug adapter can accept configuration requests like 'setBreakpoint' at any time, + // we request them early by sending an 'initializeRequest' to the frontend. + // The frontend will end the configuration sequence by calling 'configurationDone' request. + this.sendEvent(new InitializedEvent()); + } + + /** + * Called at the end of the configuration sequence. + * Indicates that all breakpoints etc. have been sent to the DA and that the 'launch' can start. + */ + protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse, args: DebugProtocol.ConfigurationDoneArguments): void { + super.configurationDoneRequest(response, args); + + // notify the launchRequest that configuration has finished + //this._configurationDone.notify(); + if (this.promiseResolve) { + this.promiseResolve(); + } + } + + protected async launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments) { + + // make sure to 'Stop' the buffered logging if 'trace' is not set + //logger.setup(args.trace ? Logger.LogLevel.Verbose : Logger.LogLevel.Stop, false); + + // wait until configuration has finished (and configurationDoneRequest has been called) + await this._configurationDone; + + // start the program in the runtime + this._runtime.start(`memfs:${args.program}`, !!args.stopOnEntry); + + this.sendResponse(response); + } + + protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): void { + + const path = args.source.path; + const clientLines = args.lines || []; + + // clear all breakpoints for this file + this._runtime.clearBreakpoints(path); + + // set and verify breakpoint locations + const actualBreakpoints = clientLines.map(l => { + let { verified, line, id } = this._runtime.setBreakPoint(path, this.convertClientLineToDebugger(l)); + const bp = new Breakpoint(verified, this.convertDebuggerLineToClient(line)); + bp.id = id; + return bp; + }); + + // send back the actual breakpoint positions + response.body = { + breakpoints: actualBreakpoints + }; + this.sendResponse(response); + } + + protected breakpointLocationsRequest(response: DebugProtocol.BreakpointLocationsResponse, args: DebugProtocol.BreakpointLocationsArguments, _request?: DebugProtocol.Request): void { + + if (args.source.path) { + const bps = this._runtime.getBreakpoints(args.source.path, this.convertClientLineToDebugger(args.line)); + response.body = { + breakpoints: bps.map(col => { + return { + line: args.line, + column: this.convertDebuggerColumnToClient(col) + }; + }) + }; + } else { + response.body = { + breakpoints: [] + }; + } + this.sendResponse(response); + } + + protected threadsRequest(response: DebugProtocol.ThreadsResponse): void { + + // runtime supports no threads so just return a default thread. + response.body = { + threads: [ + new Thread(MockDebugSession.THREAD_ID, 'thread 1') + ] + }; + this.sendResponse(response); + } + + protected stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments): void { + + const startFrame = typeof args.startFrame === 'number' ? args.startFrame : 0; + const maxLevels = typeof args.levels === 'number' ? args.levels : 1000; + const endFrame = startFrame + maxLevels; + + const stk = this._runtime.stack(startFrame, endFrame); + + response.body = { + stackFrames: stk.frames.map(f => new StackFrame(f.index, f.name, this.createSource(f.file), this.convertDebuggerLineToClient(f.line))), + totalFrames: stk.count + }; + this.sendResponse(response); + } + + protected scopesRequest(response: DebugProtocol.ScopesResponse, _args: DebugProtocol.ScopesArguments): void { + + response.body = { + scopes: [ + new Scope('Local', this._variableHandles.create('local'), false), + new Scope('Global', this._variableHandles.create('global'), true) + ] + }; + this.sendResponse(response); + } + + protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments, request?: DebugProtocol.Request) { + + const variables: DebugProtocol.Variable[] = []; + + if (this._isLongrunning.get(args.variablesReference)) { + // long running + + if (request) { + this._cancelationTokens.set(request.seq, false); + } + + for (let i = 0; i < 100; i++) { + await timeout(1000); + variables.push({ + name: `i_${i}`, + type: 'integer', + value: `${i}`, + variablesReference: 0 + }); + if (request && this._cancelationTokens.get(request.seq)) { + break; + } + } + + if (request) { + this._cancelationTokens.delete(request.seq); + } + + } else { + + const id = this._variableHandles.get(args.variablesReference); + + if (id) { + variables.push({ + name: id + '_i', + type: 'integer', + value: '123', + variablesReference: 0 + }); + variables.push({ + name: id + '_f', + type: 'float', + value: '3.14', + variablesReference: 0 + }); + variables.push({ + name: id + '_s', + type: 'string', + value: 'hello world', + variablesReference: 0 + }); + variables.push({ + name: id + '_o', + type: 'object', + value: 'Object', + variablesReference: this._variableHandles.create(id + '_o') + }); + + // cancelation support for long running requests + const nm = id + '_long_running'; + const ref = this._variableHandles.create(id + '_lr'); + variables.push({ + name: nm, + type: 'object', + value: 'Object', + variablesReference: ref + }); + this._isLongrunning.set(ref, true); + } + } + + response.body = { + variables: variables + }; + this.sendResponse(response); + } + + protected continueRequest(response: DebugProtocol.ContinueResponse, _args: DebugProtocol.ContinueArguments): void { + this._runtime.continue(); + this.sendResponse(response); + } + + protected reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, _args: DebugProtocol.ReverseContinueArguments): void { + this._runtime.continue(true); + this.sendResponse(response); + } + + protected nextRequest(response: DebugProtocol.NextResponse, _args: DebugProtocol.NextArguments): void { + this._runtime.step(); + this.sendResponse(response); + } + + protected stepBackRequest(response: DebugProtocol.StepBackResponse, _args: DebugProtocol.StepBackArguments): void { + this._runtime.step(true); + this.sendResponse(response); + } + + protected evaluateRequest(response: DebugProtocol.EvaluateResponse, args: DebugProtocol.EvaluateArguments): void { + + let reply: string | undefined = undefined; + + if (args.context === 'repl') { + // 'evaluate' supports to create and delete breakpoints from the 'repl': + const matches = /new +([0-9]+)/.exec(args.expression); + if (matches && matches.length === 2) { + if (this._runtime.sourceFile) { + const mbp = this._runtime.setBreakPoint(this._runtime.sourceFile, this.convertClientLineToDebugger(parseInt(matches[1]))); + const bp = new Breakpoint(mbp.verified, this.convertDebuggerLineToClient(mbp.line), undefined, this.createSource(this._runtime.sourceFile)); + bp.id = mbp.id; + this.sendEvent(new BreakpointEvent('new', bp)); + reply = `breakpoint created`; + } + } else { + const matches = /del +([0-9]+)/.exec(args.expression); + if (matches && matches.length === 2) { + const mbp = this._runtime.sourceFile ? this._runtime.clearBreakPoint(this._runtime.sourceFile, this.convertClientLineToDebugger(parseInt(matches[1]))) : undefined; + if (mbp) { + const bp = new Breakpoint(false); + bp.id = mbp.id; + this.sendEvent(new BreakpointEvent('removed', bp)); + reply = `breakpoint deleted`; + } + } + } + } + + response.body = { + result: reply ? reply : `evaluate(context: '${args.context}', '${args.expression}')`, + variablesReference: 0 + }; + this.sendResponse(response); + } + + protected dataBreakpointInfoRequest(response: DebugProtocol.DataBreakpointInfoResponse, args: DebugProtocol.DataBreakpointInfoArguments): void { + + response.body = { + dataId: null, + description: 'cannot break on data access', + accessTypes: undefined, + canPersist: false + }; + + if (args.variablesReference && args.name) { + const id = this._variableHandles.get(args.variablesReference); + if (id && id.startsWith('global_')) { + response.body.dataId = args.name; + response.body.description = args.name; + response.body.accessTypes = ['read']; + response.body.canPersist = false; + } + } + + this.sendResponse(response); + } + + protected setDataBreakpointsRequest(response: DebugProtocol.SetDataBreakpointsResponse, args: DebugProtocol.SetDataBreakpointsArguments): void { + + // clear all data breakpoints + this._runtime.clearAllDataBreakpoints(); + + response.body = { + breakpoints: [] + }; + + for (let dbp of args.breakpoints) { + // assume that id is the "address" to break on + const ok = this._runtime.setDataBreakpoint(dbp.dataId); + response.body.breakpoints.push({ + verified: ok + }); + } + + this.sendResponse(response); + } + + protected completionsRequest(response: DebugProtocol.CompletionsResponse, _args: DebugProtocol.CompletionsArguments): void { + + response.body = { + targets: [ + { + label: 'item 10', + sortText: '10' + }, + { + label: 'item 1', + sortText: '01' + }, + { + label: 'item 2', + sortText: '02' + } + ] + }; + this.sendResponse(response); + } + + protected cancelRequest(_response: DebugProtocol.CancelResponse, args: DebugProtocol.CancelArguments) { + if (args.requestId) { + this._cancelationTokens.set(args.requestId, true); + } + } + + //---- helpers + + private createSource(filePath: string): Source { + return new Source(basename(filePath), this.convertDebuggerPathToClient(filePath), undefined, undefined, 'mock-adapter-data'); + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------ + + +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +export interface MockBreakpoint { + id: number; + line: number; + verified: boolean; +} + +export interface MockOutputEvent { + text: string; + filePath: string; + line: number; + column: number; +} + +/** + * A Mock runtime with minimal debugger functionality. + */ +export class MockRuntime { + + private stopOnEntry = new vscode.EventEmitter(); + onStopOnEntry: vscode.Event = this.stopOnEntry.event; + + private stopOnStep = new vscode.EventEmitter(); + onStopOnStep: vscode.Event = this.stopOnStep.event; + + private stopOnBreakpoint = new vscode.EventEmitter(); + onStopOnBreakpoint: vscode.Event = this.stopOnBreakpoint.event; + + private stopOnDataBreakpoint = new vscode.EventEmitter(); + onStopOnDataBreakpoint: vscode.Event = this.stopOnDataBreakpoint.event; + + private stopOnException = new vscode.EventEmitter(); + onStopOnException: vscode.Event = this.stopOnException.event; + + private breakpointValidated = new vscode.EventEmitter(); + onBreakpointValidated: vscode.Event = this.breakpointValidated.event; + + private output = new vscode.EventEmitter(); + onOutput: vscode.Event = this.output.event; + + private end = new vscode.EventEmitter(); + onEnd: vscode.Event = this.end.event; + + + // the initial (and one and only) file we are 'debugging' + private _sourceFile?: string; + public get sourceFile() { + return this._sourceFile; + } + + // the contents (= lines) of the one and only file + private _sourceLines: string[] = []; + + // This is the next line that will be 'executed' + private _currentLine = 0; + + // maps from sourceFile to array of Mock breakpoints + private _breakPoints = new Map(); + + // since we want to send breakpoint events, we will assign an id to every event + // so that the frontend can match events with breakpoints. + private _breakpointId = 1; + + private _breakAddresses = new Set(); + + constructor(private memfs: MemFS) { + } + + /** + * Start executing the given program. + */ + public start(program: string, stopOnEntry: boolean) { + + this.loadSource(program); + this._currentLine = -1; + + if (this._sourceFile) { + this.verifyBreakpoints(this._sourceFile); + } + + if (stopOnEntry) { + // we step once + this.step(false, this.stopOnEntry); + } else { + // we just start to run until we hit a breakpoint or an exception + this.continue(); + } + } + + /** + * Continue execution to the end/beginning. + */ + public continue(reverse = false) { + this.run(reverse, undefined); + } + + /** + * Step to the next/previous non empty line. + */ + public step(reverse = false, event = this.stopOnStep) { + this.run(reverse, event); + } + + /** + * Returns a fake 'stacktrace' where every 'stackframe' is a word from the current line. + */ + public stack(startFrame: number, endFrame: number): { frames: any[], count: number } { + + const words = this._sourceLines[this._currentLine].trim().split(/\s+/); + + const frames = new Array(); + // every word of the current line becomes a stack frame. + for (let i = startFrame; i < Math.min(endFrame, words.length); i++) { + const name = words[i]; // use a word of the line as the stackframe name + frames.push({ + index: i, + name: `${name}(${i})`, + file: this._sourceFile, + line: this._currentLine + }); + } + return { + frames: frames, + count: words.length + }; + } + + public getBreakpoints(_path: string, line: number): number[] { + + const l = this._sourceLines[line]; + + let sawSpace = true; + const bps: number[] = []; + for (let i = 0; i < l.length; i++) { + if (l[i] !== ' ') { + if (sawSpace) { + bps.push(i); + sawSpace = false; + } + } else { + sawSpace = true; + } + } + + return bps; + } + + /* + * Set breakpoint in file with given line. + */ + public setBreakPoint(path: string, line: number): MockBreakpoint { + + const bp = { verified: false, line, id: this._breakpointId++ }; + let bps = this._breakPoints.get(path); + if (!bps) { + bps = new Array(); + this._breakPoints.set(path, bps); + } + bps.push(bp); + + this.verifyBreakpoints(path); + + return bp; + } + + /* + * Clear breakpoint in file with given line. + */ + public clearBreakPoint(path: string, line: number): MockBreakpoint | undefined { + let bps = this._breakPoints.get(path); + if (bps) { + const index = bps.findIndex(bp => bp.line === line); + if (index >= 0) { + const bp = bps[index]; + bps.splice(index, 1); + return bp; + } + } + return undefined; + } + + /* + * Clear all breakpoints for file. + */ + public clearBreakpoints(path: string): void { + this._breakPoints.delete(path); + } + + /* + * Set data breakpoint. + */ + public setDataBreakpoint(address: string): boolean { + if (address) { + this._breakAddresses.add(address); + return true; + } + return false; + } + + /* + * Clear all data breakpoints. + */ + public clearAllDataBreakpoints(): void { + this._breakAddresses.clear(); + } + + // private methods + + private loadSource(file: string) { + if (this._sourceFile !== file) { + this._sourceFile = file; + + const _textDecoder = new TextDecoder(); + + const uri = vscode.Uri.parse(file); + const content = _textDecoder.decode(this.memfs.readFile(uri)); + this._sourceLines = content.split('\n'); + + //this._sourceLines = readFileSync(this._sourceFile).toString().split('\n'); + } + } + + /** + * Run through the file. + * If stepEvent is specified only run a single step and emit the stepEvent. + */ + private run(reverse = false, stepEvent?: vscode.EventEmitter): void { + if (reverse) { + for (let ln = this._currentLine - 1; ln >= 0; ln--) { + if (this.fireEventsForLine(ln, stepEvent)) { + this._currentLine = ln; + return; + } + } + // no more lines: stop at first line + this._currentLine = 0; + this.stopOnEntry.fire(); + } else { + for (let ln = this._currentLine + 1; ln < this._sourceLines.length; ln++) { + if (this.fireEventsForLine(ln, stepEvent)) { + this._currentLine = ln; + return; + } + } + // no more lines: run to end + this.end.fire(); + } + } + + private verifyBreakpoints(path: string): void { + let bps = this._breakPoints.get(path); + if (bps) { + this.loadSource(path); + bps.forEach(bp => { + if (!bp.verified && bp.line < this._sourceLines.length) { + const srcLine = this._sourceLines[bp.line].trim(); + + // if a line is empty or starts with '+' we don't allow to set a breakpoint but move the breakpoint down + if (srcLine.length === 0 || srcLine.indexOf('+') === 0) { + bp.line++; + } + // if a line starts with '-' we don't allow to set a breakpoint but move the breakpoint up + if (srcLine.indexOf('-') === 0) { + bp.line--; + } + // don't set 'verified' to true if the line contains the word 'lazy' + // in this case the breakpoint will be verified 'lazy' after hitting it once. + if (srcLine.indexOf('lazy') < 0) { + bp.verified = true; + this.breakpointValidated.fire(bp); + } + } + }); + } + } + + /** + * Fire events if line has a breakpoint or the word 'exception' is found. + * Returns true is execution needs to stop. + */ + private fireEventsForLine(ln: number, stepEvent?: vscode.EventEmitter): boolean { + + const line = this._sourceLines[ln].trim(); + + // if 'log(...)' found in source -> send argument to debug console + const matches = /log\((.*)\)/.exec(line); + if (matches && matches.length === 2) { + if (this._sourceFile) { + this.output.fire({ text: matches[1], filePath: this._sourceFile, line: ln, column: matches.index }); + } + } + + // if a word in a line matches a data breakpoint, fire a 'dataBreakpoint' event + const words = line.split(' '); + for (let word of words) { + if (this._breakAddresses.has(word)) { + this.stopOnDataBreakpoint.fire(); + return true; + } + } + + // if word 'exception' found in source -> throw exception + if (line.indexOf('exception') >= 0) { + this.stopOnException.fire(); + return true; + } + + // is there a breakpoint? + const breakpoints = this._sourceFile ? this._breakPoints.get(this._sourceFile) : undefined; + if (breakpoints) { + const bps = breakpoints.filter(bp => bp.line === ln); + if (bps.length > 0) { + + // send 'stopped' event + this.stopOnBreakpoint.fire(); + + // the following shows the use of 'breakpoint' events to update properties of a breakpoint in the UI + // if breakpoint is not yet verified, verify it now and send a 'breakpoint' update event + if (!bps[0].verified) { + bps[0].verified = true; + this.breakpointValidated.fire(bps[0]); + } + return true; + } + } + + // non-empty line + if (stepEvent && line.length > 0) { + stepEvent.fire(); + return true; + } + + // nothing interesting found -> continue + return false; + } +} From 2e9c9db0f851865e3f878d81b1c76d2b0b65ab76 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Nov 2019 12:15:57 +0100 Subject: [PATCH 048/246] clarify that you cannot modify documents/files before that are created, #43768 --- src/vs/vscode.proposed.d.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 64ef7736f55..5d5d4b7c84e 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -888,10 +888,12 @@ declare module 'vscode' { /** * An event that is emitted when files are being created. * - * *Note:* This event is triggered by user gestures, like creating a file from the + * *Note 1:* This event is triggered by user gestures, like creating a file from the * explorer, or from the [`workspace.applyEdit`](#workspace.applyEdit)-api. This event is *not* fired when * files change on disk, e.g triggered by another application, or when using the * [`workspace.fs`](#FileSystem)-api. + * + * *Note 2:* When this event is fired, edits to files thare are being created cannot be applied. */ export const onWillCreateFiles: Event; From eff232047302adbb5beb4821468dfdfc74a876a1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Nov 2019 12:34:19 +0100 Subject: [PATCH 049/246] check timeout on extension host, blame extension when exceeded, #43768 --- src/vs/base/common/event.ts | 4 ++-- .../mainThreadFileSystemEventService.ts | 2 +- .../workbench/api/common/extHost.api.impl.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../common/extHostFileSystemEventService.ts | 21 ++++++++++++------- .../api/extHostFileSystemEventService.test.ts | 5 +++-- 6 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index e6c16f43df6..f0492026239 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -656,7 +656,7 @@ export class AsyncEmitter extends Emitter { private _asyncDeliveryQueue?: LinkedList<[Listener, Omit]>; - async fireAsync(data: Omit, token: CancellationToken, promiseJoin?: (p: Promise) => Promise): Promise { + async fireAsync(data: Omit, token: CancellationToken, promiseJoin?: (p: Promise, listener: Function) => Promise): Promise { if (!this._listeners) { return; } @@ -681,7 +681,7 @@ export class AsyncEmitter extends Emitter { throw new Error('waitUntil can NOT be called asynchronous'); } if (promiseJoin) { - p = promiseJoin(p); + p = promiseJoin(p, typeof listener === 'function' ? listener : listener[0]); } thenables.push(p); } diff --git a/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts b/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts index da387c35747..38f3b5fdca9 100644 --- a/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts +++ b/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts @@ -88,7 +88,7 @@ export class MainThreadFileSystemEventService { reject(new Error('timeout')); }, timeout); - proxy.$onWillRunFileOperation(e.operation, e.target, e.source, cts.token) + proxy.$onWillRunFileOperation(e.operation, e.target, e.source, timeout, cts.token) .then(resolve, reject) .finally(() => clearTimeout(timeoutHandle)); }); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 93d36038334..c35de421c57 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -116,7 +116,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol, extHostLogService)); const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService)); const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures)); - const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors)); + const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostLogService, extHostDocumentsAndEditors)); const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands)); const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService)); const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments)); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index ec42255a44d..9639c114338 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -923,7 +923,7 @@ export interface FileSystemEvents { export interface ExtHostFileSystemEventServiceShape { $onFileEvent(events: FileSystemEvents): void; - $onWillRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined, token: CancellationToken): Promise; + $onWillRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined, timeout: number, token: CancellationToken): Promise; $onDidRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined): void; } diff --git a/src/vs/workbench/api/common/extHostFileSystemEventService.ts b/src/vs/workbench/api/common/extHostFileSystemEventService.ts index d289fc249fe..39bc81f6f8c 100644 --- a/src/vs/workbench/api/common/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/common/extHostFileSystemEventService.ts @@ -15,6 +15,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { FileOperation } from 'vs/platform/files/common/files'; import { flatten } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ILogService } from 'vs/platform/log/common/log'; class FileSystemWatcher implements vscode.FileSystemWatcher { @@ -121,6 +122,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ constructor( mainContext: IMainContext, + private readonly _logService: ILogService, private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors, private readonly _mainThreadTextEditors: MainThreadTextEditorsShape = mainContext.getProxy(MainContext.MainThreadTextEditors) ) { @@ -177,32 +179,37 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ }; } - async $onWillRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined, token: CancellationToken): Promise { + async $onWillRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined, timeout: number, token: CancellationToken): Promise { switch (operation) { case FileOperation.MOVE: - await this._fireWillEvent(this._onWillRenameFile, { files: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }] }, token); + await this._fireWillEvent(this._onWillRenameFile, { files: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }] }, timeout, token); break; case FileOperation.DELETE: - await this._fireWillEvent(this._onWillDeleteFile, { files: [URI.revive(target)] }, token); + await this._fireWillEvent(this._onWillDeleteFile, { files: [URI.revive(target)] }, timeout, token); break; case FileOperation.CREATE: - await this._fireWillEvent(this._onWillCreateFile, { files: [URI.revive(target)] }, token); + await this._fireWillEvent(this._onWillCreateFile, { files: [URI.revive(target)] }, timeout, token); break; default: //ignore, dont send } } - private async _fireWillEvent(emitter: AsyncEmitter, data: Omit, token: CancellationToken): Promise { + private async _fireWillEvent(emitter: AsyncEmitter, data: Omit, timeout: number, token: CancellationToken): Promise { const edits: WorkspaceEdit[] = []; - await emitter.fireAsync(data, token, async p => { + await emitter.fireAsync(data, token, async (thenable, listener: IExtensionListener) => { // ignore all results except for WorkspaceEdits. Those are stored in an array. - const result = await Promise.resolve(p); + const now = Date.now(); + const result = await Promise.resolve(thenable); if (result instanceof WorkspaceEdit) { edits.push(result); } + + if (Date.now() - now > timeout) { + this._logService.warn('SLOW file-participant', listener.extension?.identifier); + } }); if (token.isCancellationRequested) { diff --git a/src/vs/workbench/test/electron-browser/api/extHostFileSystemEventService.test.ts b/src/vs/workbench/test/electron-browser/api/extHostFileSystemEventService.test.ts index d8b1cbadc9b..c20cee41ce1 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostFileSystemEventService.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostFileSystemEventService.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService'; import { IMainContext } from 'vs/workbench/api/common/extHost.protocol'; +import { NullLogService } from 'vs/platform/log/common/log'; suite('ExtHostFileSystemEventService', () => { @@ -17,12 +18,12 @@ suite('ExtHostFileSystemEventService', () => { assertRegistered: undefined! }; - const watcher1 = new ExtHostFileSystemEventService(protocol, undefined!).createFileSystemWatcher('**/somethingInteresting', false, false, false); + const watcher1 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher('**/somethingInteresting', false, false, false); assert.equal(watcher1.ignoreChangeEvents, false); assert.equal(watcher1.ignoreCreateEvents, false); assert.equal(watcher1.ignoreDeleteEvents, false); - const watcher2 = new ExtHostFileSystemEventService(protocol, undefined!).createFileSystemWatcher('**/somethingBoring', true, true, true); + const watcher2 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher('**/somethingBoring', true, true, true); assert.equal(watcher2.ignoreChangeEvents, true); assert.equal(watcher2.ignoreCreateEvents, true); assert.equal(watcher2.ignoreDeleteEvents, true); From e94314c26ca1633015bdf9748af255b7fef6c1f1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Nov 2019 12:08:18 +0100 Subject: [PATCH 050/246] :lipstick: --- .../contrib/userDataSync/browser/userDataSync.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index fbac49c504c..c968e8607d9 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -318,18 +318,18 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo when: CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SigningIn) }); - const stopSycCommand = { + const stopSyncCommand = { id: 'workbench.userData.actions.stopSync', title: localize('stop sync', "Sync: Turn Off") }; - CommandsRegistry.registerCommand(stopSycCommand.id, () => this.turnOff()); + CommandsRegistry.registerCommand(stopSyncCommand.id, () => this.turnOff()); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '5_sync', - command: stopSycCommand, + command: stopSyncCommand, when: ContextKeyExpr.and(ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.Active), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.HasConflicts)) }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: stopSycCommand, + command: stopSyncCommand, when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`)), }); From 2305f2187475d860f5f70c8d3bff548f6ce08929 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Nov 2019 12:32:42 +0100 Subject: [PATCH 051/246] change settings sync store to include name and account --- src/vs/platform/product/common/productService.ts | 6 +++++- .../userDataSync/common/userDataSyncStoreService.ts | 6 +++--- src/vs/workbench/contrib/logs/common/logs.contribution.ts | 2 +- .../userDataSync/browser/userDataSync.contribution.ts | 2 +- .../contrib/userDataSync/browser/userDataSync.ts | 8 +++++--- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index e905ecfa297..398ff63b78b 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -103,7 +103,11 @@ export interface IProductConfiguration { readonly msftInternalDomains?: string[]; readonly linkProtectionTrustedDomains?: readonly string[]; - readonly settingsSyncStoreUrl?: string; + readonly settingsSyncStore?: { + url: string; + name: string; + account: string; + }; } export interface IExeBasedExtensionTip { diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index faf1ec0c400..02b70038cab 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -17,7 +17,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn _serviceBrand: any; - get enabled(): boolean { return !!this.productService.settingsSyncStoreUrl; } + get enabled(): boolean { return !!this.productService.settingsSyncStore; } constructor( @IProductService private readonly productService: IProductService, @@ -32,7 +32,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn throw new Error('No settings sync store url configured.'); } - const url = joinPath(URI.parse(this.productService.settingsSyncStoreUrl!), 'resource', key, 'latest').toString(); + const url = joinPath(URI.parse(this.productService.settingsSyncStore!.url), 'resource', key, 'latest').toString(); const headers: IHeaders = {}; if (oldValue) { headers['If-None-Match'] = oldValue.ref; @@ -62,7 +62,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn throw new Error('No settings sync store url configured.'); } - const url = joinPath(URI.parse(this.productService.settingsSyncStoreUrl!), 'resource', key).toString(); + const url = joinPath(URI.parse(this.productService.settingsSyncStore!.url), 'resource', key).toString(); const headers: IHeaders = { 'Content-Type': 'text/plain' }; if (ref) { headers['If-Match'] = ref; diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 0479108f91c..becf4322848 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -47,7 +47,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { } private registerCommonContributions(): void { - if (this.productService.settingsSyncStoreUrl) { + if (this.productService.settingsSyncStore) { this.registerLogChannel(Constants.userDataSyncLogChannelId, nls.localize('userDataSyncLog', "Configuration Sync"), this.environmentService.userDataSyncLogResource); } this.registerLogChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Window"), this.environmentService.logFile); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts index 3fe61e9a7e5..0ca0ee84c7e 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts @@ -19,7 +19,7 @@ class UserDataSyncConfigurationContribution implements IWorkbenchContribution { constructor( @IProductService productService: IProductService ) { - if (productService.settingsSyncStoreUrl) { + if (productService.settingsSyncStore) { registerConfiguration(); } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index c968e8607d9..46962a7ef23 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -29,6 +29,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { FalseContext } from 'vs/platform/contextkey/common/contextkeys'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IProductService } from 'vs/platform/product/common/productService'; const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey('authTokenStatus', AuthTokenStatus.Inactive); const SYNC_PUSH_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-light.svg`)); @@ -58,6 +59,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @IDialogService private readonly dialogService: IDialogService, @IStorageService private readonly storageService: IStorageService, @IQuickInputService private readonly quickInputService: IQuickInputService, + @IProductService private readonly productService: IProductService, ) { super(); this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService); @@ -110,7 +112,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo const enabled = this.configurationService.getValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING); if (enabled) { if (this.authTokenService.status === AuthTokenStatus.Inactive) { - const handle = this.notificationService.prompt(Severity.Info, localize('ask to sign in', "Please sign in with your '{0}' account to sync configuration", "{ACCOUNT_NAME}"), + const handle = this.notificationService.prompt(Severity.Info, localize('ask to sign in', "Please sign in with your {0} account to sync configuration across all your machines", this.productService.settingsSyncStore!.account), [ { label: localize('Sign in', "Sign in"), @@ -152,8 +154,8 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (this.authTokenService.status === AuthTokenStatus.Inactive) { const result = await this.dialogService.confirm({ type: 'info', - message: localize('sign in to account', "Sign in to {0}", "{ACCOUNT_NAME}"), - detail: localize('ask to sign in', "Please sign in with your '{0}' account to sync configuration", "{ACCOUNT_NAME}"), + message: localize('sign in to account', "Sign in to {0}", this.productService.settingsSyncStore!.name), + detail: localize('ask to sign in', "Please sign in with your {0} account to sync configuration across all your machines", this.productService.settingsSyncStore!.account), primaryButton: localize('Sign in', "Sign in") }); if (!result.confirmed) { From e5eedbdd9aba708206a01882242b01124acd9b59 Mon Sep 17 00:00:00 2001 From: Sebastian Pahnke Date: Tue, 19 Nov 2019 15:00:39 +0100 Subject: [PATCH 052/246] Find model by resource --- src/vs/editor/standalone/browser/simpleServices.ts | 7 ++++++- src/vs/editor/standalone/browser/standaloneEditor.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index c2ed588baf1..d9701b08ae0 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -99,8 +99,13 @@ function withTypedEditor(widget: editorCommon.IEditor, codeEditorCallback: (e export class SimpleEditorModelResolverService implements ITextModelService { public _serviceBrand: undefined; + private readonly modelService: IModelService | undefined; private editor?: editorCommon.IEditor; + constructor(modelService: IModelService | undefined) { + this.modelService = modelService; + } + public setEditor(editor: editorCommon.IEditor): void { this.editor = editor; } @@ -132,7 +137,7 @@ export class SimpleEditorModelResolverService implements ITextModelService { } private findModel(editor: ICodeEditor, resource: URI): ITextModel | null { - let model = editor.getModel(); + let model = this.modelService ? this.modelService.getModel(resource) : editor.getModel(); if (model && model.uri.toString() !== resource.toString()) { return null; } diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 82b29c68eb8..87b5de2618b 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -47,7 +47,7 @@ function withAllStandaloneServices(domElement: H let simpleEditorModelResolverService: SimpleEditorModelResolverService | null = null; if (!services.has(ITextModelService)) { - simpleEditorModelResolverService = new SimpleEditorModelResolverService(); + simpleEditorModelResolverService = new SimpleEditorModelResolverService(StaticServices.modelService.get()); services.set(ITextModelService, simpleEditorModelResolverService); } From 04d634e1d6aff0b3c7bc9821e92f4376f68a9b17 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Nov 2019 15:33:21 +0100 Subject: [PATCH 053/246] :lipstick: sample extension --- extensions/vscode-api-tests/src/extension.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/extensions/vscode-api-tests/src/extension.ts b/extensions/vscode-api-tests/src/extension.ts index 3ed712c26f3..b5d4902eca7 100644 --- a/extensions/vscode-api-tests/src/extension.ts +++ b/extensions/vscode-api-tests/src/extension.ts @@ -22,10 +22,9 @@ export function activate(context: vscode.ExtensionContext) { enableProblems(context); enableSearch(context, memFs); enableTasks(); + enableDebug(context, memFs); vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`memfs:/sample-folder/large.ts`)); - - context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('mock', new MockDebugAdapterDescriptorFactory(memFs))); } function enableFs(context: vscode.ExtensionContext): MemFS { @@ -892,11 +891,18 @@ export class MemFS implements vscode.FileSystemProvider, vscode.FileSearchProvid } } +//--------------------------------------------------------------------------- +// DEBUG //--------------------------------------------------------------------------- -/** Declaration module describing the VS Code debug protocol. - Auto-generated from json schema. Do not edit manually. -*/ +function enableDebug(context: vscode.ExtensionContext, memFs: MemFS): void { + context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('mock', new MockDebugAdapterDescriptorFactory(memFs))); +} + +/** + * Declaration module describing the VS Code debug protocol. + * Auto-generated from json schema. Do not edit manually. + */ declare module DebugProtocol { /** Base class of requests, responses, and events. */ From e3bb26eedd1fbd794e56e3836e772d357d82c6de Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Nov 2019 15:40:48 +0100 Subject: [PATCH 054/246] open editors - remove unused method --- src/vs/workbench/contrib/files/common/files.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index 5eb63e01d92..d900202310e 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -18,7 +18,6 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; import { Registry } from 'vs/platform/registry/common/platform'; import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer } from 'vs/workbench/common/views'; -import { Schemas } from 'vs/base/common/network'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; @@ -256,10 +255,6 @@ export class OpenEditor implements IEditorIdentifier { return this._group.previewEditor === this.editor; } - isUntitled(): boolean { - return !!toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: Schemas.untitled }); - } - isDirty(): boolean { return this.editor.isDirty(); } From 16466cb05d7f667cd47682f3cc7094cac86c9dd0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Nov 2019 15:44:38 +0100 Subject: [PATCH 055/246] context - rename saveable => readonly --- src/vs/workbench/browser/contextkeys.ts | 10 +++++----- src/vs/workbench/common/editor.ts | 2 +- .../files/browser/fileActions.contribution.ts | 12 ++++++------ .../workbench/contrib/files/browser/fileCommands.ts | 8 ++++---- .../contrib/files/browser/views/openEditorsView.ts | 10 +++++----- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 66d6d8138a6..2726a5c7d68 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys'; import { IWindowsConfiguration } from 'vs/platform/windows/common/windows'; -import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorIsSaveableContext, EditorAreaVisibleContext, DirtyWorkingCopiesContext } from 'vs/workbench/common/editor'; +import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorIsReadonlyContext, EditorAreaVisibleContext, DirtyWorkingCopiesContext } from 'vs/workbench/common/editor'; import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -53,7 +53,7 @@ export class WorkbenchContextKeysHandler extends Disposable { private dirtyWorkingCopiesContext: IContextKey; private activeEditorContext: IContextKey; - private activeEditorIsSaveable: IContextKey; + private activeEditorIsReadonly: IContextKey; private activeEditorGroupEmpty: IContextKey; private activeEditorGroupIndex: IContextKey; @@ -107,7 +107,7 @@ export class WorkbenchContextKeysHandler extends Disposable { // Editors this.activeEditorContext = ActiveEditorContext.bindTo(this.contextKeyService); - this.activeEditorIsSaveable = ActiveEditorIsSaveableContext.bindTo(this.contextKeyService); + this.activeEditorIsReadonly = ActiveEditorIsReadonlyContext.bindTo(this.contextKeyService); this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService); this.textCompareEditorVisibleContext = TextCompareEditorVisibleContext.bindTo(this.contextKeyService); this.textCompareEditorActiveContext = TextCompareEditorActiveContext.bindTo(this.contextKeyService); @@ -222,10 +222,10 @@ export class WorkbenchContextKeysHandler extends Disposable { if (activeControl) { this.activeEditorContext.set(activeControl.getId()); - this.activeEditorIsSaveable.set(!activeControl.input.isReadonly()); + this.activeEditorIsReadonly.set(activeControl.input.isReadonly()); } else { this.activeEditorContext.reset(); - this.activeEditorIsSaveable.reset(); + this.activeEditorIsReadonly.reset(); } } diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 1d6a263a79b..1ac273425c2 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -26,7 +26,7 @@ import { isEqual } from 'vs/base/common/resources'; export const DirtyWorkingCopiesContext = new RawContextKey('dirtyWorkingCopies', false); export const ActiveEditorContext = new RawContextKey('activeEditor', null); -export const ActiveEditorIsSaveableContext = new RawContextKey('activeEditorIsSaveable', false); +export const ActiveEditorIsReadonlyContext = new RawContextKey('activeEditorIsReadonly', false); export const EditorsVisibleContext = new RawContextKey('editorIsOpen', false); export const EditorPinnedContext = new RawContextKey('editorPinned', false); export const EditorGroupActiveEditorDirtyContext = new RawContextKey('groupActiveEditorDirty', false); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 32abad22c0f..b1f2f64fa31 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -10,7 +10,7 @@ import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTI import { SyncActionDescriptor, MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; -import { openWindowCommand, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, DirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, newWindowCommand, SaveableEditorContext } from 'vs/workbench/contrib/files/browser/fileCommands'; +import { openWindowCommand, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, DirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, newWindowCommand, ReadonlyEditorContext } from 'vs/workbench/contrib/files/browser/fileCommands'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -26,7 +26,7 @@ import { Schemas } from 'vs/base/common/network'; import { WorkspaceFolderCountContext, IsWebContext } from 'vs/workbench/browser/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { OpenFileFolderAction, OpenFileAction, OpenFolderAction, OpenWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; -import { ActiveEditorIsSaveableContext, DirtyWorkingCopiesContext, ActiveEditorContext } from 'vs/workbench/common/editor'; +import { ActiveEditorIsReadonlyContext, DirtyWorkingCopiesContext, ActiveEditorContext } from 'vs/workbench/common/editor'; import { SidebarFocusContext } from 'vs/workbench/common/viewlet'; import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; @@ -274,7 +274,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { // Not: editor groups OpenEditorsGroupContext.toNegated(), // Not: readonly editors - SaveableEditorContext, + ReadonlyEditorContext.toNegated(), // Not: auto save after short delay AutoSaveAfterShortDelayContext.toNegated() ) @@ -293,7 +293,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { // Not: editor groups OpenEditorsGroupContext.toNegated(), // Not: readonly editors - SaveableEditorContext, + ReadonlyEditorContext.toNegated(), // Not: untitled editors (revert closes them) ResourceContextKey.Scheme.notEqualsTo(Schemas.untitled), // Not: auto save after short delay @@ -580,7 +580,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { command: { id: SAVE_FILE_COMMAND_ID, title: nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save"), - precondition: ContextKeyExpr.or(ActiveEditorIsSaveableContext, ContextKeyExpr.and(ExplorerViewletVisibleContext, SidebarFocusContext)) + precondition: ContextKeyExpr.or(ActiveEditorIsReadonlyContext.toNegated(), ContextKeyExpr.and(ExplorerViewletVisibleContext, SidebarFocusContext)) }, order: 1 }); @@ -659,7 +659,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { command: { id: REVERT_FILE_COMMAND_ID, title: nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File"), - precondition: ContextKeyExpr.or(ActiveEditorIsSaveableContext, ContextKeyExpr.and(ExplorerViewletVisibleContext, SidebarFocusContext)) + precondition: ContextKeyExpr.or(ActiveEditorIsReadonlyContext.toNegated(), ContextKeyExpr.and(ExplorerViewletVisibleContext, SidebarFocusContext)) }, order: 1 }); diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index ca9a702ee87..c1045823907 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -67,7 +67,7 @@ export const SAVE_FILES_COMMAND_ID = 'workbench.action.files.saveFiles'; export const OpenEditorsGroupContext = new RawContextKey('groupFocusedInOpenEditors', false); export const DirtyEditorContext = new RawContextKey('dirtyEditor', false); -export const SaveableEditorContext = new RawContextKey('saveableEditor', false); +export const ReadonlyEditorContext = new RawContextKey('readonlyEditor', false); export const ResourceSelectedForCompareContext = new RawContextKey('resourceSelectedForCompare', false); export const REMOVE_ROOT_FOLDER_COMMAND_ID = 'removeRootFolder'; @@ -318,16 +318,16 @@ function saveSelectedEditors(accessor: ServicesAccessor, options?: ISaveEditorsO } function saveDirtyEditorsOfGroups(accessor: ServicesAccessor, groups: ReadonlyArray, options?: ISaveEditorsOptions): Promise { - const saveableEditors: IEditorIdentifier[] = []; + const dirtyEditors: IEditorIdentifier[] = []; for (const group of groups) { for (const editor of group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)) { if (editor.isDirty()) { - saveableEditors.push({ groupId: group.id, editor }); + dirtyEditors.push({ groupId: group.id, editor }); } } } - return doSaveEditors(accessor, saveableEditors, options); + return doSaveEditors(accessor, dirtyEditors, options); } async function doSaveEditors(accessor: ServicesAccessor, editors: IEditorIdentifier[], options?: ISaveEditorsOptions): Promise { diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 45abe43d33b..5d5ce871287 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -30,7 +30,7 @@ import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; -import { DirtyEditorContext, OpenEditorsGroupContext, SaveableEditorContext } from 'vs/workbench/contrib/files/browser/fileCommands'; +import { DirtyEditorContext, OpenEditorsGroupContext, ReadonlyEditorContext as ReadonlyEditorContext } from 'vs/workbench/contrib/files/browser/fileCommands'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { ResourcesDropHandler, fillResourceDataTransfers, CodeDataTransfers, containsDragType } from 'vs/workbench/browser/dnd'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; @@ -62,7 +62,7 @@ export class OpenEditorsView extends ViewletPanel { private resourceContext!: ResourceContextKey; private groupFocusedContext!: IContextKey; private dirtyEditorFocusedContext!: IContextKey; - private saveableEditorFocusedContext!: IContextKey; + private readonlyEditorFocusedContext!: IContextKey; constructor( options: IViewletViewOptions, @@ -236,19 +236,19 @@ export class OpenEditorsView extends ViewletPanel { this._register(this.resourceContext); this.groupFocusedContext = OpenEditorsGroupContext.bindTo(this.contextKeyService); this.dirtyEditorFocusedContext = DirtyEditorContext.bindTo(this.contextKeyService); - this.saveableEditorFocusedContext = SaveableEditorContext.bindTo(this.contextKeyService); + this.readonlyEditorFocusedContext = ReadonlyEditorContext.bindTo(this.contextKeyService); this._register(this.list.onContextMenu(e => this.onListContextMenu(e))); this.list.onFocusChange(e => { this.resourceContext.reset(); this.groupFocusedContext.reset(); this.dirtyEditorFocusedContext.reset(); - this.saveableEditorFocusedContext.reset(); + this.readonlyEditorFocusedContext.reset(); const element = e.elements.length ? e.elements[0] : undefined; if (element instanceof OpenEditor) { const resource = element.getResource(); this.dirtyEditorFocusedContext.set(element.editor.isDirty()); - this.saveableEditorFocusedContext.set(!element.editor.isReadonly()); + this.readonlyEditorFocusedContext.set(element.editor.isReadonly()); this.resourceContext.set(withUndefinedAsNull(resource)); } else if (!!element) { this.groupFocusedContext.set(true); From 82e99291cefb5450caa100838947f215de739032 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Nov 2019 16:13:07 +0100 Subject: [PATCH 056/246] :lipstick: --- scripts/code-web.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/code-web.js b/scripts/code-web.js index 07c8700f7f9..e25efa47b9c 100755 --- a/scripts/code-web.js +++ b/scripts/code-web.js @@ -227,6 +227,14 @@ function getMediaMime(forPath) { */ async function serveFile(req, res, filePath, responseHeaders = Object.create(null)) { try { + + // Sanity checks + filePath = path.normalize(filePath); // ensure no "." and ".." + if (filePath.indexOf(`${APP_ROOT}${path.sep}`) !== 0) { + // invalid location outside of APP_ROOT + return serveError(req, res, 400, `Bad request.`); + } + const stat = await util.promisify(fs.stat)(filePath); // Check if file modified since From 458177f388092a66460ca302f64e45e769c00d53 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Nov 2019 12:45:03 +0100 Subject: [PATCH 057/246] more tests --- .../test/browser/parts/views/views.test.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/vs/workbench/test/browser/parts/views/views.test.ts b/src/vs/workbench/test/browser/parts/views/views.test.ts index 54121d9571a..82e6bd8c59c 100644 --- a/src/vs/workbench/test/browser/parts/views/views.test.ts +++ b/src/vs/workbench/test/browser/parts/views/views.test.ts @@ -13,6 +13,7 @@ import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestSe import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; +import sinon = require('sinon'); const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer('test'); const ViewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); @@ -294,4 +295,33 @@ suite('ContributableViewsModel', () => { assert.equal(model.visibleViewDescriptors.length, 0, 'view should still not appear since it was set not visible in view state'); assert.equal(seq.elements.length, 0); }); + + test('remove event is not triggered if view was hidden and removed', async function () { + const model = new ContributableViewsModel(container, viewsService); + const seq = new ViewDescriptorSequence(model); + + const viewDescriptor: IViewDescriptor = { + id: 'view1', + ctorDescriptor: null!, + name: 'Test View 1', + when: ContextKeyExpr.equals('showview1', true), + canToggleVisibility: true + }; + + ViewsRegistry.registerViews([viewDescriptor], container); + + const key = contextKeyService.createKey('showview1', true); + await new Promise(c => setTimeout(c, 30)); + assert.equal(model.visibleViewDescriptors.length, 1, 'view should appear after context is set'); + assert.equal(seq.elements.length, 1); + + model.setVisible('view1', false); + assert.equal(model.visibleViewDescriptors.length, 0, 'view should disappear after setting visibility to false'); + assert.equal(seq.elements.length, 0); + + const target = sinon.spy(model.onDidRemove); + key.set(false); + await new Promise(c => setTimeout(c, 30)); + assert.ok(!target.called, 'remove event should not be called since it is already hidden'); + }); }); From 1ba8fc6712f3e9fdb766abda81bb7ae083b5f22e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Nov 2019 16:40:21 +0100 Subject: [PATCH 058/246] compile jsdoc comments, #43768 --- src/vs/vscode.proposed.d.ts | 150 +++++++++++++++++++++++++++++++----- 1 file changed, 129 insertions(+), 21 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 5d5d4b7c84e..b31d3ae366d 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -826,6 +826,53 @@ declare module 'vscode' { //#region mjbvz,joh: https://github.com/Microsoft/vscode/issues/43768 + /** + * An event that is fired when files are going to be created. + * + * To make modifications to the workspace before the files are created, + * call the [`waitUntil](#FileWillCreateEvent.waitUntil)-function with a + * thenable that resolves to a [workspace edit](#WorkspaceEdit). + */ + export interface FileWillCreateEvent { + + /** + * The files that are going to be created. + */ + readonly files: ReadonlyArray; + + /** + * Allows to pause the event and to apply a [workspace edit](#WorkspaceEdit). + * + * *Note:* This function can only be called during event dispatch and not + * in an asynchronous manner: + * + * ```ts + * workspace.onWillCreateFiles(event => { + * // async, will *throw* an error + * setTimeout(() => event.waitUntil(promise)); + * + * // sync, OK + * event.waitUntil(promise); + * }) + * ``` + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + + /** + * Allows to pause the event until the provided thenable resolves. + * + * *Note:* This function can only be called during event dispatch. + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + } + + /** + * An event that is fired after files are created. + */ export interface FileCreateEvent { /** @@ -834,17 +881,53 @@ declare module 'vscode' { readonly files: ReadonlyArray; } - export interface FileWillCreateEvent { + /** + * An event that is fired when files are going to be deleted. + * + * To make modifications to the workspace before the files are deleted, + * call the [`waitUntil](#FileWillCreateEvent.waitUntil)-function with a + * thenable that resolves to a [workspace edit](#WorkspaceEdit). + */ + export interface FileWillDeleteEvent { /** - * The files that are going to be created. + * The files that are going to be deleted. */ readonly files: ReadonlyArray; + /** + * Allows to pause the event and to apply a [workspace edit](#WorkspaceEdit). + * + * *Note:* This function can only be called during event dispatch and not + * in an asynchronous manner: + * + * ```ts + * workspace.onWillCreateFiles(event => { + * // async, will *throw* an error + * setTimeout(() => event.waitUntil(promise)); + * + * // sync, OK + * event.waitUntil(promise); + * }) + * ``` + * + * @param thenable A thenable that delays saving. + */ waitUntil(thenable: Thenable): void; + + /** + * Allows to pause the event until the provided thenable resolves. + * + * *Note:* This function can only be called during event dispatch. + * + * @param thenable A thenable that delays saving. + */ waitUntil(thenable: Thenable): void; } + /** + * An event that is fired after files are deleted. + */ export interface FileDeleteEvent { /** @@ -853,25 +936,13 @@ declare module 'vscode' { readonly files: ReadonlyArray; } - export interface FileWillDeleteEvent { - - /** - * The files that are going to be deleted. - */ - readonly files: ReadonlyArray; - - waitUntil(thenable: Thenable): void; - waitUntil(thenable: Thenable): void; - } - - export interface FileRenameEvent { - - /** - * The files that got renamed. - */ - readonly files: ReadonlyArray<{ oldUri: Uri, newUri: Uri }>; - } - + /** + * An event that is fired when files are going to be renamed. + * + * To make modifications to the workspace before the files are renamed, + * call the [`waitUntil](#FileWillCreateEvent.waitUntil)-function with a + * thenable that resolves to a [workspace edit](#WorkspaceEdit). + */ export interface FileWillRenameEvent { /** @@ -879,10 +950,47 @@ declare module 'vscode' { */ readonly files: ReadonlyArray<{ oldUri: Uri, newUri: Uri }>; + /** + * Allows to pause the event and to apply a [workspace edit](#WorkspaceEdit). + * + * *Note:* This function can only be called during event dispatch and not + * in an asynchronous manner: + * + * ```ts + * workspace.onWillCreateFiles(event => { + * // async, will *throw* an error + * setTimeout(() => event.waitUntil(promise)); + * + * // sync, OK + * event.waitUntil(promise); + * }) + * ``` + * + * @param thenable A thenable that delays saving. + */ waitUntil(thenable: Thenable): void; + + /** + * Allows to pause the event until the provided thenable resolves. + * + * *Note:* This function can only be called during event dispatch. + * + * @param thenable A thenable that delays saving. + */ waitUntil(thenable: Thenable): void; } + /** + * An event that is fired after files are renamed. + */ + export interface FileRenameEvent { + + /** + * The files that got renamed. + */ + readonly files: ReadonlyArray<{ oldUri: Uri, newUri: Uri }>; + } + export namespace workspace { /** From ff2341223702fb4d5cf00d5ec7ced00c506a1b53 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Nov 2019 16:47:55 +0100 Subject: [PATCH 059/246] provide gitfs resource label formatter --- extensions/git/src/fileSystemProvider.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/fileSystemProvider.ts b/extensions/git/src/fileSystemProvider.ts index 55ea6867fa9..198f031309f 100644 --- a/extensions/git/src/fileSystemProvider.ts +++ b/extensions/git/src/fileSystemProvider.ts @@ -31,7 +31,14 @@ export class GitFileSystemProvider implements FileSystemProvider { this.disposables.push( model.onDidChangeRepository(this.onDidChangeRepository, this), model.onDidChangeOriginalResource(this.onDidChangeOriginalResource, this), - workspace.registerFileSystemProvider('gitfs', this, { isReadonly: true, isCaseSensitive: true }) + workspace.registerFileSystemProvider('gitfs', this, { isReadonly: true, isCaseSensitive: true }), + workspace.registerResourceLabelFormatter({ + scheme: 'gitfs', + formatting: { + label: '${path} (git)', + separator: '/' + } + }) ); setInterval(() => this.cleanup(), FIVE_MINUTES); From b7ead236be74c77818a0aac1fa6ab62178cc54b8 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Nov 2019 16:54:41 +0100 Subject: [PATCH 060/246] git api: expose toGitUri --- extensions/git/src/api/api1.ts | 5 +++++ extensions/git/src/api/git.d.ts | 2 ++ 2 files changed, 7 insertions(+) diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index a4fd677db27..8cee12e7726 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -8,6 +8,7 @@ import { Repository as BaseRepository, Resource } from '../repository'; import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState } from './git'; import { Event, SourceControlInputBox, Uri, SourceControl } from 'vscode'; import { mapEvent } from '../util'; +import { toGitUri } from '../uri'; class ApiInputBox implements InputBox { set value(value: string) { this._inputBox.value = value; } @@ -234,5 +235,9 @@ export class ApiImpl implements API { return this._model.repositories.map(r => new ApiRepository(r)); } + toGitUri(uri: Uri, ref: string): Uri { + return toGitUri(uri, ref); + } + constructor(private _model: Model) { } } diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index a0b2d3dad7f..a6eb7ec3d9a 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -185,6 +185,8 @@ export interface API { readonly repositories: Repository[]; readonly onDidOpenRepository: Event; readonly onDidCloseRepository: Event; + + toGitUri(uri: Uri, ref: string): Uri; } export interface GitExtension { From 920d80e28a6689a2f7158059a2f7da03b242e5c0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Nov 2019 16:56:01 +0100 Subject: [PATCH 061/246] files - improve readonly handling --- .../editor/common/services/resolverService.ts | 3 ++ src/vs/platform/files/common/fileService.ts | 1 - src/vs/platform/files/common/files.ts | 5 ---- .../browser/parts/editor/editorPart.ts | 28 +++++++++---------- .../browser/parts/editor/sideBySideEditor.ts | 2 +- .../browser/parts/editor/textEditor.ts | 13 ++++----- .../parts/editor/textResourceEditor.ts | 8 ++---- .../common/editor/resourceEditorModel.ts | 4 --- .../common/editor/textEditorModel.ts | 6 ++-- .../files/browser/editors/textFileEditor.ts | 3 +- .../files/common/editors/fileEditorInput.ts | 2 +- .../contrib/files/common/explorerModel.ts | 10 +++---- .../contrib/files/common/explorerService.ts | 6 ++-- .../textfile/common/textFileEditorModel.ts | 10 +++---- 14 files changed, 44 insertions(+), 57 deletions(-) diff --git a/src/vs/editor/common/services/resolverService.ts b/src/vs/editor/common/services/resolverService.ts index b425ebb8fde..77719683a23 100644 --- a/src/vs/editor/common/services/resolverService.ts +++ b/src/vs/editor/common/services/resolverService.ts @@ -52,6 +52,9 @@ export interface ITextEditorModel extends IEditorModel { createSnapshot(this: IResolvedTextEditorModel): ITextSnapshot; createSnapshot(this: ITextEditorModel): ITextSnapshot | null; + /** + * Signals if this model is readonly or not. + */ isReadonly(): boolean; } diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index b8250ae4267..ecf261d3610 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -220,7 +220,6 @@ export class FileService extends Disposable implements IFileService { isFile: (stat.type & FileType.File) !== 0, isDirectory: (stat.type & FileType.Directory) !== 0, isSymbolicLink: (stat.type & FileType.SymbolicLink) !== 0, - isReadonly: !!(provider.capabilities & FileSystemProviderCapabilities.Readonly), mtime: stat.mtime, ctime: stat.ctime, size: stat.size, diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 15f2e20910b..658998bd7c2 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -615,11 +615,6 @@ interface IBaseStat { * it is optional. */ etag?: string; - - /** - * The resource is readonly. - */ - isReadonly?: boolean; } export interface IBaseStatWithMetadata extends IBaseStat { diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 7cb0562675b..b1e47014aa9 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -89,26 +89,26 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro //#region Events - private readonly _onDidLayout: Emitter = this._register(new Emitter()); - readonly onDidLayout: Event = this._onDidLayout.event; + private readonly _onDidLayout = this._register(new Emitter()); + readonly onDidLayout = this._onDidLayout.event; - private readonly _onDidActiveGroupChange: Emitter = this._register(new Emitter()); - readonly onDidActiveGroupChange: Event = this._onDidActiveGroupChange.event; + private readonly _onDidActiveGroupChange = this._register(new Emitter()); + readonly onDidActiveGroupChange = this._onDidActiveGroupChange.event; - private readonly _onDidGroupIndexChange: Emitter = this._register(new Emitter()); - readonly onDidGroupIndexChange: Event = this._onDidGroupIndexChange.event; + private readonly _onDidGroupIndexChange = this._register(new Emitter()); + readonly onDidGroupIndexChange = this._onDidGroupIndexChange.event; - private readonly _onDidActivateGroup: Emitter = this._register(new Emitter()); - readonly onDidActivateGroup: Event = this._onDidActivateGroup.event; + private readonly _onDidActivateGroup = this._register(new Emitter()); + readonly onDidActivateGroup = this._onDidActivateGroup.event; - private readonly _onDidAddGroup: Emitter = this._register(new Emitter()); - readonly onDidAddGroup: Event = this._onDidAddGroup.event; + private readonly _onDidAddGroup = this._register(new Emitter()); + readonly onDidAddGroup = this._onDidAddGroup.event; - private readonly _onDidRemoveGroup: Emitter = this._register(new Emitter()); - readonly onDidRemoveGroup: Event = this._onDidRemoveGroup.event; + private readonly _onDidRemoveGroup = this._register(new Emitter()); + readonly onDidRemoveGroup = this._onDidRemoveGroup.event; - private readonly _onDidMoveGroup: Emitter = this._register(new Emitter()); - readonly onDidMoveGroup: Event = this._onDidMoveGroup.event; + private readonly _onDidMoveGroup = this._register(new Emitter()); + readonly onDidMoveGroup = this._onDidMoveGroup.event; private onDidSetGridWidget = this._register(new Emitter<{ width: number; height: number; } | undefined>()); private _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; } | undefined>()); diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index 9d8ae67d35b..c60834c5721 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -56,7 +56,7 @@ export class SideBySideEditor extends BaseEditor { private onDidCreateEditors = this._register(new Emitter<{ width: number; height: number; } | undefined>()); private _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; } | undefined>()); - readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; } | undefined> = Event.any(this.onDidCreateEditors.event, this._onDidSizeConstraintsChange.event); + readonly onDidSizeConstraintsChange = Event.any(this.onDidCreateEditors.event, this._onDidSizeConstraintsChange.event); constructor( @ITelemetryService telemetryService: ITelemetryService, diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index 4dfe5dd897f..a6684c49492 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -39,7 +39,7 @@ export interface IEditorConfiguration { */ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { private editorControl: IEditor | undefined; - private _editorContainer: HTMLElement | undefined; + private editorContainer: HTMLElement | undefined; private hasPendingConfigurationChange: boolean | undefined; private lastAppliedEditorOptions?: IEditorOptions; private editorMemento: IEditorMemento; @@ -121,20 +121,17 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { } protected getConfigurationOverrides(): IEditorOptions { - const overrides = {}; - assign(overrides, { + return { overviewRulerLanes: 3, lineNumbersMinChars: 3, fixedOverflowWidgets: true - }); - - return overrides; + }; } protected createEditor(parent: HTMLElement): void { // Editor for Text - this._editorContainer = parent; + this.editorContainer = parent; this.editorControl = this._register(this.createEditorControl(parent, this.computeConfiguration(this.configurationService.getValue(this.getResource())))); // Model & Language changes @@ -200,7 +197,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { // editor input specific options (e.g. an ARIA label depending on the input showing) this.updateEditorConfiguration(); - const editorContainer = assertIsDefined(this._editorContainer); + const editorContainer = assertIsDefined(this.editorContainer); editorContainer.setAttribute('aria-label', this.computeAriaLabel()); } diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index 78c0c9ab54b..7547aedf167 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -119,12 +119,10 @@ export class AbstractTextResourceEditor extends BaseTextEditor { } protected getAriaLabel(): string { - const input = this.input; - const isReadonly = !(this.input instanceof UntitledTextEditorInput); - let ariaLabel: string; - const inputName = input?.getName(); - if (isReadonly) { + + const inputName = this.input?.getName(); + if (this.input?.isReadonly()) { ariaLabel = inputName ? nls.localize('readonlyEditorWithInputAriaLabel', "{0}. Readonly text editor.", inputName) : nls.localize('readonlyEditorAriaLabel', "Readonly text editor."); } else { ariaLabel = inputName ? nls.localize('untitledFileEditorWithInputAriaLabel', "{0}. Untitled file text editor.", inputName) : nls.localize('untitledFileEditorAriaLabel', "Untitled file text editor."); diff --git a/src/vs/workbench/common/editor/resourceEditorModel.ts b/src/vs/workbench/common/editor/resourceEditorModel.ts index f6bc4b1555b..e8e58c3f9b2 100644 --- a/src/vs/workbench/common/editor/resourceEditorModel.ts +++ b/src/vs/workbench/common/editor/resourceEditorModel.ts @@ -21,10 +21,6 @@ export class ResourceEditorModel extends BaseTextEditorModel { super(modelService, modeService, resource); } - isReadonly(): boolean { - return true; - } - dispose(): void { // TODO@Joao: force this class to dispose the underlying model diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index e95e0d87bae..bbc5ce958ea 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -16,7 +16,7 @@ import { withUndefinedAsNull } from 'vs/base/common/types'; /** * The base text editor model leverages the code editor model. This class is only intended to be subclassed and not instantiated. */ -export abstract class BaseTextEditorModel extends EditorModel implements ITextEditorModel, IModeSupport { +export class BaseTextEditorModel extends EditorModel implements ITextEditorModel, IModeSupport { protected textEditorModelHandle: URI | null = null; @@ -61,7 +61,9 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd return this.textEditorModelHandle ? this.modelService.getModel(this.textEditorModelHandle) : null; } - abstract isReadonly(): boolean; + isReadonly(): boolean { + return true; + } setMode(mode: string): void { if (!this.isResolved()) { diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index cfb489c9d7c..5c257633406 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -243,8 +243,7 @@ export class TextFileEditor extends BaseTextEditor { } protected getAriaLabel(): string { - const input = this.input; - const inputName = input?.getName(); + const inputName = this.input?.getName(); let ariaLabel: string; if (inputName) { diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 387acd584b5..ea568d71791 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -216,7 +216,7 @@ export class FileEditorInput extends TextEditorInput implements IFileEditorInput return localize('orphanedFile', "{0} (deleted)", label); } - if (model?.isReadonly()) { + if (this.isReadonly()) { return localize('readonlyFile', "{0} (read-only)", label); } diff --git a/src/vs/workbench/contrib/files/common/explorerModel.ts b/src/vs/workbench/contrib/files/common/explorerModel.ts index fd2f87c5430..cd74eea7b4e 100644 --- a/src/vs/workbench/contrib/files/common/explorerModel.ts +++ b/src/vs/workbench/contrib/files/common/explorerModel.ts @@ -8,7 +8,7 @@ import { isEqual } from 'vs/base/common/extpath'; import { posix } from 'vs/base/common/path'; import * as resources from 'vs/base/common/resources'; import { ResourceMap } from 'vs/base/common/map'; -import { IFileStat, IFileService } from 'vs/platform/files/common/files'; +import { IFileStat, IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { rtrim, startsWithIgnoreCase, startsWith, equalsIgnoreCase } from 'vs/base/common/strings'; import { coalesce } from 'vs/base/common/arrays'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -154,8 +154,8 @@ export class ExplorerItem { return this === this.root; } - static create(raw: IFileStat, parent: ExplorerItem | undefined, resolveTo?: readonly URI[]): ExplorerItem { - const stat = new ExplorerItem(raw.resource, parent, raw.isDirectory, raw.isSymbolicLink, raw.isReadonly, raw.name, raw.mtime); + static create(service: IFileService, raw: IFileStat, parent: ExplorerItem | undefined, resolveTo?: readonly URI[]): ExplorerItem { + const stat = new ExplorerItem(raw.resource, parent, raw.isDirectory, raw.isSymbolicLink, service.hasCapability(raw.resource, FileSystemProviderCapabilities.Readonly), raw.name, raw.mtime); // Recursively add children if present if (stat.isDirectory) { @@ -170,7 +170,7 @@ export class ExplorerItem { // Recurse into children if (raw.children) { for (let i = 0, len = raw.children.length; i < len; i++) { - const child = ExplorerItem.create(raw.children[i], stat, resolveTo); + const child = ExplorerItem.create(service, raw.children[i], stat, resolveTo); stat.addChild(child); } } @@ -260,7 +260,7 @@ export class ExplorerItem { const resolveMetadata = explorerService.sortOrder === 'modified'; try { const stat = await fileService.resolve(this.resource, { resolveSingleChildDescendants: true, resolveMetadata }); - const resolved = ExplorerItem.create(stat, this); + const resolved = ExplorerItem.create(fileService, stat, this); ExplorerItem.mergeLocalWithDisk(resolved, this); } catch (e) { this.isError = true; diff --git a/src/vs/workbench/contrib/files/common/explorerService.ts b/src/vs/workbench/contrib/files/common/explorerService.ts index f9afb180963..cc9d46ab545 100644 --- a/src/vs/workbench/contrib/files/common/explorerService.ts +++ b/src/vs/workbench/contrib/files/common/explorerService.ts @@ -186,7 +186,7 @@ export class ExplorerService implements IExplorerService { const stat = await this.fileService.resolve(rootUri, options); // Convert to model - const modelStat = ExplorerItem.create(stat, undefined, options.resolveTo); + const modelStat = ExplorerItem.create(this.fileService, stat, undefined, options.resolveTo); // Update Input with disk Stat ExplorerItem.mergeLocalWithDisk(modelStat, root); const item = root.find(resource); @@ -230,11 +230,11 @@ export class ExplorerService implements IExplorerService { const thenable: Promise = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolve(p.resource, { resolveMetadata }); thenable.then(stat => { if (stat) { - const modelStat = ExplorerItem.create(stat, p.parent); + const modelStat = ExplorerItem.create(this.fileService, stat, p.parent); ExplorerItem.mergeLocalWithDisk(modelStat, p); } - const childElement = ExplorerItem.create(addedElement, p.parent); + const childElement = ExplorerItem.create(this.fileService, addedElement, p.parent); // Make sure to remove any previous version of the file if any p.removeChild(childElement); p.addChild(childElement); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index f125c5235e5..8ab10dc191a 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -15,7 +15,7 @@ import { ITextFileService, ModelState, ITextFileEditorModel, ISaveErrorHandler, import { EncodingMode, IRevertOptions, SaveReason } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { IFileService, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType, IFileStatWithMetadata, ETAG_DISABLED } from 'vs/platform/files/common/files'; +import { IFileService, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType, IFileStatWithMetadata, ETAG_DISABLED, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -340,8 +340,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil size: resolvedBackup.meta ? resolvedBackup.meta.size : 0, etag: resolvedBackup.meta ? resolvedBackup.meta.etag : ETAG_DISABLED, // etag disabled if unknown! value: resolvedBackup.value, - encoding: this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding).encoding, - isReadonly: false + encoding: this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding).encoding }, options, true /* from backup */); // Restore orphaned flag based on state @@ -426,8 +425,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil etag: content.etag, isFile: true, isDirectory: false, - isSymbolicLink: false, - isReadonly: content.isReadonly + isSymbolicLink: false }); // Keep the original encoding to not loose it when saving @@ -1030,7 +1028,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } isReadonly(): boolean { - return !!(this.lastResolvedFileStat && this.lastResolvedFileStat.isReadonly); + return this.fileService.hasCapability(this.resource, FileSystemProviderCapabilities.Readonly); } isDisposed(): boolean { From b6899391cd8bc59e491fa700e2a3adecb1533b1f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Nov 2019 17:03:08 +0100 Subject: [PATCH 062/246] wip: comment out git data uris --- extensions/git/src/commands.ts | 70 +++++++++++++++++----------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index e58f8c42292..465281f62fc 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -170,14 +170,14 @@ function command(commandId: string, options: CommandOptions = {}): Function { }; } -const ImageMimetypes = [ - 'image/png', - 'image/gif', - 'image/jpeg', - 'image/webp', - 'image/tiff', - 'image/bmp' -]; +// const ImageMimetypes = [ +// 'image/png', +// 'image/gif', +// 'image/jpeg', +// 'image/webp', +// 'image/tiff', +// 'image/bmp' +// ]; async function categorizeResourceByResolution(resources: Resource[]): Promise<{ merge: Resource[], resolved: Resource[], unresolved: Resource[], deletionConflicts: Resource[] }> { const selection = resources.filter(s => s instanceof Resource) as Resource[]; @@ -331,42 +331,42 @@ export class CommandCenter { } private async getURI(uri: Uri, ref: string): Promise { - const repository = this.model.getRepository(uri); + // const repository = this.model.getRepository(uri); - if (!repository) { - return toGitUri(uri, ref); - } + // if (!repository) { + return toGitUri(uri, ref); + // } - try { - let gitRef = ref; + // try { + // let gitRef = ref; - if (gitRef === '~') { - const uriString = uri.toString(); - const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); - gitRef = indexStatus ? '' : 'HEAD'; - } + // if (gitRef === '~') { + // const uriString = uri.toString(); + // const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); + // gitRef = indexStatus ? '' : 'HEAD'; + // } - const { size, object } = await repository.getObjectDetails(gitRef, uri.fsPath); - const { mimetype } = await repository.detectObjectType(object); + // const { size, object } = await repository.getObjectDetails(gitRef, uri.fsPath); + // const { mimetype } = await repository.detectObjectType(object); - if (mimetype === 'text/plain') { - return toGitUri(uri, ref); - } + // if (mimetype === 'text/plain') { + // return toGitUri(uri, ref); + // } - if (size > 1000000) { // 1 MB - return Uri.parse(`data:;label:${path.basename(uri.fsPath)};description:${gitRef},`); - } + // if (size > 1000000) { // 1 MB + // return Uri.parse(`data:;label:${path.basename(uri.fsPath)};description:${gitRef},`); + // } - if (ImageMimetypes.indexOf(mimetype) > -1) { - const contents = await repository.buffer(gitRef, uri.fsPath); - return Uri.parse(`data:${mimetype};label:${path.basename(uri.fsPath)};description:${gitRef};size:${size};base64,${contents.toString('base64')}`); - } + // if (ImageMimetypes.indexOf(mimetype) > -1) { + // const contents = await repository.buffer(gitRef, uri.fsPath); + // return Uri.parse(`data:${mimetype};label:${path.basename(uri.fsPath)};description:${gitRef};size:${size};base64,${contents.toString('base64')}`); + // } - return Uri.parse(`data:;label:${path.basename(uri.fsPath)};description:${gitRef},`); + // return Uri.parse(`data:;label:${path.basename(uri.fsPath)};description:${gitRef},`); - } catch (err) { - return toGitUri(uri, ref); - } + // } catch (err) { + // return toGitUri(uri, ref); + // } } private async getLeftResource(resource: Resource): Promise { From 8afb6397ee32b5e022eae311a3ea0e9d26da206a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Nov 2019 17:41:31 +0100 Subject: [PATCH 063/246] forward save to embedded editors if any (#38317) (#85139) --- .../contrib/files/browser/fileCommands.ts | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index c1045823907..1b0b5d13369 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -37,6 +37,9 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { UNTITLED_WORKSPACE_NAME } from 'vs/platform/workspaces/common/workspaces'; import { coalesce } from 'vs/base/common/arrays'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; // Commands @@ -310,11 +313,32 @@ CommandsRegistry.registerCommand({ // Save / Save As / Save All / Revert -function saveSelectedEditors(accessor: ServicesAccessor, options?: ISaveEditorsOptions): Promise { +async function saveSelectedEditors(accessor: ServicesAccessor, options?: ISaveEditorsOptions): Promise { const listService = accessor.get(IListService); const editorGroupsService = accessor.get(IEditorGroupsService); + const codeEditorService = accessor.get(ICodeEditorService); + const textFileService = accessor.get(ITextFileService); - return doSaveEditors(accessor, getMultiSelectedEditors(listService, editorGroupsService), options); + // Save editors + const editors = getMultiSelectedEditors(listService, editorGroupsService); + await doSaveEditors(accessor, editors, options); + + // Special treatment for embedded editors: if we detect that focus is + // inside an embedded code editor, we save that model as well if we + // find it in our text file models. Currently, only textual editors + // support embedded editors. + const focusedCodeEditor = codeEditorService.getFocusedCodeEditor(); + if (focusedCodeEditor instanceof EmbeddedCodeEditorWidget) { + const resource = focusedCodeEditor.getModel()?.uri; + + // Check that the resource of the model was not saved already + if (resource && !editors.some(({ editor }) => isEqual(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }), resource))) { + const model = textFileService.models.get(resource); + if (!model?.isReadonly()) { + await textFileService.save(resource, options); + } + } + } } function saveDirtyEditorsOfGroups(accessor: ServicesAccessor, groups: ReadonlyArray, options?: ISaveEditorsOptions): Promise { From 34b88074e76ede10bc4957177fbdcea02beca2e5 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 19 Nov 2019 17:42:56 +0100 Subject: [PATCH 064/246] explorer: if no file is focused respect the selection fixes #84484 --- .../files/browser/fileActions.contribution.ts | 6 +++--- .../contrib/files/browser/fileActions.ts | 16 ++++++++++------ .../contrib/files/browser/views/explorerView.ts | 11 +++++++---- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index b1f2f64fa31..aabb8afd4af 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -77,7 +77,7 @@ const MOVE_FILE_TO_TRASH_ID = 'moveFileToTrash'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: MOVE_FILE_TO_TRASH_ID, weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus, - when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext, ExplorerResourceMoveableToTrash), + when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerResourceNotReadonlyContext, ExplorerResourceMoveableToTrash), primary: KeyCode.Delete, mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace @@ -89,7 +89,7 @@ const DELETE_FILE_ID = 'deleteFile'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: DELETE_FILE_ID, weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus, - when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext), + when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerResourceNotReadonlyContext), primary: KeyMod.Shift | KeyCode.Delete, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Backspace @@ -100,7 +100,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: DELETE_FILE_ID, weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus, - when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext, ExplorerResourceMoveableToTrash.toNegated()), + when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerResourceNotReadonlyContext, ExplorerResourceMoveableToTrash.toNegated()), primary: KeyCode.Delete, mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index b21409c7e63..0e1d4b4801a 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -937,17 +937,21 @@ export const renameHandler = (accessor: ServicesAccessor) => { }); }; -export const moveFileToTrashHandler = (accessor: ServicesAccessor) => { +export const moveFileToTrashHandler = async (accessor: ServicesAccessor) => { const explorerService = accessor.get(IExplorerService); - const stats = explorerService.getContext(true); - return deleteFiles(accessor.get(ITextFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), accessor.get(IFileService), stats, true); + const stats = explorerService.getContext(true).filter(s => !s.isRoot); + if (stats.length) { + await deleteFiles(accessor.get(ITextFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), accessor.get(IFileService), stats, true); + } }; -export const deleteFileHandler = (accessor: ServicesAccessor) => { +export const deleteFileHandler = async (accessor: ServicesAccessor) => { const explorerService = accessor.get(IExplorerService); - const stats = explorerService.getContext(true); + const stats = explorerService.getContext(true).filter(s => !s.isRoot); - return deleteFiles(accessor.get(ITextFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), accessor.get(IFileService), stats, false); + if (stats.length) { + await deleteFiles(accessor.get(ITextFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), accessor.get(IFileService), stats, false); + } }; let pasteShouldMove = false; diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 9d585d6d276..050e0a9f8c7 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -293,10 +293,6 @@ export class ExplorerView extends ViewletPanel { focusedStat = focus.length ? focus[0] : undefined; } - if (!focusedStat) { - return []; - } - const selectedStats: ExplorerItem[] = []; for (const stat of this.tree.getSelection()) { @@ -308,6 +304,13 @@ export class ExplorerView extends ViewletPanel { selectedStats.push(stat); } } + if (!focusedStat) { + if (respectMultiSelection) { + return selectedStats; + } else { + return []; + } + } if (respectMultiSelection && selectedStats.indexOf(focusedStat) >= 0) { return selectedStats; From ac0e9fbe74b5695e41ecf65be88b458194bfc756 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 19 Nov 2019 08:44:25 -0800 Subject: [PATCH 065/246] Fixes #85111, updates debug step-back and reverse-continue icons --- .../ui/codiconLabel/codicon/codicon.css | 5 +++-- .../ui/codiconLabel/codicon/codicon.ttf | Bin 46964 -> 47104 bytes .../debug/browser/debug.contribution.ts | 2 +- .../contrib/debug/browser/debugToolBar.ts | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css index dfa5bc44f52..4dfc8f69330 100644 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css +++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css @@ -5,7 +5,7 @@ @font-face { font-family: "codicon"; - src: url("./codicon.ttf?34cea5159eda573d2422092d6f8c2c77") format("truetype"); + src: url("./codicon.ttf?6e26276ebddb23b7baa20bf099ba8332") format("truetype"); } .codicon[class*='codicon-'] { @@ -401,4 +401,5 @@ .codicon-debug-breakpoint-stackframe-focused:before { content: "\eb8b" } .codicon-debug-breakpoint-unsupported:before { content: "\eb8c" } .codicon-symbol-string:before { content: "\eb8d" } -.codicon-debug-step-back:before { content: "\f101" } +.codicon-debug-reverse-continue:before { content: "\f101" } +.codicon-debug-step-back:before { content: "\f102" } diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf index 9bb5589eef0d5fd6b7363729391954d9e0f94b4a..a8ad5305b9440a2b054f59ce7657892883f26aa8 100644 GIT binary patch delta 4174 zcmXxn3tW};83*v+|A?e`INUED1c3tx3W|rDT;!ryROZa6tgJvm(Lf2oTh>uBb7iDb z%^cfYIY(?O3+Kw&=F+*Ma;}S|-OOCM|6`?PO`XgAUZ2nK^L?L}a}Gz&`#v|$e&t$q z+O@$K*aevN0B)MoQs27Y_r=RV@H!yk@Vw^bb6tCvv;f|nz{8IN}w77lXyuTUftp&pRHtz9NWWn&Y^wOI0qC3ONzQZuI@(~<4 z|N6N9$4mUe-TVg(fy;T6U;G)9)tzLXH?ICeX2GET!ol?>@8IS4gX>icx_p++7y=AB zh#*%Ff680@r{q;E&Lt#V4oR^UHBJz@g;Ii3a`ZH_$Ou~ zmDk`_JcM5&%M8Jf5NeWf7iQrN^y7DU)vb`F9KMNPTY?*Sc?a-4nM}jSdT~0g-7}OY{VvX z2k;o4z)!FRKf}-Q3v9(xcpBUAD{RNF@eFU@Z}3|@haGqxFJLErk3Zl=yoBBOBVNXz z@MrABU+@a{|c@g#nV9+aXC6{tiNCYiyw3Rg2Tf}g&AsEOnacN08AoFg9|-CY+F7kC`7 zvlVYU>>R}efNfAr1=vP~eT;Jz(*t&%Vxqt{DW(kUe8uE}U7(mouy-lsGBW!E&VjiF z%i41|^=20;<{RuH#ms|cH8_}qu&e+Fvk|sUIkjmQD`qHcyJD`wb|_{o>=K16W{F+O zZ-}W3yG${uVV5g}GOkc2nbEl##FU3!shIq*_bQeF?0t#_0UJ;(4cJu*&oZud@JZGS zY^P%7z}~M|L$GTUs|nV5j#yW)YZWUD_CbX^7}qIQ9jx=IiS-Bjuwv%Ju2-x{*xMiF zfmoif&XWYTHM>!nV8%^~#S7c5SjMoADHb%WlShQ_8J|#QQ2!zKJ{PMT_8rA~hkaME z@?j4v_5j%T6uSZJ`-*)5_K0GKfIX_%D_}oR>>98iD)tZ9j}$uz?8i=u@xZ16`-x(E zfjy?!Xkb58Y&o#U6`K$2XNqkI_Jm?Xf^|}n*qUIS6eZL#excZ|U{5MGE~c1M5XAlj zYZbd0tSI(1*i(uf4)#mMUI+V?V%LNHTCxAZo>uIHu+GgR_C(lk6uTqrw+^Nl8zt;n z<%|^jopQ#Ab?z9kjlw$VL~N+A=M-Bj?0Ll|3wuGa-NHIqK$kz*MV=E|FszeW#AeL= z|A7Z$Uxsy3i`b#r^b~tFgsJ7=#e%pL`!{5eVkd_LDfV>8V8!kZVTwA~=ON5{2RlAw zhzhXzLxw7j0Z54AK!6NW91Re+;*fxZDvk??M{#gK!jw};60SH*AQ6gV1rn*iT9GJz zBp$#~1Bq4~I*=H}@dJrf97K>f#gPPwR~$}|;fiAl;#Jtnn4mbiAR`oq7$i|)n{!E% z6ek;Gq~eT&Br8rkNQ&azgQO~Fff1kLEQF*fPDMz%;(UZ;C{9YqD8-ox$)o@e9G{RZ z#X$-gtvFI4V-$xgBwKOJLUI%bE@Z6Y=!J|^9Kw)X#c>Q7uQ-??e#H@ufaLKT!d}LF z#kmbBP@Le9Ld97QDN>y3kO_+O9a5|~=^-VGGapi_IQ=1IihBTL;$^)4uk)l_aXElg zD6R>RO2vf%Ql+>$Kqe_J5s=A>>jb1)aj}3*QCu+~S1K+WkgF8e4oD6Ef2rZ8Kf-vm z;w}QYMsX{FT&uXBK&C2gDv;|GcNWMr#q9+$U2%_rT(7v%KxQcJHjo<>0w;Jr?;$8%~MR7xd+^V=ML2gsrnjo_j_b15hiklSV4(I;!z@-Xu zr{a1AsZ(6EAoYqX7i6~L@&%cr@B?FmGJGp(RK~?PSD7HjdCG(^HYqdAssH)>hK!qW zfig~mxl0)jW3w_&gK1GFoN=LY7C2d?OcZ0QGSQ58D-*-mri{~57CU(VO&m|!mGcLQ zbSN{Nafvcs#-++6FfLPO1mkjL5*b%0lf-zBG9wvRDwE21uQE>Wn13G+WYQS<%Q%eF zJXR@_!MIwPQH-66TP@^%#r+ntMsd@HJfOJqLe?tubf1d5Az?6g8Qvua_g|dhO>#J! zaQD>sjRCJa+da$O>F#yE@3x_Jp6_r2>09R8?z@8IOv-G{?9F^J^Yg59(`y` z?wAc@&Sqz12c~BCWWSibKc_9{t(rIDRh?A_C)G^aGU<)ULnc>GUNd>yao>b z)qPW@P1!JIpnt1(XngmZet-9K{-k2({yIxY17g8cEGo#0DL4Kk6Bpwv$RBG`gRjVo z@r{a0&B!nG$3_RchXqH*M7hndknVxJoc>FGSBJ^!clAzpxm=^4p3xT+7u(P{yJKEX bTjP?(w#AJ(a~3UZpTDr9v46(1DMSAU>T`e0 delta 4108 zcmXxn3tZLp8OQO@5AlL>Q&ABSK|mo<5ly_HB4}AzGb*!E60byf$y;dUK{GS6O~qVu z&AD1Px0*#4Yqhy4*%!(eI6UXiaT*E zZhda-32^IxiPPuQ%=fWFouJh!Xuo@Q{nAm$8Qb9YLA5h$W^`;E{kmRzSO(YX z3mkEG>G`vgS374>!-~|?r}h0lhWKsu^QPB4J><@fkUk#bs^-)*%#W*NyZ$^~ug{)a zGiPSbLm8u2zX1BW4H!-i*bM-|}oTDfoc&@or1lzmG(=IblG zeQL$`|F5U@i|h0o+7K6gRloFiTtQQod(^q+H(Xtd=8-L@xPg1md_Uq|rp1|OMXGI~ z#a`m$w&-sqpZN~0{2IT`zY;DAcN}+3ueBNw+r&S4jb%LV+VBLQ@&KPx?6Nt;zxkLN zdh%zk;5N2V=vs3TNv;QVT*W_lj6d?QOJx-cxrq`^GR3vxOMbzRe8o?$rQYa&i1Dq9 z(>rQGJS}NOYrT&Iy|Z>C>OG}2EpL5Q>|D;{d@f)-7xFVEa4}U}!lhisL@s9%S2CGu zu4XFJn9dAlGK<;Nat+r~&m875Pd~fsSim9{vqZ1Co(7h312?jqMpm$ro4JM6+{zl( zay#p|gP-f2u4e-qV>EFmoB0)Y^BcDETYkqq+{^E|kNbI$hjeTIz$5&L9Xv`ikMkr? zv6H8HMh)^Df8np};%~gbi@e0k{GHvr!XEbWDzEbf`}i08d6NUY#X;WY9p2?V-sc0g z+lL&X^dpY)37_E!e8Dk}^EKaag6}xR_x!+rIqjTl;SyY1*UlxnB$w<`T>GZPgl_pc zuBXd&c`o1ea=rN_cd>1;g!A~_&SNR&lCe>+1 zU4fr&)F1d6MxBD6X?R|BmQnZMXB+hqzSgLt@YkFXQ~T>lol%?NuQe2_)*F|tI>)F9 z@pFyZ5kJqUG4VPq5w$2@2PC3q#V;^wTl_+!2F5Q+iyri%+f)}Dwl&Rd*Cp0kR}DrT zj$dj>QjI>ZsO#}J81+B?Mx&E}zscxK;FlYn4!k~bMCSzGXn0t4h0)o;uY_3ipksu; z+2}ywqifO8!rx+a$ndL;jvM|~!xUA;Swu$=zt-q5;%_%F)sNPHnq2OGcN=!oOrG&=0~14hRl z|CUhz;13!_0sd{HP{6-q6c6}!je-LIo>64r-#7Y1_z$8VtR9pj_(MjCfF-j7agAyMk$Gp?vg~a#z!qA z(R}gW8D%K`lu?@EHH#w3ReW^kMG1@l!JPSEQ9FrJ7yn~R0 zGeNx-QBV`&42`NSjKZ4`ZxrJMwPi$sPEfB$6zzo8Mj=mVV`9pCLV{896WSWh075&X zX+TIcnhS&^qX|JsHkuWL6cZh!kZLqP2EUy3sr$bTyhxgbbq@Md)TUtq7S$bBoa3aIfxv$TAvcgdRp?jgW0L;0QTJ zqmIziXy_4gO|;pBJflHK$Tu2^gkDC&kMn7T18T$F}(UXxz`v75-(Pls>H`);hqm8x& zLWR-ZK&Uj@AP8fOb_v2*qpgB)j?sQWIM-;?AdJ($AI9m^A5=ZhX#F6ZZ?uRIE-+e2 z2;+^G6T*c?YYO2aqlJYq!EjjhVxuL7P-V2v5H2yqKGKs*jaD4OWk$;mVWQF6L%7^% z0U}H?QAmU(jf;Y0g>k)9R~jv{ zgqw|4TEZ%$<(6=Z(V9zGZM5(bZZ%qc32RJKoAeP$@o-$XE5f*@sp%VHofC@^s}dU$ zA545EsW_=RX;spJq_2}ZCl5}pPF|M0C3$c1sg(4T@|60NO(~zI)~0St_3g9TH?-f; zA-+RxhaDYGbR5+2u8w;;o=QtgYfQU4ZD-p4PCYwK?6j)W$EqMa zcJ0u0T}FJyn2d=Tn=_v6mfo$ZTVuCfvCPcO@tGSl4|LD#KDqnq?t8PQWo^tl*<*5# zM|ymk-8Q=}du8_4oHjYxITbmJa`yGC?)g~HBe`vI3v$QhF3H`Sdor&iuP$$U-h27U z`DOX_`H$tl)2pJ_+TPW@*Y`eFP+rhbu%_V2f)j;Bg<}fq3)dAMJuB_3ZGGr7wa<%v zP8MYr#p;XJ6m9BT(f6Ld&-W|o*U;~1|8x4U@Bcw@+v4)#&BZ4NR1H`*VC#T`CFvz) zCG$(R4`g8Rz%7GP2TdBZYtX5)i_czr_8WtT4W2l7LFtgvy3)qd`%7OgJvF3k$nGJ2 zXx`9iLst#mF?8Rs_+hJu?Hu;;@Pgr0!*`d(m(`Z7FWWO>$cRPfjMzEi#K^LdQ%7ze zd0^z{qsm6DD^D#yzx>Mby7Fb^Ys-&}E*M=kdTT{`MP0?+6$dNRDle^ERJo~gU*%6@ zs>duFn>BXoSl#^Q$1@Vrn?5VaYkIDvq { diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index e939c92fd67..3500fa7c3d8 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -397,7 +397,7 @@ registerThemingParticipant((theme, collector) => { const debugIconContinueColor = theme.getColor(debugIconContinueForeground); if (debugIconContinueColor) { - collector.addRule(`.monaco-workbench .codicon-debug-continue { color: ${debugIconContinueColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-debug-continue,.monaco-workbench .codicon-debug-reverse-continue { color: ${debugIconContinueColor} !important; }`); } const debugIconStepBackColor = theme.getColor(debugIconStepBackForeground); From 489706a777d43f8b66ecd8d22acfd8c35ef23d22 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Nov 2019 17:44:53 +0100 Subject: [PATCH 066/246] Allow to edit the left hand side of a diff editor if possible (fixes #4180) --- .../browser/parts/editor/textDiffEditor.ts | 21 ++------ .../browser/parts/editor/textEditor.ts | 4 +- .../parts/editor/textResourceEditor.ts | 9 ---- .../contrib/files/browser/fileCommands.ts | 49 ++++++++++++++++--- .../workbench/contrib/files/browser/files.ts | 16 +----- 5 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index ecbc295c0da..e36715049dc 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -10,7 +10,6 @@ import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffEditorOptions, IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions'; import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor'; import { TextEditorOptions, EditorInput, EditorOptions, TEXT_DIFF_EDITOR_ID, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, ITextDiffEditor, IEditorMemento } from 'vs/workbench/common/editor'; -import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; @@ -55,7 +54,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { @IEditorGroupsService editorGroupService: IEditorGroupsService, @ITextFileService textFileService: ITextFileService, @IHostService hostService: IHostService, - @IClipboardService private _clipboardService: IClipboardService, + @IClipboardService private clipboardService: IClipboardService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService, hostService, filesConfigurationService); @@ -74,7 +73,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { } createEditorControl(parent: HTMLElement, configuration: ICodeEditorOptions): IDiffEditor { - return this.instantiationService.createInstance(DiffEditorWidget, parent, configuration, this._clipboardService); + return this.instantiationService.createInstance(DiffEditorWidget, parent, configuration, this.clipboardService); } async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { @@ -211,7 +210,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { protected getConfigurationOverrides(): ICodeEditorOptions { const options: IDiffEditorOptions = super.getConfigurationOverrides(); - options.readOnly = this.isReadOnly(); + options.originalEditable = this.input instanceof DiffEditorInput && !this.input.originalInput.isReadonly(); options.lineDecorationsWidth = '2ch'; return options; @@ -219,8 +218,9 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { protected getAriaLabel(): string { let ariaLabel: string; + const inputName = this.input?.getName(); - if (this.isReadOnly()) { + if (this.input?.isReadonly()) { ariaLabel = inputName ? nls.localize('readonlyEditorWithInputAriaLabel', "{0}. Readonly text compare editor.", inputName) : nls.localize('readonlyEditorAriaLabel', "Readonly text compare editor."); } else { ariaLabel = inputName ? nls.localize('editableEditorWithInputAriaLabel', "{0}. Text file compare editor.", inputName) : nls.localize('editableEditorAriaLabel', "Text file compare editor."); @@ -229,17 +229,6 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { return ariaLabel; } - private isReadOnly(): boolean { - const input = this.input; - if (input instanceof DiffEditorInput) { - const modifiedInput = input.modifiedInput; - - return modifiedInput instanceof ResourceEditorInput; - } - - return false; - } - private isFileBinaryError(error: Error[]): boolean; private isFileBinaryError(error: Error): boolean; private isFileBinaryError(error: Error | Error[]): boolean { diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index a6684c49492..ac784018a61 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -124,7 +124,8 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { return { overviewRulerLanes: 3, lineNumbersMinChars: 3, - fixedOverflowWidgets: true + fixedOverflowWidgets: true, + readOnly: this.input?.isReadonly() }; } @@ -300,6 +301,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { configuration = this.configurationService.getValue(resource); } } + if (!this.editorControl || !configuration) { return; } diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index 7547aedf167..b37c9c63c54 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { assertIsDefined, isFunction } from 'vs/base/common/types'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { TextEditorOptions, EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; @@ -110,14 +109,6 @@ export class AbstractTextResourceEditor extends BaseTextEditor { } } - protected getConfigurationOverrides(): IEditorOptions { - const options = super.getConfigurationOverrides(); - - options.readOnly = !(this.input instanceof UntitledTextEditorInput); // all resource editors are readonly except for the untitled one; - - return options; - } - protected getAriaLabel(): string { let ariaLabel: string; diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 1b0b5d13369..f09033db891 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; -import { toResource, IEditorCommandsContext, SideBySideEditor, IEditorIdentifier, SaveReason } from 'vs/workbench/common/editor'; +import { toResource, IEditorCommandsContext, SideBySideEditor, IEditorIdentifier, SaveReason, SideBySideEditorInput } from 'vs/workbench/common/editor'; import { IWindowOpenable, IOpenWindowOptions, isWorkspaceToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -23,7 +23,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { isWindows } from 'vs/base/common/platform'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { getResourceForCommand, getMultiSelectedResources, getMultiSelectedEditors } from 'vs/workbench/contrib/files/browser/files'; +import { getResourceForCommand, getMultiSelectedResources, getOpenEditorsViewMultiSelection } from 'vs/workbench/contrib/files/browser/files'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { getMultiSelectedEditorContexts } from 'vs/workbench/browser/parts/editor/editorCommands'; import { Schemas } from 'vs/base/common/network'; @@ -315,12 +315,35 @@ CommandsRegistry.registerCommand({ async function saveSelectedEditors(accessor: ServicesAccessor, options?: ISaveEditorsOptions): Promise { const listService = accessor.get(IListService); - const editorGroupsService = accessor.get(IEditorGroupsService); + const editorGroupService = accessor.get(IEditorGroupsService); const codeEditorService = accessor.get(ICodeEditorService); const textFileService = accessor.get(ITextFileService); + // Retrieve selected or active editor + let editors = getOpenEditorsViewMultiSelection(listService, editorGroupService); + if (!editors) { + const activeGroup = editorGroupService.activeGroup; + if (activeGroup.activeEditor) { + editors = []; + + // Special treatment for side by side editors: if the active editor + // has 2 sides, we consider both, to support saving both sides. + // We only allow this when saving, not for "Save As". + // See also https://github.com/microsoft/vscode/issues/4180 + if (activeGroup.activeEditor instanceof SideBySideEditorInput && !options?.saveAs) { + editors.push({ groupId: activeGroup.id, editor: activeGroup.activeEditor.master }); + editors.push({ groupId: activeGroup.id, editor: activeGroup.activeEditor.details }); + } else { + editors.push({ groupId: activeGroup.id, editor: activeGroup.activeEditor }); + } + } + } + + if (!editors || editors.length === 0) { + return; // nothing to save + } + // Save editors - const editors = getMultiSelectedEditors(listService, editorGroupsService); await doSaveEditors(accessor, editors, options); // Special treatment for embedded editors: if we detect that focus is @@ -435,12 +458,24 @@ CommandsRegistry.registerCommand({ handler: async accessor => { const notificationService = accessor.get(INotificationService); const listService = accessor.get(IListService); - const editorGroupsService = accessor.get(IEditorGroupsService); + const editorGroupService = accessor.get(IEditorGroupsService); const editorService = accessor.get(IEditorService); - const editors = getMultiSelectedEditors(listService, editorGroupsService).filter(({ editor }) => !editor.isUntitled() /* all except untitled */); + // Retrieve selected or active editor + let editors = getOpenEditorsViewMultiSelection(listService, editorGroupService); + if (!editors) { + const activeGroup = editorGroupService.activeGroup; + if (activeGroup.activeEditor) { + editors = [{ groupId: activeGroup.id, editor: activeGroup.activeEditor }]; + } + } + + if (!editors || editors.length === 0) { + return; // nothing to revert + } + try { - await editorService.revert(editors, { force: true }); + await editorService.revert(editors.filter(({ editor }) => !editor.isUntitled() /* all except untitled */), { force: true }); } catch (error) { notificationService.error(nls.localize('genericRevertError', "Failed to revert '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); } diff --git a/src/vs/workbench/contrib/files/browser/files.ts b/src/vs/workbench/contrib/files/browser/files.ts index 6b99f39e922..7746773311f 100644 --- a/src/vs/workbench/contrib/files/browser/files.ts +++ b/src/vs/workbench/contrib/files/browser/files.ts @@ -53,17 +53,6 @@ export function getResourceForCommand(resource: URI | object | undefined, listSe return editorService.activeEditor ? toResource(editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : undefined; } -export function getEditorForCommand(listService: IListService, editorGroupService: IEditorGroupsService): IEditorIdentifier | undefined { - const focus = getFocus(listService); - if (focus instanceof OpenEditor) { - return focus; - } - - const activeGroup = editorGroupService.activeGroup; - - return activeGroup.activeEditor ? { groupId: activeGroup.id, editor: activeGroup.activeEditor } : undefined; -} - export function getMultiSelectedResources(resource: URI | object | undefined, listService: IListService, editorService: IEditorService): Array { const list = listService.lastFocusedList; if (list?.getHTMLElement() === document.activeElement) { @@ -103,7 +92,7 @@ export function getMultiSelectedResources(resource: URI | object | undefined, li return !!result ? [result] : []; } -export function getMultiSelectedEditors(listService: IListService, editorGroupsService: IEditorGroupsService): Array { +export function getOpenEditorsViewMultiSelection(listService: IListService, editorGroupService: IEditorGroupsService): Array | undefined { const list = listService.lastFocusedList; if (list?.getHTMLElement() === document.activeElement) { // Open editors view @@ -122,6 +111,5 @@ export function getMultiSelectedEditors(listService: IListService, editorGroupsS } } - const result = getEditorForCommand(listService, editorGroupsService); - return !!result ? [result] : []; + return undefined; } From d2411100df7cbd76b3acdcd54d3925a146f50af5 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 19 Nov 2019 09:34:38 -0800 Subject: [PATCH 067/246] Fix #84632 --- src/vs/workbench/contrib/scm/browser/media/scmViewlet.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css index 0d5b50909f6..70426f2e015 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css +++ b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css @@ -47,6 +47,7 @@ .scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar .action-label { text-overflow: ellipsis; overflow: hidden; + min-width: 14px; /* minimum size of icons */ } .scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar .action-label .codicon { From dda39439bd1d49863b19b61f19db8da2dca0e92a Mon Sep 17 00:00:00 2001 From: kieferrm Date: Tue, 19 Nov 2019 18:13:31 +0000 Subject: [PATCH 068/246] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 826068852db..60072c70101 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.41.0", - "distro": "2640e642579d97a7fca354ccb76784787f8ff50d", + "distro": "b90e5197b75b37ba34491262b786767ca6f170b7", "author": { "name": "Microsoft Corporation" }, From 8e822e1047fc5845a5572420de84b03a835b595e Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Tue, 19 Nov 2019 10:21:54 -0800 Subject: [PATCH 069/246] Clarify statuses for AuthTokenService --- src/vs/platform/auth/common/auth.ts | 7 ++++--- .../auth/electron-browser/authTokenService.ts | 14 +++++++------- .../userDataSync/common/userDataSyncService.ts | 6 +++--- .../contrib/userDataSync/browser/userDataSync.ts | 16 ++++++++-------- .../authToken/browser/authTokenService.ts | 10 +++++----- .../electron-browser/authTokenService.ts | 2 +- 6 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/vs/platform/auth/common/auth.ts b/src/vs/platform/auth/common/auth.ts index 390bc6d64da..039949e9035 100644 --- a/src/vs/platform/auth/common/auth.ts +++ b/src/vs/platform/auth/common/auth.ts @@ -8,10 +8,11 @@ import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; export const enum AuthTokenStatus { - Inactive = 'Inactive', - Active = 'Active', + Initializing = 'Initializing', + SignedOut = 'SignedOut', + SignedIn = 'SignedIn', SigningIn = 'SigningIn', - Refreshing = 'Refreshing' + RefreshingToken = 'RefreshingToken' } export const IAuthTokenService = createDecorator('IAuthTokenService'); diff --git a/src/vs/platform/auth/electron-browser/authTokenService.ts b/src/vs/platform/auth/electron-browser/authTokenService.ts index 7438d2708af..14586bab7b0 100644 --- a/src/vs/platform/auth/electron-browser/authTokenService.ts +++ b/src/vs/platform/auth/electron-browser/authTokenService.ts @@ -49,7 +49,7 @@ export interface IToken { export class AuthTokenService extends Disposable implements IAuthTokenService { _serviceBrand: undefined; - private _status: AuthTokenStatus = AuthTokenStatus.Refreshing; + private _status: AuthTokenStatus = AuthTokenStatus.Initializing; get status(): AuthTokenStatus { return this._status; } private _onDidChangeStatus: Emitter = this._register(new Emitter()); readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; @@ -67,7 +67,7 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { if (storedRefreshToken) { this.refresh(storedRefreshToken); } else { - this.setStatus(AuthTokenStatus.Inactive); + this.setStatus(AuthTokenStatus.SignedOut); } }); } @@ -91,7 +91,7 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { const timeoutPromise = new Promise((resolve: (value: IToken) => void, reject) => { const wait = setTimeout(() => { - this.setStatus(AuthTokenStatus.Inactive); + this.setStatus(AuthTokenStatus.SignedOut); clearTimeout(wait); reject('Login timed out.'); }, 1000 * 60 * 5); @@ -117,7 +117,7 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { private setToken(token: IToken) { this._activeToken = token; this.credentialsService.setPassword(SERVICE_NAME, ACCOUNT, token.refreshToken); - this.setStatus(AuthTokenStatus.Active); + this.setStatus(AuthTokenStatus.SignedIn); } private async exchangeCodeForToken(clientId: string, tenantId: string, codeVerifier: string, state: string): Promise { @@ -190,7 +190,7 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { private async refresh(refreshToken: string): Promise { return new Promise((resolve, reject) => { - this.setStatus(AuthTokenStatus.Refreshing); + this.setStatus(AuthTokenStatus.RefreshingToken); const postData = toQuery({ refresh_token: refreshToken, client_id: clientId, @@ -231,7 +231,7 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { post.end(); post.on('error', err => { - this.setStatus(AuthTokenStatus.Inactive); + this.setStatus(AuthTokenStatus.SignedOut); reject(err); }); }); @@ -240,7 +240,7 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { async logout(): Promise { await this.credentialsService.deletePassword(SERVICE_NAME, ACCOUNT); this._activeToken = undefined; - this.setStatus(AuthTokenStatus.Inactive); + this.setStatus(AuthTokenStatus.SignedOut); } private setStatus(status: AuthTokenStatus): void { diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index bebcedaf122..95f40ab9b8c 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -56,7 +56,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ if (!this.userDataSyncStoreService.enabled) { throw new Error('Not enabled'); } - if (this.authTokenService.status === AuthTokenStatus.Inactive) { + if (this.authTokenService.status === AuthTokenStatus.SignedOut) { throw new Error('Not Authenticated. Please sign in to start sync.'); } for (const synchroniser of this.synchronisers) { @@ -116,7 +116,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } private onDidChangeAuthTokenStatus(): void { - if (this.authTokenService.status === AuthTokenStatus.Inactive) { + if (this.authTokenService.status === AuthTokenStatus.SignedOut) { this.stop(); } } @@ -180,7 +180,7 @@ export class UserDataAutoSync extends Disposable { private isSyncEnabled(): boolean { return this.configurationService.getValue('configurationSync.enable') && this.userDataSyncService.status !== SyncStatus.Uninitialized - && this.authTokenService.status !== AuthTokenStatus.Inactive; + && this.authTokenService.status !== AuthTokenStatus.SignedOut; } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 46962a7ef23..b5026e0f7d4 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -31,7 +31,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IProductService } from 'vs/platform/product/common/productService'; -const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey('authTokenStatus', AuthTokenStatus.Inactive); +const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey('authTokenStatus', AuthTokenStatus.SignedOut); const SYNC_PUSH_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-light.svg`)); const SYNC_PUSH_DARK_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-dark.svg`)); @@ -75,7 +75,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private onDidChangeAuthTokenStatus(status: AuthTokenStatus) { this.authTokenContext.set(status); - if (status === AuthTokenStatus.Active) { + if (status === AuthTokenStatus.SignedIn) { this.signInNotificationDisposable.clear(); } this.updateBadge(); @@ -111,7 +111,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this.updateBadge(); const enabled = this.configurationService.getValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING); if (enabled) { - if (this.authTokenService.status === AuthTokenStatus.Inactive) { + if (this.authTokenService.status === AuthTokenStatus.SignedOut) { const handle = this.notificationService.prompt(Severity.Info, localize('ask to sign in', "Please sign in with your {0} account to sync configuration across all your machines", this.productService.settingsSyncStore!.account), [ { @@ -133,7 +133,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo let badge: IBadge | undefined = undefined; let clazz: string | undefined; - if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.configurationService.getValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING) && this.authTokenService.status === AuthTokenStatus.Inactive) { + if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.configurationService.getValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING) && this.authTokenService.status === AuthTokenStatus.SignedOut) { badge = new NumberBadge(1, () => localize('sign in', "Sync: Sign in...")); } else if (this.authTokenService.status === AuthTokenStatus.SigningIn) { badge = new ProgressBadge(() => localize('signing in', "Signin in...")); @@ -151,7 +151,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } private async turnOn(): Promise { - if (this.authTokenService.status === AuthTokenStatus.Inactive) { + if (this.authTokenService.status === AuthTokenStatus.SignedOut) { const result = await this.dialogService.confirm({ type: 'info', message: localize('sign in to account', "Sign in to {0}", this.productService.settingsSyncStore!.name), @@ -290,7 +290,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo MenuRegistry.appendMenuItem(MenuId.CommandPalette, startSyncMenuItem); const signInCommandId = 'workbench.userData.actions.signin'; - const signInWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.Inactive)); + const signInWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SignedOut)); CommandsRegistry.registerCommand(signInCommandId, () => this.signIn()); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '5_sync', @@ -328,7 +328,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '5_sync', command: stopSyncCommand, - when: ContextKeyExpr.and(ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.Active), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.HasConflicts)) + when: ContextKeyExpr.and(ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SignedIn), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.HasConflicts)) }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: stopSyncCommand, @@ -383,7 +383,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo id: 'workbench.userData.actions.signout', title: localize('sign out', "Sign Out") }, - when: ContextKeyExpr.and(CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.Active)), + when: ContextKeyExpr.and(CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SignedIn)), }; CommandsRegistry.registerCommand(signOutMenuItem.command.id, () => this.signOut()); MenuRegistry.appendMenuItem(MenuId.CommandPalette, signOutMenuItem); diff --git a/src/vs/workbench/services/authToken/browser/authTokenService.ts b/src/vs/workbench/services/authToken/browser/authTokenService.ts index 844cf7f97b5..ef7af7506a2 100644 --- a/src/vs/workbench/services/authToken/browser/authTokenService.ts +++ b/src/vs/workbench/services/authToken/browser/authTokenService.ts @@ -17,7 +17,7 @@ const ACCOUNT = 'MyAccount'; export class AuthTokenService extends Disposable implements IAuthTokenService { _serviceBrand: undefined; - private _status: AuthTokenStatus = AuthTokenStatus.Refreshing; + private _status: AuthTokenStatus = AuthTokenStatus.Initializing; get status(): AuthTokenStatus { return this._status; } private _onDidChangeStatus: Emitter = this._register(new Emitter()); readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; @@ -31,9 +31,9 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { super(); this.getToken().then(token => { if (token) { - this.setStatus(AuthTokenStatus.Active); + this.setStatus(AuthTokenStatus.SignedIn); } else { - this.setStatus(AuthTokenStatus.Inactive); + this.setStatus(AuthTokenStatus.SignedOut); } }); } @@ -51,7 +51,7 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { const token = await this.quickInputService.input({ placeHolder: localize('enter token', "Please provide the auth bearer token"), ignoreFocusLost: true, }); if (token) { await this.credentialsService.setPassword(SERVICE_NAME, ACCOUNT, token); - this.setStatus(AuthTokenStatus.Active); + this.setStatus(AuthTokenStatus.SignedIn); } } @@ -61,7 +61,7 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { async logout(): Promise { await this.credentialsService.deletePassword(SERVICE_NAME, ACCOUNT); - this.setStatus(AuthTokenStatus.Inactive); + this.setStatus(AuthTokenStatus.SignedOut); } private setStatus(status: AuthTokenStatus): void { diff --git a/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts b/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts index e29e1727aff..143a845b74d 100644 --- a/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts +++ b/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts @@ -18,7 +18,7 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { private readonly channel: IChannel; - private _status: AuthTokenStatus = AuthTokenStatus.Inactive; + private _status: AuthTokenStatus = AuthTokenStatus.Initializing; get status(): AuthTokenStatus { return this._status; } private _onDidChangeStatus: Emitter = this._register(new Emitter()); readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; From 83802834b73fb6959379301beeee19bcefc715b7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 19 Nov 2019 11:07:40 -0800 Subject: [PATCH 070/246] Fix strictFunctionTypes error in terminalQuickOpen Part of #81574 --- src/vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts b/src/vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts index 0a17ee6d236..17c61bc9c11 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts @@ -113,7 +113,7 @@ export class TerminalPickerHandler extends QuickOpenHandler { } private getTerminals(): TerminalEntry[] { - return this.terminalService.terminalTabs.reduce((terminals, tab, tabIndex) => { + return this.terminalService.terminalTabs.reduce((terminals: TerminalEntry[], tab, tabIndex) => { const terminalsInTab = tab.terminalInstances.map((terminal, terminalIndex) => { const label = `${tabIndex + 1}.${terminalIndex + 1}: ${terminal.title}`; return new TerminalEntry(terminal, label, this.terminalService); From f395cac4fff0721a8099126172c01411812bcb4a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 19 Nov 2019 11:09:35 -0800 Subject: [PATCH 071/246] Correct Terminal.processId API to include undefined Part of #81574 --- src/vs/vscode.d.ts | 2 +- src/vs/workbench/contrib/debug/node/terminals.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 4eed2e45d76..43875cbfabb 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -4843,7 +4843,7 @@ declare module 'vscode' { /** * The process ID of the shell process. */ - readonly processId: Thenable; + readonly processId: Thenable; /** * Send text to the terminal. The text is written to the stdin of the underlying pty process diff --git a/src/vs/workbench/contrib/debug/node/terminals.ts b/src/vs/workbench/contrib/debug/node/terminals.ts index 7142a363971..e4e0abd2280 100644 --- a/src/vs/workbench/contrib/debug/node/terminals.ts +++ b/src/vs/workbench/contrib/debug/node/terminals.ts @@ -47,7 +47,7 @@ function spawnAsPromised(command: string, args: string[]): Promise { }); } -export function hasChildProcesses(processId: number): Promise { +export function hasChildProcesses(processId: number | undefined): Promise { if (processId) { // if shell has at least one child process, assume that shell is busy if (env.isWindows) { From d630afbbb07e6999f0a28baf3019985f3bbf75d0 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 19 Nov 2019 11:13:59 -0800 Subject: [PATCH 072/246] Fix strictFunctionTypes in extHostTerminalService Part of #81574 --- src/vs/workbench/api/common/extHostTerminalService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 43e4f6bf753..50d9901b68d 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -268,8 +268,8 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess { // Attach the listeners this._pty.onDidWrite(e => this._onProcessData.fire(e)); if (this._pty.onDidClose) { - this._pty.onDidClose((e: number | undefined = undefined) => { - this._onProcessExit.fire(e); + this._pty.onDidClose((e: number | void = undefined) => { + this._onProcessExit.fire(e === void 0 ? undefined : e); }); } if (this._pty.onDidOverrideDimensions) { From 07574365c96e4617f3dae2cc83a13106b6862292 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Tue, 19 Nov 2019 11:14:56 -0800 Subject: [PATCH 073/246] Add HTML rename for #47069 --- .../html-language-features/server/package.json | 2 +- .../server/src/htmlServerMain.ts | 15 +++++++++++++++ .../server/src/modes/htmlMode.ts | 4 ++++ .../server/src/modes/languageModes.ts | 3 ++- .../html-language-features/server/yarn.lock | 8 ++++---- 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 6ce9bce3320..0f3ff9f9497 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -10,7 +10,7 @@ "main": "./out/htmlServerMain", "dependencies": { "vscode-css-languageservice": "^4.0.3-next.20", - "vscode-html-languageservice": "^3.0.4-next.8", + "vscode-html-languageservice": "^3.0.4-next.9", "vscode-languageserver": "^6.0.0-next.3", "vscode-nls": "^4.1.1", "vscode-uri": "^2.0.3" diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index 1e6e7e2e0ce..dcbbdca483b 100644 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/htmlServerMain.ts @@ -136,6 +136,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { colorProvider: {}, foldingRangeProvider: true, selectionRangeProvider: true, + renameProvider: true }; return { capabilities }; }); @@ -469,6 +470,20 @@ connection.onSelectionRanges((params, token) => { }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); }); +connection.onRenameRequest((params, token) => { + return runSafe(() => { + const document = documents.get(params.textDocument.uri); + const position: Position = params.position; + + if (document) { + const htmlMode = languageModes.getMode('html'); + if (htmlMode && htmlMode.doRename) { + return htmlMode.doRename(document, position, params.newName); + } + } + return null; + }, null, `Error while computing rename for ${params.textDocument.uri}`, token); +}); // Listen on the connection connection.listen(); diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 4449b01bd7c..9c64fb3a9d7 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -74,6 +74,10 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: } return null; }, + doRename(document: TextDocument, position: Position, newName: string) { + const htmlDocument = htmlDocuments.get(document); + return htmlLanguageService.doRename(document, position, newName, htmlDocument); + }, onDocumentRemoved(document: TextDocument) { htmlDocuments.onDocumentRemoved(document); }, diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index 0a8e53a4ef3..f50287330ea 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -8,7 +8,7 @@ import { ClientCapabilities, DocumentContext, getLanguageService as getHTMLLanguageService, IHTMLDataProvider, SelectionRange, CompletionItem, CompletionList, Definition, Diagnostic, DocumentHighlight, DocumentLink, FoldingRange, FormattingOptions, Hover, Location, Position, Range, SignatureHelp, SymbolInformation, TextDocument, TextEdit, - Color, ColorInformation, ColorPresentation + Color, ColorInformation, ColorPresentation, WorkspaceEdit } from 'vscode-html-languageservice'; import { WorkspaceFolder } from 'vscode-languageserver'; import { getLanguageModelCache, LanguageModelCache } from '../languageModelCache'; @@ -38,6 +38,7 @@ export interface LanguageMode { doResolve?: (document: TextDocument, item: CompletionItem) => CompletionItem; doHover?: (document: TextDocument, position: Position) => Hover | null; doSignatureHelp?: (document: TextDocument, position: Position) => SignatureHelp | null; + doRename?: (document: TextDocument, position: Position, newName: string) => WorkspaceEdit | null; findDocumentHighlight?: (document: TextDocument, position: Position) => DocumentHighlight[]; findDocumentSymbols?: (document: TextDocument) => SymbolInformation[]; findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext) => DocumentLink[]; diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 4f9260128b2..439f360ddb5 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -621,10 +621,10 @@ vscode-css-languageservice@^4.0.3-next.20: vscode-nls "^4.1.1" vscode-uri "^2.1.1" -vscode-html-languageservice@^3.0.4-next.8: - version "3.0.4-next.8" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.8.tgz#b3df7b7e8f69c1bf76392c4fd4df4cf483e77e7d" - integrity sha512-gT34wzCwM1rCJzd0EAFnuVe0FaaSr3eXaJpYcMj6rt1UspIJYaL4WtDLCcw4eBL906N1b1Vu+sapiRmV5PjZpg== +vscode-html-languageservice@^3.0.4-next.9: + version "3.0.4-next.9" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.9.tgz#b27f26c29f3af64fa32eabb7425749f95f64036a" + integrity sha512-9V9G7508ybFcn9gQpuucEZIGv8kKlBMEVD8lFFWwWS1yEonKchsxIGJZFbmSGr/n//2anfya8F8yL5ybKuWIRA== dependencies: vscode-languageserver-textdocument "^1.0.0-next.4" vscode-languageserver-types "^3.15.0-next.6" From 0e5a5f6524d24e2a828dc97f6cb31b0810bf5146 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 18 Nov 2019 19:10:45 -0800 Subject: [PATCH 074/246] Better time logging for js + markdown We don't need full date, only times --- .../markdown-language-features/src/logger.ts | 14 +++++++++++++- .../src/utils/logger.ts | 13 ++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-language-features/src/logger.ts b/extensions/markdown-language-features/src/logger.ts index dd679391dff..2136a053084 100644 --- a/extensions/markdown-language-features/src/logger.ts +++ b/extensions/markdown-language-features/src/logger.ts @@ -41,13 +41,21 @@ export class Logger { public log(message: string, data?: any): void { if (this.trace === Trace.Verbose) { - this.appendLine(`[Log - ${(new Date().toTimeString())}] ${message}`); + this.appendLine(`[Log - ${this.now()}] ${message}`); if (data) { this.appendLine(Logger.data2String(data)); } } } + + private now(): string { + const now = new Date(); + return padLeft(now.getUTCHours() + '', 2, '0') + + ':' + padLeft(now.getMinutes() + '', 2, '0') + + ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds(); + } + public updateConfiguration() { this.trace = this.readTrace(); } @@ -73,3 +81,7 @@ export class Logger { return JSON.stringify(data, undefined, 2); } } + +function padLeft(s: string, n: number, pad = ' ') { + return pad.repeat(Math.max(0, n - s.length)) + s; +} diff --git a/extensions/typescript-language-features/src/utils/logger.ts b/extensions/typescript-language-features/src/utils/logger.ts index aac42a91301..18317e58e00 100644 --- a/extensions/typescript-language-features/src/utils/logger.ts +++ b/extensions/typescript-language-features/src/utils/logger.ts @@ -41,9 +41,20 @@ export default class Logger { } public logLevel(level: LogLevel, message: string, data?: any): void { - this.output.appendLine(`[${level} - ${(new Date().toTimeString())}] ${message}`); + this.output.appendLine(`[${level} - ${this.now()}] ${message}`); if (data) { this.output.appendLine(this.data2String(data)); } } + + private now(): string { + const now = new Date(); + return padLeft(now.getUTCHours() + '', 2, '0') + + ':' + padLeft(now.getMinutes() + '', 2, '0') + + ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds(); + } +} + +function padLeft(s: string, n: number, pad = ' ') { + return pad.repeat(Math.max(0, n - s.length)) + s; } From 92417ddd108c1e4f6cb7c63617941e27e84e8d02 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Nov 2019 11:22:21 -0800 Subject: [PATCH 075/246] Revert "Fixes webview responding shortcuts twice on linux (#84967)" This reverts commit a406fd6b07604eabc718666ec7d42f6360307b93. Fixes #85102 --- .../webview/browser/baseWebviewElement.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts index 892944dadea..466e046411a 100644 --- a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts @@ -14,7 +14,6 @@ import { URI } from 'vs/base/common/uri'; import { areWebviewInputOptionsEqual } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { isLinux } from 'vs/base/common/platform'; export const enum WebviewMessageChannels { onmessage = 'onmessage', @@ -123,15 +122,12 @@ export abstract class BaseWebview extends Disposable { this.handleFocusChange(false); })); - if (!isLinux) { - // Fixes #82670 webview responding shortcuts twice on linux. - this._register(this.on('did-keydown', (data: KeyboardEvent) => { - // Electron: workaround for https://github.com/electron/electron/issues/14258 - // We have to detect keyboard events in the and dispatch them to our - // keybinding service because these events do not bubble to the parent window anymore. - this.handleKeyDown(data); - })); - } + this._register(this.on('did-keydown', (data: KeyboardEvent) => { + // Electron: workaround for https://github.com/electron/electron/issues/14258 + // We have to detect keyboard events in the and dispatch them to our + // keybinding service because these events do not bubble to the parent window anymore. + this.handleKeyDown(data); + })); this.style(); this._register(webviewThemeDataProvider.onThemeDataChanged(this.style, this)); From fa5ae1ca80617b723eeb844fc5e96a8466e59e9a Mon Sep 17 00:00:00 2001 From: Sana Ajani Date: Tue, 19 Nov 2019 11:27:30 -0800 Subject: [PATCH 076/246] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 60072c70101..4a3cc1e9002 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.41.0", - "distro": "b90e5197b75b37ba34491262b786767ca6f170b7", + "distro": "b802f3a3df7a3504acd5cec8d4dde868402f6714", "author": { "name": "Microsoft Corporation" }, From 7ab23221707abd75f2c6a299d076bd5b53a2100f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 19 Nov 2019 11:28:31 -0800 Subject: [PATCH 077/246] Fix compile --- .../vscode-api-tests/src/singlefolder-tests/terminal.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index 6431bad3635..a45c3188e00 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -55,7 +55,7 @@ suite('window namespace tests', () => { } terminal.processId.then(id => { try { - ok(id > 0); + ok(id && id > 0); } catch (e) { done(e); } From bc4550bdae624553b847890f91bfb4b4b2c153a9 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 19 Nov 2019 11:29:13 -0800 Subject: [PATCH 078/246] Remove suggest input transparency to fix #85099 --- .../suggestEnabledInput/suggestEnabledInput.css | 3 --- .../suggestEnabledInput/suggestEnabledInput.ts | 11 +++++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.css b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.css index c9512232cd3..d79f11d4ce1 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.css +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.css @@ -10,8 +10,6 @@ .suggest-input-container .monaco-editor-background, .suggest-input-container .monaco-editor, .suggest-input-container .mtk1 { - /* allow the embedded monaco to be styled from the outer context */ - background-color: transparent; color: inherit; } @@ -25,4 +23,3 @@ margin-top: 2px; margin-left: 1px; } - diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts index b7f1355fdad..b9731888148 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts @@ -224,7 +224,8 @@ export class SuggestEnabledInput extends Widget implements IThemable { public style(colors: ISuggestEnabledInputStyles): void { - this.stylingContainer.style.backgroundColor = colors.inputBackground ? colors.inputBackground.toString() : ''; + this.placeholderText.style.backgroundColor = + this.stylingContainer.style.backgroundColor = colors.inputBackground ? colors.inputBackground.toString() : ''; this.stylingContainer.style.color = colors.inputForeground ? colors.inputForeground.toString() : null; this.placeholderText.style.color = colors.inputPlaceholderForeground ? colors.inputPlaceholderForeground.toString() : null; @@ -254,7 +255,7 @@ export class SuggestEnabledInput extends Widget implements IThemable { public layout(dimension: Dimension): void { this.inputWidget.layout(dimension); - this.placeholderText.style.width = `${dimension.width}px`; + this.placeholderText.style.width = `${dimension.width - 2}px`; } private selectAll(): void { @@ -286,6 +287,12 @@ registerThemingParticipant((theme, collector) => { if (inputForegroundColor) { collector.addRule(`.suggest-input-container .monaco-editor .view-line span.inline-selected-text { color: ${inputForegroundColor}; }`); } + + const backgroundColor = theme.getColor(inputBackground); + if (backgroundColor) { + collector.addRule(`.suggest-input-container .monaco-editor-background { background-color: ${backgroundColor}; } `); + collector.addRule(`.suggest-input-container .monaco-editor { background-color: ${backgroundColor}; } `); + } }); From 4ef34de11e4222da2671416002805642b77af8b9 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 18 Nov 2019 16:31:24 -0800 Subject: [PATCH 079/246] Add double tap support for touch events. --- src/vs/base/browser/touch.ts | 24 +++++++++++++++++++ .../gotoSymbol/peek/referencesWidget.ts | 8 +++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/touch.ts b/src/vs/base/browser/touch.ts index 912b17e1319..9956bc9112b 100644 --- a/src/vs/base/browser/touch.ts +++ b/src/vs/base/browser/touch.ts @@ -33,6 +33,7 @@ export interface GestureEvent extends MouseEvent { translationY: number; pageX: number; pageY: number; + tapCount: number; } interface Touch { @@ -76,6 +77,11 @@ export class Gesture extends Disposable { private activeTouches: { [id: number]: TouchData; }; + private _lastSetTapCountTime: number; + + private static readonly CLEAR_TAP_COUNT_TIME = 400; // ms + + private constructor() { super(); @@ -83,6 +89,7 @@ export class Gesture extends Disposable { this.handle = null; this.targets = []; this.ignoreTargets = []; + this._lastSetTapCountTime = 0; this._register(DomUtils.addDisposableListener(document, 'touchstart', (e: TouchEvent) => this.onTouchStart(e))); this._register(DomUtils.addDisposableListener(document, 'touchend', (e: TouchEvent) => this.onTouchEnd(e))); this._register(DomUtils.addDisposableListener(document, 'touchmove', (e: TouchEvent) => this.onTouchMove(e))); @@ -243,10 +250,27 @@ export class Gesture extends Disposable { let event = (document.createEvent('CustomEvent')); event.initEvent(type, false, true); event.initialTarget = initialTarget; + event.tapCount = 0; return event; } private dispatchEvent(event: GestureEvent): void { + if (event.type === EventType.Tap) { + const currentTime = (new Date()).getTime(); + let setTapCount = 0; + if (currentTime - this._lastSetTapCountTime > Gesture.CLEAR_TAP_COUNT_TIME) { + setTapCount = 1; + } else { + setTapCount = 2; + } + + this._lastSetTapCountTime = currentTime; + event.tapCount = setTapCount; + } else if (event.type === EventType.Change || event.type === EventType.Contextmenu) { + // tap is canceled by scrolling or context menu + this._lastSetTapCountTime = 0; + } + for (let i = 0; i < this.ignoreTargets.length; i++) { if (event.initialTarget instanceof Node && this.ignoreTargets[i].contains(event.initialTarget)) { return; diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts index 33813997edb..48a83e6d40e 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts @@ -6,6 +6,7 @@ import 'vs/css!./referencesWidget'; import * as dom from 'vs/base/browser/dom'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { GestureEvent } from 'vs/base/browser/touch'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; @@ -364,8 +365,11 @@ export class ReferenceWidget extends peekView.PeekViewWidget { if (e.browserEvent instanceof MouseEvent && (e.browserEvent.ctrlKey || e.browserEvent.metaKey || e.browserEvent.altKey)) { // modifier-click -> open to the side onEvent(e.elements[0], 'side'); - } else if (e.browserEvent instanceof KeyboardEvent || (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2)) { - // keybinding (list service command) OR double click -> close widget and goto target + } else if (e.browserEvent instanceof KeyboardEvent || (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2) || (e.browserEvent).tapCount === 2) { + // keybinding (list service command) + // OR double click + // OR double tap + // -> close widget and goto target onEvent(e.elements[0], 'goto'); } else { // preview location From cc672674daa7d9580583db6f1e9fe9cbb7479988 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 18 Nov 2019 16:31:41 -0800 Subject: [PATCH 080/246] Double tap support for editor. --- .../editor/browser/controller/pointerHandler.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 30ab2b51656..6729b51619a 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -231,7 +231,21 @@ export class PointerEventHandler extends MouseHandler { const target = this._createMouseTarget(new EditorMouseEvent(event, this.viewHelper.viewDomNode), false); if (target.position) { - this.viewController.moveTo(target.position); + // this.viewController.moveTo(target.position); + this.viewController.dispatchMouse({ + position: target.position, + mouseColumn: target.position.column, + startedOnLineNumbers: false, + mouseDownCount: event.tapCount, + inSelectionMode: false, + altKey: false, + ctrlKey: false, + metaKey: false, + shiftKey: false, + + leftButton: false, + middleButton: false, + }); } } From afb02170c2d7bedad659fd2994bc735ab1049886 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 19 Nov 2019 11:54:31 -0800 Subject: [PATCH 081/246] Fix #85153. --- .../browser/parts/editor/tabsTitleControl.ts | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index d42242fe112..c6bc1cb1c55 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -111,6 +111,7 @@ export class TabsTitleControl extends TitleControl { this.tabsScrollbar = this._register(this.createTabsScrollbar(this.tabsContainer)); tabsAndActionsContainer.appendChild(this.tabsScrollbar.getDomNode()); + this._register(Gesture.addTarget(this.tabsContainer)); // Tabs Container listeners this.registerTabsContainerListeners(this.tabsContainer, this.tabsScrollbar); @@ -181,6 +182,18 @@ export class TabsTitleControl extends TitleControl { } })); + this._register(addDisposableListener(tabsContainer, TouchEventType.Tap, (e: GestureEvent) => { + if (e.tapCount !== 2) { + return; + } + + if (e.initialTarget === tabsContainer) { + EventHelper.stop(e); + + this.group.openEditor(this.untitledTextEditorService.createOrGet(), { pinned: true /* untitled is always pinned */, index: this.group.count /* always at the end */ }); + } + })); + // Prevent auto-scrolling (https://github.com/Microsoft/vscode/issues/16690) this._register(addDisposableListener(tabsContainer, EventType.MOUSE_DOWN, (e: MouseEvent) => { if (e.button === 1) { @@ -600,16 +613,23 @@ export class TabsTitleControl extends TitleControl { })); // Double click: either pin or toggle maximized - disposables.add(addDisposableListener(tab, EventType.DBLCLICK, (e: MouseEvent) => { - EventHelper.stop(e); + [TouchEventType.Tap, EventType.DBLCLICK].forEach(eventType => { + disposables.add(addDisposableListener(tab, eventType, (e: MouseEvent | GestureEvent) => { + if (e instanceof MouseEvent) { + EventHelper.stop(e); + } else if ((e).tapCount !== 2) { + // ignore single taps + return; + } - const editor = this.group.getEditorByIndex(index); - if (editor && this.group.isPinned(editor)) { - this.accessor.arrangeGroups(GroupsArrangement.TOGGLE, this.group); - } else { - this.group.pinEditor(editor); - } - })); + const editor = this.group.getEditorByIndex(index); + if (editor && this.group.isPinned(editor)) { + this.accessor.arrangeGroups(GroupsArrangement.TOGGLE, this.group); + } else { + this.group.pinEditor(editor); + } + })); + }); // Context menu disposables.add(addDisposableListener(tab, EventType.CONTEXT_MENU, (e: Event) => { From ef210f221dcf9930276a9ae477821f9178b590a4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Nov 2019 21:19:14 +0100 Subject: [PATCH 082/246] get store details from settings --- .../platform/product/common/productService.ts | 6 ---- .../userDataSync/common/userDataSync.ts | 17 ++++++++- .../common/userDataSyncService.ts | 8 ++--- .../common/userDataSyncStoreService.ts | 17 ++++----- .../contrib/logs/common/logs.contribution.ts | 6 +--- .../browser/userDataSync.contribution.ts | 36 ++----------------- .../userDataSync/browser/userDataSync.ts | 35 +++++++++++------- 7 files changed, 55 insertions(+), 70 deletions(-) diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index 398ff63b78b..68199163f02 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -102,12 +102,6 @@ export interface IProductConfiguration { readonly msftInternalDomains?: string[]; readonly linkProtectionTrustedDomains?: readonly string[]; - - readonly settingsSyncStore?: { - url: string; - name: string; - account: string; - }; } export interface IExeBasedExtensionTip { diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 7f8d907a421..c90c1cc0a22 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -14,8 +14,12 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ILogService } from 'vs/platform/log/common/log'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store'; export const DEFAULT_IGNORED_SETTINGS = [ + CONFIGURATION_SYNC_STORE_KEY, 'configurationSync.enable', 'configurationSync.enableSettings', 'configurationSync.enableExtensions', @@ -98,12 +102,23 @@ export class UserDataSyncStoreError extends Error { } +export interface IUserDataSyncStore { + url: string; + name: string; + account: string; +} + +export function getUserDataSyncStore(configurationService: IConfigurationService): IUserDataSyncStore | undefined { + const value = configurationService.getValue(CONFIGURATION_SYNC_STORE_KEY); + return value && value.url && value.name && value.account ? value : undefined; +} + export const IUserDataSyncStoreService = createDecorator('IUserDataSyncStoreService'); export interface IUserDataSyncStoreService { _serviceBrand: undefined; - readonly enabled: boolean; + readonly userDataSyncStore: IUserDataSyncStore | undefined; read(key: string, oldValue: IUserData | null): Promise; write(key: string, content: string, ref: string | null): Promise; diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 95f40ab9b8c..4abc9d09da9 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -44,7 +44,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.synchronisers = [this.settingsSynchroniser, this.extensionsSynchroniser]; this.updateStatus(); - if (this.userDataSyncStoreService.enabled) { + if (this.userDataSyncStoreService.userDataSyncStore) { this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeStatus, () => undefined)))(() => this.updateStatus())); this._register(authTokenService.onDidChangeStatus(() => this.onDidChangeAuthTokenStatus())); } @@ -53,7 +53,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } async sync(_continue?: boolean): Promise { - if (!this.userDataSyncStoreService.enabled) { + if (!this.userDataSyncStoreService.userDataSyncStore) { throw new Error('Not enabled'); } if (this.authTokenService.status === AuthTokenStatus.SignedOut) { @@ -68,7 +68,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } stop(): void { - if (!this.userDataSyncStoreService.enabled) { + if (!this.userDataSyncStoreService.userDataSyncStore) { throw new Error('Not enabled'); } for (const synchroniser of this.synchronisers) { @@ -93,7 +93,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } private computeStatus(): SyncStatus { - if (!this.userDataSyncStoreService.enabled) { + if (!this.userDataSyncStoreService.userDataSyncStore) { return SyncStatus.Uninitialized; } if (this.synchronisers.some(s => s.status === SyncStatus.HasConflicts)) { diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index 02b70038cab..bc499a7c48a 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -4,35 +4,36 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, } from 'vs/base/common/lifecycle'; -import { IUserData, IUserDataSyncStoreService, UserDataSyncStoreErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; -import { IProductService } from 'vs/platform/product/common/productService'; +import { IUserData, IUserDataSyncStoreService, UserDataSyncStoreErrorCode, UserDataSyncStoreError, IUserDataSyncStore, getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync'; import { IRequestService, asText, isSuccess } from 'vs/platform/request/common/request'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IHeaders, IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; import { IAuthTokenService } from 'vs/platform/auth/common/auth'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class UserDataSyncStoreService extends Disposable implements IUserDataSyncStoreService { _serviceBrand: any; - get enabled(): boolean { return !!this.productService.settingsSyncStore; } + readonly userDataSyncStore: IUserDataSyncStore | undefined; constructor( - @IProductService private readonly productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, @IRequestService private readonly requestService: IRequestService, @IAuthTokenService private readonly authTokenService: IAuthTokenService, ) { super(); + this.userDataSyncStore = getUserDataSyncStore(configurationService); } async read(key: string, oldValue: IUserData | null): Promise { - if (!this.enabled) { + if (!this.userDataSyncStore) { throw new Error('No settings sync store url configured.'); } - const url = joinPath(URI.parse(this.productService.settingsSyncStore!.url), 'resource', key, 'latest').toString(); + const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', key, 'latest').toString(); const headers: IHeaders = {}; if (oldValue) { headers['If-None-Match'] = oldValue.ref; @@ -58,11 +59,11 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn } async write(key: string, data: string, ref: string | null): Promise { - if (!this.enabled) { + if (!this.userDataSyncStore) { throw new Error('No settings sync store url configured.'); } - const url = joinPath(URI.parse(this.productService.settingsSyncStore!.url), 'resource', key).toString(); + const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', key).toString(); const headers: IHeaders = { 'Content-Type': 'text/plain' }; if (ref) { headers['If-Match'] = ref; diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index becf4322848..7a1a767f67a 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -22,7 +22,6 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { LogsDataCleaner } from 'vs/workbench/contrib/logs/common/logsDataCleaner'; -import { IProductService } from 'vs/platform/product/common/productService'; const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); const devCategory = nls.localize('developer', "Developer"); @@ -35,7 +34,6 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { @ILogService private readonly logService: ILogService, @IFileService private readonly fileService: IFileService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IProductService private readonly productService: IProductService ) { super(); this.registerCommonContributions(); @@ -47,9 +45,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { } private registerCommonContributions(): void { - if (this.productService.settingsSyncStore) { - this.registerLogChannel(Constants.userDataSyncLogChannelId, nls.localize('userDataSyncLog', "Configuration Sync"), this.environmentService.userDataSyncLogResource); - } + this.registerLogChannel(Constants.userDataSyncLogChannelId, nls.localize('userDataSyncLog', "Configuration Sync"), this.environmentService.userDataSyncLogResource); this.registerLogChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Window"), this.environmentService.logFile); } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts index 0ca0ee84c7e..367dd0b6fb3 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts @@ -3,42 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { registerConfiguration } from 'vs/platform/userDataSync/common/userDataSync'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { isWeb } from 'vs/base/common/platform'; -import { UserDataAutoSync } from 'vs/platform/userDataSync/common/userDataSyncService'; -import { IProductService } from 'vs/platform/product/common/productService'; import { UserDataSyncWorkbenchContribution } from 'vs/workbench/contrib/userDataSync/browser/userDataSync'; -class UserDataSyncConfigurationContribution implements IWorkbenchContribution { - - constructor( - @IProductService productService: IProductService - ) { - if (productService.settingsSyncStore) { - registerConfiguration(); - } - } -} - -class UserDataAutoSyncContribution extends Disposable implements IWorkbenchContribution { - - constructor( - @IInstantiationService instantiationService: IInstantiationService - ) { - super(); - if (isWeb) { - instantiationService.createInstance(UserDataAutoSync); - } - } -} - - const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(UserDataSyncConfigurationContribution, LifecyclePhase.Starting); -workbenchRegistry.registerWorkbenchContribution(UserDataSyncWorkbenchContribution, LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(UserDataAutoSyncContribution, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(UserDataSyncWorkbenchContribution, LifecyclePhase.Ready); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index b5026e0f7d4..81a54047424 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, IUserDataSyncStore, registerConfiguration, getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync'; import { localize } from 'vs/nls'; import { Disposable, MutableDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -29,7 +29,9 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { FalseContext } from 'vs/platform/contextkey/common/contextkeys'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { IProductService } from 'vs/platform/product/common/productService'; +import { isWeb } from 'vs/base/common/platform'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { UserDataAutoSync } from 'vs/platform/userDataSync/common/userDataSyncService'; const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey('authTokenStatus', AuthTokenStatus.SignedOut); const SYNC_PUSH_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-light.svg`)); @@ -39,6 +41,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private static readonly ENABLEMENT_SETTING = 'configurationSync.enable'; + private readonly userDataSyncStore: IUserDataSyncStore | undefined; private readonly syncStatusContext: IContextKey; private readonly authTokenContext: IContextKey; private readonly badgeDisposable = this._register(new MutableDisposable()); @@ -59,18 +62,26 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @IDialogService private readonly dialogService: IDialogService, @IStorageService private readonly storageService: IStorageService, @IQuickInputService private readonly quickInputService: IQuickInputService, - @IProductService private readonly productService: IProductService, + @IInstantiationService instantiationService: IInstantiationService, ) { super(); + this.userDataSyncStore = getUserDataSyncStore(configurationService); this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService); this.authTokenContext = CONTEXT_AUTH_TOKEN_STATE.bindTo(contextKeyService); - this.onDidChangeAuthTokenStatus(this.authTokenService.status); - this.onDidChangeSyncStatus(this.userDataSyncService.status); - this._register(Event.debounce(authTokenService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeAuthTokenStatus(this.authTokenService.status))); - this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeSyncStatus(this.userDataSyncService.status))); - this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING))(() => this.onDidChangeEnablement())); - this.registerActions(); + if (this.userDataSyncStore) { + registerConfiguration(); + this.onDidChangeAuthTokenStatus(this.authTokenService.status); + this.onDidChangeSyncStatus(this.userDataSyncService.status); + this._register(Event.debounce(authTokenService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeAuthTokenStatus(this.authTokenService.status))); + this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeSyncStatus(this.userDataSyncService.status))); + this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING))(() => this.onDidChangeEnablement())); + this.registerActions(); + + if (isWeb) { + this._register(instantiationService.createInstance(UserDataAutoSync)); + } + } } private onDidChangeAuthTokenStatus(status: AuthTokenStatus) { @@ -112,7 +123,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo const enabled = this.configurationService.getValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING); if (enabled) { if (this.authTokenService.status === AuthTokenStatus.SignedOut) { - const handle = this.notificationService.prompt(Severity.Info, localize('ask to sign in', "Please sign in with your {0} account to sync configuration across all your machines", this.productService.settingsSyncStore!.account), + const handle = this.notificationService.prompt(Severity.Info, localize('ask to sign in', "Please sign in with your {0} account to sync configuration across all your machines", this.userDataSyncStore!.account), [ { label: localize('Sign in', "Sign in"), @@ -154,8 +165,8 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (this.authTokenService.status === AuthTokenStatus.SignedOut) { const result = await this.dialogService.confirm({ type: 'info', - message: localize('sign in to account', "Sign in to {0}", this.productService.settingsSyncStore!.name), - detail: localize('ask to sign in', "Please sign in with your {0} account to sync configuration across all your machines", this.productService.settingsSyncStore!.account), + message: localize('sign in to account', "Sign in to {0}", this.userDataSyncStore!.name), + detail: localize('ask to sign in', "Please sign in with your {0} account to sync configuration across all your machines", this.userDataSyncStore!.account), primaryButton: localize('Sign in', "Sign in") }); if (!result.confirmed) { From ed517abe0ea2c34494c76792c32df3dc1bd6765f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Nov 2019 21:35:21 +0100 Subject: [PATCH 083/246] fix updating auth token status --- .../contrib/userDataSync/browser/userDataSync.ts | 2 +- .../authToken/electron-browser/authTokenService.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 81a54047424..d46e61b4586 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -33,7 +33,7 @@ import { isWeb } from 'vs/base/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { UserDataAutoSync } from 'vs/platform/userDataSync/common/userDataSyncService'; -const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey('authTokenStatus', AuthTokenStatus.SignedOut); +const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey('authTokenStatus', AuthTokenStatus.Initializing); const SYNC_PUSH_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-light.svg`)); const SYNC_PUSH_DARK_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-dark.svg`)); diff --git a/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts b/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts index 143a845b74d..099395471aa 100644 --- a/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts +++ b/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts @@ -31,10 +31,8 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { ) { super(); this.channel = sharedProcessService.getChannel('authToken'); - this.channel.call('_getInitialStatus').then(status => { - this.updateStatus(status); - this._register(this.channel.listen('onDidChangeStatus')(status => this.updateStatus(status))); - }); + this._register(this.channel.listen('onDidChangeStatus')(status => this.updateStatus(status))); + this.channel.call('_getInitialStatus').then(status => this.updateStatus(status)); this.urlService.registerHandler(this); } @@ -66,8 +64,10 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { } private async updateStatus(status: AuthTokenStatus): Promise { - this._status = status; - this._onDidChangeStatus.fire(status); + if (status !== AuthTokenStatus.Initializing) { + this._status = status; + this._onDidChangeStatus.fire(status); + } } } From f10533b06f2bc68d9f8e80d5368741fb7723bed0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Nov 2019 21:59:10 +0100 Subject: [PATCH 084/246] Fix auto sync enablement --- src/vs/platform/userDataSync/common/userDataSyncService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 4abc9d09da9..6ccfeac0d85 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -180,7 +180,7 @@ export class UserDataAutoSync extends Disposable { private isSyncEnabled(): boolean { return this.configurationService.getValue('configurationSync.enable') && this.userDataSyncService.status !== SyncStatus.Uninitialized - && this.authTokenService.status !== AuthTokenStatus.SignedOut; + && this.authTokenService.status === AuthTokenStatus.SignedIn; } } From 29d07e529d73552d080329a5c6481431940e8cca Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 19 Nov 2019 15:28:59 -0800 Subject: [PATCH 085/246] Fix #85152, use padding instead of margins for twisties FYI @joaomoreno --- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- src/vs/base/browser/ui/tree/media/tree.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index addcf81a6ee..52e88cc5c9b 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -336,7 +336,7 @@ class TreeRenderer implements IListRenderer } const indent = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent; - templateData.twistie.style.marginLeft = `${indent}px`; + templateData.twistie.style.paddingLeft = `${indent}px`; templateData.indent.style.width = `${indent + this.indent - 16}px`; this.renderTwistie(node, templateData); diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index dc83adc86f4..66cd61d0b87 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -41,7 +41,7 @@ .monaco-tl-twistie { font-size: 10px; text-align: right; - margin-right: 6px; + padding-right: 6px; flex-shrink: 0; width: 16px; display: flex !important; From 95d8353abfaf0362159382f55498dce946a5ab1e Mon Sep 17 00:00:00 2001 From: DiamondYuan Date: Wed, 20 Nov 2019 07:47:52 +0800 Subject: [PATCH 086/246] fix: fix typo (#85090) --- .../customEditor/browser/customEditorInputFactory.ts | 2 +- .../customEditor/browser/webviewEditor.contribution.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts index e4509a59b02..8739a05780a 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts @@ -12,7 +12,7 @@ import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/ import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { Lazy } from 'vs/base/common/lazy'; -export class CustomEditoInputFactory extends WebviewEditorInputFactory { +export class CustomEditorInputFactory extends WebviewEditorInputFactory { public static readonly ID = CustomFileEditorInput.typeId; diff --git a/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts b/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts index cfd19134b04..cdb3dc55fc9 100644 --- a/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts +++ b/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts @@ -13,7 +13,7 @@ import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } fro import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; -import { CustomEditoInputFactory } from 'vs/workbench/contrib/customEditor/browser/customEditorInputFactory'; +import { CustomEditorInputFactory } from 'vs/workbench/contrib/customEditor/browser/customEditorInputFactory'; import { ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; import './commands'; @@ -35,8 +35,8 @@ Registry.as(EditorExtensions.Editors).registerEditor( ]); Registry.as(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory( - CustomEditoInputFactory.ID, - CustomEditoInputFactory); + CustomEditorInputFactory.ID, + CustomEditorInputFactory); Registry.as(ConfigurationExtensions.Configuration) .registerConfiguration({ From 73ec85b65416e7b808d6ba3da4dc34b0774933fc Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 19 Nov 2019 16:49:15 -0800 Subject: [PATCH 087/246] Bump node-debug --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 7365025c343..cc5777332ac 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,7 +1,7 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.41.0", + "version": "1.41.1", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", From b1a02d3d190aa1f38eb633ef26c4b8bdaabc5f41 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Nov 2019 14:44:45 -0800 Subject: [PATCH 088/246] Properly hook up dirty indicator for customEditorInput --- .../contrib/customEditor/browser/customEditorInput.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index e7a2e3f5c31..5fdc6f90528 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -16,7 +16,6 @@ import { GroupIdentifier, IEditorInput, IRevertOptions, ISaveOptions, Verbosity import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; -import { CustomEditorModel } from '../common/customEditorModel'; import { IEditorModel } from 'vs/platform/editor/common/editor'; export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { @@ -111,14 +110,6 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { } } - public setModel(model: CustomEditorModel) { - if (this._model) { - throw new Error('Model is already set'); - } - this._model = model; - this._register(model.onDidChangeDirty(() => this._onDidChangeDirty.fire())); - } - public isReadonly(): boolean { return false; } @@ -142,6 +133,7 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { public async resolve(): Promise { this._model = await this.customEditorService.models.loadOrCreate(this.getResource(), this.viewType); + this._register(this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire())); return await super.resolve(); } } From 4934a6f487b9ed6b99b900ae0710c7b452792e20 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Nov 2019 17:18:33 -0800 Subject: [PATCH 089/246] Validate code action range before passing it to providers Fixes #80288 --- src/vs/editor/contrib/codeAction/codeActionModel.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index 39faf3722d1..e5743489005 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -73,8 +73,9 @@ class CodeActionOracle extends Disposable { return undefined; } for (const marker of this._markerService.read({ resource: model.uri })) { - if (Range.intersectRanges(marker, selection)) { - return Range.lift(marker); + const markerRange = model.validateRange(marker); + if (Range.intersectRanges(markerRange, selection)) { + return Range.lift(markerRange); } } From daa77e1402b19fa15599ea520e1bba70a7bc3829 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Nov 2019 07:58:40 +0100 Subject: [PATCH 090/246] editors - polish readonly handling --- src/vs/editor/browser/editorBrowser.ts | 7 ++++++- src/vs/monaco.d.ts | 4 ++++ .../browser/parts/editor/baseEditor.ts | 4 +--- .../browser/parts/editor/textDiffEditor.ts | 20 +++++++++---------- .../browser/parts/editor/textEditor.ts | 14 ++++++++++--- .../parts/editor/textResourceEditor.ts | 15 +++++++------- .../files/browser/editors/textFileEditor.ts | 14 +++++-------- 7 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index bcd0995f843..85c44e78f09 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -6,7 +6,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { OverviewRulerPosition, ConfigurationChangedEvent, EditorLayoutInfo, IComputedEditorOptions, EditorOption, FindComputedEditorOptionValueById, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { OverviewRulerPosition, ConfigurationChangedEvent, EditorLayoutInfo, IComputedEditorOptions, EditorOption, FindComputedEditorOptionValueById, IEditorOptions, IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ICursors } from 'vs/editor/common/controller/cursorCommon'; import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { IPosition, Position } from 'vs/editor/common/core/position'; @@ -938,6 +938,11 @@ export interface IDiffEditor extends editorCommon.IEditor { * If the diff computation is not finished or the model is missing, will return null. */ getDiffLineInformationForModified(lineNumber: number): IDiffLineInformation | null; + + /** + * Update the editor's options after the editor has been created. + */ + updateOptions(newOptions: IDiffEditorOptions): void; } /** diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index b1d411f7aef..0a507e809e2 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4233,6 +4233,10 @@ declare namespace monaco.editor { * If the diff computation is not finished or the model is missing, will return null. */ getDiffLineInformationForModified(lineNumber: number): IDiffLineInformation | null; + /** + * Update the editor's options after the editor has been created. + */ + updateOptions(newOptions: IDiffEditorOptions): void; } export class FontInfo extends BareFontInfo { diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index 669ee10f294..583bdb727a7 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -78,11 +78,9 @@ export abstract class BaseEditor extends Panel implements IEditor { * The provided cancellation token should be used to test if the operation * was cancelled. */ - setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { this._input = input; this._options = options; - - return Promise.resolve(); } /** diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index e36715049dc..1df88430dc7 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -123,8 +123,15 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { }); this.diffNavigatorDisposables.add(this.diffNavigator); - // Readonly flag - diffEditor.updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() }); + // Since the resolved model provides information about being readonly + // or not, we apply it here to the editor even though the editor input + // was already asked for being readonly or not. The rationale is that + // a resolved model might have more specific information about being + // readonly or not that the input did not have. + diffEditor.updateOptions({ + readOnly: resolvedDiffEditorModel.modifiedModel?.isReadonly(), + originalEditable: !resolvedDiffEditorModel.originalModel?.isReadonly() + }); } catch (error) { // In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff. @@ -136,14 +143,6 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { } } - setOptions(options: EditorOptions | undefined): void { - const textOptions = options; - if (textOptions && isFunction(textOptions.apply)) { - const diffEditor = assertIsDefined(this.getControl()); - textOptions.apply(diffEditor, ScrollType.Smooth); - } - } - private restoreTextDiffEditorViewState(editor: EditorInput, control: IDiffEditor): boolean { if (editor instanceof DiffEditorInput) { const resource = this.toDiffEditorViewStateResource(editor); @@ -210,6 +209,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { protected getConfigurationOverrides(): ICodeEditorOptions { const options: IDiffEditorOptions = super.getConfigurationOverrides(); + options.readOnly = this.input instanceof DiffEditorInput && this.input.modifiedInput.isReadonly(); options.originalEditable = this.input instanceof DiffEditorInput && !this.input.originalInput.isReadonly(); options.lineDecorationsWidth = '2ch'; diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index ac784018a61..7bf208f3877 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -6,12 +6,12 @@ import { localize } from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { distinct, deepClone, assign } from 'vs/base/common/objects'; -import { isObject, assertIsDefined, withNullAsUndefined } from 'vs/base/common/types'; +import { isObject, assertIsDefined, withNullAsUndefined, isFunction } from 'vs/base/common/types'; import { Dimension } from 'vs/base/browser/dom'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { EditorInput, EditorOptions, IEditorMemento, ITextEditor, SaveReason } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, IEditorMemento, ITextEditor, SaveReason, TextEditorOptions } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { IEditorViewState, IEditor } from 'vs/editor/common/editorCommon'; +import { IEditorViewState, IEditor, ScrollType } from 'vs/editor/common/editorCommon'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -202,6 +202,14 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { editorContainer.setAttribute('aria-label', this.computeAriaLabel()); } + setOptions(options: EditorOptions | undefined): void { + const textOptions = options as TextEditorOptions; + if (textOptions && isFunction(textOptions.apply)) { + const textEditor = assertIsDefined(this.getControl()); + textOptions.apply(textEditor, ScrollType.Smooth); + } + } + protected setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { // Pass on to Editor diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index b37c9c63c54..3d13bbc322b 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -90,6 +90,13 @@ export class AbstractTextResourceEditor extends BaseTextEditor { if (!optionsGotApplied) { this.restoreTextResourceEditorViewState(input, textEditor); } + + // Since the resolved model provides information about being readonly + // or not, we apply it here to the editor even though the editor input + // was already asked for being readonly or not. The rationale is that + // a resolved model might have more specific information about being + // readonly or not that the input did not have. + textEditor.updateOptions({ readOnly: resolvedModel.isReadonly() }); } private restoreTextResourceEditorViewState(editor: EditorInput, control: IEditor) { @@ -101,14 +108,6 @@ export class AbstractTextResourceEditor extends BaseTextEditor { } } - setOptions(options: EditorOptions | undefined): void { - const textOptions = options; - if (textOptions && isFunction(textOptions.apply)) { - const textEditor = assertIsDefined(this.getControl()); - textOptions.apply(textEditor, ScrollType.Smooth); - } - } - protected getAriaLabel(): string { let ariaLabel: string; diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index 5c257633406..1015e57032c 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -117,14 +117,6 @@ export class TextFileEditor extends BaseTextEditor { } } - setOptions(options: EditorOptions | undefined): void { - const textOptions = options as TextEditorOptions; - if (textOptions && isFunction(textOptions.apply)) { - const textEditor = assertIsDefined(this.getControl()); - textOptions.apply(textEditor, ScrollType.Smooth); - } - } - async setInput(input: FileEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { // Update/clear view settings if input changes @@ -164,7 +156,11 @@ export class TextFileEditor extends BaseTextEditor { (options).apply(textEditor, ScrollType.Immediate); } - // Readonly flag + // Since the resolved model provides information about being readonly + // or not, we apply it here to the editor even though the editor input + // was already asked for being readonly or not. The rationale is that + // a resolved model might have more specific information about being + // readonly or not that the input did not have. textEditor.updateOptions({ readOnly: textFileModel.isReadonly() }); } catch (error) { this.handleSetInputError(error, input, options); From 07ad6851d48232ffcff5720fef6be102e8a47075 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Nov 2019 08:14:51 +0100 Subject: [PATCH 091/246] editors - no need for working copy service getDirty() --- src/vs/platform/dialogs/common/dialogs.ts | 12 ++++---- .../browser/parts/editor/editorActions.ts | 19 +++++++++++- .../browser/parts/editor/editorGroupView.ts | 2 +- .../browser/abstractFileDialogService.ts | 12 ++++---- .../electron-browser/fileDialogService.ts | 4 +-- .../workingCopy/common/workingCopyService.ts | 30 ------------------- .../test/common/workingCopyService.test.ts | 20 ------------- .../workbench/test/workbenchTestServices.ts | 2 +- 8 files changed, 34 insertions(+), 67 deletions(-) diff --git a/src/vs/platform/dialogs/common/dialogs.ts b/src/vs/platform/dialogs/common/dialogs.ts index 64f783471ba..81615b1c9e3 100644 --- a/src/vs/platform/dialogs/common/dialogs.ts +++ b/src/vs/platform/dialogs/common/dialogs.ts @@ -242,7 +242,7 @@ export interface IFileDialogService { /** * Shows a confirm dialog for saving 1-N files. */ - showSaveConfirm(fileNameOrResources: string | URI[]): Promise; + showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise; /** * Shows a open file dialog and returns the chosen file URI. @@ -257,16 +257,16 @@ export const enum ConfirmResult { } const MAX_CONFIRM_FILES = 10; -export function getConfirmMessage(start: string, resourcesToConfirm: readonly URI[]): string { +export function getConfirmMessage(start: string, fileNamesOrResources: readonly (string | URI)[]): string { const message = [start]; message.push(''); - message.push(...resourcesToConfirm.slice(0, MAX_CONFIRM_FILES).map(r => basename(r))); + message.push(...fileNamesOrResources.slice(0, MAX_CONFIRM_FILES).map(fileNameOrResource => typeof fileNameOrResource === 'string' ? fileNameOrResource : basename(fileNameOrResource))); - if (resourcesToConfirm.length > MAX_CONFIRM_FILES) { - if (resourcesToConfirm.length - MAX_CONFIRM_FILES === 1) { + if (fileNamesOrResources.length > MAX_CONFIRM_FILES) { + if (fileNamesOrResources.length - MAX_CONFIRM_FILES === 1) { message.push(localize('moreFile', "...1 additional file not shown")); } else { - message.push(localize('moreFiles', "...{0} additional files not shown", resourcesToConfirm.length - MAX_CONFIRM_FILES)); + message.push(localize('moreFiles', "...{0} additional files not shown", fileNamesOrResources.length - MAX_CONFIRM_FILES)); } } diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index fc8f42b2a5a..70b32efb074 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -23,6 +23,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { ResourceMap, values } from 'vs/base/common/map'; export class ExecuteCommandAction extends Action { @@ -639,7 +640,23 @@ export abstract class BaseCloseAllAction extends Action { return undefined; })); - const confirm = await this.fileDialogService.showSaveConfirm(this.workingCopyService.getDirty().map(copy => copy.resource)); + const dirtyEditorsToConfirmByName = new Set(); + const dirtyEditorsToConfirmByResource = new ResourceMap(); + + for (const editor of this.editorService.editors) { + if (!editor.isDirty()) { + continue; // only interested in dirty editors + } + + const resource = editor.getResource(); + if (resource) { + dirtyEditorsToConfirmByResource.set(resource, true); + } else { + dirtyEditorsToConfirmByName.add(editor.getName()); + } + } + + const confirm = await this.fileDialogService.showSaveConfirm([...dirtyEditorsToConfirmByResource.keys(), ...values(dirtyEditorsToConfirmByName)]); if (confirm === ConfirmResult.CANCEL) { return; } diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 0b0fcb36fc7..c98f797eefc 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -1297,7 +1297,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { await this.openEditor(editor); const editorResource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); - const res = await this.fileDialogService.showSaveConfirm(editorResource ? [editorResource] : editor.getName()); + const res = await this.fileDialogService.showSaveConfirm(editorResource ? [editorResource] : [editor.getName()]); // It could be that the editor saved meanwhile, so we check again // to see if anything needs to happen before closing for good. diff --git a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts index cead0c626ba..9fdab42186d 100644 --- a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts @@ -80,24 +80,24 @@ export abstract class AbstractFileDialogService implements IFileDialogService { return this.defaultFilePath(schemeFilter); } - async showSaveConfirm(fileNameOrResources: string | URI[]): Promise { + async showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise { if (this.environmentService.isExtensionDevelopment) { return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests) } - if (Array.isArray(fileNameOrResources) && fileNameOrResources.length === 0) { + if (fileNamesOrResources.length === 0) { return ConfirmResult.DONT_SAVE; } let message: string; - if (typeof fileNameOrResources === 'string' || fileNameOrResources.length === 1) { - message = nls.localize('saveChangesMessage', "Do you want to save the changes you made to {0}?", typeof fileNameOrResources === 'string' ? fileNameOrResources : resources.basename(fileNameOrResources[0])); + if (fileNamesOrResources.length === 1) { + message = nls.localize('saveChangesMessage', "Do you want to save the changes you made to {0}?", typeof fileNamesOrResources[0] === 'string' ? fileNamesOrResources[0] : resources.basename(fileNamesOrResources[0])); } else { - message = getConfirmMessage(nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", fileNameOrResources.length), fileNameOrResources); + message = getConfirmMessage(nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", fileNamesOrResources.length), fileNamesOrResources); } const buttons: string[] = [ - Array.isArray(fileNameOrResources) && fileNameOrResources.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"), + fileNamesOrResources.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"), nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save"), nls.localize('cancel', "Cancel") ]; diff --git a/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts index f4452c6dc6f..af8287287a9 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts @@ -184,14 +184,14 @@ export class FileDialogService extends AbstractFileDialogService implements IFil return schema === Schemas.untitled ? [Schemas.file] : (schema !== Schemas.file ? [schema, Schemas.file] : [schema]); } - async showSaveConfirm(fileNameOrResources: string | URI[]): Promise { + async showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise { if (this.environmentService.isExtensionDevelopment) { if (!this.environmentService.args['extension-development-confirm-save']) { return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests) } } - return super.showSaveConfirm(fileNameOrResources); + return super.showSaveConfirm(fileNamesOrResources); } } diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index fd4da1c641f..ae74659bc3f 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -51,8 +51,6 @@ export interface IWorkingCopyService { isDirty(resource: URI): boolean; - getDirty(...resources: URI[]): IWorkingCopy[]; - //#endregion @@ -72,34 +70,6 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic private readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; - getDirty(...resources: URI[]): IWorkingCopy[] { - const dirtyWorkingCopies: IWorkingCopy[] = []; - - // Specific resource(s) - if (resources.length > 0) { - for (const resource of resources) { - this.fillDirty(this.mapResourceToWorkingCopy.get(resource.toString()), dirtyWorkingCopies); - } - } - - // All resources - else { - this.fillDirty(this.workingCopies, dirtyWorkingCopies); - } - - return dirtyWorkingCopies; - } - - private fillDirty(workingCopies: Set | undefined, target: IWorkingCopy[]): void { - if (workingCopies) { - for (const workingCopy of workingCopies) { - if (workingCopy.isDirty()) { - target.push(workingCopy); - } - } - } - } - isDirty(resource: URI): boolean { const workingCopies = this.mapResourceToWorkingCopy.get(resource.toString()); if (workingCopies) { diff --git a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts index 7110cd28582..a7797845a17 100644 --- a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts @@ -56,8 +56,6 @@ suite('WorkingCopyService', () => { assert.equal(service.hasDirty, false); assert.equal(service.dirtyCount, 0); - assert.equal(service.getDirty().length, 0); - assert.equal(service.getDirty(URI.file('/'), URI.file('/some')).length, 0); assert.equal(service.isDirty(URI.file('/')), false); // resource 1 @@ -68,8 +66,6 @@ suite('WorkingCopyService', () => { assert.equal(service.dirtyCount, 0); assert.equal(service.isDirty(resource1), false); assert.equal(service.hasDirty, false); - assert.equal(service.getDirty(resource1).length, 0); - assert.equal(service.getDirty().length, 0); copy1.setDirty(true); @@ -78,8 +74,6 @@ suite('WorkingCopyService', () => { assert.equal(service.hasDirty, true); assert.equal(onDidChangeDirty.length, 1); assert.equal(onDidChangeDirty[0], copy1); - assert.equal(service.getDirty(resource1).length, 1); - assert.equal(service.getDirty().length, 1); copy1.setDirty(false); @@ -88,8 +82,6 @@ suite('WorkingCopyService', () => { assert.equal(service.hasDirty, false); assert.equal(onDidChangeDirty.length, 2); assert.equal(onDidChangeDirty[1], copy1); - assert.equal(service.getDirty(resource1).length, 0); - assert.equal(service.getDirty().length, 0); unregister1.dispose(); @@ -101,8 +93,6 @@ suite('WorkingCopyService', () => { assert.equal(service.dirtyCount, 1); assert.equal(service.isDirty(resource2), true); assert.equal(service.hasDirty, true); - assert.equal(service.getDirty(resource1, resource2).length, 1); - assert.equal(service.getDirty().length, 1); assert.equal(onDidChangeDirty.length, 3); assert.equal(onDidChangeDirty[2], copy2); @@ -112,8 +102,6 @@ suite('WorkingCopyService', () => { assert.equal(service.hasDirty, false); assert.equal(onDidChangeDirty.length, 4); assert.equal(onDidChangeDirty[3], copy2); - assert.equal(service.getDirty(resource1, resource2).length, 0); - assert.equal(service.getDirty().length, 0); }); test('registry - multiple copies on same resource', () => { @@ -135,31 +123,23 @@ suite('WorkingCopyService', () => { assert.equal(service.dirtyCount, 1); assert.equal(onDidChangeDirty.length, 1); assert.equal(service.isDirty(resource), true); - assert.equal(service.getDirty(resource).length, 1); - assert.equal(service.getDirty().length, 1); copy2.setDirty(true); assert.equal(service.dirtyCount, 2); assert.equal(onDidChangeDirty.length, 2); assert.equal(service.isDirty(resource), true); - assert.equal(service.getDirty(resource).length, 2); - assert.equal(service.getDirty().length, 2); unregister1.dispose(); assert.equal(service.dirtyCount, 1); assert.equal(onDidChangeDirty.length, 3); assert.equal(service.isDirty(resource), true); - assert.equal(service.getDirty(resource).length, 1); - assert.equal(service.getDirty().length, 1); unregister2.dispose(); assert.equal(service.dirtyCount, 0); assert.equal(onDidChangeDirty.length, 4); assert.equal(service.isDirty(resource), false); - assert.equal(service.getDirty(resource).length, 0); - assert.equal(service.getDirty().length, 0); }); }); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 5b0e3cdadef..531bfd37b51 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -444,7 +444,7 @@ export class TestFileDialogService implements IFileDialogService { public setConfirmResult(result: ConfirmResult): void { this.confirmResult = result; } - public showSaveConfirm(resources: string | URI[]): Promise { + public showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise { return Promise.resolve(this.confirmResult); } } From e2e55d63684976ad7f25085a9405579f88e20611 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Nov 2019 08:36:49 +0100 Subject: [PATCH 092/246] fix #85180 --- .../textfile/browser/textFileService.ts | 31 ++++++++----------- .../services/textfile/common/textfiles.ts | 2 +- .../textfile/test/textFileService.test.ts | 2 +- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 6228f117813..d0fb5b46abd 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -239,7 +239,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex if (confirm === ConfirmResult.SAVE) { const result = await this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true }); - if (result.results.some(r => !r.success)) { + if (result.results.some(r => r.error)) { return true; // veto if some saves failed } @@ -492,9 +492,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex } } - const result = await this.saveAll([resource], options); - - return result.results.length === 1 && !!result.results[0].success; + return !(await this.saveAll([resource], options)).results.some(result => result.error); } saveAll(includeUntitled?: boolean, options?: ITextFileSaveOptions): Promise; @@ -560,7 +558,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex result.results.push({ source: untitledResources[index], target: uri, - success: !!uri + error: !uri // the operation was canceled or failed, so mark as error }); })); @@ -648,10 +646,11 @@ export abstract class AbstractTextFileService extends Disposable implements ITex await Promise.all(dirtyFileModels.map(async model => { await model.save(options); - if (!model.isDirty()) { + // If model is still dirty, mark the resulting operation as error + if (model.isDirty()) { const result = mapResourceToResult.get(model.resource); if (result) { - result.success = true; + result.error = true; } } })); @@ -838,9 +837,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex } async revert(resource: URI, options?: IRevertOptions): Promise { - const result = await this.revertAll([resource], options); - - return result.results.length === 1 && !!result.results[0].success; + return !(await this.revertAll([resource], options)).results.some(result => result.error); } async revertAll(resources?: URI[], options?: IRevertOptions): Promise { @@ -850,7 +847,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // Revert untitled const untitledReverted = this.untitledTextEditorService.revertAll(resources); - untitledReverted.forEach(untitled => revertOperationResult.results.push({ source: untitled, success: true })); + untitledReverted.forEach(untitled => revertOperationResult.results.push({ source: untitled })); return revertOperationResult; } @@ -869,20 +866,18 @@ export abstract class AbstractTextFileService extends Disposable implements ITex try { await model.revert(options); - if (!model.isDirty()) { + // If model is still dirty, mark the resulting operation as error + if (model.isDirty()) { const result = mapResourceToResult.get(model.resource); if (result) { - result.success = true; + result.error = true; } } } catch (error) { - // FileNotFound means the file got deleted meanwhile, so still record as successful revert + // FileNotFound means the file got deleted meanwhile, so ignore it if ((error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) { - const result = mapResourceToResult.get(model.resource); - if (result) { - result.success = true; - } + return; } // Otherwise bubble up the error diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index a1383b93901..f2eef06db52 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -309,7 +309,7 @@ export interface ITextFileOperationResult { export interface IResult { source: URI; target?: URI; - success?: boolean; + error?: boolean; } export const enum LoadReason { diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts index db7e2bd8033..e0bb57aac5b 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -205,7 +205,7 @@ suite('Files - TextFileService', () => { const res = await accessor.textFileService.saveAll(true); assert.ok(loadOrCreateStub.calledOnce); assert.equal(res.results.length, 1); - assert.ok(res.results[0].success); + assert.ok(!res.results[0].error); assert.equal(res.results[0].target!.scheme, Schemas.file); assert.equal(res.results[0].target!.authority, untitledUncUri.authority); assert.equal(res.results[0].target!.path, untitledUncUri.path); From 65050271c24788f74456cc9210931dbecf9c79c5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Nov 2019 08:47:11 +0100 Subject: [PATCH 093/246] :lipstick: gesture handling in tabs title --- .../browser/parts/editor/tabsTitleControl.ts | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index c6bc1cb1c55..eb3e326f378 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -106,12 +106,12 @@ export class TabsTitleControl extends TitleControl { this.tabsContainer.setAttribute('role', 'tablist'); this.tabsContainer.draggable = true; addClass(this.tabsContainer, 'tabs-container'); + this._register(Gesture.addTarget(this.tabsContainer)); // Tabs Scrollbar this.tabsScrollbar = this._register(this.createTabsScrollbar(this.tabsContainer)); tabsAndActionsContainer.appendChild(this.tabsScrollbar.getDomNode()); - this._register(Gesture.addTarget(this.tabsContainer)); // Tabs Container listeners this.registerTabsContainerListeners(this.tabsContainer, this.tabsScrollbar); @@ -174,25 +174,27 @@ export class TabsTitleControl extends TitleControl { })); // New file when double clicking on tabs container (but not tabs) - this._register(addDisposableListener(tabsContainer, EventType.DBLCLICK, e => { - if (e.target === tabsContainer) { + [TouchEventType.Tap, EventType.DBLCLICK].forEach(eventType => { + this._register(addDisposableListener(tabsContainer, eventType, (e: MouseEvent | GestureEvent) => { + if (eventType === EventType.DBLCLICK) { + if (e.target !== tabsContainer) { + return; // ignore if target is not tabs container + } + } else { + if ((e).tapCount !== 2) { + return; // ignore single taps + } + + if ((e).initialTarget !== tabsContainer) { + return; // ignore if target is not tabs container + } + } + EventHelper.stop(e); this.group.openEditor(this.untitledTextEditorService.createOrGet(), { pinned: true /* untitled is always pinned */, index: this.group.count /* always at the end */ }); - } - })); - - this._register(addDisposableListener(tabsContainer, TouchEventType.Tap, (e: GestureEvent) => { - if (e.tapCount !== 2) { - return; - } - - if (e.initialTarget === tabsContainer) { - EventHelper.stop(e); - - this.group.openEditor(this.untitledTextEditorService.createOrGet(), { pinned: true /* untitled is always pinned */, index: this.group.count /* always at the end */ }); - } - })); + })); + }); // Prevent auto-scrolling (https://github.com/Microsoft/vscode/issues/16690) this._register(addDisposableListener(tabsContainer, EventType.MOUSE_DOWN, (e: MouseEvent) => { @@ -615,11 +617,10 @@ export class TabsTitleControl extends TitleControl { // Double click: either pin or toggle maximized [TouchEventType.Tap, EventType.DBLCLICK].forEach(eventType => { disposables.add(addDisposableListener(tab, eventType, (e: MouseEvent | GestureEvent) => { - if (e instanceof MouseEvent) { + if (eventType === EventType.DBLCLICK) { EventHelper.stop(e); } else if ((e).tapCount !== 2) { - // ignore single taps - return; + return; // ignore single taps } const editor = this.group.getEditorByIndex(index); From 6df05c85534654405c61a42592d2ad939a1fe9f4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 20 Nov 2019 09:27:11 +0100 Subject: [PATCH 094/246] clarify prepareRename, #85157 --- src/vs/vscode.d.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 43875cbfabb..8f254f8dec9 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -3015,6 +3015,9 @@ declare module 'vscode' { * be a range or a range and a placeholder text. The placeholder text should be the identifier of the symbol * which is being renamed - when omitted the text in the returned range is used. * + * *Note: * This function should throw an error or return a rejected thenable when the provided location + * doesn't allow for a rename. + * * @param document The document in which rename will be invoked. * @param position The position at which rename will be invoked. * @param token A cancellation token. From e055299e77032c377a2563a7188903e29dbdee6e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Nov 2019 09:36:52 +0100 Subject: [PATCH 095/246] fix git commands --- extensions/git/package.json | 18 +++++++++--------- extensions/git/src/commands.ts | 8 ++++---- extensions/git/src/uri.ts | 4 ++++ 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index eff48607109..9d7086dbaad 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1194,7 +1194,7 @@ { "command": "git.openFile", "group": "navigation", - "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" }, { "command": "git.openChange", @@ -1204,44 +1204,44 @@ { "command": "git.stageSelectedRanges", "group": "2_git@1", - "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" }, { "command": "git.unstageSelectedRanges", "group": "2_git@2", - "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" }, { "command": "git.revertSelectedRanges", "group": "2_git@3", - "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" } ], "editor/context": [ { "command": "git.stageSelectedRanges", "group": "2_git@1", - "when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" }, { "command": "git.unstageSelectedRanges", "group": "2_git@2", - "when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" }, { "command": "git.revertSelectedRanges", "group": "2_git@3", - "when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" } ], "scm/change/title": [ { "command": "git.stageChange", - "when": "originalResourceScheme == git" + "when": "originalResourceScheme == gitfs" }, { "command": "git.revertChange", - "when": "originalResourceScheme == git" + "when": "originalResourceScheme == gitfs" } ] }, diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 465281f62fc..3636d26b491 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -14,7 +14,7 @@ import { CommitOptions, ForcePushMode, Git, Stash } from './git'; import { Model } from './model'; import { Repository, Resource, ResourceGroupType } from './repository'; import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging'; -import { fromGitUri, toGitUri } from './uri'; +import { fromGitUri, toGitUri, isGitUri } from './uri'; import { grep, isDescendant, pathEquals } from './util'; const localize = nls.loadMessageBundle(); @@ -692,7 +692,7 @@ export class CommandCenter { let uris: Uri[] | undefined; if (arg instanceof Uri) { - if (arg.scheme === 'git') { + if (isGitUri(arg)) { uris = [Uri.file(fromGitUri(arg).path)]; } else if (arg.scheme === 'file') { uris = [arg]; @@ -1117,7 +1117,7 @@ export class CommandCenter { const modifiedDocument = textEditor.document; const modifiedUri = modifiedDocument.uri; - if (modifiedUri.scheme !== 'git') { + if (!isGitUri(modifiedUri)) { return; } @@ -2454,7 +2454,7 @@ export class CommandCenter { return undefined; } - if (uri.scheme === 'git') { + if (isGitUri(uri)) { const { path } = fromGitUri(uri); uri = Uri.file(path); } diff --git a/extensions/git/src/uri.ts b/extensions/git/src/uri.ts index 9b40f428853..cacd9d4f940 100644 --- a/extensions/git/src/uri.ts +++ b/extensions/git/src/uri.ts @@ -11,6 +11,10 @@ export interface GitUriParams { submoduleOf?: string; } +export function isGitUri(uri: Uri): boolean { + return /^git(fs)?$/.test(uri.scheme); +} + export function fromGitUri(uri: Uri): GitUriParams { return JSON.parse(uri.query); } From bd8425fa9b6427dca51e882d39b9ae764c55e08c Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 20 Nov 2019 09:36:24 +0100 Subject: [PATCH 096/246] Sampe height as QuickOpen (fixes #85147) --- src/vs/workbench/browser/parts/quickinput/quickInput.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index bc24b962426..fb9eca271bb 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -1528,7 +1528,7 @@ export class QuickInputService extends Component implements IQuickInputService { style.marginLeft = '-' + (width / 2) + 'px'; this.ui.inputBox.layout(); - this.ui.list.layout(this.dimension && this.dimension.height * 0.6); + this.ui.list.layout(this.dimension && this.dimension.height * 0.4); } } From fb2f985aa9cd1c9ee74bbac24a2dc467bee7b77a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Nov 2019 10:12:24 +0100 Subject: [PATCH 097/246] git.publish should not ask if only one remote related to #84697 --- extensions/git/src/commands.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 3636d26b491..46e13bd0b8a 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2162,9 +2162,14 @@ export class CommandCenter { return; } + const branchName = repository.HEAD && repository.HEAD.name || ''; + + if (remotes.length === 1) { + return await repository.pushTo(remotes[0].name, branchName, true); + } + const addRemote = new AddRemoteItem(this); const picks = [...repository.remotes.map(r => ({ label: r.name, description: r.pushUrl })), addRemote]; - const branchName = repository.HEAD && repository.HEAD.name || ''; const placeHolder = localize('pick remote', "Pick a remote to publish the branch '{0}' to:", branchName); const choice = await window.showQuickPick(picks, { placeHolder }); From 0167ec6768fa2c96b310c694a1e8e2389b9b7260 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Nov 2019 10:32:14 +0100 Subject: [PATCH 098/246] fixes #85188 --- src/vs/base/browser/ui/list/listView.ts | 6 +++++- src/vs/base/browser/ui/list/listWidget.ts | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 8280f4e05c4..73a183dfd2b 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -856,7 +856,11 @@ export class ListView implements ISpliceable, IDisposable { if (feedback[0] === -1) { // entire list feedback DOM.addClass(this.domNode, 'drop-target'); - this.currentDragFeedbackDisposable = toDisposable(() => DOM.removeClass(this.domNode, 'drop-target')); + DOM.addClass(this.rowsContainer, 'drop-target'); + this.currentDragFeedbackDisposable = toDisposable(() => { + DOM.removeClass(this.domNode, 'drop-target'); + DOM.removeClass(this.rowsContainer, 'drop-target'); + }); } else { for (const index of feedback) { const item = this.items[index]!; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 30dde3e1f90..417998fd379 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -796,6 +796,7 @@ export class DefaultStyleController implements IStyleController { if (styles.listDropBackground) { content.push(` .monaco-list${suffix}.drop-target, + .monaco-list${suffix} .monaco-list-rows.drop-target, .monaco-list${suffix} .monaco-list-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; } `); } From e82dd213dde5a1b042d6c2e65dd7bd49345c683b Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 20 Nov 2019 09:33:14 +0000 Subject: [PATCH 099/246] Make typescript problem match watch patterns less locale specific Improves #46373 --- extensions/typescript-language-features/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index b3211985319..13e2f020be3 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -859,10 +859,10 @@ "background": { "activeOnStart": true, "beginsPattern": { - "regexp": "^\\s*(?:message TS6032:|\\[?\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM| a\\.m\\.| p\\.m\\.)?(?:\\]| -)) File change detected\\. Starting incremental compilation\\.\\.\\." + "regexp": "^\\s*(?:message TS6032:|\\[?\\D*\\d{1,2}:\\d{1,2}:\\d{1,2}\\D*(?:\\]| -)) File change detected\\. Starting incremental compilation\\.\\.\\." }, "endsPattern": { - "regexp": "^\\s*(?:message TS6042:|\\[?\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM| a\\.m\\.| p\\.m\\.)?(?:\\]| -)) (?:Compilation complete\\.|Found \\d+ errors?\\.) Watching for file changes\\." + "regexp": "^\\s*(?:message TS6042:|\\[?\\D*\\d{1,2}:\\d{1,2}:\\d{1,2}\\D*(?:\\]| -)) (?:Compilation complete\\.|Found \\d+ errors?\\.) Watching for file changes\\." } } } From 54421ea4faa6b99699b2be0c5a2194f8a970030d Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 20 Nov 2019 09:46:24 +0000 Subject: [PATCH 100/246] Update PHP grammar Fixes #85132 --- extensions/php/cgmanifest.json | 4 +-- extensions/php/syntaxes/php.tmLanguage.json | 36 ++++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/extensions/php/cgmanifest.json b/extensions/php/cgmanifest.json index 71caf1a84da..bd31a070acd 100644 --- a/extensions/php/cgmanifest.json +++ b/extensions/php/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "language-php", "repositoryUrl": "https://github.com/atom/language-php", - "commitHash": "43a7b842fb58ac7561184db142344a1b127e0d52" + "commitHash": "b95dc79f30084c25547397ab41388af154e69895" } }, "license": "MIT", - "version": "0.44.2" + "version": "0.44.3" } ], "version": 1 diff --git a/extensions/php/syntaxes/php.tmLanguage.json b/extensions/php/syntaxes/php.tmLanguage.json index 2b9370f917c..fef8a2da2a6 100644 --- a/extensions/php/syntaxes/php.tmLanguage.json +++ b/extensions/php/syntaxes/php.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-php/commit/43a7b842fb58ac7561184db142344a1b127e0d52", + "version": "https://github.com/atom/language-php/commit/b95dc79f30084c25547397ab41388af154e69895", "scopeName": "source.php", "patterns": [ { @@ -439,22 +439,25 @@ "name": "keyword.control.exception.php" }, { - "begin": "(?i)\\b(function)\\s*(?=\\()", + "begin": "(?i)\\b(function)\\s*(?=&?\\s*\\()", "beginCaptures": { "1": { "name": "storage.type.function.php" } }, - "end": "(?={)", + "end": "(?=\\s*{)", "name": "meta.function.closure.php", "patterns": [ { "include": "#comments" }, { - "begin": "\\(", + "begin": "(&)?\\s*(\\()", "beginCaptures": { - "0": { + "1": { + "name": "storage.modifier.reference.php" + }, + "2": { "name": "punctuation.definition.parameters.begin.bracket.round.php" } }, @@ -504,11 +507,25 @@ "name": "meta.function.closure.use.php" } ] + }, + { + "match": "(:)\\s*(\\?)?\\s*((?:\\\\?[a-zA-Z_\\x{7f}-\\x{7fffffff}][a-zA-Z0-9_\\x{7f}-\\x{7fffffff}]*)+)", + "captures": { + "1": { + "name": "keyword.operator.return-value.php" + }, + "2": { + "name": "keyword.operator.nullable-type.php" + }, + "3": { + "name": "storage.type.php" + } + } } ] }, { - "begin": "(?x)\n((?:(?:final|abstract|public|private|protected|static)\\s+)*)\n(function)\\s+\n(?i:\n (__(?:call|construct|debugInfo|destruct|get|set|isset|unset|toString|\n clone|set_state|sleep|wakeup|autoload|invoke|callStatic))\n |([a-zA-Z_\\x{7f}-\\x{7fffffff}][a-zA-Z0-9_\\x{7f}-\\x{7fffffff}]*)\n)\n\\s*(\\()", + "begin": "(?x)\n((?:(?:final|abstract|public|private|protected|static)\\s+)*)\n(function)\\s+\n(?i:\n (__(?:call|construct|debugInfo|destruct|get|set|isset|unset|toString|\n clone|set_state|sleep|wakeup|autoload|invoke|callStatic))\n |(?:(&)?\\s*([a-zA-Z_\\x{7f}-\\x{7fffffff}][a-zA-Z0-9_\\x{7f}-\\x{7fffffff}]*))\n)\n\\s*(\\()", "beginCaptures": { "1": { "patterns": [ @@ -525,14 +542,17 @@ "name": "support.function.magic.php" }, "4": { - "name": "entity.name.function.php" + "name": "storage.modifier.reference.php" }, "5": { + "name": "entity.name.function.php" + }, + "6": { "name": "punctuation.definition.parameters.begin.bracket.round.php" } }, "contentName": "meta.function.parameters.php", - "end": "(\\))(?:\\s*(:)\\s*(\\?)?\\s*([a-zA-Z_\\x{7f}-\\x{7fffffff}][a-zA-Z0-9_\\x{7f}-\\x{7fffffff}]*))?", + "end": "(\\))(?:\\s*(:)\\s*(\\?)?\\s*((?:\\\\?[a-zA-Z_\\x{7f}-\\x{7fffffff}][a-zA-Z0-9_\\x{7f}-\\x{7fffffff}]*)+))?", "endCaptures": { "1": { "name": "punctuation.definition.parameters.end.bracket.round.php" From 5fdfdd7e6c8021b6f0413b50729b6b9eae937767 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Nov 2019 10:50:48 +0100 Subject: [PATCH 101/246] git: use querystring instead of JSON for uris --- extensions/git/src/uri.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/git/src/uri.ts b/extensions/git/src/uri.ts index cacd9d4f940..e2dc44088bd 100644 --- a/extensions/git/src/uri.ts +++ b/extensions/git/src/uri.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Uri } from 'vscode'; +import * as qs from 'querystring'; export interface GitUriParams { path: string; @@ -16,7 +17,7 @@ export function isGitUri(uri: Uri): boolean { } export function fromGitUri(uri: Uri): GitUriParams { - return JSON.parse(uri.query); + return qs.parse(uri.query) as any; } export interface GitUriOptions { @@ -48,6 +49,6 @@ export function toGitUri(uri: Uri, ref: string, options: GitUriOptions = {}): Ur return uri.with({ scheme: 'gitfs', path, - query: JSON.stringify(params) + query: qs.stringify(params as any) }); } From ec8de4eb45198dba5044a5206870ffd25e4c7ab3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Nov 2019 10:53:20 +0100 Subject: [PATCH 102/246] git: better uri parsing error message --- extensions/git/src/uri.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/uri.ts b/extensions/git/src/uri.ts index e2dc44088bd..3ef84a642f7 100644 --- a/extensions/git/src/uri.ts +++ b/extensions/git/src/uri.ts @@ -17,7 +17,21 @@ export function isGitUri(uri: Uri): boolean { } export function fromGitUri(uri: Uri): GitUriParams { - return qs.parse(uri.query) as any; + const result = qs.parse(uri.query) as any; + + if (!result) { + throw new Error('Invalid git URI: empty query'); + } + + if (typeof result.path !== 'string') { + throw new Error('Invalid git URI: missing path'); + } + + if (typeof result.ref !== 'string') { + throw new Error('Invalid git URI: missing ref'); + } + + return result; } export interface GitUriOptions { From ea785bceb54e2839815d3e475610dbe2b9426f3f Mon Sep 17 00:00:00 2001 From: rzj17 Date: Wed, 20 Nov 2019 10:12:23 +0000 Subject: [PATCH 103/246] Correct spelling of Implementation --- src/vs/editor/common/config/editorOptions.ts | 12 ++++++------ src/vs/editor/contrib/gotoSymbol/goToCommands.ts | 2 +- src/vs/monaco.d.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index ac610aa5626..4b170409e03 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1307,7 +1307,7 @@ export interface IGotoLocationOptions { multipleDefinitions?: GoToLocationValues; multipleTypeDefinitions?: GoToLocationValues; multipleDeclarations?: GoToLocationValues; - multipleImplemenations?: GoToLocationValues; + multipleImplementations?: GoToLocationValues; multipleReferences?: GoToLocationValues; } @@ -1321,7 +1321,7 @@ class EditorGoToLocation extends BaseEditorOption(input.multipleDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleTypeDefinitions: input.multipleTypeDefinitions ?? EditorStringEnumOption.stringSet(input.multipleTypeDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleDeclarations: input.multipleDeclarations ?? EditorStringEnumOption.stringSet(input.multipleDeclarations, 'peek', ['peek', 'gotoAndPeek', 'goto']), - multipleImplemenations: input.multipleImplemenations ?? EditorStringEnumOption.stringSet(input.multipleImplemenations, 'peek', ['peek', 'gotoAndPeek', 'goto']), + multipleImplementations: input.multipleImplementations ?? EditorStringEnumOption.stringSet(input.multipleImplementations, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleReferences: input.multipleReferences ?? EditorStringEnumOption.stringSet(input.multipleReferences, 'peek', ['peek', 'gotoAndPeek', 'goto']), }; } diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index ed7fe586b9c..3e5a95069d9 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -483,7 +483,7 @@ class ImplementationAction extends SymbolNavigationAction { } protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { - return editor.getOption(EditorOption.gotoLocation).multipleImplemenations; + return editor.getOption(EditorOption.gotoLocation).multipleImplementations; } } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 0a507e809e2..60982c5b34b 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3073,7 +3073,7 @@ declare namespace monaco.editor { multipleDefinitions?: GoToLocationValues; multipleTypeDefinitions?: GoToLocationValues; multipleDeclarations?: GoToLocationValues; - multipleImplemenations?: GoToLocationValues; + multipleImplementations?: GoToLocationValues; multipleReferences?: GoToLocationValues; } From 9dcd33183cd7c7057f816c013336d8b087472f80 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 20 Nov 2019 10:26:13 +0000 Subject: [PATCH 104/246] Finalize tree view title API #84466 --- src/vs/vscode.d.ts | 6 ++++++ src/vs/vscode.proposed.d.ts | 8 -------- src/vs/workbench/api/common/extHostTreeViews.ts | 2 -- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 8f254f8dec9..555187ec1d0 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -7287,6 +7287,12 @@ declare module 'vscode' { */ message?: string; + /** + * The tree view title is initially taken from the extension package.json + * Changes to the title property will be properly reflected in the UI in the title of the view. + */ + title?: string; + /** * Reveals the given element in the tree view. * If the tree view is not visible then the tree view is shown and element is revealed. diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index b31d3ae366d..19bfef3caba 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1076,14 +1076,6 @@ declare module 'vscode' { //#region Tree View - export interface TreeView { - /** - * The tree view title is initially taken from the extension package.json - * Changes to the title property will be properly reflected in the UI in the title of the view. - */ - title?: string; - } - /** * Label describing the [Tree item](#TreeItem) */ diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 15e80858930..54cc945d6ae 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -93,12 +93,10 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { get onDidChangeVisibility() { return treeView.onDidChangeVisibility; }, get message() { return treeView.message; }, set message(message: string) { - checkProposedApiEnabled(extension); treeView.message = message; }, get title() { return treeView.title; }, set title(title: string) { - checkProposedApiEnabled(extension); treeView.title = title; }, reveal: (element: T, options?: IRevealOptions): Promise => { From 05e62bb84724f2a5ce115db3086ad454925250d5 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Nov 2019 12:40:00 +0100 Subject: [PATCH 105/246] fix extensions watch build: should also copy non-ts files --- build/gulpfile.extensions.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index ca28c413a71..25cccf6d8e5 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -109,7 +109,8 @@ const tasks = compilations.map(function (tsconfigFile) { const compileTask = task.define(`compile-extension:${name}`, task.series(cleanTask, () => { const pipeline = createPipeline(false, true); - const input = pipeline.tsProjectSrc(); + const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); + const input = es.merge(nonts, pipeline.tsProjectSrc()); return input .pipe(pipeline()) @@ -118,7 +119,8 @@ const tasks = compilations.map(function (tsconfigFile) { const watchTask = task.define(`watch-extension:${name}`, task.series(cleanTask, () => { const pipeline = createPipeline(false); - const input = pipeline.tsProjectSrc(); + const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); + const input = es.merge(nonts, pipeline.tsProjectSrc()); const watchInput = watcher(src, { ...srcOpts, ...{ readDelay: 200 } }); return watchInput @@ -128,7 +130,8 @@ const tasks = compilations.map(function (tsconfigFile) { const compileBuildTask = task.define(`compile-build-extension-${name}`, task.series(cleanTask, () => { const pipeline = createPipeline(true, true); - const input = pipeline.tsProjectSrc(); + const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); + const input = es.merge(nonts, pipeline.tsProjectSrc()); return input .pipe(pipeline()) From 1b3ccd3ec979f1d343f24bc78f5e89722059e93f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Nov 2019 12:48:31 +0100 Subject: [PATCH 106/246] editors - move a bit of auto saving into the editor service --- .../browser/parts/editor/textDiffEditor.ts | 7 +-- .../browser/parts/editor/textEditor.ts | 33 ++++---------- .../parts/editor/textResourceEditor.ts | 10 +---- .../files/browser/editors/textFileEditor.ts | 6 +-- .../files/electron-browser/textFileEditor.ts | 4 +- .../contrib/output/browser/logViewer.ts | 6 +-- .../contrib/output/browser/outputPanel.ts | 2 +- .../preferences/browser/preferencesEditor.ts | 6 +-- .../services/editor/browser/editorService.ts | 45 +++++++++++++++++-- 9 files changed, 59 insertions(+), 60 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 1df88430dc7..54242a90fc4 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -19,7 +19,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; +import { TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; import { ScrollType, IDiffEditorViewState, IDiffEditorModel } from 'vs/editor/common/editorCommon'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -29,7 +29,6 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/editor'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; @@ -52,12 +51,10 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { @IEditorService editorService: IEditorService, @IThemeService themeService: IThemeService, @IEditorGroupsService editorGroupService: IEditorGroupsService, - @ITextFileService textFileService: ITextFileService, - @IHostService hostService: IHostService, @IClipboardService private clipboardService: IClipboardService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { - super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService, hostService, filesConfigurationService); + super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService, filesConfigurationService); } protected getEditorMemento(editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento { diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index 7bf208f3877..2df36074f61 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -16,14 +16,12 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { isDiffEditor, isCodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; const TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'textEditorViewState'; @@ -51,10 +49,8 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { @IStorageService storageService: IStorageService, @ITextResourceConfigurationService private readonly _configurationService: ITextResourceConfigurationService, @IThemeService protected themeService: IThemeService, - @ITextFileService private readonly _textFileService: ITextFileService, @IEditorService protected editorService: IEditorService, @IEditorGroupsService protected editorGroupService: IEditorGroupsService, - @IHostService private readonly hostService: IHostService, @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService ) { super(id, telemetryService, themeService, storageService); @@ -76,10 +72,6 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { return this._configurationService; } - protected get textFileService(): ITextFileService { - return this._textFileService; - } - protected handleConfigurationChangeEvent(configuration?: IEditorConfiguration): void { if (this.isVisible()) { this.updateEditorConfiguration(configuration); @@ -138,8 +130,8 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { // Model & Language changes const codeEditor = getCodeEditor(this.editorControl); if (codeEditor) { - this._register(codeEditor.onDidChangeModelLanguage(e => this.updateEditorConfiguration())); - this._register(codeEditor.onDidChangeModel(e => this.updateEditorConfiguration())); + this._register(codeEditor.onDidChangeModelLanguage(() => this.updateEditorConfiguration())); + this._register(codeEditor.onDidChangeModel(() => this.updateEditorConfiguration())); } // Application & Editor focus change to respect auto save settings @@ -149,32 +141,23 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { this._register(this.editorControl.getOriginalEditor().onDidBlurEditorWidget(() => this.onEditorFocusLost())); this._register(this.editorControl.getModifiedEditor().onDidBlurEditorWidget(() => this.onEditorFocusLost())); } - - this._register(this.editorService.onDidActiveEditorChange(() => this.onEditorFocusLost())); - this._register(this.hostService.onDidChangeFocus(focused => this.onWindowFocusChange(focused))); } private onEditorFocusLost(): void { this.maybeTriggerSaveAll(SaveReason.FOCUS_CHANGE); } - private onWindowFocusChange(focused: boolean): void { - if (!focused) { - this.maybeTriggerSaveAll(SaveReason.WINDOW_CHANGE); - } - } - private maybeTriggerSaveAll(reason: SaveReason): void { const mode = this.filesConfigurationService.getAutoSaveMode(); // Determine if we need to save all. In case of a window focus change we also save if auto save mode // is configured to be ON_FOCUS_CHANGE (editor focus change) - if ( - (reason === SaveReason.WINDOW_CHANGE && (mode === AutoSaveMode.ON_FOCUS_CHANGE || mode === AutoSaveMode.ON_WINDOW_CHANGE)) || - (reason === SaveReason.FOCUS_CHANGE && mode === AutoSaveMode.ON_FOCUS_CHANGE) - ) { - if (this.textFileService.isDirty()) { - this.textFileService.saveAll(undefined, { reason }); + if (reason === SaveReason.FOCUS_CHANGE && mode === AutoSaveMode.ON_FOCUS_CHANGE) { + const input = this.input; + const group = this.group; + + if (input?.isDirty() && group) { + this.editorService.save([{ groupId: group.id, editor: input }], { reason }); } } } diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index 3d13bbc322b..aee0f7187fe 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -16,13 +16,11 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { Event } from 'vs/base/common/event'; import { ScrollType, IEditor } from 'vs/editor/common/editorCommon'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; /** @@ -39,12 +37,10 @@ export class AbstractTextResourceEditor extends BaseTextEditor { @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, @IThemeService themeService: IThemeService, @IEditorGroupsService editorGroupService: IEditorGroupsService, - @ITextFileService textFileService: ITextFileService, @IEditorService editorService: IEditorService, - @IHostService hostService: IHostService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { - super(id, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService, hostService, filesConfigurationService); + super(id, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService, filesConfigurationService); } getTitle(): string | undefined { @@ -192,12 +188,10 @@ export class TextResourceEditor extends AbstractTextResourceEditor { @IStorageService storageService: IStorageService, @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, @IThemeService themeService: IThemeService, - @ITextFileService textFileService: ITextFileService, @IEditorService editorService: IEditorService, @IEditorGroupsService editorGroupService: IEditorGroupsService, - @IHostService hostService: IHostService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { - super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, textFileService, editorService, hostService, filesConfigurationService); + super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, editorService, filesConfigurationService); } } diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index 1015e57032c..3da672cc90e 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -24,7 +24,6 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ScrollType } from 'vs/editor/common/editorCommon'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -55,12 +54,11 @@ export class TextFileEditor extends BaseTextEditor { @IEditorService editorService: IEditorService, @IThemeService themeService: IThemeService, @IEditorGroupsService editorGroupService: IEditorGroupsService, - @ITextFileService textFileService: ITextFileService, - @IHostService hostService: IHostService, + @ITextFileService private readonly textFileService: ITextFileService, @IExplorerService private readonly explorerService: IExplorerService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { - super(TextFileEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService, hostService, filesConfigurationService); + super(TextFileEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService, filesConfigurationService); this.updateRestoreViewStateConfiguration(); diff --git a/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts b/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts index 2afebd9cbe2..dabb58411e1 100644 --- a/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts @@ -21,7 +21,6 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { IElectronService } from 'vs/platform/electron/node/electron'; @@ -46,11 +45,10 @@ export class NativeTextFileEditor extends TextFileEditor { @ITextFileService textFileService: ITextFileService, @IElectronService private readonly electronService: IElectronService, @IPreferencesService private readonly preferencesService: IPreferencesService, - @IHostService hostService: IHostService, @IExplorerService explorerService: IExplorerService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { - super(telemetryService, fileService, viewletService, instantiationService, contextService, storageService, configurationService, editorService, themeService, editorGroupService, textFileService, hostService, explorerService, filesConfigurationService); + super(telemetryService, fileService, viewletService, instantiationService, contextService, storageService, configurationService, editorService, themeService, editorGroupService, textFileService, explorerService, filesConfigurationService); } protected handleSetInputError(error: Error, input: FileEditorInput, options: EditorOptions | undefined): void { diff --git a/src/vs/workbench/contrib/output/browser/logViewer.ts b/src/vs/workbench/contrib/output/browser/logViewer.ts index 4f192347a5d..90e2c3c0f71 100644 --- a/src/vs/workbench/contrib/output/browser/logViewer.ts +++ b/src/vs/workbench/contrib/output/browser/logViewer.ts @@ -11,7 +11,6 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { AbstractTextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { URI } from 'vs/base/common/uri'; @@ -19,7 +18,6 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { LOG_SCHEME, IFileOutputChannelDescriptor } from 'vs/workbench/contrib/output/common/output'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; export class LogViewerInput extends ResourceEditorInput { @@ -53,12 +51,10 @@ export class LogViewer extends AbstractTextResourceEditor { @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, @IThemeService themeService: IThemeService, @IEditorGroupsService editorGroupService: IEditorGroupsService, - @ITextFileService textFileService: ITextFileService, @IEditorService editorService: IEditorService, - @IHostService hostService: IHostService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { - super(LogViewer.LOG_VIEWER_EDITOR_ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, textFileService, editorService, hostService, filesConfigurationService); + super(LogViewer.LOG_VIEWER_EDITOR_ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, editorService, filesConfigurationService); } protected getConfigurationOverrides(): IEditorOptions { diff --git a/src/vs/workbench/contrib/output/browser/outputPanel.ts b/src/vs/workbench/contrib/output/browser/outputPanel.ts index 9ecc2c1d47c..2f7820fe1a3 100644 --- a/src/vs/workbench/contrib/output/browser/outputPanel.ts +++ b/src/vs/workbench/contrib/output/browser/outputPanel.ts @@ -48,7 +48,7 @@ export class OutputPanel extends AbstractTextResourceEditor { @IHostService hostService: IHostService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { - super(OUTPUT_PANEL_ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, textFileService, editorService, hostService, filesConfigurationService); + super(OUTPUT_PANEL_ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, editorService, filesConfigurationService); this.scopedInstantiationService = instantiationService; } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index 77284a59676..0877e6773d3 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -52,8 +52,6 @@ import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor import { IFilterResult, IPreferencesService, ISetting, ISettingsEditorModel, ISettingsGroup, SettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { DefaultSettingsEditorModel, SettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; import { withNullAsUndefined, withUndefinedAsNull, assertIsDefined } from 'vs/base/common/types'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; @@ -979,13 +977,11 @@ export class DefaultPreferencesEditor extends BaseTextEditor { @IStorageService storageService: IStorageService, @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, @IThemeService themeService: IThemeService, - @ITextFileService textFileService: ITextFileService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @IEditorService editorService: IEditorService, - @IHostService hostService: IHostService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { - super(DefaultPreferencesEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService, hostService, filesConfigurationService); + super(DefaultPreferencesEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService, filesConfigurationService); } private static _getContributions(): IEditorContributionDescription[] { diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index a24a906db1f..7659b3a6f6a 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -5,7 +5,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IResourceInput, ITextEditorOptions, IEditorOptions, EditorActivation } from 'vs/platform/editor/common/editor'; -import { IEditorInput, IEditor, GroupIdentifier, IFileEditorInput, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IFileInputFactory, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditor, ITextDiffEditor, ITextSideBySideEditor, toResource, SideBySideEditor, IRevertOptions } from 'vs/workbench/common/editor'; +import { IEditorInput, IEditor, GroupIdentifier, IFileEditorInput, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IFileInputFactory, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditor, ITextDiffEditor, ITextSideBySideEditor, toResource, SideBySideEditor, IRevertOptions, SaveReason } from 'vs/workbench/common/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { DataUriEditorInput } from 'vs/workbench/common/editor/dataUriEditorInput'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -28,6 +28,8 @@ import { IEditorGroupView, IEditorOpeningEvent, EditorServiceImpl } from 'vs/wor import { ILabelService } from 'vs/platform/label/common/label'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; type CachedEditorInput = ResourceEditorInput | IFileEditorInput | DataUriEditorInput; type OpenInEditorGroup = IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE; @@ -66,7 +68,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { @IInstantiationService private readonly instantiationService: IInstantiationService, @ILabelService private readonly labelService: ILabelService, @IFileService private readonly fileService: IFileService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService, + @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, + @IHostService private readonly hostService: IHostService ) { super(); @@ -76,9 +80,38 @@ export class EditorService extends Disposable implements EditorServiceImpl { } private registerListeners(): void { + + // Editor & group changes this.editorGroupService.whenRestored.then(() => this.onEditorsRestored()); this.editorGroupService.onDidActiveGroupChange(group => this.handleActiveEditorChange(group)); this.editorGroupService.onDidAddGroup(group => this.registerGroupListeners(group as IEditorGroupView)); + + // Auto save support + this._register(this.onDidActiveEditorChange(() => this.onEditorFocusLost())); + this._register(this.hostService.onDidChangeFocus(focused => this.onWindowFocusChange(focused))); + } + + private onEditorFocusLost(): void { + this.maybeTriggerSaveAll(SaveReason.FOCUS_CHANGE); + } + + private onWindowFocusChange(focused: boolean): void { + if (!focused) { + this.maybeTriggerSaveAll(SaveReason.WINDOW_CHANGE); + } + } + + private maybeTriggerSaveAll(reason: SaveReason): void { + const mode = this.filesConfigurationService.getAutoSaveMode(); + + // Determine if we need to save all. In case of a window focus change we also save if auto save mode + // is configured to be ON_FOCUS_CHANGE (editor focus change) + if ( + (reason === SaveReason.WINDOW_CHANGE && (mode === AutoSaveMode.ON_FOCUS_CHANGE || mode === AutoSaveMode.ON_WINDOW_CHANGE)) || + (reason === SaveReason.FOCUS_CHANGE && mode === AutoSaveMode.ON_FOCUS_CHANGE) + ) { + this.saveAll({ reason }); + } } private onEditorsRestored(): void { @@ -776,7 +809,9 @@ export class DelegatingEditorService extends EditorService { @IInstantiationService instantiationService: IInstantiationService, @ILabelService labelService: ILabelService, @IFileService fileService: IFileService, - @IConfigurationService configurationService: IConfigurationService + @IConfigurationService configurationService: IConfigurationService, + @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, + @IHostService hostService: IHostService ) { super( editorGroupService, @@ -784,7 +819,9 @@ export class DelegatingEditorService extends EditorService { instantiationService, labelService, fileService, - configurationService + configurationService, + filesConfigurationService, + hostService ); } From 14cc75aa22f870e1348c265b7e4f6ddc1152ed49 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Nov 2019 12:53:27 +0100 Subject: [PATCH 107/246] git: extract ipc --- extensions/git/src/askpass-main.ts | 37 ++-------- extensions/git/src/askpass.ts | 98 +++++--------------------- extensions/git/src/ipc/ipcClient.ts | 35 ++++++++++ extensions/git/src/ipc/ipcServer.ts | 103 ++++++++++++++++++++++++++++ extensions/git/src/main.ts | 19 ++++- 5 files changed, 178 insertions(+), 114 deletions(-) create mode 100644 extensions/git/src/ipc/ipcClient.ts create mode 100644 extensions/git/src/ipc/ipcServer.ts diff --git a/extensions/git/src/askpass-main.ts b/extensions/git/src/askpass-main.ts index c08aa2a0afc..066ff536683 100644 --- a/extensions/git/src/askpass-main.ts +++ b/extensions/git/src/askpass-main.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as http from 'http'; import * as fs from 'fs'; import * as nls from 'vscode-nls'; +import { IPCClient } from './ipc/ipcClient'; const localize = nls.loadMessageBundle(); @@ -36,37 +36,12 @@ function main(argv: string[]): void { const socketPath = process.env['VSCODE_GIT_ASKPASS_HANDLE'] as string; const request = argv[2]; const host = argv[4].substring(1, argv[4].length - 2); - const opts: http.RequestOptions = { - socketPath, - path: '/', - method: 'POST' - }; + const ipcClient = new IPCClient('askpass', socketPath); - const req = http.request(opts, res => { - if (res.statusCode !== 200) { - return fatal(`Bad status code: ${res.statusCode}`); - } - - const chunks: string[] = []; - res.setEncoding('utf8'); - res.on('data', (d: string) => chunks.push(d)); - res.on('end', () => { - const raw = chunks.join(''); - - try { - const result = JSON.parse(raw); - fs.writeFileSync(output, result + '\n'); - } catch (err) { - return fatal(`Error parsing response`); - } - - setTimeout(() => process.exit(0), 0); - }); - }); - - req.on('error', () => fatal('Error in request')); - req.write(JSON.stringify({ request, host })); - req.end(); + ipcClient.call({ request, host }).then(res => { + fs.writeFileSync(output, res + '\n'); + setTimeout(() => process.exit(0), 0); + }).catch(err => fatal(err)); } main(process.argv); diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index 03000c7290d..005179d8c0c 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -3,15 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, window, InputBoxOptions } from 'vscode'; -import { denodeify } from './util'; +import { window, InputBoxOptions } from 'vscode'; +import { IDisposable } from './util'; import * as path from 'path'; -import * as http from 'http'; -import * as os from 'os'; -import * as fs from 'fs'; -import * as crypto from 'crypto'; - -const randomBytes = denodeify(crypto.randomBytes); +import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; export interface AskpassEnvironment { GIT_ASKPASS: string; @@ -21,68 +16,21 @@ export interface AskpassEnvironment { VSCODE_GIT_ASKPASS_HANDLE?: string; } -function getIPCHandlePath(nonce: string): string { - if (process.platform === 'win32') { - return `\\\\.\\pipe\\vscode-git-askpass-${nonce}-sock`; +export class Askpass implements IIPCHandler { + + private disposable: IDisposable; + + static getDisabledEnv(): AskpassEnvironment { + return { + GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh') + }; } - if (process.env['XDG_RUNTIME_DIR']) { - return path.join(process.env['XDG_RUNTIME_DIR'] as string, `vscode-git-askpass-${nonce}.sock`); + constructor(private ipc: IIPCServer) { + this.disposable = ipc.registerHandler('askpass', this); } - return path.join(os.tmpdir(), `vscode-git-askpass-${nonce}.sock`); -} - -export class Askpass implements Disposable { - - private server: http.Server; - private ipcHandlePathPromise: Promise; - private ipcHandlePath: string | undefined; - private enabled = true; - - constructor() { - this.server = http.createServer((req, res) => this.onRequest(req, res)); - this.ipcHandlePathPromise = this.setup().catch(err => { - console.error(err); - return ''; - }); - } - - private async setup(): Promise { - const buffer = await randomBytes(20); - const nonce = buffer.toString('hex'); - const ipcHandlePath = getIPCHandlePath(nonce); - this.ipcHandlePath = ipcHandlePath; - - try { - this.server.listen(ipcHandlePath); - this.server.on('error', err => console.error(err)); - } catch (err) { - console.error('Could not launch git askpass helper.'); - this.enabled = false; - } - - return ipcHandlePath; - } - - private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void { - const chunks: string[] = []; - req.setEncoding('utf8'); - req.on('data', (d: string) => chunks.push(d)); - req.on('end', () => { - const { request, host } = JSON.parse(chunks.join('')); - - this.prompt(host, request).then(result => { - res.writeHead(200); - res.end(JSON.stringify(result)); - }, () => { - res.writeHead(500); - res.end(); - }); - }); - } - - private async prompt(host: string, request: string): Promise { + async handle({ request, host }: { request: string, host: string }): Promise { const options: InputBoxOptions = { password: /password/i.test(request), placeHolder: request, @@ -93,27 +41,17 @@ export class Askpass implements Disposable { return await window.showInputBox(options) || ''; } - async getEnv(): Promise { - if (!this.enabled) { - return { - GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh') - }; - } - + getEnv(): AskpassEnvironment { return { ELECTRON_RUN_AS_NODE: '1', GIT_ASKPASS: path.join(__dirname, 'askpass.sh'), VSCODE_GIT_ASKPASS_NODE: process.execPath, VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js'), - VSCODE_GIT_ASKPASS_HANDLE: await this.ipcHandlePathPromise + VSCODE_GIT_ASKPASS_HANDLE: this.ipc.ipcHandlePath }; } dispose(): void { - this.server.close(); - - if (this.ipcHandlePath && process.platform !== 'win32') { - fs.unlinkSync(this.ipcHandlePath); - } + this.disposable.dispose(); } -} \ No newline at end of file +} diff --git a/extensions/git/src/ipc/ipcClient.ts b/extensions/git/src/ipc/ipcClient.ts new file mode 100644 index 00000000000..4a26a74e8e3 --- /dev/null +++ b/extensions/git/src/ipc/ipcClient.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as http from 'http'; + +export class IPCClient { + + constructor(private handlerName: string, private ipcHandlePath: string) { } + + call(request: any): Promise { + const opts: http.RequestOptions = { + socketPath: this.ipcHandlePath, + path: `/${this.handlerName}`, + method: 'POST' + }; + + return new Promise((c, e) => { + const req = http.request(opts, res => { + if (res.statusCode !== 200) { + return e(new Error(`Bad status code: ${res.statusCode}`)); + } + + const chunks: Buffer[] = []; + res.on('data', d => chunks.push(d)); + res.on('end', () => c(JSON.parse(Buffer.concat(chunks).toString('utf8')))); + }); + + req.on('error', err => e(err)); + req.write(JSON.stringify(request)); + req.end(); + }); + } +} diff --git a/extensions/git/src/ipc/ipcServer.ts b/extensions/git/src/ipc/ipcServer.ts new file mode 100644 index 00000000000..7d29baf70f0 --- /dev/null +++ b/extensions/git/src/ipc/ipcServer.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vscode'; +import { denodeify, toDisposable } from '../util'; +import * as path from 'path'; +import * as http from 'http'; +import * as os from 'os'; +import * as fs from 'fs'; +import * as crypto from 'crypto'; + +const randomBytes = denodeify(crypto.randomBytes); + +function getIPCHandlePath(nonce: string): string { + if (process.platform === 'win32') { + return `\\\\.\\pipe\\vscode-git-ipc-${nonce}-sock`; + } + + if (process.env['XDG_RUNTIME_DIR']) { + return path.join(process.env['XDG_RUNTIME_DIR'] as string, `vscode-git-ipc-${nonce}.sock`); + } + + return path.join(os.tmpdir(), `vscode-git-ipc-${nonce}.sock`); +} + +export interface IIPCHandler { + handle(request: any): Promise; +} + +export async function createIPCServer(): Promise { + const server = http.createServer(); + const buffer = await randomBytes(20); + const nonce = buffer.toString('hex'); + const ipcHandlePath = getIPCHandlePath(nonce); + + return new Promise((c, e) => { + try { + server.listen(ipcHandlePath); + c(new IPCServer(server, ipcHandlePath)); + server.on('error', err => e(err)); + } catch (err) { + e(err); + } + }); +} + +export interface IIPCServer extends Disposable { + readonly ipcHandlePath: string | undefined; + registerHandler(name: string, handler: IIPCHandler): Disposable; +} + +class IPCServer implements IIPCServer, Disposable { + + private handlers = new Map(); + get ipcHandlePath(): string { return this._ipcHandlePath; } + + constructor(private server: http.Server, private _ipcHandlePath: string) { + this.server.on('request', this.onRequest.bind(this)); + } + + registerHandler(name: string, handler: IIPCHandler): Disposable { + this.handlers.set(`/${name}`, handler); + return toDisposable(() => this.handlers.delete(name)); + } + + private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void { + if (!req.url) { + console.warn(`Request lacks url`); + return; + } + + const handler = this.handlers.get(req.url); + + if (!handler) { + console.warn(`IPC handler for ${req.url} not found`); + return; + } + + const chunks: Buffer[] = []; + req.on('data', d => chunks.push(d)); + req.on('end', () => { + const request = JSON.parse(Buffer.concat(chunks).toString('utf8')); + handler.handle(request).then(result => { + res.writeHead(200); + res.end(JSON.stringify(result)); + }, () => { + res.writeHead(500); + res.end(); + }); + }); + } + + dispose(): void { + this.handlers.clear(); + this.server.close(); + + if (this._ipcHandlePath && process.platform !== 'win32') { + fs.unlinkSync(this._ipcHandlePath); + } + } +} diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 74b26975b20..5f237b28858 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -21,6 +21,7 @@ import { GitProtocolHandler } from './protocolHandler'; import { GitExtensionImpl } from './api/extension'; import * as path from 'path'; import * as fs from 'fs'; +import { createIPCServer } from './ipc/ipcServer'; const deactivateTasks: { (): Promise; }[] = []; @@ -33,10 +34,22 @@ export async function deactivate(): Promise { async function createModel(context: ExtensionContext, outputChannel: OutputChannel, telemetryReporter: TelemetryReporter, disposables: Disposable[]): Promise { const pathHint = workspace.getConfiguration('git').get('path'); const info = await findGit(pathHint, path => outputChannel.appendLine(localize('looking', "Looking for git in: {0}", path))); - const askpass = new Askpass(); - disposables.push(askpass); + let env: any = undefined; + + try { + const ipc = await createIPCServer(); + disposables.push(ipc); + + const askpass = new Askpass(ipc); + disposables.push(askpass); + env = askpass.getEnv(); + } catch { + env = Askpass.getDisabledEnv(); + } + + // TODO@joao remove + console.log(env); - const env = await askpass.getEnv(); const git = new Git({ gitPath: info.path, version: info.version, env }); const model = new Model(git, context.globalState, outputChannel); disposables.push(model); From 39c14fc431f438ec8c41d54783d517f439ea1de4 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Nov 2019 12:54:41 +0100 Subject: [PATCH 108/246] :lipstick: --- extensions/git/src/ipc/ipcServer.ts | 2 +- extensions/git/src/main.ts | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/extensions/git/src/ipc/ipcServer.ts b/extensions/git/src/ipc/ipcServer.ts index 7d29baf70f0..cf3c1b8b61e 100644 --- a/extensions/git/src/ipc/ipcServer.ts +++ b/extensions/git/src/ipc/ipcServer.ts @@ -37,9 +37,9 @@ export async function createIPCServer(): Promise { return new Promise((c, e) => { try { + server.on('error', err => e(err)); server.listen(ipcHandlePath); c(new IPCServer(server, ipcHandlePath)); - server.on('error', err => e(err)); } catch (err) { e(err); } diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 5f237b28858..35fbf87ddd3 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -47,9 +47,6 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann env = Askpass.getDisabledEnv(); } - // TODO@joao remove - console.log(env); - const git = new Git({ gitPath: info.path, version: info.version, env }); const model = new Model(git, context.globalState, outputChannel); disposables.push(model); From 29a2c568f1c27ac1c5d7bc4ad424cb2a1eb51b13 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Nov 2019 15:33:05 +0100 Subject: [PATCH 109/246] editors - put the auto save on focus lost observer into a central place --- .../browser/parts/editor/textEditor.ts | 35 ++-------------- .../services/editor/browser/editorService.ts | 41 +++++++++++++------ 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index 2df36074f61..738e69b193b 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -9,7 +9,7 @@ import { distinct, deepClone, assign } from 'vs/base/common/objects'; import { isObject, assertIsDefined, withNullAsUndefined, isFunction } from 'vs/base/common/types'; import { Dimension } from 'vs/base/browser/dom'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { EditorInput, EditorOptions, IEditorMemento, ITextEditor, SaveReason, TextEditorOptions } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, IEditorMemento, ITextEditor, TextEditorOptions } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditorViewState, IEditor, ScrollType } from 'vs/editor/common/editorCommon'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -18,11 +18,10 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { isDiffEditor, isCodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { isCodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; const TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'textEditorViewState'; @@ -50,8 +49,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { @ITextResourceConfigurationService private readonly _configurationService: ITextResourceConfigurationService, @IThemeService protected themeService: IThemeService, @IEditorService protected editorService: IEditorService, - @IEditorGroupsService protected editorGroupService: IEditorGroupsService, - @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService + @IEditorGroupsService protected editorGroupService: IEditorGroupsService ) { super(id, telemetryService, themeService, storageService); @@ -133,33 +131,6 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { this._register(codeEditor.onDidChangeModelLanguage(() => this.updateEditorConfiguration())); this._register(codeEditor.onDidChangeModel(() => this.updateEditorConfiguration())); } - - // Application & Editor focus change to respect auto save settings - if (isCodeEditor(this.editorControl)) { - this._register(this.editorControl.onDidBlurEditorWidget(() => this.onEditorFocusLost())); - } else if (isDiffEditor(this.editorControl)) { - this._register(this.editorControl.getOriginalEditor().onDidBlurEditorWidget(() => this.onEditorFocusLost())); - this._register(this.editorControl.getModifiedEditor().onDidBlurEditorWidget(() => this.onEditorFocusLost())); - } - } - - private onEditorFocusLost(): void { - this.maybeTriggerSaveAll(SaveReason.FOCUS_CHANGE); - } - - private maybeTriggerSaveAll(reason: SaveReason): void { - const mode = this.filesConfigurationService.getAutoSaveMode(); - - // Determine if we need to save all. In case of a window focus change we also save if auto save mode - // is configured to be ON_FOCUS_CHANGE (editor focus change) - if (reason === SaveReason.FOCUS_CHANGE && mode === AutoSaveMode.ON_FOCUS_CHANGE) { - const input = this.input; - const group = this.group; - - if (input?.isDirty() && group) { - this.editorService.save([{ groupId: group.id, editor: input }], { reason }); - } - } } /** diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 7659b3a6f6a..dbf66587513 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -38,21 +38,21 @@ export class EditorService extends Disposable implements EditorServiceImpl { _serviceBrand: undefined; - private static CACHE: ResourceMap = new ResourceMap(); + private static CACHE = new ResourceMap(); //#region events - private readonly _onDidActiveEditorChange: Emitter = this._register(new Emitter()); - readonly onDidActiveEditorChange: Event = this._onDidActiveEditorChange.event; + private readonly _onDidActiveEditorChange = this._register(new Emitter()); + readonly onDidActiveEditorChange = this._onDidActiveEditorChange.event; - private readonly _onDidVisibleEditorsChange: Emitter = this._register(new Emitter()); - readonly onDidVisibleEditorsChange: Event = this._onDidVisibleEditorsChange.event; + private readonly _onDidVisibleEditorsChange = this._register(new Emitter()); + readonly onDidVisibleEditorsChange = this._onDidVisibleEditorsChange.event; - private readonly _onDidCloseEditor: Emitter = this._register(new Emitter()); - readonly onDidCloseEditor: Event = this._onDidCloseEditor.event; + private readonly _onDidCloseEditor = this._register(new Emitter()); + readonly onDidCloseEditor = this._onDidCloseEditor.event; - private readonly _onDidOpenEditorFail: Emitter = this._register(new Emitter()); - readonly onDidOpenEditorFail: Event = this._onDidOpenEditorFail.event; + private readonly _onDidOpenEditorFail = this._register(new Emitter()); + readonly onDidOpenEditorFail = this._onDidOpenEditorFail.event; //#endregion @@ -61,6 +61,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { private lastActiveEditor: IEditorInput | null = null; private lastActiveGroupId: GroupIdentifier | null = null; + private lastActiveEditorControlDisposable = this._register(new DisposableStore()); constructor( @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -121,7 +122,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Fire initial set of editor events if there is an active editor if (this.activeEditor) { - this.doEmitActiveEditorChangeEvent(); + this.doHandleActiveEditorChangeEvent(); this._onDidVisibleEditorsChange.fire(); } } @@ -139,15 +140,29 @@ export class EditorService extends Disposable implements EditorServiceImpl { return; // ignore if the editor actually did not change } - this.doEmitActiveEditorChangeEvent(); + this.doHandleActiveEditorChangeEvent(); } - private doEmitActiveEditorChangeEvent(): void { - const activeGroup = this.editorGroupService.activeGroup; + private doHandleActiveEditorChangeEvent(): void { + // Remember as last active + const activeGroup = this.editorGroupService.activeGroup; this.lastActiveGroupId = activeGroup.id; this.lastActiveEditor = activeGroup.activeEditor; + // Dispose previous active control listeners + this.lastActiveEditorControlDisposable.clear(); + + // Listen to focus changes on control for auto save + const controlsToObserve: ICodeEditor[] = []; + if (isCodeEditor(this.activeTextEditorWidget)) { + controlsToObserve.push(this.activeTextEditorWidget); + } else if (isDiffEditor(this.activeTextEditorWidget)) { + controlsToObserve.push(this.activeTextEditorWidget.getOriginalEditor(), this.activeTextEditorWidget.getModifiedEditor()); + } + controlsToObserve.forEach(control => this.lastActiveEditorControlDisposable.add(control.onDidBlurEditorWidget(() => this.onEditorFocusLost()))); + + // Fire event to outside parties this._onDidActiveEditorChange.fire(); } From c780ebab8e7db3f9e9447dbb2067c420e51d4a9f Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 20 Nov 2019 15:34:33 +0100 Subject: [PATCH 110/246] Use   instead of   to support XHTML (fixes microsoft/monaco-editor#1492) --- src/vs/base/browser/ui/inputbox/inputBox.ts | 4 +-- .../editor/browser/config/charWidthReader.ts | 2 +- src/vs/editor/browser/widget/diffReview.ts | 6 ++-- .../common/modes/textToHtmlTokenizer.ts | 4 +-- .../editor/contrib/codelens/codelensWidget.ts | 4 +-- .../common/modes/textToHtmlTokenizer.test.ts | 36 +++++++++---------- 6 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 28d20f9f371..29b1aa58e9d 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -171,7 +171,7 @@ export class InputBox extends Widget { this.maxHeight = typeof this.options.flexibleMaxHeight === 'number' ? this.options.flexibleMaxHeight : Number.POSITIVE_INFINITY; this.mirror = dom.append(wrapper, $('div.mirror')); - this.mirror.innerHTML = ' '; + this.mirror.innerHTML = ' '; this.scrollableElement = new ScrollableElement(this.element, { vertical: ScrollbarVisibility.Auto }); @@ -529,7 +529,7 @@ export class InputBox extends Widget { if (mirrorTextContent) { this.mirror.textContent = value + suffix; } else { - this.mirror.innerHTML = ' '; + this.mirror.innerHTML = ' '; } this.layout(); diff --git a/src/vs/editor/browser/config/charWidthReader.ts b/src/vs/editor/browser/config/charWidthReader.ts index 21a54663412..479a35c7bea 100644 --- a/src/vs/editor/browser/config/charWidthReader.ts +++ b/src/vs/editor/browser/config/charWidthReader.ts @@ -124,7 +124,7 @@ class DomCharWidthReader { private static _render(testElement: HTMLElement, request: CharWidthRequest): void { if (request.chr === ' ') { - let htmlString = ' '; + let htmlString = ' '; // Repeat character 256 (2^8) times for (let i = 0; i < 8; i++) { htmlString += htmlString; diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index 8a915a659ff..b0437b6d47f 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -695,7 +695,7 @@ export class DiffReview extends Disposable { if (originalLine !== 0) { originalLineNumber.appendChild(document.createTextNode(String(originalLine))); } else { - originalLineNumber.innerHTML = ' '; + originalLineNumber.innerHTML = ' '; } cell.appendChild(originalLineNumber); @@ -707,13 +707,13 @@ export class DiffReview extends Disposable { if (modifiedLine !== 0) { modifiedLineNumber.appendChild(document.createTextNode(String(modifiedLine))); } else { - modifiedLineNumber.innerHTML = ' '; + modifiedLineNumber.innerHTML = ' '; } cell.appendChild(modifiedLineNumber); const spacer = document.createElement('span'); spacer.className = spacerClassName; - spacer.innerHTML = '  '; + spacer.innerHTML = '  '; cell.appendChild(spacer); let lineContent: string; diff --git a/src/vs/editor/common/modes/textToHtmlTokenizer.ts b/src/vs/editor/common/modes/textToHtmlTokenizer.ts index e98f2b37f19..c8c4acac430 100644 --- a/src/vs/editor/common/modes/textToHtmlTokenizer.ts +++ b/src/vs/editor/common/modes/textToHtmlTokenizer.ts @@ -46,7 +46,7 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize; tabsCharDelta += insertSpacesCount - 1; while (insertSpacesCount > 0) { - partContent += useNbsp ? ' ' : ' '; + partContent += useNbsp ? ' ' : ' '; insertSpacesCount--; } break; @@ -78,7 +78,7 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens break; case CharCode.Space: - partContent += useNbsp ? ' ' : ' '; + partContent += useNbsp ? ' ' : ' '; break; default: diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index 7470178f85d..a0bde211451 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -96,7 +96,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { innerHtml += `${title}`; } if (i + 1 < lenses.length) { - innerHtml += ' | '; + innerHtml += ' | '; } } } @@ -108,7 +108,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { } else { // symbols and commands if (!innerHtml) { - innerHtml = ' '; + innerHtml = ' '; } this._domNode.innerHTML = innerHtml; if (this._isEmpty && animate) { diff --git a/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts b/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts index 77f96fc20af..fb4c4028beb 100644 --- a/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts +++ b/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts @@ -109,9 +109,9 @@ suite('Editor Modes - textToHtmlTokenizer', () => { [ '
', 'Ciao', - ' ', + ' ', 'hello', - ' ', + ' ', 'world!', '
' ].join('') @@ -122,9 +122,9 @@ suite('Editor Modes - textToHtmlTokenizer', () => { [ '
', 'Ciao', - ' ', + ' ', 'hello', - ' ', + ' ', 'w', '
' ].join('') @@ -135,9 +135,9 @@ suite('Editor Modes - textToHtmlTokenizer', () => { [ '
', 'Ciao', - ' ', + ' ', 'hello', - ' ', + ' ', '
' ].join('') ); @@ -147,9 +147,9 @@ suite('Editor Modes - textToHtmlTokenizer', () => { [ '
', 'iao', - ' ', + ' ', 'hello', - ' ', + ' ', '
' ].join('') ); @@ -158,9 +158,9 @@ suite('Editor Modes - textToHtmlTokenizer', () => { tokenizeLineToHTML(text, lineTokens, colorMap, 4, 11, 4, true), [ '
', - ' ', + ' ', 'hello', - ' ', + ' ', '
' ].join('') ); @@ -170,7 +170,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { [ '
', 'hello', - ' ', + ' ', '
' ].join('') ); @@ -241,11 +241,11 @@ suite('Editor Modes - textToHtmlTokenizer', () => { tokenizeLineToHTML(text, lineTokens, colorMap, 0, 21, 4, true), [ '
', - '  ', + '  ', 'Ciao', - '   ', + '   ', 'hello', - ' ', + ' ', 'world!', '
' ].join('') @@ -255,11 +255,11 @@ suite('Editor Modes - textToHtmlTokenizer', () => { tokenizeLineToHTML(text, lineTokens, colorMap, 0, 17, 4, true), [ '
', - '  ', + '  ', 'Ciao', - '   ', + '   ', 'hello', - ' ', + ' ', 'wo', '
' ].join('') @@ -269,7 +269,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { tokenizeLineToHTML(text, lineTokens, colorMap, 0, 3, 4, true), [ '
', - '  ', + '  ', 'C', '
' ].join('') From 6d88ff32b2c1880726cb81c67e7b16d71ff75a4a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Nov 2019 15:41:24 +0100 Subject: [PATCH 111/246] fix compile --- .../workbench/browser/parts/editor/textDiffEditor.ts | 6 ++---- .../browser/parts/editor/textResourceEditor.ts | 11 ++++------- .../contrib/files/browser/editors/textFileEditor.ts | 6 ++---- .../contrib/files/electron-browser/textFileEditor.ts | 6 ++---- src/vs/workbench/contrib/output/browser/logViewer.ts | 8 ++------ .../workbench/contrib/output/browser/outputPanel.ts | 10 ++-------- .../contrib/preferences/browser/preferencesEditor.ts | 6 ++---- 7 files changed, 16 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 54242a90fc4..0ad7afc0e07 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -31,7 +31,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/editor'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; /** * The text editor that leverages the diff text editor for the editing experience. @@ -51,10 +50,9 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { @IEditorService editorService: IEditorService, @IThemeService themeService: IThemeService, @IEditorGroupsService editorGroupService: IEditorGroupsService, - @IClipboardService private clipboardService: IClipboardService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService + @IClipboardService private clipboardService: IClipboardService ) { - super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService, filesConfigurationService); + super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService); } protected getEditorMemento(editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento { diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index aee0f7187fe..a5433f5cd75 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -21,7 +21,6 @@ import { ScrollType, IEditor } from 'vs/editor/common/editorCommon'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; /** * An editor implementation that is capable of showing the contents of resource inputs. Uses @@ -37,10 +36,9 @@ export class AbstractTextResourceEditor extends BaseTextEditor { @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, @IThemeService themeService: IThemeService, @IEditorGroupsService editorGroupService: IEditorGroupsService, - @IEditorService editorService: IEditorService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService + @IEditorService editorService: IEditorService ) { - super(id, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService, filesConfigurationService); + super(id, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService); } getTitle(): string | undefined { @@ -189,9 +187,8 @@ export class TextResourceEditor extends AbstractTextResourceEditor { @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, @IThemeService themeService: IThemeService, @IEditorService editorService: IEditorService, - @IEditorGroupsService editorGroupService: IEditorGroupsService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService + @IEditorGroupsService editorGroupService: IEditorGroupsService ) { - super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, editorService, filesConfigurationService); + super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, editorService); } } diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index 3da672cc90e..d36942793a8 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -31,7 +31,6 @@ import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { MutableDisposable } from 'vs/base/common/lifecycle'; import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/editor'; -import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; /** * An implementation of editor for file system resources. @@ -55,10 +54,9 @@ export class TextFileEditor extends BaseTextEditor { @IThemeService themeService: IThemeService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @ITextFileService private readonly textFileService: ITextFileService, - @IExplorerService private readonly explorerService: IExplorerService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService + @IExplorerService private readonly explorerService: IExplorerService ) { - super(TextFileEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService, filesConfigurationService); + super(TextFileEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService); this.updateRestoreViewStateConfiguration(); diff --git a/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts b/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts index dabb58411e1..d34fc54cc67 100644 --- a/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts @@ -24,7 +24,6 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { IElectronService } from 'vs/platform/electron/node/electron'; -import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; /** * An implementation of editor for file system resources. @@ -45,10 +44,9 @@ export class NativeTextFileEditor extends TextFileEditor { @ITextFileService textFileService: ITextFileService, @IElectronService private readonly electronService: IElectronService, @IPreferencesService private readonly preferencesService: IPreferencesService, - @IExplorerService explorerService: IExplorerService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService + @IExplorerService explorerService: IExplorerService ) { - super(telemetryService, fileService, viewletService, instantiationService, contextService, storageService, configurationService, editorService, themeService, editorGroupService, textFileService, explorerService, filesConfigurationService); + super(telemetryService, fileService, viewletService, instantiationService, contextService, storageService, configurationService, editorService, themeService, editorGroupService, textFileService, explorerService); } protected handleSetInputError(error: Error, input: FileEditorInput, options: EditorOptions | undefined): void { diff --git a/src/vs/workbench/contrib/output/browser/logViewer.ts b/src/vs/workbench/contrib/output/browser/logViewer.ts index 90e2c3c0f71..e1f3a1fd3b4 100644 --- a/src/vs/workbench/contrib/output/browser/logViewer.ts +++ b/src/vs/workbench/contrib/output/browser/logViewer.ts @@ -11,14 +11,12 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { AbstractTextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { URI } from 'vs/base/common/uri'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { LOG_SCHEME, IFileOutputChannelDescriptor } from 'vs/workbench/contrib/output/common/output'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; export class LogViewerInput extends ResourceEditorInput { @@ -47,14 +45,12 @@ export class LogViewer extends AbstractTextResourceEditor { @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService instantiationService: IInstantiationService, @IStorageService storageService: IStorageService, - @IConfigurationService baseConfigurationService: IConfigurationService, @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, @IThemeService themeService: IThemeService, @IEditorGroupsService editorGroupService: IEditorGroupsService, - @IEditorService editorService: IEditorService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService + @IEditorService editorService: IEditorService ) { - super(LogViewer.LOG_VIEWER_EDITOR_ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, editorService, filesConfigurationService); + super(LogViewer.LOG_VIEWER_EDITOR_ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, editorService); } protected getConfigurationOverrides(): IEditorOptions { diff --git a/src/vs/workbench/contrib/output/browser/outputPanel.ts b/src/vs/workbench/contrib/output/browser/outputPanel.ts index 2f7820fe1a3..3cf422ed07f 100644 --- a/src/vs/workbench/contrib/output/browser/outputPanel.ts +++ b/src/vs/workbench/contrib/output/browser/outputPanel.ts @@ -19,14 +19,11 @@ import { AbstractTextResourceEditor } from 'vs/workbench/browser/parts/editor/te import { OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT } from 'vs/workbench/contrib/output/common/output'; import { SwitchOutputAction, SwitchOutputActionViewItem, ClearOutputAction, ToggleOrSetOutputScrollLockAction, OpenLogOutputFile } from 'vs/workbench/contrib/output/browser/outputActions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; -import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; export class OutputPanel extends AbstractTextResourceEditor { private actions: IAction[] | undefined; @@ -43,12 +40,9 @@ export class OutputPanel extends AbstractTextResourceEditor { @IOutputService private readonly outputService: IOutputService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IEditorGroupsService editorGroupService: IEditorGroupsService, - @ITextFileService textFileService: ITextFileService, - @IEditorService editorService: IEditorService, - @IHostService hostService: IHostService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService + @IEditorService editorService: IEditorService ) { - super(OUTPUT_PANEL_ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, editorService, filesConfigurationService); + super(OUTPUT_PANEL_ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, editorService); this.scopedInstantiationService = instantiationService; } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index 0877e6773d3..29e80ca36b1 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -53,7 +53,6 @@ import { IFilterResult, IPreferencesService, ISetting, ISettingsEditorModel, ISe import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { DefaultSettingsEditorModel, SettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { withNullAsUndefined, withUndefinedAsNull, assertIsDefined } from 'vs/base/common/types'; -import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; export class PreferencesEditor extends BaseEditor { @@ -978,10 +977,9 @@ export class DefaultPreferencesEditor extends BaseTextEditor { @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, @IThemeService themeService: IThemeService, @IEditorGroupsService editorGroupService: IEditorGroupsService, - @IEditorService editorService: IEditorService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService + @IEditorService editorService: IEditorService ) { - super(DefaultPreferencesEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService, filesConfigurationService); + super(DefaultPreferencesEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService); } private static _getContributions(): IEditorContributionDescription[] { From af864aed2f167376f9b950f7415786e59cf25d5d Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 20 Nov 2019 16:05:21 +0100 Subject: [PATCH 112/246] fixes #84295 --- src/vs/workbench/contrib/debug/browser/debugSession.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 89f0cb1e6d2..86287b627a5 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -720,8 +720,8 @@ export class DebugSession implements IDebugSession { await this.debugService.sendAllBreakpoints(this); } finally { await sendConfigurationDone(); + await this.fetchThreads(); } - await this.fetchThreads(); })); this.rawListeners.push(this.raw.onDidStop(async event => { From f37b9d742c697bdf20203b963fe99473d11be163 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Nov 2019 16:06:55 +0100 Subject: [PATCH 113/246] git: isolate ipc env --- extensions/git/src/askpass-main.ts | 7 +------ extensions/git/src/askpass.ts | 5 ++--- extensions/git/src/ipc/ipcClient.ts | 12 +++++++++++- extensions/git/src/ipc/ipcServer.ts | 5 +++++ extensions/git/src/main.ts | 5 +++-- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/extensions/git/src/askpass-main.ts b/extensions/git/src/askpass-main.ts index 066ff536683..685fc08ef86 100644 --- a/extensions/git/src/askpass-main.ts +++ b/extensions/git/src/askpass-main.ts @@ -20,10 +20,6 @@ function main(argv: string[]): void { return fatal('Wrong number of arguments'); } - if (!process.env['VSCODE_GIT_ASKPASS_HANDLE']) { - return fatal('Missing handle'); - } - if (!process.env['VSCODE_GIT_ASKPASS_PIPE']) { return fatal('Missing pipe'); } @@ -33,10 +29,9 @@ function main(argv: string[]): void { } const output = process.env['VSCODE_GIT_ASKPASS_PIPE'] as string; - const socketPath = process.env['VSCODE_GIT_ASKPASS_HANDLE'] as string; const request = argv[2]; const host = argv[4].substring(1, argv[4].length - 2); - const ipcClient = new IPCClient('askpass', socketPath); + const ipcClient = new IPCClient('askpass'); ipcClient.call({ request, host }).then(res => { fs.writeFileSync(output, res + '\n'); diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index 005179d8c0c..dd7e84c2e03 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -26,7 +26,7 @@ export class Askpass implements IIPCHandler { }; } - constructor(private ipc: IIPCServer) { + constructor(ipc: IIPCServer) { this.disposable = ipc.registerHandler('askpass', this); } @@ -46,8 +46,7 @@ export class Askpass implements IIPCHandler { ELECTRON_RUN_AS_NODE: '1', GIT_ASKPASS: path.join(__dirname, 'askpass.sh'), VSCODE_GIT_ASKPASS_NODE: process.execPath, - VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js'), - VSCODE_GIT_ASKPASS_HANDLE: this.ipc.ipcHandlePath + VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js') }; } diff --git a/extensions/git/src/ipc/ipcClient.ts b/extensions/git/src/ipc/ipcClient.ts index 4a26a74e8e3..f623b3f7b6f 100644 --- a/extensions/git/src/ipc/ipcClient.ts +++ b/extensions/git/src/ipc/ipcClient.ts @@ -7,7 +7,17 @@ import * as http from 'http'; export class IPCClient { - constructor(private handlerName: string, private ipcHandlePath: string) { } + private ipcHandlePath: string; + + constructor(private handlerName: string) { + const ipcHandlePath = process.env['VSCODE_GIT_IPC_HANDLE']; + + if (!ipcHandlePath) { + throw new Error('Missing VSCODE_GIT_IPC_HANDLE'); + } + + this.ipcHandlePath = ipcHandlePath; + } call(request: any): Promise { const opts: http.RequestOptions = { diff --git a/extensions/git/src/ipc/ipcServer.ts b/extensions/git/src/ipc/ipcServer.ts index cf3c1b8b61e..39d2b59e6f8 100644 --- a/extensions/git/src/ipc/ipcServer.ts +++ b/extensions/git/src/ipc/ipcServer.ts @@ -48,6 +48,7 @@ export async function createIPCServer(): Promise { export interface IIPCServer extends Disposable { readonly ipcHandlePath: string | undefined; + getEnv(): any; registerHandler(name: string, handler: IIPCHandler): Disposable; } @@ -92,6 +93,10 @@ class IPCServer implements IIPCServer, Disposable { }); } + getEnv(): any { + return { VSCODE_GIT_IPC_HANDLE: this.ipcHandlePath }; + } + dispose(): void { this.handlers.clear(); this.server.close(); diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 35fbf87ddd3..0c34f4e0f3f 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -34,15 +34,16 @@ export async function deactivate(): Promise { async function createModel(context: ExtensionContext, outputChannel: OutputChannel, telemetryReporter: TelemetryReporter, disposables: Disposable[]): Promise { const pathHint = workspace.getConfiguration('git').get('path'); const info = await findGit(pathHint, path => outputChannel.appendLine(localize('looking', "Looking for git in: {0}", path))); - let env: any = undefined; + let env: any = {}; try { const ipc = await createIPCServer(); disposables.push(ipc); + env = { ...env, ...ipc.getEnv() }; const askpass = new Askpass(ipc); disposables.push(askpass); - env = askpass.getEnv(); + env = { ...env, ...askpass.getEnv() }; } catch { env = Askpass.getDisabledEnv(); } From 8ce220f3da6f3df1e2230b1155c7e07eea488e82 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 20 Nov 2019 15:31:36 +0100 Subject: [PATCH 114/246] Fix #85209 --- src/vs/platform/userDataSync/common/extensionsSync.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 54fcb05a56c..72537329e7f 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -73,7 +73,10 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser this.logService.trace('Extensions: Skipping synchronizing extensions as it is disabled.'); return false; } - + if (!this.extensionGalleryService.isEnabled()) { + this.logService.trace('Extensions: Skipping synchronizing extensions as gallery is disabled.'); + return false; + } if (this.status !== SyncStatus.Idle) { this.logService.trace('Extensions: Skipping synchronizing extensions as it is running already.'); return false; From 0742fcdf20171ab5a800e5a3cf64ce42dceea55c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 20 Nov 2019 16:07:01 +0100 Subject: [PATCH 115/246] #85216 First cut --- .../userDataSync/browser/userDataSync.ts | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index d46e61b4586..edfc6b5bcf5 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -174,21 +174,23 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } await this.signIn(); } - await this.configureSyncOptions(); + if (this.storageService.getBoolean('userDataSync.isFirstTime', StorageScope.GLOBAL, true)) { + await this.configureSyncOptions(); + this.storageService.store('userDataSync.isFirstTime', false, StorageScope.GLOBAL); + } await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, true); this.notificationService.info(localize('Sync Started', "Sync Started.")); } private async configureSyncOptions(): Promise { - if (this.storageService.getBoolean('userDataSync.configureOptions.donotAsk', StorageScope.GLOBAL, false)) { - return; - } return new Promise((c, e) => { const disposables: DisposableStore = new DisposableStore(); const quickPick = this.quickInputService.createQuickPick(); disposables.add(quickPick); + quickPick.title = localize('configure sync title', "Sync: Configure"); quickPick.placeholder = localize('select configurations to sync', "Choose what to sync"); quickPick.canSelectMany = true; + quickPick.ignoreFocusOut = true; const items = [{ id: 'configurationSync.enableSettings', label: localize('user settings', "User Settings") @@ -198,14 +200,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }]; quickPick.items = items; quickPick.selectedItems = items.filter(item => this.configurationService.getValue(item.id)); - quickPick.customButton = true; - quickPick.customLabel = localize('do not ask', "Don't Ask Again"); - disposables.add(quickPick.onDidCustom(() => { - this.storageService.store('userDataSync.configureOptions.donotAsk', true, StorageScope.GLOBAL); - quickPick.hide(); - })); - disposables.add(quickPick.onDidAccept(() => quickPick.hide())); - disposables.add(quickPick.onDidHide(() => { + disposables.add(quickPick.onDidAccept(() => { for (const item of items) { const wasEnabled = this.configurationService.getValue(item.id); const isEnabled = !!quickPick.selectedItems.filter(selected => selected.id === item.id)[0]; @@ -213,6 +208,9 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this.configurationService.updateValue(item.id!, isEnabled); } } + quickPick.hide(); + })); + disposables.add(quickPick.onDidHide(() => { disposables.dispose(); c(); })); From 3bf8e81517b30168e809e767c65cf7057cde9a21 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Nov 2019 16:25:10 +0100 Subject: [PATCH 116/246] fixes #85205 --- extensions/git/package.nls.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index ad377c36b61..c4821f5decd 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -61,7 +61,7 @@ "command.publish": "Publish Branch...", "command.showOutput": "Show Git Output", "command.ignore": "Add to .gitignore", - "command.revealInExplorer": "Reveal in Explorer", + "command.revealInExplorer": "Reveal in Side Bar", "command.stashIncludeUntracked": "Stash (Include Untracked)", "command.stash": "Stash", "command.stashPop": "Pop Stash...", From 51a128c5382d50ce18d2fdb36046b78a5e36029c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Nov 2019 16:45:08 +0100 Subject: [PATCH 117/246] fix #85204 --- src/vs/workbench/services/history/browser/history.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 5c3bb1e5f2e..6e061b1dcc1 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -33,6 +33,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; import { addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; +import { Schemas } from 'vs/base/common/network'; /** * Stores the selection & view state of an editor and allows to compare it to other selection states. @@ -735,8 +736,10 @@ export class HistoryService extends Disposable implements IHistoryService { private preferResourceInput(input: IEditorInput): IEditorInput | IResourceInput { const resource = input.getResource(); - if (resource && this.fileService.canHandleResource(resource)) { - return { resource: resource }; + if (resource && (resource.scheme === Schemas.file || resource.scheme === Schemas.vscodeRemote || resource.scheme === Schemas.userData)) { + // for now, only prefer well known schemes that we control to prevent + // issues such as https://github.com/microsoft/vscode/issues/85204 + return { resource }; } return input; From aa83ef27fd5f220b66adb87326ab6676bac063bf Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 20 Nov 2019 07:49:52 -0800 Subject: [PATCH 118/246] Don't respect escape chords in terminal Fixes #85186 --- .../workbench/contrib/terminal/browser/terminalInstance.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 8ddb38770fc..18dd357cc65 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -589,7 +589,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // within commandsToSkipShell const standardKeyboardEvent = new StandardKeyboardEvent(event); const resolveResult = this._keybindingService.softDispatch(standardKeyboardEvent, standardKeyboardEvent.target); - const allowChords = resolveResult && resolveResult.enterChord && this._configHelper.config.allowChords; + // Respect chords if the allowChords setting is set and it's not Escape. Escape is + // handled specially for Zen Mode's Escape, Escape chord, plus it's important in + // terminals generally + const allowChords = resolveResult && resolveResult.enterChord && this._configHelper.config.allowChords && event.key !== 'Escape'; if (allowChords || resolveResult && this._skipTerminalCommands.some(k => k === resolveResult.commandId)) { event.preventDefault(); return false; @@ -599,6 +602,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (TabFocus.getTabFocusMode() && event.keyCode === 9) { return false; } + // Always have alt+F4 skip the terminal on Windows and allow it to be handled by the // system if (platform.isWindows && event.altKey && event.key === 'F4' && !event.ctrlKey) { From 6be5444bf5c8c1c15155f6a1789783ff9eec0408 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 20 Nov 2019 16:55:08 +0100 Subject: [PATCH 119/246] Update devcontainer schema (microsoft/vscode-remote-release#1201) --- .../schemas/attachContainer.schema.json | 7 +++++++ .../schemas/devContainer.schema.json | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/extensions/configuration-editing/schemas/attachContainer.schema.json b/extensions/configuration-editing/schemas/attachContainer.schema.json index 5f3d28abb89..d2ea16a783a 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -18,6 +18,13 @@ "type": "integer" } }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Remote environment variables." + }, "extensions": { "type": "array", "description": "An array of extensions that should be installed into the container.", diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index 1358c74f2d2..9c1d7aad1a3 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -22,6 +22,13 @@ "$ref": "vscode://schemas/settings/machine", "description": "Machine specific settings that should be copied into the container." }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Remote environment variables." + }, "postCreateCommand": { "type": [ "string", @@ -55,6 +62,13 @@ ] } }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, "runArgs": { "type": "array", "description": "The arguments required when starting in the container.", From 0f6df6304802bec186d990db093345fececb9fc6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 20 Nov 2019 12:52:54 +0100 Subject: [PATCH 120/246] highlight future replace range, add setting to disable, #10266 --- src/vs/editor/common/config/editorOptions.ts | 11 + .../contrib/suggest/suggestController.ts | 190 ++++++++++-------- .../suggest/suggestRangeHighlighter.ts | 98 +++++++++ .../suggest/test/completionModel.test.ts | 1 + src/vs/monaco.d.ts | 4 + 5 files changed, 219 insertions(+), 85 deletions(-) create mode 100644 src/vs/editor/contrib/suggest/suggestRangeHighlighter.ts diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 4b170409e03..8ab1eef12fc 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2354,6 +2354,10 @@ export interface ISuggestOptions { * Overwrite word ends on accept. Default to false. */ overwriteOnAccept?: boolean; + /** + * Should the editor highlight what text suggest will replace. + */ + highlightReplaceRange?: boolean; /** * Enable graceful matching. Defaults to true. */ @@ -2487,6 +2491,7 @@ class EditorSuggest extends BaseEditorOption(SuggestController.ID); } - private readonly _model: SuggestModel; - private readonly _widget: IdleValue; + readonly editor: ICodeEditor; + readonly model: SuggestModel; + readonly widget: IdleValue; + private readonly _alternatives: IdleValue; private readonly _lineSuffix = new MutableDisposable(); private readonly _toDispose = new DisposableStore(); constructor( - private _editor: ICodeEditor, + editor: ICodeEditor, @IEditorWorkerService editorWorker: IEditorWorkerService, @ISuggestMemoryService private readonly _memoryService: ISuggestMemoryService, @ICommandService private readonly _commandService: ICommandService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { - this._model = new SuggestModel(this._editor, editorWorker); + this.editor = editor; + this.model = new SuggestModel(this.editor, editorWorker); - this._widget = new IdleValue(() => { + this.widget = new IdleValue(() => { - const widget = this._instantiationService.createInstance(SuggestWidget, this._editor); + const widget = this._instantiationService.createInstance(SuggestWidget, this.editor); this._toDispose.add(widget); this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, 0), this)); // Wire up logic to accept a suggestion on certain characters - const commitCharacterController = new CommitCharacterController(this._editor, widget, item => this._insertSuggestion(item, InsertFlags.NoAfterUndoStop)); + const commitCharacterController = new CommitCharacterController(this.editor, widget, item => this._insertSuggestion(item, InsertFlags.NoAfterUndoStop)); this._toDispose.add(commitCharacterController); - this._toDispose.add(this._model.onDidSuggest(e => { + this._toDispose.add(this.model.onDidSuggest(e => { if (e.completionModel.items.length === 0) { commitCharacterController.reset(); } @@ -137,19 +141,19 @@ export class SuggestController implements IEditorContribution { let makesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService); this._toDispose.add(widget.onDidFocus(({ item }) => { - const position = this._editor.getPosition()!; + const position = this.editor.getPosition()!; const startColumn = item.editStart.column; const endColumn = position.column; let value = true; if ( - this._editor.getOption(EditorOption.acceptSuggestionOnEnter) === 'smart' - && this._model.state === State.Auto + this.editor.getOption(EditorOption.acceptSuggestionOnEnter) === 'smart' + && this.model.state === State.Auto && !item.completion.command && !item.completion.additionalTextEdits && !(item.completion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet) && endColumn - startColumn === item.completion.insertText.length ) { - const oldText = this._editor.getModel()!.getValueInRange({ + const oldText = this.editor.getModel()!.getValueInRange({ startLineNumber: position.lineNumber, startColumn, endLineNumber: position.lineNumber, @@ -161,38 +165,40 @@ export class SuggestController implements IEditorContribution { })); this._toDispose.add(toDisposable(() => makesTextEdit.reset())); + + return widget; }); this._alternatives = new IdleValue(() => { - return this._toDispose.add(new SuggestAlternatives(this._editor, this._contextKeyService)); + return this._toDispose.add(new SuggestAlternatives(this.editor, this._contextKeyService)); }); - this._toDispose.add(_instantiationService.createInstance(WordContextKey, _editor)); + this._toDispose.add(_instantiationService.createInstance(WordContextKey, editor)); - this._toDispose.add(this._model.onDidTrigger(e => { - this._widget.getValue().showTriggered(e.auto, e.shy ? 250 : 50); - this._lineSuffix.value = new LineSuffix(this._editor.getModel()!, e.position); + this._toDispose.add(this.model.onDidTrigger(e => { + this.widget.getValue().showTriggered(e.auto, e.shy ? 250 : 50); + this._lineSuffix.value = new LineSuffix(this.editor.getModel()!, e.position); })); - this._toDispose.add(this._model.onDidSuggest(e => { + this._toDispose.add(this.model.onDidSuggest(e => { if (!e.shy) { - let index = this._memoryService.select(this._editor.getModel()!, this._editor.getPosition()!, e.completionModel.items); - this._widget.getValue().showSuggestions(e.completionModel, index, e.isFrozen, e.auto); + let index = this._memoryService.select(this.editor.getModel()!, this.editor.getPosition()!, e.completionModel.items); + this.widget.getValue().showSuggestions(e.completionModel, index, e.isFrozen, e.auto); } })); - this._toDispose.add(this._model.onDidCancel(e => { + this._toDispose.add(this.model.onDidCancel(e => { if (!e.retrigger) { - this._widget.getValue().hideWidget(); + this.widget.getValue().hideWidget(); } })); - this._toDispose.add(this._editor.onDidBlurEditorWidget(() => { + this._toDispose.add(this.editor.onDidBlurEditorWidget(() => { if (!_sticky) { - this._model.cancel(); - this._model.clear(); + this.model.cancel(); + this.model.clear(); } })); - this._toDispose.add(this._widget.getValue().onDetailsKeyDown(e => { + this._toDispose.add(this.widget.getValue().onDetailsKeyDown(e => { // cmd + c on macOS, ctrl + c on Win / Linux if ( e.toKeybinding().equals(new SimpleKeybinding(true, false, false, false, KeyCode.KEY_C)) || @@ -203,25 +209,28 @@ export class SuggestController implements IEditorContribution { } if (!e.toKeybinding().isModifierKey()) { - this._editor.focus(); + this.editor.focus(); } })); // Manage the acceptSuggestionsOnEnter context key let acceptSuggestionsOnEnter = SuggestContext.AcceptSuggestionsOnEnter.bindTo(_contextKeyService); let updateFromConfig = () => { - const acceptSuggestionOnEnter = this._editor.getOption(EditorOption.acceptSuggestionOnEnter); + const acceptSuggestionOnEnter = this.editor.getOption(EditorOption.acceptSuggestionOnEnter); acceptSuggestionsOnEnter.set(acceptSuggestionOnEnter === 'on' || acceptSuggestionOnEnter === 'smart'); }; - this._toDispose.add(this._editor.onDidChangeConfiguration(() => updateFromConfig())); + this._toDispose.add(this.editor.onDidChangeConfiguration(() => updateFromConfig())); updateFromConfig(); + + // create range highlighter + this._toDispose.add(new SuggestRangeHighlighter(this)); } dispose(): void { this._alternatives.dispose(); this._toDispose.dispose(); - this._widget.dispose(); - this._model.dispose(); + this.widget.dispose(); + this.model.dispose(); this._lineSuffix.dispose(); } @@ -231,32 +240,31 @@ export class SuggestController implements IEditorContribution { ): void { if (!event || !event.item) { this._alternatives.getValue().reset(); - this._model.cancel(); - this._model.clear(); + this.model.cancel(); + this.model.clear(); return; } - if (!this._editor.hasModel()) { + if (!this.editor.hasModel()) { return; } - const model = this._editor.getModel(); + const model = this.editor.getModel(); const modelVersionNow = model.getAlternativeVersionId(); const { item } = event; - const { completion: suggestion, position } = item; - const columnDelta = this._editor.getPosition().column - position.column; + const { completion: suggestion } = item; // pushing undo stops *before* additional text edits and // *after* the main edit if (!(flags & InsertFlags.NoBeforeUndoStop)) { - this._editor.pushUndoStop(); + this.editor.pushUndoStop(); } if (Array.isArray(suggestion.additionalTextEdits)) { - this._editor.executeEdits('suggestController.additionalTextEdits', suggestion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text))); + this.editor.executeEdits('suggestController.additionalTextEdits', suggestion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text))); } // keep item in memory - this._memoryService.memorize(model, this._editor.getPosition(), item); + this._memoryService.memorize(model, this.editor.getPosition(), item); let { insertText } = suggestion; if (!(suggestion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)) { @@ -264,40 +272,38 @@ export class SuggestController implements IEditorContribution { } const overwriteConfig = flags & InsertFlags.AlternativeOverwriteConfig - ? !this._editor.getOption(EditorOption.suggest).overwriteOnAccept - : this._editor.getOption(EditorOption.suggest).overwriteOnAccept; + ? !this.editor.getOption(EditorOption.suggest).overwriteOnAccept + : this.editor.getOption(EditorOption.suggest).overwriteOnAccept; - const overwriteBefore = position.column - item.editStart.column; - const overwriteAfter = (overwriteConfig ? item.editReplaceEnd.column : item.editInsertEnd.column) - position.column; - const suffixDelta = this._lineSuffix.value ? this._lineSuffix.value.delta(this._editor.getPosition()) : 0; + const info = this.getOverwriteInfo(item, overwriteConfig); - SnippetController2.get(this._editor).insert(insertText, { - overwriteBefore: overwriteBefore + columnDelta, - overwriteAfter: overwriteAfter + suffixDelta, + SnippetController2.get(this.editor).insert(insertText, { + overwriteBefore: info.overwriteBefore, + overwriteAfter: info.overwriteAfter, undoStopBefore: false, undoStopAfter: false, adjustWhitespace: !(suggestion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace) }); if (!(flags & InsertFlags.NoAfterUndoStop)) { - this._editor.pushUndoStop(); + this.editor.pushUndoStop(); } if (!suggestion.command) { // done - this._model.cancel(); - this._model.clear(); + this.model.cancel(); + this.model.clear(); } else if (suggestion.command.id === TriggerSuggestAction.id) { // retigger - this._model.trigger({ auto: true, shy: false }, true); + this.model.trigger({ auto: true, shy: false }, true); } else { // exec command, done this._commandService.executeCommand(suggestion.command.id, ...(suggestion.command.arguments ? [...suggestion.command.arguments] : [])) .catch(onUnexpectedError) - .finally(() => this._model.clear()); // <- clear only now, keep commands alive - this._model.cancel(); + .finally(() => this.model.clear()); // <- clear only now, keep commands alive + this.model.cancel(); } if (flags & InsertFlags.KeepAlternativeSuggestions) { @@ -321,6 +327,20 @@ export class SuggestController implements IEditorContribution { this._alertCompletionItem(event.item); } + getOverwriteInfo(item: CompletionItem, overwriteOnAccept: boolean): { overwriteBefore: number, overwriteAfter: number } { + assertType(this.editor.hasModel()); + + const overwriteBefore = item.position.column - item.editStart.column; + const overwriteAfter = (overwriteOnAccept ? item.editReplaceEnd.column : item.editInsertEnd.column) - item.position.column; + const columnDelta = this.editor.getPosition().column - item.position.column; + const suffixDelta = this._lineSuffix.value ? this._lineSuffix.value.delta(this.editor.getPosition()) : 0; + + return { + overwriteBefore: overwriteBefore + columnDelta, + overwriteAfter: overwriteAfter + suffixDelta + }; + } + private _alertCompletionItem({ completion: suggestion }: CompletionItem): void { if (isNonEmptyArray(suggestion.additionalTextEdits)) { let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' made {1} additional edits", suggestion.label, suggestion.additionalTextEdits.length); @@ -329,22 +349,22 @@ export class SuggestController implements IEditorContribution { } triggerSuggest(onlyFrom?: Set): void { - if (this._editor.hasModel()) { - this._model.trigger({ auto: false, shy: false }, false, onlyFrom); - this._editor.revealLine(this._editor.getPosition().lineNumber, ScrollType.Smooth); - this._editor.focus(); + if (this.editor.hasModel()) { + this.model.trigger({ auto: false, shy: false }, false, onlyFrom); + this.editor.revealLine(this.editor.getPosition().lineNumber, ScrollType.Smooth); + this.editor.focus(); } } triggerSuggestAndAcceptBest(arg: { fallback: string }): void { - if (!this._editor.hasModel()) { + if (!this.editor.hasModel()) { return; } - const positionNow = this._editor.getPosition(); + const positionNow = this.editor.getPosition(); const fallback = () => { - if (positionNow.equals(this._editor.getPosition()!)) { + if (positionNow.equals(this.editor.getPosition()!)) { this._commandService.executeCommand(arg.fallback); } }; @@ -354,14 +374,14 @@ export class SuggestController implements IEditorContribution { // snippet, other editor -> makes edit return true; } - const position = this._editor.getPosition()!; + const position = this.editor.getPosition()!; const startColumn = item.editStart.column; const endColumn = position.column; if (endColumn - startColumn !== item.completion.insertText.length) { // unequal lengths -> makes edit return true; } - const textNow = this._editor.getModel()!.getValueInRange({ + const textNow = this.editor.getModel()!.getValueInRange({ startLineNumber: position.lineNumber, startColumn, endLineNumber: position.lineNumber, @@ -371,41 +391,41 @@ export class SuggestController implements IEditorContribution { return textNow !== item.completion.insertText; }; - Event.once(this._model.onDidTrigger)(_ => { + Event.once(this.model.onDidTrigger)(_ => { // wait for trigger because only then the cancel-event is trustworthy let listener: IDisposable[] = []; - Event.any(this._model.onDidTrigger, this._model.onDidCancel)(() => { + Event.any(this.model.onDidTrigger, this.model.onDidCancel)(() => { // retrigger or cancel -> try to type default text dispose(listener); fallback(); }, undefined, listener); - this._model.onDidSuggest(({ completionModel }) => { + this.model.onDidSuggest(({ completionModel }) => { dispose(listener); if (completionModel.items.length === 0) { fallback(); return; } - const index = this._memoryService.select(this._editor.getModel()!, this._editor.getPosition()!, completionModel.items); + const index = this._memoryService.select(this.editor.getModel()!, this.editor.getPosition()!, completionModel.items); const item = completionModel.items[index]; if (!makesTextEdit(item)) { fallback(); return; } - this._editor.pushUndoStop(); + this.editor.pushUndoStop(); this._insertSuggestion({ index, item, model: completionModel }, InsertFlags.KeepAlternativeSuggestions | InsertFlags.NoBeforeUndoStop | InsertFlags.NoAfterUndoStop); }, undefined, listener); }); - this._model.trigger({ auto: false, shy: true }); - this._editor.revealLine(positionNow.lineNumber, ScrollType.Smooth); - this._editor.focus(); + this.model.trigger({ auto: false, shy: true }); + this.editor.revealLine(positionNow.lineNumber, ScrollType.Smooth); + this.editor.focus(); } acceptSelectedSuggestion(keepAlternativeSuggestions: boolean, alternativeOverwriteConfig: boolean): void { - const item = this._widget.getValue().getFocusedItem(); + const item = this.widget.getValue().getFocusedItem(); let flags = 0; if (keepAlternativeSuggestions) { flags |= InsertFlags.KeepAlternativeSuggestions; @@ -425,45 +445,45 @@ export class SuggestController implements IEditorContribution { } cancelSuggestWidget(): void { - this._model.cancel(); - this._model.clear(); - this._widget.getValue().hideWidget(); + this.model.cancel(); + this.model.clear(); + this.widget.getValue().hideWidget(); } selectNextSuggestion(): void { - this._widget.getValue().selectNext(); + this.widget.getValue().selectNext(); } selectNextPageSuggestion(): void { - this._widget.getValue().selectNextPage(); + this.widget.getValue().selectNextPage(); } selectLastSuggestion(): void { - this._widget.getValue().selectLast(); + this.widget.getValue().selectLast(); } selectPrevSuggestion(): void { - this._widget.getValue().selectPrevious(); + this.widget.getValue().selectPrevious(); } selectPrevPageSuggestion(): void { - this._widget.getValue().selectPreviousPage(); + this.widget.getValue().selectPreviousPage(); } selectFirstSuggestion(): void { - this._widget.getValue().selectFirst(); + this.widget.getValue().selectFirst(); } toggleSuggestionDetails(): void { - this._widget.getValue().toggleDetails(); + this.widget.getValue().toggleDetails(); } toggleExplainMode(): void { - this._widget.getValue().toggleExplainMode(); + this.widget.getValue().toggleExplainMode(); } toggleSuggestionFocus(): void { - this._widget.getValue().toggleDetailsFocus(); + this.widget.getValue().toggleDetailsFocus(); } } diff --git a/src/vs/editor/contrib/suggest/suggestRangeHighlighter.ts b/src/vs/editor/contrib/suggest/suggestRangeHighlighter.ts new file mode 100644 index 00000000000..6d6f55bd0ca --- /dev/null +++ b/src/vs/editor/contrib/suggest/suggestRangeHighlighter.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Range } from 'vs/editor/common/core/range'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorSelectionBackground, registerColor, editorSelectionHighlightBorder } from 'vs/platform/theme/common/colorRegistry'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { CompletionItem } from 'vs/editor/contrib/suggest/suggest'; +import { TrackedRangeStickiness } from 'vs/editor/common/model'; +import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; +import { localize } from 'vs/nls'; + + +const suggestReplaceBackgroundColor = registerColor( + 'editor.suggestReplaceBackground', + { light: editorSelectionBackground, dark: editorSelectionBackground, hc: editorSelectionBackground }, + localize('suggestReplaceBackground', "Background color of text that suggest will replace.") +); + +const suggestReplaceBorderColor = registerColor( + 'editor.suggestReplaceBorder', + { light: null, dark: null, hc: editorSelectionHighlightBorder }, + localize('suggestReplaceBorder', "Border color of text that suggest will replace.") +); + +registerThemingParticipant((theme, collector) => { + const suggestReplaceBackground = theme.getColor(suggestReplaceBackgroundColor); + if (suggestReplaceBackground) { + collector.addRule(`.monaco-editor .suggestReplace { background-color: ${suggestReplaceBackground}; }`); + } + const suggestReplaceBorder = theme.getColor(suggestReplaceBorderColor); + if (suggestReplaceBorder) { + collector.addRule(`.monaco-editor .suggestReplace { border: 1px ${theme.type === 'hc' ? 'dotted' : 'solid'} ${suggestReplaceBorder}; }`); + } +}); + +export class SuggestRangeHighlighter { + + private readonly _disposables = new DisposableStore(); + + private _decorations: string[] = []; + private _hasWidgetListener: boolean = false; + + constructor(private readonly _controller: SuggestController) { + + this._disposables.add(_controller.model.onDidSuggest(e => { + if (!e.shy) { + const widget = this._controller.widget.getValue(); + const focused = widget.getFocusedItem(); + if (focused) { + this._highlight(focused.item); + } + + if (!this._hasWidgetListener) { + this._hasWidgetListener = true; + widget.onDidFocus(e => this._highlight(e.item), undefined, this._disposables); + } + } + })); + this._disposables.add(_controller.model.onDidCancel(() => { + this._reset(); + })); + } + + dispose(): void { + this._reset(); + this._disposables.dispose(); + } + + private _reset(): void { + this._decorations = this._controller.editor.deltaDecorations(this._decorations, []); + } + + private _highlight(item: CompletionItem) { + + const { overwriteOnAccept, highlightReplaceRange } = this._controller.editor.getOption(EditorOption.suggest); + + if (highlightReplaceRange) { + const info = this._controller.getOverwriteInfo(item, overwriteOnAccept); + const position = this._controller.editor.getPosition()!; + const range = new Range( + position.lineNumber, position.column - info.overwriteBefore, + position.lineNumber, position.column + info.overwriteAfter + ); + + this._decorations = this._controller.editor.deltaDecorations(this._decorations, [{ + range, + options: { + className: 'suggestReplace', + stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges + } + }]); + } + } +} diff --git a/src/vs/editor/contrib/suggest/test/completionModel.test.ts b/src/vs/editor/contrib/suggest/test/completionModel.test.ts index 0f4d43d9902..05eda1333a9 100644 --- a/src/vs/editor/contrib/suggest/test/completionModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/completionModel.test.ts @@ -35,6 +35,7 @@ suite('CompletionModel', function () { let defaultOptions = { overwriteOnAccept: false, + highlightReplaceRange: true, snippetsPreventQuickSuggestions: true, filterGraceful: true, localityBonus: false, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 60982c5b34b..990de2aa73d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3397,6 +3397,10 @@ declare namespace monaco.editor { * Overwrite word ends on accept. Default to false. */ overwriteOnAccept?: boolean; + /** + * Should the editor highlight what text suggest will replace. + */ + highlightReplaceRange?: boolean; /** * Enable graceful matching. Defaults to true. */ From 0ea4167ff6dd34da6890755aad98a7169e708984 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 20 Nov 2019 16:55:59 +0100 Subject: [PATCH 121/246] change setting to `editor.suggest.insertMode`, highlight in editor when suggestion doesn't what you expect, #10266 --- src/vs/editor/common/config/editorOptions.ts | 30 ++--- .../editor/contrib/suggest/media/suggest.css | 11 ++ .../contrib/suggest/suggestController.ts | 14 +- .../suggest/suggestRangeHighlighter.ts | 125 +++++++++++------- .../suggest/test/completionModel.test.ts | 7 +- src/vs/monaco.d.ts | 6 +- 6 files changed, 109 insertions(+), 84 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 8ab1eef12fc..6baacc81469 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2353,11 +2353,7 @@ export interface ISuggestOptions { /** * Overwrite word ends on accept. Default to false. */ - overwriteOnAccept?: boolean; - /** - * Should the editor highlight what text suggest will replace. - */ - highlightReplaceRange?: boolean; + insertMode?: 'insert' | 'replace'; /** * Enable graceful matching. Defaults to true. */ @@ -2490,8 +2486,7 @@ class EditorSuggest extends BaseEditorOption { - const suggestReplaceBackground = theme.getColor(suggestReplaceBackgroundColor); - if (suggestReplaceBackground) { - collector.addRule(`.monaco-editor .suggestReplace { background-color: ${suggestReplaceBackground}; }`); - } - const suggestReplaceBorder = theme.getColor(suggestReplaceBorderColor); - if (suggestReplaceBorder) { - collector.addRule(`.monaco-editor .suggestReplace { border: 1px ${theme.type === 'hc' ? 'dotted' : 'solid'} ${suggestReplaceBorder}; }`); - } -}); +import { Emitter } from 'vs/base/common/event'; +import { domEvent } from 'vs/base/browser/event'; export class SuggestRangeHighlighter { private readonly _disposables = new DisposableStore(); private _decorations: string[] = []; - private _hasWidgetListener: boolean = false; + private _widgetListener?: IDisposable; + private _shiftKeyListener?: IDisposable; + private _currentItem?: CompletionItem; constructor(private readonly _controller: SuggestController) { @@ -53,13 +30,12 @@ export class SuggestRangeHighlighter { if (focused) { this._highlight(focused.item); } - - if (!this._hasWidgetListener) { - this._hasWidgetListener = true; - widget.onDidFocus(e => this._highlight(e.item), undefined, this._disposables); + if (!this._widgetListener) { + this._widgetListener = widget.onDidFocus(e => this._highlight(e.item)); } } })); + this._disposables.add(_controller.model.onDidCancel(() => { this._reset(); })); @@ -68,31 +44,80 @@ export class SuggestRangeHighlighter { dispose(): void { this._reset(); this._disposables.dispose(); + dispose(this._widgetListener); + dispose(this._shiftKeyListener); } private _reset(): void { this._decorations = this._controller.editor.deltaDecorations(this._decorations, []); + if (this._shiftKeyListener) { + this._shiftKeyListener.dispose(); + this._shiftKeyListener = undefined; + } } private _highlight(item: CompletionItem) { - const { overwriteOnAccept, highlightReplaceRange } = this._controller.editor.getOption(EditorOption.suggest); + this._currentItem = item; + const opts = this._controller.editor.getOption(EditorOption.suggest); + let newDeco: IModelDeltaDecoration[] = []; - if (highlightReplaceRange) { - const info = this._controller.getOverwriteInfo(item, overwriteOnAccept); - const position = this._controller.editor.getPosition()!; - const range = new Range( - position.lineNumber, position.column - info.overwriteBefore, - position.lineNumber, position.column + info.overwriteAfter - ); - - this._decorations = this._controller.editor.deltaDecorations(this._decorations, [{ - range, - options: { - className: 'suggestReplace', - stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges - } - }]); + if (!this._shiftKeyListener) { + this._shiftKeyListener = shiftKey.event(() => this._highlight(this._currentItem!)); } + + const info = this._controller.getOverwriteInfo(item, shiftKey.isPressed); + const position = this._controller.editor.getPosition()!; + + if (opts.insertMode === 'insert' && info.overwriteAfter > 0) { + // wants inserts but got replace-mode -> highlight AFTER range + newDeco = [{ + range: new Range(position.lineNumber, position.column, position.lineNumber, position.column + info.overwriteAfter), + options: { inlineClassName: 'suggest-insertMode-goes' } + }]; + + } else if (opts.insertMode === 'replace' && info.overwriteAfter === 0) { + // want replace but likely got insert -> highlight AFTER range + const wordInfo = this._controller.editor.getModel()?.getWordAtPosition(position); + if (wordInfo && wordInfo.endColumn > position.column) { + newDeco = [{ + range: new Range(position.lineNumber, position.column, position.lineNumber, wordInfo.endColumn), + options: { inlineClassName: 'suggest-insertMode-stays' } + }]; + } + } + + // update editor decorations + this._decorations = this._controller.editor.deltaDecorations(this._decorations, newDeco); } } + +const shiftKey = new class ShiftKey extends Emitter { + + private readonly _subscriptions = new DisposableStore(); + private _isPressed: boolean = false; + + constructor() { + super(); + this._subscriptions.add(domEvent(document.body, 'keydown')(e => this.isPressed = e.shiftKey)); + this._subscriptions.add(domEvent(document.body, 'keyup')(() => this.isPressed = false)); + this._subscriptions.add(domEvent(document.body, 'mouseleave')(() => this.isPressed = false)); + this._subscriptions.add(domEvent(document.body, 'blur')(() => this.isPressed = false)); + } + + get isPressed(): boolean { + return this._isPressed; + } + + set isPressed(value: boolean) { + if (this._isPressed !== value) { + this._isPressed = value; + this.fire(value); + } + } + + dispose() { + this._subscriptions.dispose(); + super.dispose(); + } +}; diff --git a/src/vs/editor/contrib/suggest/test/completionModel.test.ts b/src/vs/editor/contrib/suggest/test/completionModel.test.ts index 05eda1333a9..6a26c5fd584 100644 --- a/src/vs/editor/contrib/suggest/test/completionModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/completionModel.test.ts @@ -8,7 +8,7 @@ import * as modes from 'vs/editor/common/modes'; import { CompletionModel } from 'vs/editor/contrib/suggest/completionModel'; import { CompletionItem, getSuggestionComparator, SnippetSortOrder } from 'vs/editor/contrib/suggest/suggest'; import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance'; -import { EditorOptions } from 'vs/editor/common/config/editorOptions'; +import { EditorOptions, InternalSuggestOptions } from 'vs/editor/common/config/editorOptions'; export function createSuggestItem(label: string, overwriteBefore: number, kind = modes.CompletionItemKind.Property, incomplete: boolean = false, position: IPosition = { lineNumber: 1, column: 1 }, sortText?: string, filterText?: string): CompletionItem { const suggestion: modes.CompletionItem = { @@ -33,9 +33,8 @@ export function createSuggestItem(label: string, overwriteBefore: number, kind = } suite('CompletionModel', function () { - let defaultOptions = { - overwriteOnAccept: false, - highlightReplaceRange: true, + let defaultOptions = { + insertMode: 'insert', snippetsPreventQuickSuggestions: true, filterGraceful: true, localityBonus: false, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 990de2aa73d..42f8e0317ae 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3396,11 +3396,7 @@ declare namespace monaco.editor { /** * Overwrite word ends on accept. Default to false. */ - overwriteOnAccept?: boolean; - /** - * Should the editor highlight what text suggest will replace. - */ - highlightReplaceRange?: boolean; + insertMode?: 'insert' | 'replace'; /** * Enable graceful matching. Defaults to true. */ From 22c104ce5b7705e9010fabd479712985089b5ec8 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 20 Nov 2019 17:18:12 +0100 Subject: [PATCH 122/246] Watch view: reveal newly added expression fixes #84006 --- .../contrib/debug/browser/watchExpressionsView.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index 2a601f78e9d..f4711050305 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -86,11 +86,14 @@ export class WatchExpressionsView extends ViewletPanel { this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); this._register(this.tree.onMouseDblClick(e => this.onMouseDblClick(e))); - this._register(this.debugService.getModel().onDidChangeWatchExpressions(we => { + this._register(this.debugService.getModel().onDidChangeWatchExpressions(async we => { if (!this.isBodyVisible()) { this.needsRefresh = true; } else { - this.tree.updateChildren(); + await this.tree.updateChildren(); + if (we instanceof Expression) { + this.tree.reveal(we); + } } })); this._register(this.debugService.getViewModel().onDidFocusStackFrame(() => { From 1a88e9d1662c5cd95a1ac5b20d17889390d6e5d6 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 20 Nov 2019 17:20:01 +0100 Subject: [PATCH 123/246] Allow null (microsoft/vscode-remote-release#1201) --- .../schemas/attachContainer.schema.json | 5 ++++- .../configuration-editing/schemas/devContainer.schema.json | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/extensions/configuration-editing/schemas/attachContainer.schema.json b/extensions/configuration-editing/schemas/attachContainer.schema.json index d2ea16a783a..2bce4ff6f00 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -21,7 +21,10 @@ "remoteEnv": { "type": "object", "additionalProperties": { - "type": "string" + "type": [ + "string", + "null" + ] }, "description": "Remote environment variables." }, diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index 9c1d7aad1a3..5a1ace7e41f 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -25,7 +25,10 @@ "remoteEnv": { "type": "object", "additionalProperties": { - "type": "string" + "type": [ + "string", + "null" + ] }, "description": "Remote environment variables." }, From ae0796f03a275e29082e741c79001d58b6d7a0ca Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Nov 2019 17:29:33 +0100 Subject: [PATCH 124/246] editor - move auto save to its own file (fixes web to start) --- .../parts/editor/editor.contribution.ts | 4 + .../browser/parts/editor/editorAutoSave.ts | 95 +++++++++++++++++++ .../services/editor/browser/editorService.ts | 56 +---------- 3 files changed, 103 insertions(+), 52 deletions(-) create mode 100644 src/vs/workbench/browser/parts/editor/editorAutoSave.ts diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 7dee5c30a3a..dbfd43037c9 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -54,6 +54,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { EditorAutoSave } from 'vs/workbench/browser/parts/editor/editorAutoSave'; // Register String Editor Registry.as(EditorExtensions.Editors).registerEditor( @@ -224,6 +225,9 @@ registerEditorContribution(OpenWorkspaceButtonContribution.ID, OpenWorkspaceButt // Register Editor Status Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorStatus, LifecyclePhase.Ready); +// Register Editor Auto Save +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorAutoSave, LifecyclePhase.Ready); + // Register Status Actions const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(SyncActionDescriptor.create(ChangeModeAction, ChangeModeAction.ID, ChangeModeAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_M) }), 'Change Language Mode'); diff --git a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts new file mode 100644 index 00000000000..1b437764387 --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { SaveReason, IEditorIdentifier, IEditorInput, GroupIdentifier } from 'vs/workbench/common/editor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; + +export class EditorAutoSave extends Disposable implements IWorkbenchContribution { + + private lastActiveEditor: IEditorInput | null = null; + private lastActiveGroupId: GroupIdentifier | null = null; + private lastActiveEditorControlDisposable = this._register(new DisposableStore()); + + constructor( + @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, + @IHostService private readonly hostService: IHostService, + @IEditorService private readonly editorService: IEditorService, + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService + ) { + super(); + + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.hostService.onDidChangeFocus(focused => this.onWindowFocusChange(focused))); + this._register(this.editorService.onDidActiveEditorChange(() => this.onDidActiveEditorChange())); + } + + private onWindowFocusChange(focused: boolean): void { + if (!focused) { + this.maybeTriggerAutoSave(SaveReason.WINDOW_CHANGE); + } + } + + private onDidActiveEditorChange(): void { + + // Treat editor change like a focus change for our last active editor if any + if (this.lastActiveEditor && typeof this.lastActiveGroupId === 'number') { + this.maybeTriggerAutoSave(SaveReason.FOCUS_CHANGE, { groupId: this.lastActiveGroupId, editor: this.lastActiveEditor }); + } + + // Remember as last active + const activeGroup = this.editorGroupService.activeGroup; + const activeEditor = this.lastActiveEditor = activeGroup.activeEditor; + this.lastActiveGroupId = activeGroup.id; + + // Dispose previous active control listeners + this.lastActiveEditorControlDisposable.clear(); + + // Listen to focus changes on control for auto save + if (activeGroup && activeEditor) { + const activeTextEditorWidget = this.editorService.activeTextEditorWidget; + const controlsToObserve: ICodeEditor[] = []; + + if (isCodeEditor(activeTextEditorWidget)) { + controlsToObserve.push(activeTextEditorWidget); + } else if (isDiffEditor(activeTextEditorWidget)) { + controlsToObserve.push(activeTextEditorWidget.getOriginalEditor(), activeTextEditorWidget.getModifiedEditor()); + } + + controlsToObserve.forEach(control => this.lastActiveEditorControlDisposable.add(control.onDidBlurEditorWidget(() => { + this.maybeTriggerAutoSave(SaveReason.FOCUS_CHANGE, { groupId: activeGroup.id, editor: activeEditor }); + }))); + } + } + + private maybeTriggerAutoSave(reason: SaveReason, editorIdentifier?: IEditorIdentifier): void { + if (editorIdentifier && (editorIdentifier.editor.isReadonly() || editorIdentifier.editor.isUntitled())) { + return; // no auto save for readonly or untitled editors + } + + const mode = this.filesConfigurationService.getAutoSaveMode(); + + // Determine if we need to save all. In case of a window focus change we also save if auto save mode + // is configured to be ON_FOCUS_CHANGE (editor focus change) + if ( + (reason === SaveReason.WINDOW_CHANGE && (mode === AutoSaveMode.ON_FOCUS_CHANGE || mode === AutoSaveMode.ON_WINDOW_CHANGE)) || + (reason === SaveReason.FOCUS_CHANGE && mode === AutoSaveMode.ON_FOCUS_CHANGE) + ) { + if (editorIdentifier) { + this.editorService.save(editorIdentifier, { reason }); + } else { + this.editorService.saveAll({ reason }); + } + } + } +} diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index dbf66587513..81a8a42b18e 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -5,7 +5,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IResourceInput, ITextEditorOptions, IEditorOptions, EditorActivation } from 'vs/platform/editor/common/editor'; -import { IEditorInput, IEditor, GroupIdentifier, IFileEditorInput, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IFileInputFactory, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditor, ITextDiffEditor, ITextSideBySideEditor, toResource, SideBySideEditor, IRevertOptions, SaveReason } from 'vs/workbench/common/editor'; +import { IEditorInput, IEditor, GroupIdentifier, IFileEditorInput, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IFileInputFactory, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditor, ITextDiffEditor, ITextSideBySideEditor, toResource, SideBySideEditor, IRevertOptions } from 'vs/workbench/common/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { DataUriEditorInput } from 'vs/workbench/common/editor/dataUriEditorInput'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -28,8 +28,6 @@ import { IEditorGroupView, IEditorOpeningEvent, EditorServiceImpl } from 'vs/wor import { ILabelService } from 'vs/platform/label/common/label'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; type CachedEditorInput = ResourceEditorInput | IFileEditorInput | DataUriEditorInput; type OpenInEditorGroup = IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE; @@ -61,7 +59,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { private lastActiveEditor: IEditorInput | null = null; private lastActiveGroupId: GroupIdentifier | null = null; - private lastActiveEditorControlDisposable = this._register(new DisposableStore()); constructor( @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -69,9 +66,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { @IInstantiationService private readonly instantiationService: IInstantiationService, @ILabelService private readonly labelService: ILabelService, @IFileService private readonly fileService: IFileService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, - @IHostService private readonly hostService: IHostService + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(); @@ -86,33 +81,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { this.editorGroupService.whenRestored.then(() => this.onEditorsRestored()); this.editorGroupService.onDidActiveGroupChange(group => this.handleActiveEditorChange(group)); this.editorGroupService.onDidAddGroup(group => this.registerGroupListeners(group as IEditorGroupView)); - - // Auto save support - this._register(this.onDidActiveEditorChange(() => this.onEditorFocusLost())); - this._register(this.hostService.onDidChangeFocus(focused => this.onWindowFocusChange(focused))); - } - - private onEditorFocusLost(): void { - this.maybeTriggerSaveAll(SaveReason.FOCUS_CHANGE); - } - - private onWindowFocusChange(focused: boolean): void { - if (!focused) { - this.maybeTriggerSaveAll(SaveReason.WINDOW_CHANGE); - } - } - - private maybeTriggerSaveAll(reason: SaveReason): void { - const mode = this.filesConfigurationService.getAutoSaveMode(); - - // Determine if we need to save all. In case of a window focus change we also save if auto save mode - // is configured to be ON_FOCUS_CHANGE (editor focus change) - if ( - (reason === SaveReason.WINDOW_CHANGE && (mode === AutoSaveMode.ON_FOCUS_CHANGE || mode === AutoSaveMode.ON_WINDOW_CHANGE)) || - (reason === SaveReason.FOCUS_CHANGE && mode === AutoSaveMode.ON_FOCUS_CHANGE) - ) { - this.saveAll({ reason }); - } } private onEditorsRestored(): void { @@ -150,18 +118,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { this.lastActiveGroupId = activeGroup.id; this.lastActiveEditor = activeGroup.activeEditor; - // Dispose previous active control listeners - this.lastActiveEditorControlDisposable.clear(); - - // Listen to focus changes on control for auto save - const controlsToObserve: ICodeEditor[] = []; - if (isCodeEditor(this.activeTextEditorWidget)) { - controlsToObserve.push(this.activeTextEditorWidget); - } else if (isDiffEditor(this.activeTextEditorWidget)) { - controlsToObserve.push(this.activeTextEditorWidget.getOriginalEditor(), this.activeTextEditorWidget.getModifiedEditor()); - } - controlsToObserve.forEach(control => this.lastActiveEditorControlDisposable.add(control.onDidBlurEditorWidget(() => this.onEditorFocusLost()))); - // Fire event to outside parties this._onDidActiveEditorChange.fire(); } @@ -824,9 +780,7 @@ export class DelegatingEditorService extends EditorService { @IInstantiationService instantiationService: IInstantiationService, @ILabelService labelService: ILabelService, @IFileService fileService: IFileService, - @IConfigurationService configurationService: IConfigurationService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, - @IHostService hostService: IHostService + @IConfigurationService configurationService: IConfigurationService ) { super( editorGroupService, @@ -834,9 +788,7 @@ export class DelegatingEditorService extends EditorService { instantiationService, labelService, fileService, - configurationService, - filesConfigurationService, - hostService + configurationService ); } From 3e554edc58fa67670236c0becd6bfad869d3f4ee Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Nov 2019 17:31:12 +0100 Subject: [PATCH 125/246] use undefined over null --- src/vs/workbench/browser/parts/editor/editorAutoSave.ts | 7 ++++--- src/vs/workbench/services/editor/browser/editorService.ts | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts index 1b437764387..0902582bf2a 100644 --- a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts +++ b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts @@ -11,11 +11,12 @@ import { SaveReason, IEditorIdentifier, IEditorInput, GroupIdentifier } from 'vs import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { withNullAsUndefined } from 'vs/base/common/types'; export class EditorAutoSave extends Disposable implements IWorkbenchContribution { - private lastActiveEditor: IEditorInput | null = null; - private lastActiveGroupId: GroupIdentifier | null = null; + private lastActiveEditor: IEditorInput | undefined = undefined; + private lastActiveGroupId: GroupIdentifier | undefined = undefined; private lastActiveEditorControlDisposable = this._register(new DisposableStore()); constructor( @@ -49,7 +50,7 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution // Remember as last active const activeGroup = this.editorGroupService.activeGroup; - const activeEditor = this.lastActiveEditor = activeGroup.activeEditor; + const activeEditor = this.lastActiveEditor = withNullAsUndefined(activeGroup.activeEditor); this.lastActiveGroupId = activeGroup.id; // Dispose previous active control listeners diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 81a8a42b18e..56f020049ae 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -57,8 +57,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { private fileInputFactory: IFileInputFactory; private openEditorHandlers: IOpenEditorOverrideHandler[] = []; - private lastActiveEditor: IEditorInput | null = null; - private lastActiveGroupId: GroupIdentifier | null = null; + private lastActiveEditor: IEditorInput | undefined = undefined; + private lastActiveGroupId: GroupIdentifier | undefined = undefined; constructor( @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -116,7 +116,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Remember as last active const activeGroup = this.editorGroupService.activeGroup; this.lastActiveGroupId = activeGroup.id; - this.lastActiveEditor = activeGroup.activeEditor; + this.lastActiveEditor = withNullAsUndefined(activeGroup.activeEditor); // Fire event to outside parties this._onDidActiveEditorChange.fire(); From f09c852a28156d4904dabf581c103b5dcc17721e Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 20 Nov 2019 17:21:39 +0100 Subject: [PATCH 126/246] web worker: support F5 debugging w/o launch.config --- extensions/vscode-api-tests/package.json | 7 +++- extensions/vscode-api-tests/src/extension.ts | 41 +++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index d80a384c6fd..8ac6b2806ca 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -7,7 +7,8 @@ "enableProposedApi": true, "private": true, "activationEvents": [ - "onFileSystem:memfs" + "onFileSystem:memfs", + "onDebug" ], "main": "./out/extension", "engines": { @@ -72,6 +73,10 @@ { "type": "mock", "label": "Mock Debug", + "languages": [ + "markdown" + ], + "configurationAttributes": { "launch": { "required": [ diff --git a/extensions/vscode-api-tests/src/extension.ts b/extensions/vscode-api-tests/src/extension.ts index b5d4902eca7..e662ba6b52a 100644 --- a/extensions/vscode-api-tests/src/extension.ts +++ b/extensions/vscode-api-tests/src/extension.ts @@ -896,6 +896,7 @@ export class MemFS implements vscode.FileSystemProvider, vscode.FileSearchProvid //--------------------------------------------------------------------------- function enableDebug(context: vscode.ExtensionContext, memFs: MemFS): void { + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('mock', new MockConfigurationProvider())); context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('mock', new MockDebugAdapterDescriptorFactory(memFs))); } @@ -3796,12 +3797,34 @@ export class Handles { //--------------------------------------------------------------------------- -function basename(path: string): string { - const pos = path.lastIndexOf('/'); - if (pos >= 0) { - return path.substring(pos + 1); +class MockConfigurationProvider implements vscode.DebugConfigurationProvider { + + /** + * Massage a debug configuration just before a debug session is being launched, + * e.g. add all missing attributes to the debug configuration. + */ + resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult { + + // if launch.json is missing or empty + if (!config.type && !config.request && !config.name) { + const editor = vscode.window.activeTextEditor; + if (editor && editor.document.languageId === 'markdown') { + config.type = 'mock'; + config.name = 'Launch'; + config.request = 'launch'; + config.program = '${file}'; // editor.document.fileName; // '${file}'; + config.stopOnEntry = true; + } + } + + if (!config.program) { + return vscode.window.showInformationMessage('Cannot find a program to debug').then(_ => { + return undefined; // abort launch + }); + } + + return config; } - return path; } export class MockDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory { @@ -3818,6 +3841,14 @@ export class MockDebugAdapterDescriptorFactory implements vscode.DebugAdapterDes } } +function basename(path: string): string { + const pos = path.lastIndexOf('/'); + if (pos >= 0) { + return path.substring(pos + 1); + } + return path; +} + function timeout(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } From e38b49b5a3714ca9bad98d6a2822ab8763fb20d2 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 20 Nov 2019 17:35:36 +0100 Subject: [PATCH 127/246] restructure variable resolver --- .../api/common/extHostDebugService.ts | 19 ++++--- .../workbench/api/node/extHostDebugService.ts | 54 ++----------------- src/vs/workbench/api/node/extHostTask.ts | 5 +- .../api/node/extHostTerminalService.ts | 4 +- .../common/variableResolver.ts | 38 +++++++------ 5 files changed, 44 insertions(+), 76 deletions(-) diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 5050a8dc1df..6c1802ec3e4 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -33,6 +33,7 @@ import * as vscode from 'vscode'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { IProcessEnvironment } from 'vs/base/common/platform'; export const IExtHostDebugService = createDecorator('IExtHostDebugService'); @@ -372,10 +373,14 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb return Promise.resolve(undefined); } + protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService { + return new ExtHostVariableResolverService(folders, editorService, configurationService); + } + public async $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise { if (!this._variableResolver) { const [workspaceFolders, configProvider] = await Promise.all([this._workspaceService.getWorkspaceFolders2(), this._configurationService.getConfigProvider()]); - this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._editorsService, configProvider!); + this._variableResolver = this.createVariableResolver(workspaceFolders || [], this._editorsService, configProvider); } let ws: IWorkspaceFolder | undefined; const folder = await this.getFolder(folderUri); @@ -954,9 +959,9 @@ export class ExtHostDebugConsole implements vscode.DebugConsole { } } -class ExtHostVariableResolverService extends AbstractVariableResolverService { +export class ExtHostVariableResolverService extends AbstractVariableResolverService { - constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider) { + constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment) { super({ getFolderUri: (folderName: string): URI | undefined => { const found = folders.filter(f => f.name === folderName); @@ -972,7 +977,7 @@ class ExtHostVariableResolverService extends AbstractVariableResolverService { return configurationService.getConfiguration(undefined, folderUri).get(section); }, getExecPath: (): string | undefined => { - return undefined; + return env ? env['VSCODE_EXEC_PATH'] : undefined; }, getFilePath: (): string | undefined => { const activeEditor = editorService.activeEditor(); @@ -998,7 +1003,7 @@ class ExtHostVariableResolverService extends AbstractVariableResolverService { } return undefined; } - }, {} /* process.env as IProcessEnvironment */); + }, env); } } @@ -1056,9 +1061,11 @@ interface IDapTransport { stop(): void; } +/* + * experimental: call directly into a debug adapter implementation + */ class DirectDebugAdapter extends AbstractDebugAdapter implements IDapTransport { - private _sendUp!: (msg: DebugProtocol.ProtocolMessage) => void; constructor(implementation: any) { diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 3f62931e053..ac05c037e00 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import * as vscode from 'vscode'; -import * as path from 'vs/base/common/path'; import { DebugAdapterExecutable } from 'vs/workbench/api/common/extHostTypes'; import { ExecutableDebugAdapter, SocketDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; @@ -18,14 +17,12 @@ import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -import { ExtHostDebugServiceBase, ExtHostDebugSession } from 'vs/workbench/api/common/extHostDebugService'; +import { ExtHostDebugServiceBase, ExtHostDebugSession, ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService'; import { ISignService } from 'vs/platform/sign/common/sign'; import { SignService } from 'vs/platform/sign/node/signService'; import { hasChildProcesses, prepareCommand, runInExternalTerminal } from 'vs/workbench/contrib/debug/node/terminals'; import { IDisposable } from 'vs/base/common/lifecycle'; import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver'; -import { URI } from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; import { IProcessEnvironment } from 'vs/base/common/platform'; @@ -122,52 +119,9 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { } return super.$runInTerminal(args); } -} -export class ExtHostVariableResolverService extends AbstractVariableResolverService { - - constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider) { - super({ - getFolderUri: (folderName: string): URI | undefined => { - const found = folders.filter(f => f.name === folderName); - if (found && found.length > 0) { - return found[0].uri; - } - return undefined; - }, - getWorkspaceFolderCount: (): number => { - return folders.length; - }, - getConfigurationValue: (folderUri: URI, section: string): string | undefined => { - return configurationService.getConfiguration(undefined, folderUri).get(section); - }, - getExecPath: (): string | undefined => { - return process.env['VSCODE_EXEC_PATH']; - }, - getFilePath: (): string | undefined => { - const activeEditor = editorService.activeEditor(); - if (activeEditor) { - const resource = activeEditor.document.uri; - if (resource.scheme === Schemas.file) { - return path.normalize(resource.fsPath); - } - } - return undefined; - }, - getSelectedText: (): string | undefined => { - const activeEditor = editorService.activeEditor(); - if (activeEditor && !activeEditor.selection.isEmpty) { - return activeEditor.document.getText(activeEditor.selection); - } - return undefined; - }, - getLineNumber: (): string | undefined => { - const activeEditor = editorService.activeEditor(); - if (activeEditor) { - return String(activeEditor.selection.end.line + 1); - } - return undefined; - } - }, process.env as IProcessEnvironment); + protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService { + return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as IProcessEnvironment); } + } diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 6e1a647067a..d6d9a1ee84c 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -12,7 +12,7 @@ import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import * as vscode from 'vscode'; import * as tasks from '../common/shared/tasks'; import * as Objects from 'vs/base/common/objects'; -import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService'; +import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService'; import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -23,6 +23,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData import { ExtHostTaskBase, TaskHandleDTO, TaskDTO, CustomExecutionDTO, HandlerData } from 'vs/workbench/api/common/extHostTask'; import { Schemas } from 'vs/base/common/network'; import { ILogService } from 'vs/platform/log/common/log'; +import { IProcessEnvironment } from 'vs/base/common/platform'; export class ExtHostTask extends ExtHostTaskBase { private _variableResolver: ExtHostVariableResolverService | undefined; @@ -102,7 +103,7 @@ export class ExtHostTask extends ExtHostTaskBase { private async getVariableResolver(workspaceFolders: vscode.WorkspaceFolder[]): Promise { if (this._variableResolver === undefined) { const configProvider = await this._configurationService.getConfigProvider(); - this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider); + this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider, process.env as IProcessEnvironment); } return this._variableResolver; } diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 64d1a0005b7..bac43635693 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -16,7 +16,7 @@ import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/t import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService'; +import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService'; import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { getSystemShell, detectAvailableShells } from 'vs/workbench/contrib/terminal/node/terminal'; import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; @@ -120,7 +120,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { private async _updateVariableResolver(): Promise { const configProvider = await this._extHostConfiguration.getConfigProvider(); const workspaceFolders = await this._extHostWorkspace.getWorkspaceFolders2(); - this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider); + this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider, process.env as platform.IProcessEnvironment); } public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index c6f302d1b3c..5947f5e4e96 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -32,17 +32,24 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe _serviceBrand: undefined; + private _context: IVariableResolveContext; + private _envVariables?: IProcessEnvironment; protected _contributedVariables: Map Promise> = new Map(); - constructor( - private _context: IVariableResolveContext, - private _envVariables: IProcessEnvironment - ) { - if (isWindows && _envVariables) { - this._envVariables = Object.create(null); - Object.keys(_envVariables).forEach(key => { - this._envVariables[key.toLowerCase()] = _envVariables[key]; - }); + + constructor(_context: IVariableResolveContext, _envVariables?: IProcessEnvironment) { + this._context = _context; + if (_envVariables) { + if (isWindows) { + // windows env variables are case insensitive + const ev: IProcessEnvironment = Object.create(null); + this._envVariables = ev; + Object.keys(_envVariables).forEach(key => { + ev[key.toLowerCase()] = _envVariables[key]; + }); + } else { + this._envVariables = _envVariables; + } } } @@ -180,14 +187,13 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe case 'env': if (argument) { - if (isWindows) { - argument = argument.toLowerCase(); + if (this._envVariables) { + const env = this._envVariables[isWindows ? argument.toLowerCase() : argument]; + if (types.isString(env)) { + return env; + } } - const env = this._envVariables[argument]; - if (types.isString(env)) { - return env; - } - // For `env` we should do the same as a normal shell does - evaluates missing envs to an empty string #46436 + // For `env` we should do the same as a normal shell does - evaluates undefined envs to an empty string #46436 return ''; } throw new Error(localize('missingEnvVarName', "'{0}' can not be resolved because no environment variable name is given.", match)); From 81275d9ce5d65672560313985a3db1a696354674 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Wed, 20 Nov 2019 12:24:11 -0500 Subject: [PATCH 128/246] Fixes #85056 - manually size workbench again This resolves issues with vh units on iOS/Android --- src/vs/workbench/browser/layout.ts | 8 +++++--- src/vs/workbench/browser/media/style.css | 2 -- .../browser/parts/activitybar/media/activityaction.css | 4 ++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 70b956eb862..94382d7ceb4 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -5,7 +5,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { EventType, addDisposableListener, addClass, removeClass, isAncestor, getClientArea, Dimension, toggleClass } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, addClass, removeClass, isAncestor, getClientArea, Dimension, toggleClass, position, size } from 'vs/base/browser/dom'; import { onDidChangeFullscreen, isFullscreen } from 'vs/base/browser/browser'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -905,14 +905,16 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } getClientArea(): Dimension { - const dim = getClientArea(this.parent); - return this.state.windowBorder ? new Dimension(dim.width - 2, dim.height - 2) : dim; + return getClientArea(this.parent); } layout(): void { if (!this.disposed) { this._dimension = this.getClientArea(); + position(this.container, 0, 0, 0, 0, 'relative'); + size(this.container, this._dimension.width, this._dimension.height); + // Layout the grid widget this.workbenchGrid.layout(this._dimension.width, this._dimension.height); diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css index d087cd74997..f6c7bd7b620 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -56,8 +56,6 @@ body.web { position: relative; z-index: 1; overflow: hidden; - height: 100vh; - width: 100vw; } .monaco-workbench.border:not(.fullscreen) { diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index 28c43d3ab7a..b15991bd888 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -91,6 +91,10 @@ height: 100%; } +.monaco-workbench.border .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .active-item-indicator { + left: -2px; +} + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .badge .badge-content { position: absolute; top: 24px; From 247907cde182440bcb237ee0b20b9cba6a3127a0 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 20 Nov 2019 18:32:22 +0100 Subject: [PATCH 129/246] fix Debug console stop listening output fixes #83906 --- src/vs/workbench/contrib/debug/common/replModel.ts | 5 +++-- .../workbench/contrib/debug/test/browser/debugModel.test.ts | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/common/replModel.ts b/src/vs/workbench/contrib/debug/common/replModel.ts index a02e8305f8d..4083fafaa6c 100644 --- a/src/vs/workbench/contrib/debug/common/replModel.ts +++ b/src/vs/workbench/contrib/debug/common/replModel.ts @@ -10,7 +10,7 @@ import { ExpressionContainer } from 'vs/workbench/contrib/debug/common/debugMode import { isString, isUndefinedOrNull, isObject } from 'vs/base/common/types'; import { basenameOrAuthority } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { endsWith } from 'vs/base/common/strings'; +import { endsWith, startsWith } from 'vs/base/common/strings'; import { generateUuid } from 'vs/base/common/uuid'; import { Emitter } from 'vs/base/common/event'; @@ -134,8 +134,9 @@ 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 && !endsWith(previousElement.value, '\n') && !endsWith(previousElement.value, '\r\n')) { + if (!startsWith(data, '\n') && previousElement instanceof SimpleReplElement && previousElement.severity === sev && !endsWith(previousElement.value, '\n') && !endsWith(previousElement.value, '\r\n')) { previousElement.value += data; + this._onDidChangeElements.fire(); } else { const element = new SimpleReplElement(session, `topReplElement:${topReplElementCounter++}`, data, sev, source); this.addReplElement(element); diff --git a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts index 9198304bfab..a7256a2cd44 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts @@ -498,11 +498,13 @@ suite('Debug - Model', () => { repl.appendToRepl(session, '3\n4', severity.Info); repl.appendToRepl(session, '5\n', severity.Info); repl.appendToRepl(session, '6', severity.Info); + repl.appendToRepl(session, '\n7', severity.Info); elements = repl.getReplElements(); - assert.equal(elements.length, 3); + assert.equal(elements.length, 4); assert.equal(elements[0], '1\n'); assert.equal(elements[1], '23\n45\n'); assert.equal(elements[2], '6'); + assert.equal(elements[3], '\n7'); }); test('repl merging', () => { From e3ad8490a36e2ded99902c72e9c49d2cda556225 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 20 Nov 2019 18:47:52 +0100 Subject: [PATCH 130/246] fixes #83753 --- src/vs/workbench/contrib/debug/browser/media/repl.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index 9b6b7dccecc..68c1f96bacc 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -14,6 +14,7 @@ .repl .repl-tree .monaco-tl-contents { user-select: text; -webkit-user-select: text; + white-space: pre; } .repl .repl-tree.word-wrap .monaco-tl-contents { From af7bca12297d4f5138fa3d23081af90535bc556c Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 20 Nov 2019 10:58:56 -0800 Subject: [PATCH 131/246] Fix #85162, vertically center breakpoint icons --- .../contrib/debug/browser/media/debug.contribution.css | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css index b7129400782..47fdbe45c60 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css @@ -11,11 +11,19 @@ opacity: .4 !important; } +.inline-breakpoint-widget.codicon { + display: flex !important; + align-items: center; +} + /* overlapped icons */ .inline-breakpoint-widget.codicon-debug-breakpoint-stackframe-dot::after { position: absolute; top: 0; left: 0; + bottom: 0; + margin: auto; + display: table; } .codicon-debug-breakpoint.codicon-debug-breakpoint-stackframe-focused::after { @@ -31,7 +39,7 @@ } .monaco-editor .inline-breakpoint-widget.line-start { - left: -0.45em !important; + left: -8px !important; } .monaco-editor .debug-breakpoint-placeholder::before, From 8eaf2a37421c64a1b25441038c5d77d37a9709b5 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Wed, 20 Nov 2019 10:59:22 -0800 Subject: [PATCH 132/246] rename splitview panels to panes (#85166) * rename splitview panels to panes * rename files * customViewPane --- .../splitview/{panelview.css => paneview.css} | 46 ++--- .../splitview/{panelview.ts => paneview.ts} | 144 ++++++++-------- .../{panelviewlet.css => paneviewlet.css} | 2 +- .../api/browser/viewsExtensionPoint.ts | 4 +- .../browser/parts/views/customView.ts | 6 +- .../{panelviewlet.css => paneviewlet.css} | 8 +- .../views/{panelViewlet.ts => paneViewlet.ts} | 162 +++++++++--------- .../browser/parts/views/viewsViewlet.ts | 86 +++++----- .../contrib/debug/browser/breakpointsView.ts | 6 +- .../contrib/debug/browser/callStackView.ts | 6 +- .../contrib/debug/browser/debugViewlet.ts | 30 ++-- .../debug/browser/loadedScriptsView.ts | 6 +- .../contrib/debug/browser/variablesView.ts | 6 +- .../debug/browser/watchExpressionsView.ts | 6 +- .../extensions/browser/extensionsViewlet.ts | 10 +- .../extensions/browser/extensionsViews.ts | 6 +- .../contrib/files/browser/explorerViewlet.ts | 4 +- .../files/browser/media/explorerviewlet.css | 4 +- .../contrib/files/browser/views/emptyView.ts | 6 +- .../files/browser/views/explorerView.ts | 8 +- .../files/browser/views/openEditorsView.ts | 6 +- .../outline/browser/outline.contribution.ts | 4 +- .../{outlinePanel.css => outlinePane.css} | 18 +- .../{outlinePanel.ts => outlinePane.ts} | 12 +- .../scm/browser/{mainPanel.ts => mainPane.ts} | 16 +- .../contrib/scm/browser/media/scmViewlet.css | 2 +- .../{repositoryPanel.ts => repositoryPane.ts} | 8 +- .../contrib/scm/browser/scmViewlet.ts | 26 +-- .../contrib/search/browser/searchView.ts | 6 +- 29 files changed, 327 insertions(+), 327 deletions(-) rename src/vs/base/browser/ui/splitview/{panelview.css => paneview.css} (50%) rename src/vs/base/browser/ui/splitview/{panelview.ts => paneview.ts} (71%) rename src/vs/base/browser/ui/tree/media/{panelviewlet.css => paneviewlet.css} (89%) rename src/vs/workbench/browser/parts/views/media/{panelviewlet.css => paneviewlet.css} (72%) rename src/vs/workbench/browser/parts/views/{panelViewlet.ts => paneViewlet.ts} (71%) rename src/vs/workbench/contrib/outline/browser/{outlinePanel.css => outlinePane.css} (67%) rename src/vs/workbench/contrib/outline/browser/{outlinePanel.ts => outlinePane.ts} (98%) rename src/vs/workbench/contrib/scm/browser/{mainPanel.ts => mainPane.ts} (96%) rename src/vs/workbench/contrib/scm/browser/{repositoryPanel.ts => repositoryPane.ts} (99%) diff --git a/src/vs/base/browser/ui/splitview/panelview.css b/src/vs/base/browser/ui/splitview/paneview.css similarity index 50% rename from src/vs/base/browser/ui/splitview/panelview.css rename to src/vs/base/browser/ui/splitview/paneview.css index ff3554c4227..c1397d6c62c 100644 --- a/src/vs/base/browser/ui/splitview/panelview.css +++ b/src/vs/base/browser/ui/splitview/paneview.css @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-panel-view { +.monaco-pane-view { width: 100%; height: 100%; } -.monaco-panel-view .panel { +.monaco-pane-view .pane { overflow: hidden; width: 100%; height: 100%; @@ -16,7 +16,7 @@ flex-direction: column; } -.monaco-panel-view .panel > .panel-header { +.monaco-pane-view .pane > .pane-header { font-size: 11px; font-weight: bold; text-transform: uppercase; @@ -26,7 +26,7 @@ align-items: center; } -.monaco-panel-view .panel > .panel-header > .twisties { +.monaco-pane-view .pane > .pane-header > .twisties { width: 20px; display: flex; align-items: center; @@ -36,26 +36,26 @@ flex-shrink: 0; } -.monaco-panel-view .panel > .panel-header.expanded > .twisties::before { +.monaco-pane-view .pane > .pane-header.expanded > .twisties::before { transform: rotate(90deg); } -/* TODO: actions should be part of the panel, but they aren't yet */ -.monaco-panel-view .panel > .panel-header > .actions { +/* TODO: actions should be part of the pane, but they aren't yet */ +.monaco-pane-view .pane > .pane-header > .actions { display: none; flex: 1; } -/* TODO: actions should be part of the panel, but they aren't yet */ -.monaco-panel-view .panel:hover > .panel-header.expanded > .actions, -.monaco-panel-view .panel > .panel-header.actions-always-visible.expanded > .actions, -.monaco-panel-view .panel > .panel-header.focused.expanded > .actions { +/* TODO: actions should be part of the pane, but they aren't yet */ +.monaco-pane-view .pane:hover > .pane-header.expanded > .actions, +.monaco-pane-view .pane > .pane-header.actions-always-visible.expanded > .actions, +.monaco-pane-view .pane > .pane-header.focused.expanded > .actions { display: initial; } -/* TODO: actions should be part of the panel, but they aren't yet */ -.monaco-panel-view .panel > .panel-header > .actions .action-label.icon, -.monaco-panel-view .panel > .panel-header > .actions .action-label.codicon { +/* TODO: actions should be part of the pane, but they aren't yet */ +.monaco-pane-view .pane > .pane-header > .actions .action-label.icon, +.monaco-pane-view .pane > .pane-header > .actions .action-label.codicon { width: 28px; height: 22px; background-size: 16px; @@ -69,33 +69,33 @@ } /* Bold font style does not go well with CJK fonts */ -.monaco-panel-view:lang(zh-Hans) .panel > .panel-header, -.monaco-panel-view:lang(zh-Hant) .panel > .panel-header, -.monaco-panel-view:lang(ja) .panel > .panel-header, -.monaco-panel-view:lang(ko) .panel > .panel-header { +.monaco-pane-view:lang(zh-Hans) .pane > .pane-header, +.monaco-pane-view:lang(zh-Hant) .pane > .pane-header, +.monaco-pane-view:lang(ja) .pane > .pane-header, +.monaco-pane-view:lang(ko) .pane > .pane-header { font-weight: normal; } -.monaco-panel-view .panel > .panel-header.hidden { +.monaco-pane-view .pane > .pane-header.hidden { display: none; } -.monaco-panel-view .panel > .panel-body { +.monaco-pane-view .pane > .pane-body { overflow: hidden; flex: 1; } /* Animation */ -.monaco-panel-view.animated .split-view-view { +.monaco-pane-view.animated .split-view-view { transition-duration: 0.15s; transition-timing-function: ease-out; } -.monaco-panel-view.animated.vertical .split-view-view { +.monaco-pane-view.animated.vertical .split-view-view { transition-property: height; } -.monaco-panel-view.animated.horizontal .split-view-view { +.monaco-pane-view.animated.horizontal .split-view-view { transition-property: width; } diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/paneview.ts similarity index 71% rename from src/vs/base/browser/ui/splitview/panelview.ts rename to src/vs/base/browser/ui/splitview/paneview.ts index 85d24c6bc7c..e97fab25404 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./panelview'; +import 'vs/css!./paneview'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; @@ -16,14 +16,14 @@ import { SplitView, IView } from './splitview'; import { isFirefox } from 'vs/base/browser/browser'; import { DataTransfers } from 'vs/base/browser/dnd'; -export interface IPanelOptions { +export interface IPaneOptions { ariaHeaderLabel?: string; minimumBodySize?: number; maximumBodySize?: number; expanded?: boolean; } -export interface IPanelStyles { +export interface IPaneStyles { dropBackground?: Color; headerForeground?: Color; headerBackground?: Color; @@ -31,7 +31,7 @@ export interface IPanelStyles { } /** - * A Panel is a structured SplitView view. + * A Pane is a structured SplitView view. * * WARNING: You must call `render()` after you contruct it. * It can't be done automatically at the end of the ctor @@ -39,7 +39,7 @@ export interface IPanelStyles { * Subclasses wouldn't be able to set own properties * before the `render()` call, thus forbiding their use. */ -export abstract class Panel extends Disposable implements IView { +export abstract class Pane extends Disposable implements IView { private static readonly HEADER_SIZE = 22; @@ -54,7 +54,7 @@ export abstract class Panel extends Disposable implements IView { private _minimumBodySize: number; private _maximumBodySize: number; private ariaHeaderLabel: string; - private styles: IPanelStyles = {}; + private styles: IPaneStyles = {}; private animationTimer: number | undefined = undefined; private readonly _onDidChange = this._register(new Emitter()); @@ -95,7 +95,7 @@ export abstract class Panel extends Disposable implements IView { } private get headerSize(): number { - return this.headerVisible ? Panel.HEADER_SIZE : 0; + return this.headerVisible ? Pane.HEADER_SIZE : 0; } get minimumSize(): number { @@ -116,14 +116,14 @@ export abstract class Panel extends Disposable implements IView { width: number = 0; - constructor(options: IPanelOptions = {}) { + constructor(options: IPaneOptions = {}) { super(); this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded; this.ariaHeaderLabel = options.ariaHeaderLabel || ''; this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : 120; this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize : Number.POSITIVE_INFINITY; - this.element = $('.panel'); + this.element = $('.pane'); } isExpanded(): boolean { @@ -169,7 +169,7 @@ export abstract class Panel extends Disposable implements IView { } render(): void { - this.header = $('.panel-header'); + this.header = $('.pane-header'); append(this.element, this.header); this.header.setAttribute('tabindex', '0'); this.header.setAttribute('role', 'toolbar'); @@ -198,12 +198,12 @@ export abstract class Panel extends Disposable implements IView { this._register(domEvent(this.header, 'click') (() => this.setExpanded(!this.isExpanded()), null)); - this.body = append(this.element, $('.panel-body')); + this.body = append(this.element, $('.pane-body')); this.renderBody(this.body); } layout(height: number): void { - const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0; + const headerSize = this.headerVisible ? Pane.HEADER_SIZE : 0; if (this.isExpanded()) { this.layoutBody(height - headerSize, this.width); @@ -211,7 +211,7 @@ export abstract class Panel extends Disposable implements IView { } } - style(styles: IPanelStyles): void { + style(styles: IPaneStyles): void { this.styles = styles; if (!this.header) { @@ -242,31 +242,31 @@ export abstract class Panel extends Disposable implements IView { } interface IDndContext { - draggable: PanelDraggable | null; + draggable: PaneDraggable | null; } -class PanelDraggable extends Disposable { +class PaneDraggable extends Disposable { private static readonly DefaultDragOverBackgroundColor = new Color(new RGBA(128, 128, 128, 0.5)); private dragOverCounter = 0; // see https://github.com/Microsoft/vscode/issues/14470 - private _onDidDrop = this._register(new Emitter<{ from: Panel, to: Panel }>()); + private _onDidDrop = this._register(new Emitter<{ from: Pane, to: Pane }>()); readonly onDidDrop = this._onDidDrop.event; - constructor(private panel: Panel, private dnd: IPanelDndController, private context: IDndContext) { + constructor(private pane: Pane, private dnd: IPaneDndController, private context: IDndContext) { super(); - panel.draggableElement.draggable = true; - this._register(domEvent(panel.draggableElement, 'dragstart')(this.onDragStart, this)); - this._register(domEvent(panel.dropTargetElement, 'dragenter')(this.onDragEnter, this)); - this._register(domEvent(panel.dropTargetElement, 'dragleave')(this.onDragLeave, this)); - this._register(domEvent(panel.dropTargetElement, 'dragend')(this.onDragEnd, this)); - this._register(domEvent(panel.dropTargetElement, 'drop')(this.onDrop, this)); + pane.draggableElement.draggable = true; + this._register(domEvent(pane.draggableElement, 'dragstart')(this.onDragStart, this)); + this._register(domEvent(pane.dropTargetElement, 'dragenter')(this.onDragEnter, this)); + this._register(domEvent(pane.dropTargetElement, 'dragleave')(this.onDragLeave, this)); + this._register(domEvent(pane.dropTargetElement, 'dragend')(this.onDragEnd, this)); + this._register(domEvent(pane.dropTargetElement, 'drop')(this.onDrop, this)); } private onDragStart(e: DragEvent): void { - if (!this.dnd.canDrag(this.panel) || !e.dataTransfer) { + if (!this.dnd.canDrag(this.pane) || !e.dataTransfer) { e.preventDefault(); e.stopPropagation(); return; @@ -276,10 +276,10 @@ class PanelDraggable extends Disposable { if (isFirefox) { // Firefox: requires to set a text data transfer to get going - e.dataTransfer?.setData(DataTransfers.TEXT, this.panel.draggableElement.textContent || ''); + e.dataTransfer?.setData(DataTransfers.TEXT, this.pane.draggableElement.textContent || ''); } - const dragImage = append(document.body, $('.monaco-drag-image', {}, this.panel.draggableElement.textContent || '')); + const dragImage = append(document.body, $('.monaco-drag-image', {}, this.pane.draggableElement.textContent || '')); e.dataTransfer.setDragImage(dragImage, -10, -10); setTimeout(() => document.body.removeChild(dragImage), 0); @@ -291,7 +291,7 @@ class PanelDraggable extends Disposable { return; } - if (!this.dnd.canDrop(this.context.draggable.panel, this.panel)) { + if (!this.dnd.canDrop(this.context.draggable.pane, this.pane)) { return; } @@ -304,7 +304,7 @@ class PanelDraggable extends Disposable { return; } - if (!this.dnd.canDrop(this.context.draggable.panel, this.panel)) { + if (!this.dnd.canDrop(this.context.draggable.pane, this.pane)) { return; } @@ -335,8 +335,8 @@ class PanelDraggable extends Disposable { this.dragOverCounter = 0; this.render(); - if (this.dnd.canDrop(this.context.draggable.panel, this.panel) && this.context.draggable !== this) { - this._onDidDrop.fire({ from: this.context.draggable.panel, to: this.panel }); + if (this.dnd.canDrop(this.context.draggable.pane, this.pane) && this.context.draggable !== this) { + this._onDidDrop.fire({ from: this.context.draggable.pane, to: this.pane }); } this.context.draggable = null; @@ -346,106 +346,106 @@ class PanelDraggable extends Disposable { let backgroundColor: string | null = null; if (this.dragOverCounter > 0) { - backgroundColor = (this.panel.dropBackground || PanelDraggable.DefaultDragOverBackgroundColor).toString(); + backgroundColor = (this.pane.dropBackground || PaneDraggable.DefaultDragOverBackgroundColor).toString(); } - this.panel.dropTargetElement.style.backgroundColor = backgroundColor || ''; + this.pane.dropTargetElement.style.backgroundColor = backgroundColor || ''; } } -export interface IPanelDndController { - canDrag(panel: Panel): boolean; - canDrop(panel: Panel, overPanel: Panel): boolean; +export interface IPaneDndController { + canDrag(pane: Pane): boolean; + canDrop(pane: Pane, overPane: Pane): boolean; } -export class DefaultPanelDndController implements IPanelDndController { +export class DefaultPaneDndController implements IPaneDndController { - canDrag(panel: Panel): boolean { + canDrag(pane: Pane): boolean { return true; } - canDrop(panel: Panel, overPanel: Panel): boolean { + canDrop(pane: Pane, overPane: Pane): boolean { return true; } } -export interface IPanelViewOptions { - dnd?: IPanelDndController; +export interface IPaneViewOptions { + dnd?: IPaneDndController; } -interface IPanelItem { - panel: Panel; +interface IPaneItem { + pane: Pane; disposable: IDisposable; } -export class PanelView extends Disposable { +export class PaneView extends Disposable { - private dnd: IPanelDndController | undefined; + private dnd: IPaneDndController | undefined; private dndContext: IDndContext = { draggable: null }; private el: HTMLElement; - private panelItems: IPanelItem[] = []; + private paneItems: IPaneItem[] = []; private width: number = 0; private splitview: SplitView; private animationTimer: number | undefined = undefined; - private _onDidDrop = this._register(new Emitter<{ from: Panel, to: Panel }>()); - readonly onDidDrop: Event<{ from: Panel, to: Panel }> = this._onDidDrop.event; + private _onDidDrop = this._register(new Emitter<{ from: Pane, to: Pane }>()); + readonly onDidDrop: Event<{ from: Pane, to: Pane }> = this._onDidDrop.event; readonly onDidSashChange: Event; - constructor(container: HTMLElement, options: IPanelViewOptions = {}) { + constructor(container: HTMLElement, options: IPaneViewOptions = {}) { super(); this.dnd = options.dnd; - this.el = append(container, $('.monaco-panel-view')); + this.el = append(container, $('.monaco-pane-view')); this.splitview = this._register(new SplitView(this.el)); this.onDidSashChange = this.splitview.onDidSashChange; } - addPanel(panel: Panel, size: number, index = this.splitview.length): void { + addPane(pane: Pane, size: number, index = this.splitview.length): void { const disposables = new DisposableStore(); - panel.onDidChangeExpansionState(this.setupAnimation, this, disposables); + pane.onDidChangeExpansionState(this.setupAnimation, this, disposables); - const panelItem = { panel, disposable: disposables }; - this.panelItems.splice(index, 0, panelItem); - panel.width = this.width; - this.splitview.addView(panel, size, index); + const paneItem = { pane: pane, disposable: disposables }; + this.paneItems.splice(index, 0, paneItem); + pane.width = this.width; + this.splitview.addView(pane, size, index); if (this.dnd) { - const draggable = new PanelDraggable(panel, this.dnd, this.dndContext); + const draggable = new PaneDraggable(pane, this.dnd, this.dndContext); disposables.add(draggable); disposables.add(draggable.onDidDrop(this._onDidDrop.fire, this._onDidDrop)); } } - removePanel(panel: Panel): void { - const index = firstIndex(this.panelItems, item => item.panel === panel); + removePane(pane: Pane): void { + const index = firstIndex(this.paneItems, item => item.pane === pane); if (index === -1) { return; } this.splitview.removeView(index); - const panelItem = this.panelItems.splice(index, 1)[0]; - panelItem.disposable.dispose(); + const paneItem = this.paneItems.splice(index, 1)[0]; + paneItem.disposable.dispose(); } - movePanel(from: Panel, to: Panel): void { - const fromIndex = firstIndex(this.panelItems, item => item.panel === from); - const toIndex = firstIndex(this.panelItems, item => item.panel === to); + movePane(from: Pane, to: Pane): void { + const fromIndex = firstIndex(this.paneItems, item => item.pane === from); + const toIndex = firstIndex(this.paneItems, item => item.pane === to); if (fromIndex === -1 || toIndex === -1) { return; } - const [panelItem] = this.panelItems.splice(fromIndex, 1); - this.panelItems.splice(toIndex, 0, panelItem); + const [paneItem] = this.paneItems.splice(fromIndex, 1); + this.paneItems.splice(toIndex, 0, paneItem); this.splitview.moveView(fromIndex, toIndex); } - resizePanel(panel: Panel, size: number): void { - const index = firstIndex(this.panelItems, item => item.panel === panel); + resizePane(pane: Pane, size: number): void { + const index = firstIndex(this.paneItems, item => item.pane === pane); if (index === -1) { return; @@ -454,8 +454,8 @@ export class PanelView extends Disposable { this.splitview.resizeView(index, size); } - getPanelSize(panel: Panel): number { - const index = firstIndex(this.panelItems, item => item.panel === panel); + getPaneSize(pane: Pane): number { + const index = firstIndex(this.paneItems, item => item.pane === pane); if (index === -1) { return -1; @@ -467,8 +467,8 @@ export class PanelView extends Disposable { layout(height: number, width: number): void { this.width = width; - for (const panelItem of this.panelItems) { - panelItem.panel.width = width; + for (const paneItem of this.paneItems) { + paneItem.pane.width = width; } this.splitview.layout(height); @@ -490,6 +490,6 @@ export class PanelView extends Disposable { dispose(): void { super.dispose(); - this.panelItems.forEach(i => i.disposable.dispose()); + this.paneItems.forEach(i => i.disposable.dispose()); } } diff --git a/src/vs/base/browser/ui/tree/media/panelviewlet.css b/src/vs/base/browser/ui/tree/media/paneviewlet.css similarity index 89% rename from src/vs/base/browser/ui/tree/media/panelviewlet.css rename to src/vs/base/browser/ui/tree/media/paneviewlet.css index 7aba33ef440..eb1e0be70b2 100644 --- a/src/vs/base/browser/ui/tree/media/panelviewlet.css +++ b/src/vs/base/browser/ui/tree/media/paneviewlet.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-panel-view .panel > .panel-header h3.title { +.monaco-pane-view .pane > .pane-header h3.title { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 691cd725136..3fad85d1949 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -9,7 +9,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import * as resources from 'vs/base/common/resources'; import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor } from 'vs/workbench/common/views'; -import { CustomTreeViewPanel, CustomTreeView } from 'vs/workbench/browser/parts/views/customView'; +import { CustomTreeViewPane, CustomTreeView } from 'vs/workbench/browser/parts/views/customView'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { coalesce, } from 'vs/base/common/arrays'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -422,7 +422,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { const viewDescriptor = { id: item.id, name: item.name, - ctorDescriptor: { ctor: CustomTreeViewPanel }, + ctorDescriptor: { ctor: CustomTreeViewPane }, when: ContextKeyExpr.deserialize(item.when), canToggleVisibility: true, collapsed: this.showCollapsed(container), diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 9a47283f370..68b13c2d377 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -29,7 +29,7 @@ import { dirname, basename } from 'vs/base/common/resources'; import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { FileKind } from 'vs/platform/files/common/files'; import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; -import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; import { localize } from 'vs/nls'; import { timeout } from 'vs/base/common/async'; import { textLinkForeground, textCodeBlockBackground, focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry'; @@ -43,7 +43,7 @@ import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -export class CustomTreeViewPanel extends ViewletPanel { +export class CustomTreeViewPane extends ViewletPane { private treeView: ITreeView; @@ -55,7 +55,7 @@ export class CustomTreeViewPanel extends ViewletPanel { @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService); const { treeView } = (Registry.as(Extensions.ViewsRegistry).getView(options.id)); this.treeView = treeView; this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this)); diff --git a/src/vs/workbench/browser/parts/views/media/panelviewlet.css b/src/vs/workbench/browser/parts/views/media/paneviewlet.css similarity index 72% rename from src/vs/workbench/browser/parts/views/media/panelviewlet.css rename to src/vs/workbench/browser/parts/views/media/paneviewlet.css index e022f97beb8..fc7221b9eaa 100644 --- a/src/vs/workbench/browser/parts/views/media/panelviewlet.css +++ b/src/vs/workbench/browser/parts/views/media/paneviewlet.css @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-panel-view .split-view-view:first-of-type > .panel > .panel-header { - border-top: none !important; /* less clutter: do not show any border for first views in a panel */ +.monaco-pane-view .split-view-view:first-of-type > .pane > .pane-header { + border-top: none !important; /* less clutter: do not show any border for first views in a pane */ } -.monaco-panel-view .panel > .panel-header > .actions.show { +.monaco-pane-view .pane > .pane-header > .actions.show { display: initial; } -.monaco-panel-view .panel > .panel-header h3.title { +.monaco-pane-view .pane > .pane-header h3.title { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/paneViewlet.ts similarity index 71% rename from src/vs/workbench/browser/parts/views/panelViewlet.ts rename to src/vs/workbench/browser/parts/views/paneViewlet.ts index b4fd837aaa6..9125018139a 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/paneViewlet.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/panelviewlet'; +import 'vs/css!./media/paneviewlet'; import * as nls from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; @@ -22,7 +22,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { PanelView, IPanelViewOptions, IPanelOptions, Panel } from 'vs/base/browser/ui/splitview/panelview'; +import { PaneView, IPaneViewOptions, IPaneOptions, Pane } from 'vs/base/browser/ui/splitview/paneview'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -31,21 +31,21 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { assertIsDefined } from 'vs/base/common/types'; -export interface IPanelColors extends IColorMapping { +export interface IPaneColors extends IColorMapping { dropBackground?: ColorIdentifier; headerForeground?: ColorIdentifier; headerBackground?: ColorIdentifier; headerBorder?: ColorIdentifier; } -export interface IViewletPanelOptions extends IPanelOptions { +export interface IViewletPaneOptions extends IPaneOptions { actionRunner?: IActionRunner; id: string; title: string; showActionsAlways?: boolean; } -export abstract class ViewletPanel extends Panel implements IView { +export abstract class ViewletPane extends Pane implements IView { private static readonly AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions'; @@ -75,7 +75,7 @@ export abstract class ViewletPanel extends Panel implements IView { protected twistiesContainer?: HTMLElement; constructor( - options: IViewletPanelOptions, + options: IViewletPaneOptions, @IKeybindingService protected keybindingService: IKeybindingService, @IContextMenuService protected contextMenuService: IContextMenuService, @IConfigurationService protected readonly configurationService: IConfigurationService, @@ -152,7 +152,7 @@ export abstract class ViewletPanel extends Panel implements IView { this._register(this.toolbar); this.setActions(); - const onDidRelevantConfigurationChange = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(ViewletPanel.AlwaysShowActionsConfig)); + const onDidRelevantConfigurationChange = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(ViewletPane.AlwaysShowActionsConfig)); this._register(onDidRelevantConfigurationChange(this.updateActionsVisibility, this)); this.updateActionsVisibility(); } @@ -225,31 +225,31 @@ export abstract class ViewletPanel extends Panel implements IView { } } -export interface IViewsViewletOptions extends IPanelViewOptions { +export interface IViewsViewletOptions extends IPaneViewOptions { showHeaderInTitleWhenSingleView: boolean; } -interface IViewletPanelItem { - panel: ViewletPanel; +interface IViewletPaneItem { + pane: ViewletPane; disposable: IDisposable; } -export class PanelViewlet extends Viewlet { +export class PaneViewlet extends Viewlet { - private lastFocusedPanel: ViewletPanel | undefined; - private panelItems: IViewletPanelItem[] = []; - private panelview?: PanelView; + private lastFocusedPane: ViewletPane | undefined; + private paneItems: IViewletPaneItem[] = []; + private paneview?: PaneView; get onDidSashChange(): Event { - return assertIsDefined(this.panelview).onDidSashChange; + return assertIsDefined(this.paneview).onDidSashChange; } - protected get panels(): ViewletPanel[] { - return this.panelItems.map(i => i.panel); + protected get panes(): ViewletPane[] { + return this.paneItems.map(i => i.pane); } protected get length(): number { - return this.panelItems.length; + return this.paneItems.length; } constructor( @@ -267,15 +267,15 @@ export class PanelViewlet extends Viewlet { create(parent: HTMLElement): void { super.create(parent); - this.panelview = this._register(new PanelView(parent, this.options)); - this._register(this.panelview.onDidDrop(({ from, to }) => this.movePanel(from as ViewletPanel, to as ViewletPanel))); + this.paneview = this._register(new PaneView(parent, this.options)); + this._register(this.paneview.onDidDrop(({ from, to }) => this.movePane(from as ViewletPane, to as ViewletPane))); this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e)))); } private showContextMenu(event: StandardMouseEvent): void { - for (const panelItem of this.panelItems) { - // Do not show context menu if target is coming from inside panel views - if (isAncestor(event.target, panelItem.panel.element)) { + for (const paneItem of this.paneItems) { + // Do not show context menu if target is coming from inside pane views + if (isAncestor(event.target, paneItem.pane.element)) { return; } } @@ -294,8 +294,8 @@ export class PanelViewlet extends Viewlet { let title = Registry.as(Extensions.Viewlets).getViewlet(this.getId()).name; if (this.isSingleView()) { - const panelItemTitle = this.panelItems[0].panel.title; - title = panelItemTitle ? `${title}: ${panelItemTitle}` : title; + const paneItemTitle = this.paneItems[0].pane.title; + title = paneItemTitle ? `${title}: ${paneItemTitle}` : title; } return title; @@ -303,7 +303,7 @@ export class PanelViewlet extends Viewlet { getActions(): IAction[] { if (this.isSingleView()) { - return this.panelItems[0].panel.getActions(); + return this.paneItems[0].pane.getActions(); } return []; @@ -311,7 +311,7 @@ export class PanelViewlet extends Viewlet { getSecondaryActions(): IAction[] { if (this.isSingleView()) { - return this.panelItems[0].panel.getSecondaryActions(); + return this.paneItems[0].pane.getSecondaryActions(); } return []; @@ -319,7 +319,7 @@ export class PanelViewlet extends Viewlet { getActionViewItem(action: IAction): IActionViewItem | undefined { if (this.isSingleView()) { - return this.panelItems[0].panel.getActionViewItem(action); + return this.paneItems[0].pane.getActionViewItem(action); } return super.getActionViewItem(action); @@ -328,12 +328,12 @@ export class PanelViewlet extends Viewlet { focus(): void { super.focus(); - if (this.lastFocusedPanel) { - this.lastFocusedPanel.focus(); - } else if (this.panelItems.length > 0) { - for (const { panel } of this.panelItems) { - if (panel.isExpanded()) { - panel.focus(); + if (this.lastFocusedPane) { + this.lastFocusedPane.focus(); + } else if (this.paneItems.length > 0) { + for (const { pane: pane } of this.paneItems) { + if (pane.isExpanded()) { + pane.focus(); return; } } @@ -341,23 +341,23 @@ export class PanelViewlet extends Viewlet { } layout(dimension: Dimension): void { - if (this.panelview) { - this.panelview.layout(dimension.height, dimension.width); + if (this.paneview) { + this.paneview.layout(dimension.height, dimension.width); } } getOptimalWidth(): number { - const sizes = this.panelItems - .map(panelItem => panelItem.panel.getOptimalWidth() || 0); + const sizes = this.paneItems + .map(paneItem => paneItem.pane.getOptimalWidth() || 0); return Math.max(...sizes); } - addPanels(panels: { panel: ViewletPanel, size: number, index?: number; }[]): void { + addPanes(panes: { pane: ViewletPane, size: number, index?: number; }[]): void { const wasSingleView = this.isSingleView(); - for (const { panel, size, index } of panels) { - this.addPanel(panel, size, index); + for (const { pane: pane, size, index } of panes) { + this.addPane(pane, size, index); } this.updateViewHeaders(); @@ -366,36 +366,36 @@ export class PanelViewlet extends Viewlet { } } - private addPanel(panel: ViewletPanel, size: number, index = this.panelItems.length - 1): void { - const onDidFocus = panel.onDidFocus(() => this.lastFocusedPanel = panel); - const onDidChangeTitleArea = panel.onDidChangeTitleArea(() => { + private addPane(pane: ViewletPane, size: number, index = this.paneItems.length - 1): void { + const onDidFocus = pane.onDidFocus(() => this.lastFocusedPane = pane); + const onDidChangeTitleArea = pane.onDidChangeTitleArea(() => { if (this.isSingleView()) { this.updateTitleArea(); } }); - const onDidChange = panel.onDidChange(() => { - if (panel === this.lastFocusedPanel && !panel.isExpanded()) { - this.lastFocusedPanel = undefined; + const onDidChange = pane.onDidChange(() => { + if (pane === this.lastFocusedPane && !pane.isExpanded()) { + this.lastFocusedPane = undefined; } }); - const panelStyler = attachStyler(this.themeService, { + const paneStyler = attachStyler(this.themeService, { headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND, headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND, headerBorder: SIDE_BAR_SECTION_HEADER_BORDER, dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND - }, panel); - const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, panelStyler, onDidChange); - const panelItem: IViewletPanelItem = { panel, disposable }; + }, pane); + const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange); + const paneItem: IViewletPaneItem = { pane: pane, disposable }; - this.panelItems.splice(index, 0, panelItem); - assertIsDefined(this.panelview).addPanel(panel, size, index); + this.paneItems.splice(index, 0, paneItem); + assertIsDefined(this.paneview).addPane(pane, size, index); } - removePanels(panels: ViewletPanel[]): void { + removePanes(panes: ViewletPane[]): void { const wasSingleView = this.isSingleView(); - panels.forEach(panel => this.removePanel(panel)); + panes.forEach(pane => this.removePane(pane)); this.updateViewHeaders(); if (wasSingleView !== this.isSingleView()) { @@ -403,67 +403,67 @@ export class PanelViewlet extends Viewlet { } } - private removePanel(panel: ViewletPanel): void { - const index = firstIndex(this.panelItems, i => i.panel === panel); + private removePane(pane: ViewletPane): void { + const index = firstIndex(this.paneItems, i => i.pane === pane); if (index === -1) { return; } - if (this.lastFocusedPanel === panel) { - this.lastFocusedPanel = undefined; + if (this.lastFocusedPane === pane) { + this.lastFocusedPane = undefined; } - assertIsDefined(this.panelview).removePanel(panel); - const [panelItem] = this.panelItems.splice(index, 1); - panelItem.disposable.dispose(); + assertIsDefined(this.paneview).removePane(pane); + const [paneItem] = this.paneItems.splice(index, 1); + paneItem.disposable.dispose(); } - movePanel(from: ViewletPanel, to: ViewletPanel): void { - const fromIndex = firstIndex(this.panelItems, item => item.panel === from); - const toIndex = firstIndex(this.panelItems, item => item.panel === to); + movePane(from: ViewletPane, to: ViewletPane): void { + const fromIndex = firstIndex(this.paneItems, item => item.pane === from); + const toIndex = firstIndex(this.paneItems, item => item.pane === to); - if (fromIndex < 0 || fromIndex >= this.panelItems.length) { + if (fromIndex < 0 || fromIndex >= this.paneItems.length) { return; } - if (toIndex < 0 || toIndex >= this.panelItems.length) { + if (toIndex < 0 || toIndex >= this.paneItems.length) { return; } - const [panelItem] = this.panelItems.splice(fromIndex, 1); - this.panelItems.splice(toIndex, 0, panelItem); + const [paneItem] = this.paneItems.splice(fromIndex, 1); + this.paneItems.splice(toIndex, 0, paneItem); - assertIsDefined(this.panelview).movePanel(from, to); + assertIsDefined(this.paneview).movePane(from, to); } - resizePanel(panel: ViewletPanel, size: number): void { - assertIsDefined(this.panelview).resizePanel(panel, size); + resizePane(pane: ViewletPane, size: number): void { + assertIsDefined(this.paneview).resizePane(pane, size); } - getPanelSize(panel: ViewletPanel): number { - return assertIsDefined(this.panelview).getPanelSize(panel); + getPaneSize(pane: ViewletPane): number { + return assertIsDefined(this.paneview).getPaneSize(pane); } protected updateViewHeaders(): void { if (this.isSingleView()) { - this.panelItems[0].panel.setExpanded(true); - this.panelItems[0].panel.headerVisible = false; + this.paneItems[0].pane.setExpanded(true); + this.paneItems[0].pane.headerVisible = false; } else { - this.panelItems.forEach(i => i.panel.headerVisible = true); + this.paneItems.forEach(i => i.pane.headerVisible = true); } } protected isSingleView(): boolean { - return this.options.showHeaderInTitleWhenSingleView && this.panelItems.length === 1; + return this.options.showHeaderInTitleWhenSingleView && this.paneItems.length === 1; } dispose(): void { super.dispose(); - this.panelItems.forEach(i => i.disposable.dispose()); - if (this.panelview) { - this.panelview.dispose(); + this.paneItems.forEach(i => i.disposable.dispose()); + if (this.paneview) { + this.paneview.dispose(); } } } diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 806dadb4443..1399797b257 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -18,8 +18,8 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { PanelViewlet, ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; -import { DefaultPanelDndController } from 'vs/base/browser/ui/splitview/panelview'; +import { PaneViewlet, ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { DefaultPaneDndController } from 'vs/base/browser/ui/splitview/paneview'; import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService'; import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree'; @@ -31,11 +31,11 @@ import { IAddedViewDescriptorRef, IViewDescriptorRef, PersistentContributableVie import { Registry } from 'vs/platform/registry/common/platform'; import { MementoObject } from 'vs/workbench/common/memento'; -export interface IViewletViewOptions extends IViewletPanelOptions { +export interface IViewletViewOptions extends IViewletPaneOptions { viewletState: MementoObject; } -export abstract class ViewContainerViewlet extends PanelViewlet implements IViewsViewlet { +export abstract class ViewContainerViewlet extends PaneViewlet implements IViewsViewlet { private readonly viewletState: MementoObject; private didLayout = false; @@ -61,7 +61,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView @IExtensionService protected extensionService: IExtensionService, @IWorkspaceContextService protected contextService: IWorkspaceContextService ) { - super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPanelDndController() }, configurationService, layoutService, contextMenuService, telemetryService, themeService, storageService); + super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPaneDndController() }, configurationService, layoutService, contextMenuService, telemetryService, themeService, storageService); const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).get(id); if (!container) { @@ -92,7 +92,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView // Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609 this.extensionService.whenInstalledExtensionsRegistered().then(() => { this.areExtensionsReady = true; - if (this.panels.length) { + if (this.panes.length) { this.updateTitleArea(); this.updateViewHeaders(); } @@ -127,7 +127,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView setVisible(visible: boolean): void { super.setVisible(visible); - this.panels.filter(view => view.isVisible() !== visible) + this.panes.filter(view => view.isVisible() !== visible) .map((view) => view.setVisible(visible)); } @@ -147,13 +147,13 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView return view; } - movePanel(from: ViewletPanel, to: ViewletPanel): void { - const fromIndex = firstIndex(this.panels, panel => panel === from); - const toIndex = firstIndex(this.panels, panel => panel === to); + movePane(from: ViewletPane, to: ViewletPane): void { + const fromIndex = firstIndex(this.panes, pane => pane === from); + const toIndex = firstIndex(this.panes, pane => pane === to); const fromViewDescriptor = this.viewsModel.visibleViewDescriptors[fromIndex]; const toViewDescriptor = this.viewsModel.visibleViewDescriptors[toIndex]; - super.movePanel(from, to); + super.movePane(from, to); this.viewsModel.move(fromViewDescriptor.id, toViewDescriptor.id); } @@ -170,7 +170,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView getOptimalWidth(): number { const additionalMargin = 16; - const optimalWidth = Math.max(...this.panels.map(view => view.getOptimalWidth() || 0)); + const optimalWidth = Math.max(...this.panes.map(view => view.getOptimalWidth() || 0)); return optimalWidth + additionalMargin; } @@ -188,18 +188,18 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView return true; } - protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPanel { - return (this.instantiationService as any).createInstance(viewDescriptor.ctorDescriptor.ctor, ...(viewDescriptor.ctorDescriptor.arguments || []), options) as ViewletPanel; + protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPane { + return (this.instantiationService as any).createInstance(viewDescriptor.ctorDescriptor.ctor, ...(viewDescriptor.ctorDescriptor.arguments || []), options) as ViewletPane; } - protected getView(id: string): ViewletPanel | undefined { - return this.panels.filter(view => view.id === id)[0]; + protected getView(id: string): ViewletPane | undefined { + return this.panes.filter(view => view.id === id)[0]; } - protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] { - const panelsToAdd: { panel: ViewletPanel, size: number, index: number }[] = []; + protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPane[] { + const panesToAdd: { pane: ViewletPane, size: number, index: number }[] = []; for (const { viewDescriptor, collapsed, index, size } of added) { - const panel = this.createView(viewDescriptor, + const pane = this.createView(viewDescriptor, { id: viewDescriptor.id, title: viewDescriptor.name, @@ -207,42 +207,42 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView expanded: !collapsed, viewletState: this.viewletState }); - panel.render(); - const contextMenuDisposable = DOM.addDisposableListener(panel.draggableElement, 'contextmenu', e => { + pane.render(); + const contextMenuDisposable = DOM.addDisposableListener(pane.draggableElement, 'contextmenu', e => { e.stopPropagation(); e.preventDefault(); this.onContextMenu(new StandardMouseEvent(e), viewDescriptor); }); - const collapseDisposable = Event.latch(Event.map(panel.onDidChange, () => !panel.isExpanded()))(collapsed => { + const collapseDisposable = Event.latch(Event.map(pane.onDidChange, () => !pane.isExpanded()))(collapsed => { this.viewsModel.setCollapsed(viewDescriptor.id, collapsed); }); this.viewDisposables.splice(index, 0, combinedDisposable(contextMenuDisposable, collapseDisposable)); - panelsToAdd.push({ panel, size: size || panel.minimumSize, index }); + panesToAdd.push({ pane, size: size || pane.minimumSize, index }); } - this.addPanels(panelsToAdd); + this.addPanes(panesToAdd); this.restoreViewSizes(); - const panels: ViewletPanel[] = []; - for (const { panel } of panelsToAdd) { - panel.setVisible(this.isVisible()); - panels.push(panel); + const panes: ViewletPane[] = []; + for (const { pane } of panesToAdd) { + pane.setVisible(this.isVisible()); + panes.push(pane); } - return panels; + return panes; } private onDidRemoveViews(removed: IViewDescriptorRef[]): void { removed = removed.sort((a, b) => b.index - a.index); - const panelsToRemove: ViewletPanel[] = []; + const panesToRemove: ViewletPane[] = []; for (const { index } of removed) { const [disposable] = this.viewDisposables.splice(index, 1); disposable.dispose(); - panelsToRemove.push(this.panels[index]); + panesToRemove.push(this.panes[index]); } - this.removePanels(panelsToRemove); - dispose(panelsToRemove); + this.removePanes(panesToRemove); + dispose(panesToRemove); } private onContextMenu(event: StandardMouseEvent, viewDescriptor: IViewDescriptor): void { @@ -281,8 +281,8 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView private saveViewSizes(): void { // Save size only when the layout has happened if (this.didLayout) { - for (const view of this.panels) { - this.viewsModel.setSize(view.id, this.getPanelSize(view)); + for (const view of this.panes) { + this.viewsModel.setSize(view.id, this.getPaneSize(view)); } } } @@ -292,15 +292,15 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView if (this.didLayout) { let initialSizes; for (let i = 0; i < this.viewsModel.visibleViewDescriptors.length; i++) { - const panel = this.panels[i]; + const pane = this.panes[i]; const viewDescriptor = this.viewsModel.visibleViewDescriptors[i]; const size = this.viewsModel.getSize(viewDescriptor.id); if (typeof size === 'number') { - this.resizePanel(panel, size); + this.resizePane(pane, size); } else { initialSizes = initialSizes ? initialSizes : this.computeInitialSizes(); - this.resizePanel(panel, initialSizes.get(panel.id) || 200); + this.resizePane(pane, initialSizes.get(pane.id) || 200); } } } @@ -318,7 +318,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView } protected saveState(): void { - this.panels.forEach((view) => view.saveState()); + this.panes.forEach((view) => view.saveState()); this.storageService.store(this.visibleViewsStorageId, this.length, StorageScope.WORKSPACE); super.saveState(); @@ -418,18 +418,18 @@ export abstract class FilterViewContainerViewlet extends ViewContainerViewlet { return views; } - onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] { - const panels: ViewletPanel[] = super.onDidAddViews(added); + onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPane[] { + const panes: ViewletPane[] = super.onDidAddViews(added); for (let i = 0; i < added.length; i++) { if (this.constantViewDescriptors.has(added[i].viewDescriptor.id)) { - panels[i].setExpanded(false); + panes[i].setExpanded(false); } } // Check that allViews is ready if (this.allViews.size === 0) { this.updateAllViews(this.viewsModel.viewDescriptors); } - return panels; + return panes; } abstract getTitle(): string; diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 789df7edfd6..aeaf6e7cebf 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -28,7 +28,7 @@ import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; @@ -43,7 +43,7 @@ function createCheckbox(): HTMLInputElement { return checkbox; } -export class BreakpointsView extends ViewletPanel { +export class BreakpointsView extends ViewletPane { private static readonly MAX_VISIBLE_FILES = 9; private list!: WorkbenchList; @@ -61,7 +61,7 @@ export class BreakpointsView extends ViewletPanel { @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize(); this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange())); diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 667f578a5f1..ef83c73203a 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -18,7 +18,7 @@ import { IAction, Action } from 'vs/base/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { IViewletPaneOptions, ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -40,7 +40,7 @@ const $ = dom.$; type CallStackItem = IStackFrame | IThread | IDebugSession | string | ThreadAndSessionIds | IStackFrame[]; -export class CallStackView extends ViewletPanel { +export class CallStackView extends ViewletPane { private pauseMessage!: HTMLSpanElement; private pauseMessageLabel!: HTMLSpanElement; @@ -66,7 +66,7 @@ export class CallStackView extends ViewletPanel { @IMenuService menuService: IMenuService, @IContextKeyService readonly contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this.callStackItemType = CONTEXT_CALLSTACK_ITEM_TYPE.bindTo(contextKeyService); this.contributedContextMenu = menuService.createMenu(MenuId.DebugCallStackContext, contextKeyService); diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index b3eb22bde17..c48c0ecf957 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -26,7 +26,7 @@ import { memoize } from 'vs/base/common/decorators'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; import { IMenu, MenuId, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -38,8 +38,8 @@ export class DebugViewlet extends ViewContainerViewlet { private startDebugActionViewItem: StartDebugActionViewItem | undefined; private progressResolve: (() => void) | undefined; - private breakpointView: ViewletPanel | undefined; - private panelListeners = new Map(); + private breakpointView: ViewletPane | undefined; + private paneListeners = new Map(); private debugToolBarMenu: IMenu | undefined; private disposeOnTitleUpdate: IDisposable | undefined; @@ -181,32 +181,32 @@ export class DebugViewlet extends ViewContainerViewlet { } } - addPanels(panels: { panel: ViewletPanel, size: number, index?: number }[]): void { - super.addPanels(panels); + addPanes(panes: { pane: ViewletPane, size: number, index?: number }[]): void { + super.addPanes(panes); - for (const { panel } of panels) { + for (const { pane: pane } of panes) { // attach event listener to - if (panel.id === BREAKPOINTS_VIEW_ID) { - this.breakpointView = panel; + if (pane.id === BREAKPOINTS_VIEW_ID) { + this.breakpointView = pane; this.updateBreakpointsMaxSize(); } else { - this.panelListeners.set(panel.id, panel.onDidChange(() => this.updateBreakpointsMaxSize())); + this.paneListeners.set(pane.id, pane.onDidChange(() => this.updateBreakpointsMaxSize())); } } } - removePanels(panels: ViewletPanel[]): void { - super.removePanels(panels); - for (const panel of panels) { - dispose(this.panelListeners.get(panel.id)); - this.panelListeners.delete(panel.id); + removePanes(panes: ViewletPane[]): void { + super.removePanes(panes); + for (const pane of panes) { + dispose(this.paneListeners.get(pane.id)); + this.paneListeners.delete(pane.id); } } private updateBreakpointsMaxSize(): void { if (this.breakpointView) { // We need to update the breakpoints view since all other views are collapsed #25384 - const allOtherCollapsed = this.panels.every(view => !view.isExpanded() || view === this.breakpointView); + const allOtherCollapsed = this.panes.every(view => !view.isExpanded() || view === this.breakpointView); this.breakpointView.maximumBodySize = allOtherCollapsed ? Number.POSITIVE_INFINITY : this.breakpointView.minimumBodySize; } } diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index 28c7604d2a0..c825a856bdd 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { normalize, isAbsolute, posix } from 'vs/base/common/path'; -import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { IViewletPaneOptions, ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -388,7 +388,7 @@ class SessionTreeItem extends BaseTreeItem { } } -export class LoadedScriptsView extends ViewletPanel { +export class LoadedScriptsView extends ViewletPane { private treeContainer!: HTMLElement; private loadedScriptsItemType: IContextKey; @@ -411,7 +411,7 @@ export class LoadedScriptsView extends ViewletPanel { @IDebugService private readonly debugService: IDebugService, @ILabelService private readonly labelService: ILabelService ) { - super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService); } diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index a6104ac4e9b..81012b401ae 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -17,7 +17,7 @@ import { IAction, Action } from 'vs/base/common/actions'; import { CopyValueAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { IViewletPaneOptions, ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, ITreeMouseEvent, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; @@ -37,7 +37,7 @@ let forgetScopes = true; export const variableSetEmitter = new Emitter(); -export class VariablesView extends ViewletPanel { +export class VariablesView extends ViewletPane { private onFocusStackFrameScheduler: RunOnceScheduler; private needsRefresh = false; @@ -54,7 +54,7 @@ export class VariablesView extends ViewletPanel { @IClipboardService private readonly clipboardService: IClipboardService, @IContextKeyService contextKeyService: IContextKeyService ) { - super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); // Use scheduler to prevent unnecessary flashing this.onFocusStackFrameScheduler = new RunOnceScheduler(async () => { diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index f4711050305..23928662699 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -18,7 +18,7 @@ import { IAction, Action } from 'vs/base/common/actions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { renderExpressionValue, renderViewTree, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { IViewletPaneOptions, ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; @@ -34,7 +34,7 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; -export class WatchExpressionsView extends ViewletPanel { +export class WatchExpressionsView extends ViewletPane { private onWatchExpressionsUpdatedScheduler: RunOnceScheduler; private needsRefresh = false; @@ -49,7 +49,7 @@ export class WatchExpressionsView extends ViewletPanel { @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('watchExpressionsSection', "Watch Expressions Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize('watchExpressionsSection', "Watch Expressions Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => { this.needsRefresh = false; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 250084615b9..4ac7d34337e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -46,7 +46,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IAddedViewDescriptorRef } from 'vs/workbench/browser/parts/views/views'; -import { ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; import { SuggestEnabledInput, attachSuggestEnabledInputBoxStyler } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; import { alert } from 'vs/base/browser/ui/aria/aria'; @@ -526,13 +526,13 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.nonEmptyWorkspaceContextKey.set(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY); this.defaultViewsContextKey.set(!value); - return this.progress(Promise.all(this.panels.map(view => + return this.progress(Promise.all(this.panes.map(view => (view).show(this.normalizedQuery()) .then(model => this.alertSearchResult(model.length, view.id)) ))).then(() => undefined); } - protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] { + protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPane[] { const addedViews = super.onDidAddViews(added); this.progress(Promise.all(addedViews.map(addedView => (addedView).show(this.normalizedQuery()) @@ -563,12 +563,12 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio } private count(): number { - return this.panels.reduce((count, view) => (view).count() + count, 0); + return this.panes.reduce((count, view) => (view).count() + count, 0); } private focusListView(): void { if (this.count() > 0) { - this.panels[0].focus(); + this.panes[0].focus(); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 530fd637d9a..1c3e99c9a7e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -32,7 +32,7 @@ import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRe import { WorkbenchPagedList } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { distinct, coalesce } from 'vs/base/common/arrays'; import { IExperimentService, IExperiment, ExperimentActionType } from 'vs/workbench/contrib/experiments/common/experimentService'; @@ -72,7 +72,7 @@ export interface ExtensionsListViewOptions extends IViewletViewOptions { class ExtensionListViewWarning extends Error { } -export class ExtensionsListView extends ViewletPanel { +export class ExtensionsListView extends ViewletPane { protected readonly server: IExtensionManagementServer | undefined; private bodyTemplate: { @@ -105,7 +105,7 @@ export class ExtensionsListView extends ViewletPanel { @IProductService protected readonly productService: IProductService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title, showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: options.title, showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService); this.server = options.server; } diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index 85cecc75dfa..fe6dd9eafc1 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -29,7 +29,7 @@ import { DelegatingEditorService } from 'vs/workbench/services/editor/browser/ed import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditor } from 'vs/workbench/common/editor'; -import { ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; import { KeyChord, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; @@ -177,7 +177,7 @@ export class ExplorerViewlet extends ViewContainerViewlet { DOM.addClass(parent, 'explorer-viewlet'); } - protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPanel { + protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPane { if (viewDescriptor.id === ExplorerView.ID) { // Create a delegating editor service for the explorer to be able to delay the refresh in the opened // editors view above. This is a workaround for being able to double click on a file to make it pinned diff --git a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css index 40510378cfe..8933103c4d3 100644 --- a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css @@ -61,14 +61,14 @@ align-items: center; } -.explorer-viewlet .panel-header .count { +.explorer-viewlet .pane-header .count { min-width: fit-content; min-width: -moz-fit-content; display: flex; align-items: center; } -.explorer-viewlet .panel-header .monaco-count-badge.hidden { +.explorer-viewlet .pane-header .monaco-count-badge.hidden { display: none; } diff --git a/src/vs/workbench/contrib/files/browser/views/emptyView.ts b/src/vs/workbench/contrib/files/browser/views/emptyView.ts index 7d9fbe1c034..d1b2af81a5f 100644 --- a/src/vs/workbench/contrib/files/browser/views/emptyView.ts +++ b/src/vs/workbench/contrib/files/browser/views/emptyView.ts @@ -16,7 +16,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; import { ResourcesDropHandler, DragAndDropObserver } from 'vs/workbench/browser/dnd'; import { listDropBackground } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; @@ -26,7 +26,7 @@ import { Schemas } from 'vs/base/common/network'; import { isWeb } from 'vs/base/common/platform'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -export class EmptyView extends ViewletPanel { +export class EmptyView extends ViewletPane { static readonly ID: string = 'workbench.explorer.emptyView'; static readonly NAME = nls.localize('noWorkspace', "No Folder Opened"); @@ -46,7 +46,7 @@ export class EmptyView extends ViewletPanel { @ILabelService private labelService: ILabelService, @IContextKeyService contextKeyService: IContextKeyService ) { - super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this._register(this.contextService.onDidChangeWorkbenchState(() => this.setLabels())); this._register(this.labelService.onDidChangeFormatters(() => this.setLabels())); } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 050e0a9f8c7..5020df769b7 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -27,7 +27,7 @@ import { IDecorationsService } from 'vs/workbench/services/decorations/browser/d import { TreeResourceNavigator2, WorkbenchCompressibleAsyncDataTree } from 'vs/platform/list/browser/listService'; import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { IViewletPaneOptions, ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; import { ExplorerDelegate, ExplorerAccessibilityProvider, ExplorerDataSource, FilesRenderer, ICompressedNavigationController, FilesFilter, FileSorter, FileDragAndDrop, ExplorerCompressionDelegate, isCompressedFolderName } from 'vs/workbench/contrib/files/browser/views/explorerViewer'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -64,7 +64,7 @@ interface IExplorerViewStyles { listDropBackground?: Color; } -export class ExplorerView extends ViewletPanel { +export class ExplorerView extends ViewletPane { static readonly ID: string = 'workbench.explorer.fileView'; static readonly TREE_VIEW_STATE_STORAGE_KEY: string = 'workbench.explorer.treeViewState'; @@ -92,7 +92,7 @@ export class ExplorerView extends ViewletPanel { private actions: IAction[] | undefined; constructor( - options: IViewletPanelOptions, + options: IViewletPaneOptions, @IContextMenuService contextMenuService: IContextMenuService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @@ -112,7 +112,7 @@ export class ExplorerView extends ViewletPanel { @IClipboardService private clipboardService: IClipboardService, @IFileService private readonly fileService: IFileService ) { - super({ ...(options as IViewletPanelOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewletPaneOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this.resourceContext = instantiationService.createInstance(ResourceContextKey); this._register(this.resourceContext); diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 5d5ce871287..de1f8bf059b 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -33,7 +33,7 @@ import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions' import { DirtyEditorContext, OpenEditorsGroupContext, ReadonlyEditorContext as ReadonlyEditorContext } from 'vs/workbench/contrib/files/browser/fileCommands'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { ResourcesDropHandler, fillResourceDataTransfers, CodeDataTransfers, containsDragType } from 'vs/workbench/browser/dnd'; -import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IDragAndDropData, DataTransfers } from 'vs/base/browser/dnd'; import { memoize } from 'vs/base/common/decorators'; @@ -46,7 +46,7 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; const $ = dom.$; -export class OpenEditorsView extends ViewletPanel { +export class OpenEditorsView extends ViewletPane { private static readonly DEFAULT_VISIBLE_OPEN_EDITORS = 9; static readonly ID = 'workbench.explorer.openEditorsView'; @@ -79,7 +79,7 @@ export class OpenEditorsView extends ViewletPanel { @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService ) { super({ - ...(options as IViewletPanelOptions), + ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize({ key: 'openEditosrSection', comment: ['Open is an adjective'] }, "Open Editors Section"), }, keybindingService, contextMenuService, configurationService, contextKeyService); diff --git a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts index b99786acfc2..0affdee3013 100644 --- a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts +++ b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions } from 'vs/workbench/common/views'; -import { OutlinePanel } from './outlinePanel'; +import { OutlinePane } from './outlinePane'; import { VIEW_CONTAINER } from 'vs/workbench/contrib/files/common/files'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; @@ -16,7 +16,7 @@ import './outlineNavigation'; const _outlineDesc = { id: OutlineViewId, name: localize('name', "Outline"), - ctorDescriptor: { ctor: OutlinePanel }, + ctorDescriptor: { ctor: OutlinePane }, canToggleVisibility: true, hideByDefault: false, collapsed: true, diff --git a/src/vs/workbench/contrib/outline/browser/outlinePanel.css b/src/vs/workbench/contrib/outline/browser/outlinePane.css similarity index 67% rename from src/vs/workbench/contrib/outline/browser/outlinePanel.css rename to src/vs/workbench/contrib/outline/browser/outlinePane.css index 950fb41088b..4f443d9f29e 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePanel.css +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.css @@ -3,45 +3,45 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .outline-panel { +.monaco-workbench .outline-pane { display: flex; flex-direction: column; } -.monaco-workbench .outline-panel .outline-progress { +.monaco-workbench .outline-pane .outline-progress { width: 100%; height: 2px; padding-bottom: 3px; position: absolute; } -.monaco-workbench .outline-panel .outline-progress .monaco-progress-container { +.monaco-workbench .outline-pane .outline-progress .monaco-progress-container { height: 2px; } -.monaco-workbench .outline-panel .outline-progress .monaco-progress-container .progress-bit { +.monaco-workbench .outline-pane .outline-progress .monaco-progress-container .progress-bit { height: 2px; } -.monaco-workbench .outline-panel .outline-tree { +.monaco-workbench .outline-pane .outline-tree { height: 100%; } -.monaco-workbench .outline-panel .outline-message { +.monaco-workbench .outline-pane .outline-message { display: none; padding: 10px 22px 0 22px; opacity: 0.5; } -.monaco-workbench .outline-panel.message .outline-message { +.monaco-workbench .outline-pane.message .outline-message { display: inherit; } -.monaco-workbench .outline-panel.message .outline-progress { +.monaco-workbench .outline-pane.message .outline-progress { display: none; } -.monaco-workbench .outline-panel.message .outline-tree { +.monaco-workbench .outline-pane.message .outline-tree { display: none; } diff --git a/src/vs/workbench/contrib/outline/browser/outlinePanel.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts similarity index 98% rename from src/vs/workbench/contrib/outline/browser/outlinePanel.ts rename to src/vs/workbench/contrib/outline/browser/outlinePane.ts index 9d563e905c0..068c3471ed5 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePanel.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -14,7 +14,7 @@ import { defaultGenerator } from 'vs/base/common/idGenerator'; import { dispose, IDisposable, toDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { LRUCache } from 'vs/base/common/map'; import { escape } from 'vs/base/common/strings'; -import 'vs/css!./outlinePanel'; +import 'vs/css!./outlinePane'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -34,7 +34,7 @@ import { WorkbenchDataTree } from 'vs/platform/list/browser/listService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; @@ -233,7 +233,7 @@ class OutlineViewState { } } -export class OutlinePanel extends ViewletPanel { +export class OutlinePane extends ViewletPane { private _disposables = new Array(); @@ -297,7 +297,7 @@ export class OutlinePanel extends ViewletPanel { protected renderBody(container: HTMLElement): void { this._domNode = container; this._domNode.tabIndex = 0; - dom.addClass(container, 'outline-panel'); + dom.addClass(container, 'outline-pane'); let progressContainer = dom.$('.outline-progress'); this._message = dom.$('.outline-message'); @@ -318,7 +318,7 @@ export class OutlinePanel extends ViewletPanel { this._treeFilter = this._instantiationService.createInstance(OutlineFilter, 'outline'); this._tree = this._instantiationService.createInstance( WorkbenchDataTree, - 'OutlinePanel', + 'OutlinePane', treeContainer, new OutlineVirtualDelegate(), [new OutlineGroupRenderer(), this._treeRenderer], @@ -482,7 +482,7 @@ export class OutlinePanel extends ViewletPanel { const requestDelay = OutlineModel.getRequestDelay(textModel); this._progressBar.infinite().show(requestDelay); - const createdModel = await OutlinePanel._createOutlineModel(textModel, this._editorDisposables); + const createdModel = await OutlinePane._createOutlineModel(textModel, this._editorDisposables); dispose(loadingMessage); if (!createdModel) { return; diff --git a/src/vs/workbench/contrib/scm/browser/mainPanel.ts b/src/vs/workbench/contrib/scm/browser/mainPane.ts similarity index 96% rename from src/vs/workbench/contrib/scm/browser/mainPanel.ts rename to src/vs/workbench/contrib/scm/browser/mainPane.ts index c8903522901..02a3e45afd2 100644 --- a/src/vs/workbench/contrib/scm/browser/mainPanel.ts +++ b/src/vs/workbench/contrib/scm/browser/mainPane.ts @@ -8,7 +8,7 @@ import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { basename } from 'vs/base/common/resources'; import { IDisposable, dispose, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; -import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; import { append, $, toggleClass } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; import { ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; @@ -168,16 +168,16 @@ class ProviderRenderer implements IListRenderer; constructor( protected viewModel: IViewModel, - options: IViewletPanelOptions, + options: IViewletPaneOptions, @IKeybindingService protected keybindingService: IKeybindingService, @IContextMenuService protected contextMenuService: IContextMenuService, @ISCMService protected scmService: ISCMService, @@ -315,10 +315,10 @@ export class MainPanel extends ViewletPanel { } } -export class MainPanelDescriptor implements IViewDescriptor { +export class MainPaneDescriptor implements IViewDescriptor { - readonly id = MainPanel.ID; - readonly name = MainPanel.TITLE; + readonly id = MainPane.ID; + readonly name = MainPane.TITLE; readonly ctorDescriptor: { ctor: any, arguments?: any[] }; readonly canToggleVisibility = true; readonly hideByDefault = false; @@ -327,6 +327,6 @@ export class MainPanelDescriptor implements IViewDescriptor { readonly when = ContextKeyExpr.or(ContextKeyExpr.equals('config.scm.alwaysShowProviders', true), ContextKeyExpr.and(ContextKeyExpr.notEquals('scm.providerCount', 0), ContextKeyExpr.notEquals('scm.providerCount', 1))); constructor(viewModel: IViewModel) { - this.ctorDescriptor = { ctor: MainPanel, arguments: [viewModel] }; + this.ctorDescriptor = { ctor: MainPane, arguments: [viewModel] }; } } diff --git a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css index 70426f2e015..1a56521f66e 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css +++ b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css @@ -15,7 +15,7 @@ } .scm-viewlet:not(.empty) .empty-message, -.scm-viewlet.empty .monaco-panel-view { +.scm-viewlet.empty .monaco-pane-view { display: none; } diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts similarity index 99% rename from src/vs/workbench/contrib/scm/browser/repositoryPanel.ts rename to src/vs/workbench/contrib/scm/browser/repositoryPane.ts index 894717d716f..ab1d61454b2 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { basename, isEqual } from 'vs/base/common/resources'; import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; -import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; import { append, $, addClass, toggleClass, trackFocus, removeClass } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { ISCMRepository, ISCMResourceGroup, ISCMResource, InputValidationType } from 'vs/workbench/contrib/scm/common/scm'; @@ -585,7 +585,7 @@ function convertValidationType(type: InputValidationType): MessageType { } } -export class RepositoryPanel extends ViewletPanel { +export class RepositoryPane extends ViewletPane { private cachedHeight: number | undefined = undefined; private cachedWidth: number | undefined = undefined; @@ -602,7 +602,7 @@ export class RepositoryPanel extends ViewletPanel { constructor( readonly repository: ISCMRepository, - options: IViewletPanelOptions, + options: IViewletPaneOptions, @IKeybindingService protected keybindingService: IKeybindingService, @IWorkbenchThemeService protected themeService: IWorkbenchThemeService, @IContextMenuService protected contextMenuService: IContextMenuService, @@ -966,6 +966,6 @@ export class RepositoryViewDescriptor implements IViewDescriptor { this.id = `scm:repository:${repository.provider.label}:${repoId}`; this.name = repository.provider.rootUri ? basename(repository.provider.rootUri) : repository.provider.label; - this.ctorDescriptor = { ctor: RepositoryPanel, arguments: [repository] }; + this.ctorDescriptor = { ctor: RepositoryPane, arguments: [repository] }; } } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts index a4207212fd4..2593b355533 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts @@ -29,8 +29,8 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IViewsRegistry, Extensions } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; import { nextTick } from 'vs/base/common/process'; -import { RepositoryPanel, RepositoryViewDescriptor } from 'vs/workbench/contrib/scm/browser/repositoryPanel'; -import { MainPanelDescriptor, MainPanel } from 'vs/workbench/contrib/scm/browser/mainPanel'; +import { RepositoryPane, RepositoryViewDescriptor } from 'vs/workbench/contrib/scm/browser/repositoryPane'; +import { MainPaneDescriptor, MainPane } from 'vs/workbench/contrib/scm/browser/mainPane'; export interface ISpliceEvent { index: number; @@ -73,8 +73,8 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel { } get visibleRepositories(): ISCMRepository[] { - return this.panels.filter(panel => panel instanceof RepositoryPanel) - .map(panel => (panel as RepositoryPanel).repository); + return this.panes.filter(pane => pane instanceof RepositoryPane) + .map(pane => (pane as RepositoryPane).repository); } get onDidChangeVisibleRepositories(): Event { @@ -107,11 +107,11 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel { this.message = $('.empty-message', { tabIndex: 0 }, localize('no open repo', "No source control providers registered.")); const viewsRegistry = Registry.as(Extensions.ViewsRegistry); - viewsRegistry.registerViews([new MainPanelDescriptor(this)], VIEW_CONTAINER); + viewsRegistry.registerViews([new MainPaneDescriptor(this)], VIEW_CONTAINER); this._register(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('scm.alwaysShowProviders') && configurationService.getValue('scm.alwaysShowProviders')) { - this.viewsModel.setVisible(MainPanel.ID, true); + this.viewsModel.setVisible(MainPane.ID, true); } })); @@ -184,11 +184,11 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel { const repository = this.visibleRepositories[0]; if (repository) { - const panel = this.panels - .filter(panel => panel instanceof RepositoryPanel && panel.repository === repository)[0] as RepositoryPanel | undefined; + const pane = this.panes + .filter(pane => pane instanceof RepositoryPane && pane.repository === repository)[0] as RepositoryPane | undefined; - if (panel) { - panel.focus(); + if (pane) { + pane.focus(); } else { super.focus(); } @@ -257,10 +257,10 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel { for (const viewDescriptor of toSetInvisible) { if (oneToOne) { - const panel = this.panels.filter(panel => panel.id === viewDescriptor.id)[0]; + const pane = this.panes.filter(pane => pane.id === viewDescriptor.id)[0]; - if (panel) { - size = this.getPanelSize(panel); + if (pane) { + size = this.getPaneSize(pane); } } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 26df3d5d2db..2fc338d2dab 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -56,7 +56,7 @@ import { IPreferencesService, ISettingsEditorOptions } from 'vs/workbench/servic import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { relativePath } from 'vs/base/common/resources'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -77,7 +77,7 @@ export enum SearchViewPosition { } const SEARCH_CANCELLED_MESSAGE = nls.localize('searchCanceled', "Search was canceled before any results could be found - "); -export class SearchView extends ViewletPanel { +export class SearchView extends ViewletPane { private static readonly MAX_TEXT_RESULTS = 10000; @@ -139,7 +139,7 @@ export class SearchView extends ViewletPanel { constructor( private position: SearchViewPosition, - options: IViewletPanelOptions, + options: IViewletPaneOptions, @IFileService private readonly fileService: IFileService, @IEditorService private readonly editorService: IEditorService, @IProgressService private readonly progressService: IProgressService, From 8caf39ef37932f5b9492a88a7f921625ef120080 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Nov 2019 11:40:32 -0800 Subject: [PATCH 133/246] Fix loading of images with spaces in the name Partially revert #84667 Also seems to fix #85190 --- extensions/image-preview/src/preview.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index e383ad4e613..f78061d008e 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -240,9 +240,9 @@ class Preview extends Disposable implements vscode.WebviewEditorEditingCapabilit default: // Avoid adding cache busting if there is already a query string if (resource.query) { - return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString()); + return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true)); } - return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString() + `?version=${version}`); + return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true) + `?version=${version}`); } } From 2517b6096f69e174c6dc9935fa3471867de74f51 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 19 Nov 2019 15:24:36 -0800 Subject: [PATCH 134/246] Tap handler for noTabsTitleControl --- .../parts/editor/noTabsTitleControl.ts | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 645159a3399..8f3af39d0ae 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -89,7 +89,10 @@ export class NoTabsTitleControl extends TitleControl { EventHelper.stop(e, false); // delayed to let the onTitleClick() come first which can cause a focus change which can close quick open - setTimeout(() => this.quickOpenService.show()); + setTimeout(() => { + console.log('quick open show'); + this.quickOpenService.show(); + }); } private onTitleDoubleClick(e: MouseEvent): void { @@ -100,13 +103,23 @@ export class NoTabsTitleControl extends TitleControl { private onTitleClick(e: MouseEvent | GestureEvent): void { - // Close editor on middle mouse click - if (e instanceof MouseEvent && e.button === 1 /* Middle Button */) { - EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */); + if (e instanceof MouseEvent) { + // Close editor on middle mouse click + if (e.button === 1 /* Middle Button */) { + EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */); - if (this.group.activeEditor) { - this.group.closeEditor(this.group.activeEditor); + if (this.group.activeEditor) { + this.group.closeEditor(this.group.activeEditor); + } } + } else { + // @rebornix + // gesture tap should open the quick open + // editorGroupView will focus on the editor again when there are mouse/pointer/touch down events + // we need to wait a bit as `GesureEvent.Tap` is generated from `touchstart` and then `touchend` evnets, which are not an atom event. + setTimeout(() => { + this.quickOpenService.show(); + }, 50); } } From 2422b62a269db191224c2904c2ad8759242c1f9d Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 20 Nov 2019 11:26:52 -0800 Subject: [PATCH 135/246] Avoid mouse down and touch conflicts. --- src/vs/editor/browser/controller/pointerHandler.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 6729b51619a..f1a997e1a4d 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -202,7 +202,7 @@ export class PointerEventHandler extends MouseHandler { this._lastPointerType = 'mouse'; - this.viewHelper.linesContentDomNode.addEventListener('pointerdown', (e: any) => { + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'pointerdown', (e: any) => { const pointerType = e.pointerType; if (pointerType === 'mouse') { this._lastPointerType = 'mouse'; @@ -212,7 +212,7 @@ export class PointerEventHandler extends MouseHandler { } else { this._lastPointerType = 'pen'; } - }); + })); // PonterEvents const pointerEvents = new EditorPointerEventFactory(this.viewHelper.viewDomNode); @@ -256,9 +256,11 @@ export class PointerEventHandler extends MouseHandler { } public _onMouseDown(e: EditorMouseEvent): void { - if (this._lastPointerType !== 'touch') { - super._onMouseDown(e); + if (e.target && this.viewHelper.linesContentDomNode.contains(e.target) && this._lastPointerType === 'touch') { + return; } + + super._onMouseDown(e); } } From ee3adf5fd0f3122cd3b5038fa9adbd9c5ff7dd2d Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 20 Nov 2019 11:46:04 -0800 Subject: [PATCH 136/246] Adopt generic mouse events for debug toolbar. --- src/vs/base/browser/dom.ts | 5 +++++ src/vs/workbench/contrib/debug/browser/debugToolBar.ts | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 671c8d93d9e..ba6ea61595a 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -271,6 +271,10 @@ export function addDisposableGenericMouseDownListner(node: EventTarget, handler: return addDisposableListener(node, platform.isIOS && BrowserFeatures.pointerEvents ? EventType.POINTER_DOWN : EventType.MOUSE_DOWN, handler, useCapture); } +export function addDisposableGenericMouseMoveListner(node: EventTarget, handler: (event: any) => void, useCapture?: boolean): IDisposable { + return addDisposableListener(node, platform.isIOS && BrowserFeatures.pointerEvents ? EventType.POINTER_MOVE : EventType.MOUSE_MOVE, handler, useCapture); +} + export function addDisposableGenericMouseUpListner(node: EventTarget, handler: (event: any) => void, useCapture?: boolean): IDisposable { return addDisposableListener(node, platform.isIOS && BrowserFeatures.pointerEvents ? EventType.POINTER_UP : EventType.MOUSE_UP, handler, useCapture); } @@ -882,6 +886,7 @@ export const EventType = { MOUSE_LEAVE: 'mouseleave', POINTER_UP: 'pointerup', POINTER_DOWN: 'pointerdown', + POINTER_MOVE: 'pointermove', CONTEXT_MENU: 'contextmenu', WHEEL: 'wheel', // Keyboard diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 3500fa7c3d8..cf629a37d01 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -208,7 +208,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { })); this._register(dom.addDisposableListener(window, dom.EventType.RESIZE, () => this.setCoordinates())); - this._register(dom.addDisposableListener(this.dragArea, dom.EventType.MOUSE_UP, (event: MouseEvent) => { + this._register(dom.addDisposableGenericMouseUpListner(this.dragArea, (event: MouseEvent) => { const mouseClickEvent = new StandardMouseEvent(event); if (mouseClickEvent.detail === 2) { // double click on debug bar centers it again #8250 @@ -218,10 +218,10 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { } })); - this._register(dom.addDisposableListener(this.dragArea, dom.EventType.MOUSE_DOWN, (event: MouseEvent) => { + this._register(dom.addDisposableGenericMouseDownListner(this.dragArea, (event: MouseEvent) => { dom.addClass(this.dragArea, 'dragged'); - const mouseMoveListener = dom.addDisposableListener(window, 'mousemove', (e: MouseEvent) => { + const mouseMoveListener = dom.addDisposableGenericMouseMoveListner(window, (e: MouseEvent) => { const mouseMoveEvent = new StandardMouseEvent(e); // Prevent default to stop editor selecting text #8524 mouseMoveEvent.preventDefault(); @@ -229,7 +229,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.setCoordinates(mouseMoveEvent.posx - 14, mouseMoveEvent.posy - this.layoutService.getTitleBarOffset()); }); - const mouseUpListener = dom.addDisposableListener(window, 'mouseup', (e: MouseEvent) => { + const mouseUpListener = dom.addDisposableGenericMouseUpListner(window, (e: MouseEvent) => { this.storePosition(); dom.removeClass(this.dragArea, 'dragged'); From 2ad623990f7578d0544978ca7ad2648dce830b07 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 20 Nov 2019 11:51:34 -0800 Subject: [PATCH 137/246] ignore native checkbox in breakpoints view. --- src/vs/workbench/contrib/debug/browser/breakpointsView.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index aeaf6e7cebf..d9711706037 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -32,6 +32,7 @@ import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/vie import { ILabelService } from 'vs/platform/label/common/label'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { Gesture } from 'vs/base/browser/touch'; const $ = dom.$; @@ -39,6 +40,7 @@ function createCheckbox(): HTMLInputElement { const checkbox = $('input'); checkbox.type = 'checkbox'; checkbox.tabIndex = -1; + Gesture.ignoreTarget(checkbox); return checkbox; } From 37f18e8f2d89f2d90fd0fa2d955011e0c03dc5a6 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 20 Nov 2019 23:23:01 +0100 Subject: [PATCH 138/246] don't activate vscode-api-test on debug; fixes #85230 --- extensions/vscode-api-tests/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 8ac6b2806ca..09efe39ca76 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -7,8 +7,7 @@ "enableProposedApi": true, "private": true, "activationEvents": [ - "onFileSystem:memfs", - "onDebug" + "onFileSystem:memfs" ], "main": "./out/extension", "engines": { From 4cc8710a38460ca60d60ecac029203c88f05a798 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Nov 2019 14:29:27 -0800 Subject: [PATCH 139/246] Add CodeAction.disabled For #85160 --- src/vs/editor/common/modes.ts | 1 + src/vs/editor/contrib/codeAction/codeAction.ts | 13 ++++++++----- .../editor/contrib/codeAction/codeActionUi.ts | 11 +++++------ .../contrib/codeAction/codeActionWidget.ts | 6 +++--- .../contrib/codeAction/lightBulbWidget.ts | 2 +- .../contrib/codeAction/test/codeAction.test.ts | 18 +++++++++--------- .../codeAction/test/codeActionModel.test.ts | 4 ++-- .../editor/contrib/hover/modesContentHover.ts | 2 +- src/vs/monaco.d.ts | 1 + src/vs/vscode.proposed.d.ts | 14 ++++++++++++++ .../api/browser/mainThreadSaveParticipant.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 1 + .../api/common/extHostLanguageFeatures.ts | 1 + .../markers/browser/markersTreeViewer.ts | 2 +- .../api/extHostLanguageFeatures.test.ts | 8 ++++---- 15 files changed, 53 insertions(+), 33 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 4fd62bcd685..5d3f0109194 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -548,6 +548,7 @@ export interface CodeAction { diagnostics?: IMarkerData[]; kind?: string; isPreferred?: boolean; + disabled?: string; } /** diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 2cfc82d83cc..c4850175c63 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -24,7 +24,8 @@ export const organizeImportsCommandId = 'editor.action.organizeImports'; export const fixAllCommandId = 'editor.action.fixAll'; export interface CodeActionSet extends IDisposable { - readonly actions: readonly CodeAction[]; + readonly validActions: readonly CodeAction[]; + readonly allActions: readonly CodeAction[]; readonly hasAutoFix: boolean; } @@ -44,16 +45,18 @@ class ManagedCodeActionSet extends Disposable implements CodeActionSet { } } - public readonly actions: readonly CodeAction[]; + public readonly validActions: readonly CodeAction[]; + public readonly allActions: readonly CodeAction[]; public constructor(actions: readonly CodeAction[], disposables: DisposableStore) { super(); this._register(disposables); - this.actions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator); + this.allActions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator); + this.validActions = this.allActions.filter(action => !action.disabled); } public get hasAutoFix() { - return this.actions.some(fix => !!fix.kind && CodeActionKind.QuickFix.contains(new CodeActionKind(fix.kind)) && !!fix.isPreferred); + return this.validActions.some(fix => !!fix.kind && CodeActionKind.QuickFix.contains(new CodeActionKind(fix.kind)) && !!fix.isPreferred); } } @@ -150,5 +153,5 @@ registerLanguageCommand('_executeCodeActionProvider', async function (accessor, CancellationToken.None); setTimeout(() => codeActionSet.dispose(), 100); - return codeActionSet.actions; + return codeActionSet.validActions; }); diff --git a/src/vs/editor/contrib/codeAction/codeActionUi.ts b/src/vs/editor/contrib/codeAction/codeActionUi.ts index 5dfd9228ff0..40c6dd2d04d 100644 --- a/src/vs/editor/contrib/codeAction/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/codeActionUi.ts @@ -68,20 +68,19 @@ export class CodeActionUi extends Disposable { this._lightBulbWidget.getValue().update(actions, newState.position); - if (!actions.actions.length && newState.trigger.context) { + if (!actions.validActions.length && newState.trigger.context) { MessageController.get(this._editor).showMessage(newState.trigger.context.notAvailableMessage, newState.trigger.context.position); this._activeCodeActions.value = actions; return; } if (newState.trigger.type === 'manual') { - if (newState.trigger.filter && newState.trigger.filter.include) { - // Triggered for specific scope - if (actions.actions.length > 0) { + if (newState.trigger.filter?.include) { // Triggered for specific scope + if (actions.validActions.length > 0) { // Apply if we only have one action or requested autoApply - if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && actions.actions.length === 1)) { + if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && actions.validActions.length === 1)) { try { - await this.delegate.applyCodeAction(actions.actions[0], false); + await this.delegate.applyCodeAction(actions.validActions[0], false); } finally { actions.dispose(); } diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index a40ffdfa45c..b0608b3dbcb 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -35,7 +35,7 @@ class CodeActionAction extends Action { public readonly action: CodeAction, callback: () => Promise, ) { - super(action.command ? action.command.id : action.title, action.title, undefined, true, callback); + super(action.command ? action.command.id : action.title, action.title, undefined, !action.disabled, callback); } } @@ -64,7 +64,7 @@ export class CodeActionWidget extends Disposable { } public async show(codeActions: CodeActionSet, at: IAnchor | IPosition): Promise { - if (!codeActions.actions.length) { + if (!codeActions.validActions.length) { this._visible = false; return; } @@ -78,7 +78,7 @@ export class CodeActionWidget extends Disposable { this._visible = true; this._showingActions.value = codeActions; - const actions = codeActions.actions.map(action => + const actions = codeActions.validActions.map(action => new CodeActionAction(action, () => this._delegate.onSelectCodeAction(action))); const anchor = Position.isIPosition(at) ? this._toCoords(at) : at || { x: 0, y: 0 }; diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 0fc03b3a8ba..5512a63a60f 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -137,7 +137,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget { } public update(actions: CodeActionSet, atPosition: IPosition) { - if (actions.actions.length <= 0) { + if (actions.validActions.length <= 0) { return this.hide(); } diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index 730d96fc9a5..0b7fb8ba4a6 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -125,7 +125,7 @@ suite('CodeAction', () => { testData.tsLint.abc ]; - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'manual' }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'manual' }, CancellationToken.None); assert.equal(actions.length, 6); assert.deepEqual(actions, expected); }); @@ -140,20 +140,20 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a') } }, CancellationToken.None); assert.equal(actions.length, 2); assert.strictEqual(actions[0].title, 'a'); assert.strictEqual(actions[1].title, 'a.b'); } { - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a.b'); } { - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None); assert.equal(actions.length, 0); } }); @@ -172,7 +172,7 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a') } }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a'); }); @@ -186,13 +186,13 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto' }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto' }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'b'); } { - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a'); } @@ -208,7 +208,7 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: CodeActionKind.Source.append('test'), excludes: [CodeActionKind.Source], @@ -233,7 +233,7 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: CodeActionKind.QuickFix diff --git a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts index e394f6838c1..c4a7cba5173 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts @@ -64,7 +64,7 @@ suite('CodeActionModel', () => { e.actions.then(fixes => { model.dispose(); - assert.equal(fixes.actions.length, 1); + assert.equal(fixes.validActions.length, 1); done(); }, done); })); @@ -104,7 +104,7 @@ suite('CodeActionModel', () => { assert.ok(e.actions); e.actions.then(fixes => { model.dispose(); - assert.equal(fixes.actions.length, 1); + assert.equal(fixes.validActions.length, 1); resolve(undefined); }, reject); })); diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index ca710263b24..de0e08e7099 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -548,7 +548,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { quickfixPlaceholderElement.style.transition = ''; quickfixPlaceholderElement.style.opacity = '1'; - if (!actions.actions.length) { + if (!actions.validActions.length) { actions.dispose(); quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available"); return; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 42f8e0317ae..5baa27de579 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4977,6 +4977,7 @@ declare namespace monaco.languages { diagnostics?: editor.IMarkerData[]; kind?: string; isPreferred?: boolean; + disabled?: string; } export interface CodeActionList extends IDisposable { diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 19bfef3caba..7f26e5267b9 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1326,4 +1326,18 @@ declare module 'vscode' { } //#endregion + + //#region mjbvz - Surfacing reasons why a code action cannot be applied to users — https://github.com/microsoft/vscode/issues/85160 + + export interface CodeAction { + /** + * Marks that the code action cannot currently be applied. + * + * This should be a human readable description of why the code action is currently disabled. Disabled code actions + * will be surfaced in the refactor UI but cannot be applied. + */ + disabled?: string; + } + + //#endregion } diff --git a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts index 023e413f8cc..bf7d5c6660d 100644 --- a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts @@ -307,7 +307,7 @@ class CodeActionOnSaveParticipant implements ISaveParticipant { for (const codeActionKind of codeActionsOnSave) { const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, token); try { - await this.applyCodeActions(actionsToRun.actions); + await this.applyCodeActions(actionsToRun.validActions); } catch { // Failure to apply a code action should not block other on save actions } finally { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9639c114338..800b3ee2f15 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1086,6 +1086,7 @@ export interface ICodeActionDto { command?: ICommandDto; kind?: string; isPreferred?: boolean; + disabled?: string; } export interface ICodeActionListDto { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 30c3818861f..88464ef0ead 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -389,6 +389,7 @@ class CodeActionAdapter { edit: candidate.edit && typeConvert.WorkspaceEdit.from(candidate.edit), kind: candidate.kind && candidate.kind.value, isPreferred: candidate.isPreferred, + disabled: candidate.disabled }); } } diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index d8f74092225..19fac10efdc 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -564,7 +564,7 @@ export class MarkerViewModel extends Disposable { } private toActions(codeActions: CodeActionSet): IAction[] { - return codeActions.actions.map(codeAction => new Action( + return codeActions.validActions.map(codeAction => new Action( codeAction.command ? codeAction.command.id : codeAction.title, codeAction.title, undefined, diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index 3793608226f..891f0358784 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -589,7 +589,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); assert.equal(actions.length, 2); const [first, second] = actions; assert.equal(first.title, 'Testing1'); @@ -613,7 +613,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); assert.equal(actions.length, 1); const [first] = actions; assert.equal(first.title, 'Testing1'); @@ -636,7 +636,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); assert.equal(actions.length, 1); }); @@ -654,7 +654,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); assert.equal(actions.length, 1); }); From 827e94edefbf51f48629e23bee14344e5aeb0c37 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Nov 2019 17:08:42 -0800 Subject: [PATCH 140/246] Hook up basic alert of why a code action could not be applied For #85160 --- .../src/features/refactor.ts | 17 ++++- .../editor/contrib/codeAction/codeActionUi.ts | 66 +++++++++++++++---- .../contrib/codeAction/codeActionWidget.ts | 4 +- 3 files changed, 71 insertions(+), 16 deletions(-) diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index a55236781b6..a6ed687fbc2 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -238,9 +238,14 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { return undefined; } - return this.convertApplicableRefactors(response.body, document, rangeOrSelection); + const actions = this.convertApplicableRefactors(response.body, document, rangeOrSelection); + if (!context.only) { + return actions; + } + return this.appendInvalidActions(actions); } + private convertApplicableRefactors( body: Proto.ApplicableRefactorInfo[], document: vscode.TextDocument, @@ -305,6 +310,16 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { } return false; } + + private appendInvalidActions(actions: vscode.CodeAction[]): vscode.CodeAction[] { + if (!actions.some(action => action.kind && Extract_Constant.kind.contains(action.kind))) { + const disabledAction = new vscode.CodeAction('Extract to constant', Extract_Constant.kind); + disabledAction.disabled = localize('extract.disabled', "The current selection cannot be extracted"); + disabledAction.isPreferred = true; + actions.push(disabledAction); + } + return actions; + } } export function register( diff --git a/src/vs/editor/contrib/codeAction/codeActionUi.ts b/src/vs/editor/contrib/codeAction/codeActionUi.ts index 40c6dd2d04d..57e857cf285 100644 --- a/src/vs/editor/contrib/codeAction/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/codeActionUi.ts @@ -3,21 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; +import { find } from 'vs/base/common/arrays'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { Lazy } from 'vs/base/common/lazy'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IPosition } from 'vs/editor/common/core/position'; import { CodeAction } from 'vs/editor/common/modes'; import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; import { MessageController } from 'vs/editor/contrib/message/messageController'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { CodeActionsState } from './codeActionModel'; -import { CodeActionAutoApply } from './types'; import { CodeActionWidget } from './codeActionWidget'; import { LightBulbWidget } from './lightBulbWidget'; -import { IPosition } from 'vs/editor/common/core/position'; -import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; -import { Lazy } from 'vs/base/common/lazy'; +import { CodeActionAutoApply, CodeActionTrigger } from './types'; export class CodeActionUi extends Disposable { @@ -68,7 +69,7 @@ export class CodeActionUi extends Disposable { this._lightBulbWidget.getValue().update(actions, newState.position); - if (!actions.validActions.length && newState.trigger.context) { + if (!actions.allActions.length && newState.trigger.context) { MessageController.get(this._editor).showMessage(newState.trigger.context.notAvailableMessage, newState.trigger.context.position); this._activeCodeActions.value = actions; return; @@ -76,18 +77,29 @@ export class CodeActionUi extends Disposable { if (newState.trigger.type === 'manual') { if (newState.trigger.filter?.include) { // Triggered for specific scope - if (actions.validActions.length > 0) { - // Apply if we only have one action or requested autoApply - if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && actions.validActions.length === 1)) { - try { - await this.delegate.applyCodeAction(actions.validActions[0], false); - } finally { - actions.dispose(); - } + // Check to see if we want to auto apply. + + const validActionToApply = this.tryGetValidActionToApply(newState.trigger, actions); + if (validActionToApply) { + try { + await this.delegate.applyCodeAction(validActionToApply, false); + } finally { + actions.dispose(); + } + return; + } + + // Check to see if there is an action that we would have applied were it not invalid + if (newState.trigger.context) { + const invalidAction = this.getInvalidActionThatWouldHaveBeenApplied(newState.trigger, actions); + if (invalidAction && invalidAction.disabled) { + MessageController.get(this._editor).showMessage(invalidAction.disabled, newState.trigger.context.position); + actions.dispose(); return; } } } + this._activeCodeActions.value = actions; this._codeActionWidget.getValue().show(actions, newState.position); } else { @@ -101,6 +113,34 @@ export class CodeActionUi extends Disposable { } } + private getInvalidActionThatWouldHaveBeenApplied(trigger: CodeActionTrigger, actions: CodeActionSet): CodeAction | undefined { + if (!actions.allActions.length) { + return undefined; + } + + if ((trigger.autoApply === CodeActionAutoApply.First && actions.validActions.length === 0) + || (trigger.autoApply === CodeActionAutoApply.IfSingle && actions.allActions.length === 1) + ) { + return find(actions.allActions, action => action.disabled); + } + + return undefined; + } + + private tryGetValidActionToApply(trigger: CodeActionTrigger, actions: CodeActionSet): CodeAction | undefined { + if (!actions.validActions.length) { + return undefined; + } + + if ((trigger.autoApply === CodeActionAutoApply.First && actions.validActions.length > 0) + || (trigger.autoApply === CodeActionAutoApply.IfSingle && actions.validActions.length === 1) + ) { + return actions.validActions[0]; + } + + return undefined; + } + public async showCodeActionList(actions: CodeActionSet, at: IAnchor | IPosition): Promise { this._codeActionWidget.getValue().show(actions, at); } diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index b0608b3dbcb..8abde0d0ebe 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -64,7 +64,7 @@ export class CodeActionWidget extends Disposable { } public async show(codeActions: CodeActionSet, at: IAnchor | IPosition): Promise { - if (!codeActions.validActions.length) { + if (!codeActions.allActions.length) { this._visible = false; return; } @@ -78,7 +78,7 @@ export class CodeActionWidget extends Disposable { this._visible = true; this._showingActions.value = codeActions; - const actions = codeActions.validActions.map(action => + const actions = codeActions.allActions.map(action => new CodeActionAction(action, () => this._delegate.onSelectCodeAction(action))); const anchor = Position.isIPosition(at) ? this._toCoords(at) : at || { x: 0, y: 0 }; From bad004944211d9021634ce7a65ebac7c537381ff Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Nov 2019 17:25:53 -0800 Subject: [PATCH 141/246] Make sure we don't include disabled actions in the standard light bulb menu --- .../editor/contrib/codeAction/codeActionUi.ts | 26 +++++++++++-------- .../contrib/codeAction/codeActionWidget.ts | 13 +++++++--- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionUi.ts b/src/vs/editor/contrib/codeAction/codeActionUi.ts index 57e857cf285..61f1a9b29dd 100644 --- a/src/vs/editor/contrib/codeAction/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/codeActionUi.ts @@ -16,7 +16,7 @@ import { MessageController } from 'vs/editor/contrib/message/messageController'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { CodeActionsState } from './codeActionModel'; -import { CodeActionWidget } from './codeActionWidget'; +import { CodeActionWidget, CodeActionShowOptions } from './codeActionWidget'; import { LightBulbWidget } from './lightBulbWidget'; import { CodeActionAutoApply, CodeActionTrigger } from './types'; @@ -48,7 +48,7 @@ export class CodeActionUi extends Disposable { this._lightBulbWidget = new Lazy(() => { const widget = this._register(new LightBulbWidget(this._editor, quickFixActionId, preferredFixActionId, keybindingService)); - this._register(widget.onClick(e => this.showCodeActionList(e.actions, e))); + this._register(widget.onClick(e => this.showCodeActionList(e.actions, e, { includeDisabledActions: false }))); return widget; }); } @@ -69,12 +69,6 @@ export class CodeActionUi extends Disposable { this._lightBulbWidget.getValue().update(actions, newState.position); - if (!actions.allActions.length && newState.trigger.context) { - MessageController.get(this._editor).showMessage(newState.trigger.context.notAvailableMessage, newState.trigger.context.position); - this._activeCodeActions.value = actions; - return; - } - if (newState.trigger.type === 'manual') { if (newState.trigger.filter?.include) { // Triggered for specific scope // Check to see if we want to auto apply. @@ -100,8 +94,18 @@ export class CodeActionUi extends Disposable { } } + const includeDisabledActions = !!newState.trigger.filter?.include; + if (newState.trigger.context) { + if (!actions.allActions.length || !includeDisabledActions && !actions.validActions.length) { + MessageController.get(this._editor).showMessage(newState.trigger.context.notAvailableMessage, newState.trigger.context.position); + this._activeCodeActions.value = actions; + actions.dispose(); + return; + } + } + this._activeCodeActions.value = actions; - this._codeActionWidget.getValue().show(actions, newState.position); + this._codeActionWidget.getValue().show(actions, newState.position, { includeDisabledActions }); } else { // auto magically triggered if (this._codeActionWidget.getValue().isVisible) { @@ -141,7 +145,7 @@ export class CodeActionUi extends Disposable { return undefined; } - public async showCodeActionList(actions: CodeActionSet, at: IAnchor | IPosition): Promise { - this._codeActionWidget.getValue().show(actions, at); + public async showCodeActionList(actions: CodeActionSet, at: IAnchor | IPosition, options: CodeActionShowOptions): Promise { + this._codeActionWidget.getValue().show(actions, at, options); } } diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index 8abde0d0ebe..4e85aa712d2 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -39,6 +39,10 @@ class CodeActionAction extends Action { } } +export interface CodeActionShowOptions { + readonly includeDisabledActions: boolean; +} + export class CodeActionWidget extends Disposable { private _visible: boolean = false; @@ -63,8 +67,9 @@ export class CodeActionWidget extends Disposable { return this._visible; } - public async show(codeActions: CodeActionSet, at: IAnchor | IPosition): Promise { - if (!codeActions.allActions.length) { + public async show(codeActions: CodeActionSet, at: IAnchor | IPosition, options: CodeActionShowOptions): Promise { + const actionsToShow = options.includeDisabledActions ? codeActions.allActions : codeActions.validActions; + if (!actionsToShow.length) { this._visible = false; return; } @@ -78,7 +83,7 @@ export class CodeActionWidget extends Disposable { this._visible = true; this._showingActions.value = codeActions; - const actions = codeActions.allActions.map(action => + const menuActions = actionsToShow.map(action => new CodeActionAction(action, () => this._delegate.onSelectCodeAction(action))); const anchor = Position.isIPosition(at) ? this._toCoords(at) : at || { x: 0, y: 0 }; @@ -86,7 +91,7 @@ export class CodeActionWidget extends Disposable { this._contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => actions, + getActions: () => menuActions, onHide: () => { this._visible = false; this._editor.focus(); From 46d64ac435465facfa8773cefb30000b29f134f7 Mon Sep 17 00:00:00 2001 From: Waterpolymer Date: Wed, 20 Nov 2019 23:02:36 -0500 Subject: [PATCH 142/246] Changed instances of Code to VS Code --- README.md | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 5d39d7e40c6..bde4fa2e82c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Visual Studio Code - Open Source ("Code - OSS") + [![Build Status](https://dev.azure.com/vscode/VSCode/_apis/build/status/VS%20Code?branchName=master)](https://dev.azure.com/vscode/VSCode/_build/latest?definitionId=12) [![Feature Requests](https://img.shields.io/github/issues/Microsoft/vscode/feature-request.svg)](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) [![Bugs](https://img.shields.io/github/issues/Microsoft/vscode/bug.svg)](https://github.com/Microsoft/vscode/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3Abug) @@ -22,41 +23,39 @@ This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Stu Visual Studio Code is updated monthly with new features and bug fixes. You can download it for Windows, macOS, and Linux on [Visual Studio Code's website](https://code.visualstudio.com/Download). To get the latest releases every day, install the [Insiders build](https://code.visualstudio.com/insiders). - - ## Contributing There are many ways in which you can participate in the project, for example: -* [Submit bugs and feature requests](https://github.com/microsoft/vscode/issues), and help us verify as they are checked in -* Review [source code changes](https://github.com/microsoft/vscode/pulls) -* Review the [documentation](https://github.com/microsoft/vscode-docs) and make pull requests for anything from typos to new content +- [Submit bugs and feature requests](https://github.com/microsoft/vscode/issues), and help us verify as they are checked in +- Review [source code changes](https://github.com/microsoft/vscode/pulls) +- Review the [documentation](https://github.com/microsoft/vscode-docs) and make pull requests for anything from typos to new content If you are interested in fixing issues and contributing directly to the code base, please see the document [How to Contribute](https://github.com/Microsoft/vscode/wiki/How-to-Contribute), which covers the following: -* [How to build and run from source](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#build-and-run) -* [The development workflow, including debugging and running tests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#debugging) -* [Coding guidelines](https://github.com/Microsoft/vscode/wiki/Coding-Guidelines) -* [Submitting pull requests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#pull-requests) -* [Finding an issue to work on](https://github.com/microsoft/vscode/wiki/How-to-Contribute#where-to-contribute) -* [Contributing to translations](https://aka.ms/vscodeloc) +- [How to build and run from source](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#build-and-run) +- [The development workflow, including debugging and running tests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#debugging) +- [Coding guidelines](https://github.com/Microsoft/vscode/wiki/Coding-Guidelines) +- [Submitting pull requests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#pull-requests) +- [Finding an issue to work on](https://github.com/microsoft/vscode/wiki/How-to-Contribute#where-to-contribute) +- [Contributing to translations](https://aka.ms/vscodeloc) ## Feedback -* Ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/vscode) -* [Request a new feature](CONTRIBUTING.md) -* Up vote [popular feature requests](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) -* [File an issue](https://github.com/Microsoft/vscode/issues) -* Follow [@code](https://twitter.com/code) and let us know what you think! +- Ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/vscode) +- [Request a new feature](CONTRIBUTING.md) +- Up vote [popular feature requests](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) +- [File an issue](https://github.com/Microsoft/vscode/issues) +- Follow [@code](https://twitter.com/code) and let us know what you think! ## Related Projects -Many of the core components and extensions to Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/Microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/Microsoft/vscode/wiki). +Many of the core components and extensions to VS Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/Microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/Microsoft/vscode/wiki). ## Bundled Extensions -Code includes a set of built-in extensions located in the [extensions](extensions) folder, including grammars and snippets for many languages. Extensions that provide rich language support (code completion, Go to Definition) for a language have the suffix `language-features`. For example, the `json` extension provides coloring for `JSON` and the `json-language-features` provides rich language support for `JSON`. +VS Code includes a set of built-in extensions located in the [extensions](extensions) folder, including grammars and snippets for many languages. Extensions that provide rich language support (code completion, Go to Definition) for a language have the suffix `language-features`. For example, the `json` extension provides coloring for `JSON` and the `json-language-features` provides rich language support for `JSON`. ## Code of Conduct From 4b6e4ee176acba945da27405b41712999f46f4c9 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Nov 2019 20:42:28 -0800 Subject: [PATCH 143/246] Fix compile error --- src/vs/editor/contrib/codeAction/codeActionCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index c555ceec6d6..20561a76ee9 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -115,7 +115,7 @@ export class QuickFixController extends Disposable implements IEditorContributio } public showCodeActions(actions: CodeActionSet, at: IAnchor | IPosition) { - return this._ui.getValue().showCodeActionList(actions, at); + return this._ui.getValue().showCodeActionList(actions, at, { includeDisabledActions: false }); } public manualTriggerAtCurrentPosition( From 61cf4155b349a90910079de4b8ad982551bd419e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Nov 2019 21:16:01 -0800 Subject: [PATCH 144/246] Remove data uri support for custom editors The git extension no longer user data uris --- .../customEditor/browser/customEditorInput.ts | 25 ++----------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 5fdc6f90528..9d3653b9d8f 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -6,17 +6,16 @@ import { memoize } from 'vs/base/common/decorators'; import { Lazy } from 'vs/base/common/lazy'; import { UnownedDisposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; import { basename } from 'vs/base/common/path'; -import { DataUri, isEqual } from 'vs/base/common/resources'; +import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; +import { IEditorModel } from 'vs/platform/editor/common/editor'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { GroupIdentifier, IEditorInput, IRevertOptions, ISaveOptions, Verbosity } from 'vs/workbench/common/editor'; import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; -import { IEditorModel } from 'vs/platform/editor/common/editor'; export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { @@ -49,25 +48,11 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { @memoize getName(): string { - if (this.getResource().scheme === Schemas.data) { - const metadata = DataUri.parseMetaData(this.getResource()); - const label = metadata.get(DataUri.META_DATA_LABEL); - if (typeof label === 'string') { - return label; - } - } return basename(this.labelService.getUriLabel(this.getResource())); } @memoize getDescription(): string | undefined { - if (this.getResource().scheme === Schemas.data) { - const metadata = DataUri.parseMetaData(this.getResource()); - const description = metadata.get(DataUri.META_DATA_DESCRIPTION); - if (typeof description === 'string') { - return description; - } - } return super.getDescription(); } @@ -84,17 +69,11 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { @memoize private get mediumTitle(): string { - if (this.getResource().scheme === Schemas.data) { - return this.getName(); - } return this.labelService.getUriLabel(this.getResource(), { relative: true }); } @memoize private get longTitle(): string { - if (this.getResource().scheme === Schemas.data) { - return this.getName(); - } return this.labelService.getUriLabel(this.getResource()); } From 8fa16bd74f6a87e61cc6fec284e2f9f63956c952 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Nov 2019 21:35:29 -0800 Subject: [PATCH 145/246] Remove custom editor support for data uris For #85203. Data uris are no longer used by VS Code core --- extensions/image-preview/package.json | 1 - .../customEditor/browser/customEditors.ts | 16 +--------------- .../contrib/customEditor/common/customEditor.ts | 1 - 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/extensions/image-preview/package.json b/extensions/image-preview/package.json index 1ad08ffa55e..a59b8a1c89a 100644 --- a/extensions/image-preview/package.json +++ b/extensions/image-preview/package.json @@ -30,7 +30,6 @@ "selector": [ { "filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,webp}", - "mime": "image/*" } ] } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 56c6b0320a9..814c4d6b301 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -7,8 +7,7 @@ import { coalesce, distinct, find, mergeSort } from 'vs/base/common/arrays'; import * as glob from 'vs/base/common/glob'; import { Lazy } from 'vs/base/common/lazy'; import { Disposable, UnownedDisposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; -import { basename, DataUri, isEqual } from 'vs/base/common/resources'; +import { basename, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import * as nls from 'vs/nls'; @@ -31,7 +30,6 @@ import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsSe import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { CustomFileEditorInput } from './customEditorInput'; - const defaultEditorId = 'default'; const defaultEditorInfo: CustomEditorInfo = { @@ -408,18 +406,6 @@ function priorityToRank(priority: CustomEditorPriority): number { } function matches(selector: CustomEditorSelector, resource: URI): boolean { - if (resource.scheme === Schemas.data) { - if (!selector.mime) { - return false; - } - const metadata = DataUri.parseMetaData(resource); - const mime = metadata.get(DataUri.META_DATA_MIME); - if (!mime) { - return false; - } - return glob.match(selector.mime, mime.toLowerCase()); - } - if (selector.filenamePattern) { if (glob.match(selector.filenamePattern.toLowerCase(), basename(resource).toLowerCase())) { return true; diff --git a/src/vs/workbench/contrib/customEditor/common/customEditor.ts b/src/vs/workbench/contrib/customEditor/common/customEditor.ts index bfdf0c5177c..f8a50cdb9d1 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditor.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditor.ts @@ -70,7 +70,6 @@ export const enum CustomEditorPriority { export interface CustomEditorSelector { readonly filenamePattern?: string; - readonly mime?: string; } export interface CustomEditorInfo { From d9c4923f981eee4278839663544649fb196f695b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Nov 2019 21:36:05 -0800 Subject: [PATCH 146/246] Fix trailiing comma --- extensions/image-preview/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/image-preview/package.json b/extensions/image-preview/package.json index a59b8a1c89a..f39fc9b6012 100644 --- a/extensions/image-preview/package.json +++ b/extensions/image-preview/package.json @@ -29,7 +29,7 @@ "priority": "builtin", "selector": [ { - "filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,webp}", + "filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,webp}" } ] } From a825aaaa909ef33eee11cb1d322e9a64981978d2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 21 Nov 2019 07:14:26 +0100 Subject: [PATCH 147/246] support auto save on focus lost for all editors (#84672) --- src/vs/workbench/browser/composite.ts | 16 +++++++------- .../browser/parts/editor/editorAutoSave.ts | 22 +++++-------------- src/vs/workbench/common/composite.ts | 11 ++++++++++ src/vs/workbench/common/editor.ts | 13 ++--------- .../progress/test/progressIndicator.test.ts | 4 ++++ 5 files changed, 31 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index 6414496a6d3..3a1eb2c4e6e 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -31,11 +31,11 @@ import { find } from 'vs/base/common/arrays'; */ export abstract class Composite extends Component implements IComposite { - private readonly _onTitleAreaUpdate: Emitter = this._register(new Emitter()); - readonly onTitleAreaUpdate: Event = this._onTitleAreaUpdate.event; + private readonly _onTitleAreaUpdate = this._register(new Emitter()); + readonly onTitleAreaUpdate = this._onTitleAreaUpdate.event; - private readonly _onDidChangeVisibility: Emitter = this._register(new Emitter()); - readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; + private readonly _onDidChangeVisibility = this._register(new Emitter()); + readonly onDidChangeVisibility = this._onDidChangeVisibility.event; private _onDidFocus: Emitter | undefined; get onDidFocus(): Event { @@ -250,11 +250,11 @@ export abstract class CompositeDescriptor { export abstract class CompositeRegistry extends Disposable { - private readonly _onDidRegister: Emitter> = this._register(new Emitter>()); - get onDidRegister(): Event> { return this._onDidRegister.event; } + private readonly _onDidRegister = this._register(new Emitter>()); + readonly onDidRegister = this._onDidRegister.event; - private readonly _onDidDeregister: Emitter> = this._register(new Emitter>()); - get onDidDeregister(): Event> { return this._onDidDeregister.event; } + private readonly _onDidDeregister = this._register(new Emitter>()); + readonly onDidDeregister = this._onDidDeregister.event; private composites: CompositeDescriptor[] = []; diff --git a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts index 0902582bf2a..b4eb3a41f04 100644 --- a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts +++ b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts @@ -9,7 +9,6 @@ import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { SaveReason, IEditorIdentifier, IEditorInput, GroupIdentifier } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { withNullAsUndefined } from 'vs/base/common/types'; @@ -57,19 +56,11 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution this.lastActiveEditorControlDisposable.clear(); // Listen to focus changes on control for auto save - if (activeGroup && activeEditor) { - const activeTextEditorWidget = this.editorService.activeTextEditorWidget; - const controlsToObserve: ICodeEditor[] = []; - - if (isCodeEditor(activeTextEditorWidget)) { - controlsToObserve.push(activeTextEditorWidget); - } else if (isDiffEditor(activeTextEditorWidget)) { - controlsToObserve.push(activeTextEditorWidget.getOriginalEditor(), activeTextEditorWidget.getModifiedEditor()); - } - - controlsToObserve.forEach(control => this.lastActiveEditorControlDisposable.add(control.onDidBlurEditorWidget(() => { + const activeEditorControl = this.editorService.activeControl; + if (activeEditor && activeEditorControl) { + this.lastActiveEditorControlDisposable.add(activeEditorControl.onDidBlur(() => { this.maybeTriggerAutoSave(SaveReason.FOCUS_CHANGE, { groupId: activeGroup.id, editor: activeEditor }); - }))); + })); } } @@ -78,10 +69,9 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution return; // no auto save for readonly or untitled editors } + // Determine if we need to save all. In case of a window focus change we also save if  + // auto save mode is configured to be ON_FOCUS_CHANGE (editor focus change) const mode = this.filesConfigurationService.getAutoSaveMode(); - - // Determine if we need to save all. In case of a window focus change we also save if auto save mode - // is configured to be ON_FOCUS_CHANGE (editor focus change) if ( (reason === SaveReason.WINDOW_CHANGE && (mode === AutoSaveMode.ON_FOCUS_CHANGE || mode === AutoSaveMode.ON_WINDOW_CHANGE)) || (reason === SaveReason.FOCUS_CHANGE && mode === AutoSaveMode.ON_FOCUS_CHANGE) diff --git a/src/vs/workbench/common/composite.ts b/src/vs/workbench/common/composite.ts index b6ea6f2ca73..8b48a7f4de8 100644 --- a/src/vs/workbench/common/composite.ts +++ b/src/vs/workbench/common/composite.ts @@ -4,9 +4,20 @@ *--------------------------------------------------------------------------------------------*/ import { IAction, IActionViewItem } from 'vs/base/common/actions'; +import { Event } from 'vs/base/common/event'; export interface IComposite { + /** + * An event when the composite gained focus. + */ + readonly onDidFocus: Event; + + /** + * An event when the composite lost focus. + */ + readonly onDidBlur: Event; + /** * Returns the unique identifier of this composite. */ diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 1ac273425c2..c2f430cfefe 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -23,6 +23,7 @@ import { coalesce, firstOrDefault } from 'vs/base/common/arrays'; import { ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { isEqual } from 'vs/base/common/resources'; +import { IPanel } from 'vs/workbench/common/panel'; export const DirtyWorkingCopiesContext = new RawContextKey('dirtyWorkingCopies', false); export const ActiveEditorContext = new RawContextKey('activeEditor', null); @@ -54,7 +55,7 @@ export const TEXT_DIFF_EDITOR_ID = 'workbench.editors.textDiffEditor'; */ export const BINARY_DIFF_EDITOR_ID = 'workbench.editors.binaryResourceDiffEditor'; -export interface IEditor { +export interface IEditor extends IPanel { /** * The assigned input of this editor. @@ -96,21 +97,11 @@ export interface IEditor { */ readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; } | undefined>; - /** - * Returns the unique identifier of this editor. - */ - getId(): string; - /** * Returns the underlying control of this editor. */ getControl(): IEditorControl | undefined; - /** - * Asks the underlying control to focus. - */ - focus(): void; - /** * Finds out if this editor is visible or not. */ diff --git a/src/vs/workbench/services/progress/test/progressIndicator.test.ts b/src/vs/workbench/services/progress/test/progressIndicator.test.ts index 85a8e2f7275..f50511e22d8 100644 --- a/src/vs/workbench/services/progress/test/progressIndicator.test.ts +++ b/src/vs/workbench/services/progress/test/progressIndicator.test.ts @@ -11,11 +11,15 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { TestViewletService, TestPanelService } from 'vs/workbench/test/workbenchTestServices'; +import { Event } from 'vs/base/common/event'; class TestViewlet implements IViewlet { constructor(private id: string) { } + readonly onDidBlur = Event.None; + readonly onDidFocus = Event.None; + getId(): string { return this.id; } getTitle(): string { return this.id; } getActions(): IAction[] { return []; } From 2340d870724b727090189824a0e8fab3d8e15ef0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 21 Nov 2019 07:20:11 +0100 Subject: [PATCH 148/246] debt - remove data uri inputs (#85203) --- .../browser/parts/editor/resourceViewer.ts | 11 +-- .../common/editor/binaryEditorModel.ts | 14 ---- .../common/editor/dataUriEditorInput.ts | 79 ------------------- .../files/browser/files.contribution.ts | 4 +- .../services/editor/browser/editorService.ts | 14 +--- .../common/editor/dataUriEditorInput.test.ts | 34 -------- 6 files changed, 9 insertions(+), 147 deletions(-) delete mode 100644 src/vs/workbench/common/editor/dataUriEditorInput.ts delete mode 100644 src/vs/workbench/test/common/editor/dataUriEditorInput.test.ts diff --git a/src/vs/workbench/browser/parts/editor/resourceViewer.ts b/src/vs/workbench/browser/parts/editor/resourceViewer.ts index c87b88479de..1879da12053 100644 --- a/src/vs/workbench/browser/parts/editor/resourceViewer.ts +++ b/src/vs/workbench/browser/parts/editor/resourceViewer.ts @@ -6,7 +6,6 @@ import * as DOM from 'vs/base/browser/dom'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/resourceviewer'; import * as nls from 'vs/nls'; @@ -132,13 +131,11 @@ class FileSeemsBinaryFileView { label.textContent = nls.localize('nativeBinaryError', "The file is not displayed in the editor because it is either binary or uses an unsupported text encoding."); container.appendChild(label); - if (descriptor.resource.scheme !== Schemas.data) { - const link = DOM.append(label, DOM.$('a.embedded-link')); - link.setAttribute('role', 'button'); - link.textContent = nls.localize('openAsText', "Do you want to open it anyway?"); + const link = DOM.append(label, DOM.$('a.embedded-link')); + link.setAttribute('role', 'button'); + link.textContent = nls.localize('openAsText', "Do you want to open it anyway?"); - disposables.add(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => delegate.openInternalClb(descriptor.resource))); - } + disposables.add(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => delegate.openInternalClb(descriptor.resource))); scrollbar.scanDomNode(); diff --git a/src/vs/workbench/common/editor/binaryEditorModel.ts b/src/vs/workbench/common/editor/binaryEditorModel.ts index a211169a6b7..edf4d54b15e 100644 --- a/src/vs/workbench/common/editor/binaryEditorModel.ts +++ b/src/vs/workbench/common/editor/binaryEditorModel.ts @@ -6,8 +6,6 @@ import { EditorModel } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; -import { Schemas } from 'vs/base/common/network'; -import { DataUri } from 'vs/base/common/resources'; import { MIME_BINARY } from 'vs/base/common/mime'; /** @@ -28,18 +26,6 @@ export class BinaryEditorModel extends EditorModel { this.resource = resource; this.name = name; this.mime = MIME_BINARY; - - if (resource.scheme === Schemas.data) { - const metadata = DataUri.parseMetaData(resource); - if (metadata.has(DataUri.META_DATA_SIZE)) { - this.size = Number(metadata.get(DataUri.META_DATA_SIZE)); - } - - const metadataMime = metadata.get(DataUri.META_DATA_MIME); - if (metadataMime) { - this.mime = metadataMime; - } - } } /** diff --git a/src/vs/workbench/common/editor/dataUriEditorInput.ts b/src/vs/workbench/common/editor/dataUriEditorInput.ts deleted file mode 100644 index 94911e9c9db..00000000000 --- a/src/vs/workbench/common/editor/dataUriEditorInput.ts +++ /dev/null @@ -1,79 +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 { EditorInput } from 'vs/workbench/common/editor'; -import { URI } from 'vs/base/common/uri'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; -import { DataUri, basename } from 'vs/base/common/resources'; - -/** - * An editor input to present data URIs in a binary editor. Data URIs have the form of: - * data:[mime type];[meta data ;...];base64,[base64 encoded value] - */ -export class DataUriEditorInput extends EditorInput { - - static readonly ID: string = 'workbench.editors.dataUriEditorInput'; - - private readonly name: string; - private readonly description: string | undefined; - - constructor( - name: string | undefined, - description: string | undefined, - private readonly resource: URI, - @IInstantiationService private readonly instantiationService: IInstantiationService - ) { - super(); - - if (!name || !description) { - const metadata = DataUri.parseMetaData(this.resource); - - if (!name) { - name = metadata.get(DataUri.META_DATA_LABEL) || basename(resource); - } - - if (!description) { - description = metadata.get(DataUri.META_DATA_DESCRIPTION); - } - } - - this.name = name; - this.description = description; - } - - getResource(): URI { - return this.resource; - } - - getTypeId(): string { - return DataUriEditorInput.ID; - } - - getName(): string { - return this.name; - } - - getDescription(): string | undefined { - return this.description; - } - - resolve(): Promise { - return this.instantiationService.createInstance(BinaryEditorModel, this.resource, this.getName()).load(); - } - - matches(otherInput: unknown): boolean { - if (super.matches(otherInput) === true) { - return true; - } - - // Compare by resource - if (otherInput instanceof DataUriEditorInput) { - return otherInput.resource.toString() === this.resource.toString(); - } - - return false; - } -} diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index e8727ebc798..a3ffb18b605 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -27,7 +27,6 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import * as platform from 'vs/base/common/platform'; import { ExplorerViewlet, ExplorerViewletViewsContribution } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; -import { DataUriEditorInput } from 'vs/workbench/common/editor/dataUriEditorInput'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -107,8 +106,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( nls.localize('binaryFileEditor', "Binary File Editor") ), [ - new SyncDescriptor(FileEditorInput), - new SyncDescriptor(DataUriEditorInput) + new SyncDescriptor(FileEditorInput) ] ); diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 56f020049ae..9e01064847f 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -7,7 +7,6 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { IResourceInput, ITextEditorOptions, IEditorOptions, EditorActivation } from 'vs/platform/editor/common/editor'; import { IEditorInput, IEditor, GroupIdentifier, IFileEditorInput, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IFileInputFactory, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditor, ITextDiffEditor, ITextSideBySideEditor, toResource, SideBySideEditor, IRevertOptions } from 'vs/workbench/common/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; -import { DataUriEditorInput } from 'vs/workbench/common/editor/dataUriEditorInput'; import { Registry } from 'vs/platform/registry/common/platform'; import { ResourceMap } from 'vs/base/common/map'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; @@ -29,7 +28,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; -type CachedEditorInput = ResourceEditorInput | IFileEditorInput | DataUriEditorInput; +type CachedEditorInput = ResourceEditorInput | IFileEditorInput; type OpenInEditorGroup = IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE; export class EditorService extends Disposable implements EditorServiceImpl { @@ -584,8 +583,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { const resourceInput = input as IResourceInput; if (resourceInput.resource instanceof URI) { let label = resourceInput.label; - if (!label && resourceInput.resource.scheme !== Schemas.data) { - label = basename(resourceInput.resource); // derive the label from the path (but not for data URIs) + if (!label) { + label = basename(resourceInput.resource); // derive the label from the path } return this.createOrGet(resourceInput.resource, this.instantiationService, label, resourceInput.description, resourceInput.encoding, resourceInput.mode, resourceInput.forceFile) as EditorInput; @@ -609,7 +608,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { if (mode) { input.setPreferredMode(mode); } - } else if (!(input instanceof DataUriEditorInput)) { + } else { if (encoding) { input.setPreferredEncoding(encoding); } @@ -628,11 +627,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { input = this.fileInputFactory.createFileInput(resource, encoding, mode, instantiationService); } - // Data URI - else if (resource.scheme === Schemas.data) { - input = instantiationService.createInstance(DataUriEditorInput, label || basename(resource), description, resource); - } - // Resource else { input = instantiationService.createInstance(ResourceEditorInput, label, description, resource, mode); diff --git a/src/vs/workbench/test/common/editor/dataUriEditorInput.test.ts b/src/vs/workbench/test/common/editor/dataUriEditorInput.test.ts deleted file mode 100644 index 44d111a1f81..00000000000 --- a/src/vs/workbench/test/common/editor/dataUriEditorInput.test.ts +++ /dev/null @@ -1,34 +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 assert from 'assert'; -import { URI } from 'vs/base/common/uri'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; -import { DataUriEditorInput } from 'vs/workbench/common/editor/dataUriEditorInput'; -import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; - -suite('DataUriEditorInput', () => { - - let instantiationService: IInstantiationService; - - setup(() => { - instantiationService = workbenchInstantiationService(); - }); - - test('simple', () => { - const resource = URI.parse('data:image/png;label:SomeLabel;description:SomeDescription;size:1024;base64,77+9UE5'); - const input: DataUriEditorInput = instantiationService.createInstance(DataUriEditorInput, undefined, undefined, resource); - - assert.equal(input.getName(), 'SomeLabel'); - assert.equal(input.getDescription(), 'SomeDescription'); - - return input.resolve().then((model: BinaryEditorModel) => { - assert.ok(model); - assert.equal(model.getSize(), 1024); - assert.equal(model.getMime(), 'image/png'); - }); - }); -}); From 7521ebdeb48207b6848a9e13627ed653d08c7eb8 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 08:44:26 +0100 Subject: [PATCH 149/246] Adopt latest vscode-textmate (fixes #84401) --- package.json | 2 +- remote/package.json | 2 +- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 +- remote/yarn.lock | 8 +- src/typings/vscode-textmate.d.ts | 256 ------------------ .../inspectTMScopes/inspectTMScopes.ts | 9 +- .../themes.test.contribution.ts | 3 + .../browser/abstractTextMateService.ts | 5 +- .../textMate/common/TMGrammarFactory.ts | 2 +- .../textMate/common/textMateService.ts | 2 +- yarn.lock | 8 +- 12 files changed, 31 insertions(+), 276 deletions(-) delete mode 100644 src/typings/vscode-textmate.d.ts diff --git a/package.json b/package.json index 4a3cc1e9002..30b567bb9d5 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "vscode-proxy-agent": "^0.5.2", "vscode-ripgrep": "^1.5.7", "vscode-sqlite3": "4.0.9", - "vscode-textmate": "^4.3.0", + "vscode-textmate": "^4.4.0", "xterm": "4.3.0-beta17", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", diff --git a/remote/package.json b/remote/package.json index 1a1c1a0e64a..94e15be6512 100644 --- a/remote/package.json +++ b/remote/package.json @@ -19,7 +19,7 @@ "vscode-nsfw": "1.2.8", "vscode-proxy-agent": "^0.5.2", "vscode-ripgrep": "^1.5.7", - "vscode-textmate": "^4.3.0", + "vscode-textmate": "^4.4.0", "xterm": "4.3.0-beta17", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", diff --git a/remote/web/package.json b/remote/web/package.json index c4a39c50bb3..3c8b489c130 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -4,7 +4,7 @@ "dependencies": { "onigasm-umd": "^2.2.4", "semver-umd": "^5.5.3", - "vscode-textmate": "^4.3.0", + "vscode-textmate": "^4.4.0", "xterm": "4.3.0-beta17", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index d5d1fcdd499..a09ab549ac8 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -24,10 +24,10 @@ semver-umd@^5.5.3: resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.3.tgz#b64d7a2d4f5a717b369d56e31940a38e47e34d1e" integrity sha512-HOnQrn2iKnVe/xlqCTzMXQdvSz3rPbD0DmQXYuQ+oK1dpptGFfPghonQrx5JHl2O7EJwDqtQnjhE7ME23q6ngw== -vscode-textmate@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.3.0.tgz#6e1f0f273d84148cfa1e9c7ed85bd16c974f9f61" - integrity sha512-MhEZ3hvxOVuYGsrRzW/PZLDR2VdtG2+V6TIKPvmE9JT+RAq/OtPlrFd1+ZQwBefoHEhjRNuRJ0OktcFezuxPmg== +vscode-textmate@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.4.0.tgz#14032afeb50152e8f53258c95643e555f2948305" + integrity sha512-dFpm2eK0HwEjeFSD1DDh3j0q47bDSVuZt20RiJWxGqjtm73Wu2jip3C2KaZI3dQx/fSeeXCr/uEN4LNaNj7Ytw== dependencies: oniguruma "^7.2.0" diff --git a/remote/yarn.lock b/remote/yarn.lock index c2f8da25470..3d08be2e5d0 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -399,10 +399,10 @@ vscode-ripgrep@^1.5.7: resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.7.tgz#acb6b548af488a4bca5d0f1bb5faf761343289ce" integrity sha512-/Vsz/+k8kTvui0q3O74pif9FK0nKopgFTiGNVvxicZANxtSA8J8gUE9GQ/4dpi7D/2yI/YVORszwVskFbz46hQ== -vscode-textmate@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.3.0.tgz#6e1f0f273d84148cfa1e9c7ed85bd16c974f9f61" - integrity sha512-MhEZ3hvxOVuYGsrRzW/PZLDR2VdtG2+V6TIKPvmE9JT+RAq/OtPlrFd1+ZQwBefoHEhjRNuRJ0OktcFezuxPmg== +vscode-textmate@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.4.0.tgz#14032afeb50152e8f53258c95643e555f2948305" + integrity sha512-dFpm2eK0HwEjeFSD1DDh3j0q47bDSVuZt20RiJWxGqjtm73Wu2jip3C2KaZI3dQx/fSeeXCr/uEN4LNaNj7Ytw== dependencies: oniguruma "^7.2.0" diff --git a/src/typings/vscode-textmate.d.ts b/src/typings/vscode-textmate.d.ts deleted file mode 100644 index 835b33008ac..00000000000 --- a/src/typings/vscode-textmate.d.ts +++ /dev/null @@ -1,256 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module "vscode-textmate" { - /** - * A single theme setting. - */ - export interface IRawThemeSetting { - readonly name?: string; - readonly scope?: string | string[]; - readonly settings: { - readonly fontStyle?: string; - readonly foreground?: string; - readonly background?: string; - }; - } - /** - * A TextMate theme. - */ - export interface IRawTheme { - readonly name?: string; - readonly settings: IRawThemeSetting[]; - } - export interface Thenable extends PromiseLike { - } - /** - * A registry helper that can locate grammar file paths given scope names. - */ - export interface RegistryOptions { - theme?: IRawTheme; - loadGrammar(scopeName: string): Thenable; - getInjections?(scopeName: string): string[]; - getOnigLib?(): Thenable; - } - /** - * A map from scope name to a language id. Please do not use language id 0. - */ - export interface IEmbeddedLanguagesMap { - [scopeName: string]: number; - } - /** - * A map from selectors to token types. - */ - export interface ITokenTypeMap { - [selector: string]: StandardTokenType; - } - export const enum StandardTokenType { - Other = 0, - Comment = 1, - String = 2, - RegEx = 4, - } - export interface IGrammarConfiguration { - embeddedLanguages?: IEmbeddedLanguagesMap; - tokenTypes?: ITokenTypeMap; - } - /** - * The registry that will hold all grammars. - */ - export class Registry { - private readonly _locator; - private readonly _syncRegistry; - constructor(locator?: RegistryOptions); - /** - * Change the theme. Once called, no previous `ruleStack` should be used anymore. - */ - setTheme(theme: IRawTheme): void; - /** - * Returns a lookup array for color ids. - */ - getColorMap(): string[]; - /** - * Load the grammar for `scopeName` and all referenced included grammars asynchronously. - * Please do not use language id 0. - */ - loadGrammarWithEmbeddedLanguages(initialScopeName: string, initialLanguage: number, embeddedLanguages: IEmbeddedLanguagesMap): Thenable; - /** - * Load the grammar for `scopeName` and all referenced included grammars asynchronously. - * Please do not use language id 0. - */ - loadGrammarWithConfiguration(initialScopeName: string, initialLanguage: number, configuration: IGrammarConfiguration): Thenable; - /** - * Load the grammar for `scopeName` and all referenced included grammars asynchronously. - */ - loadGrammar(initialScopeName: string): Thenable; - private _loadGrammar; - /** - * Adds a rawGrammar. - */ - addGrammar(rawGrammar: IRawGrammar, injections?: string[], initialLanguage?: number, embeddedLanguages?: IEmbeddedLanguagesMap): Thenable; - /** - * Get the grammar for `scopeName`. The grammar must first be created via `loadGrammar` or `addGrammar`. - */ - grammarForScopeName(scopeName: string, initialLanguage?: number, embeddedLanguages?: IEmbeddedLanguagesMap, tokenTypes?: ITokenTypeMap): Thenable; - } - /** - * A grammar - */ - export interface IGrammar { - /** - * Tokenize `lineText` using previous line state `prevState`. - */ - tokenizeLine(lineText: string, prevState: StackElement | null): ITokenizeLineResult; - /** - * Tokenize `lineText` using previous line state `prevState`. - * The result contains the tokens in binary format, resolved with the following information: - * - language - * - token type (regex, string, comment, other) - * - font style - * - foreground color - * - background color - * e.g. for getting the languageId: `(metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET` - */ - tokenizeLine2(lineText: string, prevState: StackElement | null): ITokenizeLineResult2; - } - export interface ITokenizeLineResult { - readonly tokens: IToken[]; - /** - * The `prevState` to be passed on to the next line tokenization. - */ - readonly ruleStack: StackElement; - } - /** - * Helpers to manage the "collapsed" metadata of an entire StackElement stack. - * The following assumptions have been made: - * - languageId < 256 => needs 8 bits - * - unique color count < 512 => needs 9 bits - * - * The binary format is: - * - ------------------------------------------- - * 3322 2222 2222 1111 1111 1100 0000 0000 - * 1098 7654 3210 9876 5432 1098 7654 3210 - * - ------------------------------------------- - * xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - * bbbb bbbb bfff ffff ffFF FTTT LLLL LLLL - * - ------------------------------------------- - * - L = LanguageId (8 bits) - * - T = StandardTokenType (3 bits) - * - F = FontStyle (3 bits) - * - f = foreground color (9 bits) - * - b = background color (9 bits) - */ - export const enum MetadataConsts { - LANGUAGEID_MASK = 255, - TOKEN_TYPE_MASK = 1792, - FONT_STYLE_MASK = 14336, - FOREGROUND_MASK = 8372224, - BACKGROUND_MASK = 4286578688, - LANGUAGEID_OFFSET = 0, - TOKEN_TYPE_OFFSET = 8, - FONT_STYLE_OFFSET = 11, - FOREGROUND_OFFSET = 14, - BACKGROUND_OFFSET = 23, - } - export interface ITokenizeLineResult2 { - /** - * The tokens in binary format. Each token occupies two array indices. For token i: - * - at offset 2*i => startIndex - * - at offset 2*i + 1 => metadata - * - */ - readonly tokens: Uint32Array; - /** - * The `prevState` to be passed on to the next line tokenization. - */ - readonly ruleStack: StackElement; - } - export interface IToken { - startIndex: number; - readonly endIndex: number; - readonly scopes: string[]; - } - /** - * **IMPORTANT** - Immutable! - */ - export interface StackElement { - _stackElementBrand: void; - readonly depth: number; - clone(): StackElement; - equals(other: StackElement): boolean; - } - export const INITIAL: StackElement; - export const parseRawGrammar: (content: string, filePath?: string) => IRawGrammar; - export interface ILocation { - readonly filename: string; - readonly line: number; - readonly char: number; - } - export interface ILocatable { - readonly $vscodeTextmateLocation?: ILocation; - } - export interface IRawGrammar extends ILocatable { - repository: IRawRepository; - readonly scopeName: string; - readonly patterns: IRawRule[]; - readonly injections?: { - [expression: string]: IRawRule; - }; - readonly injectionSelector?: string; - readonly fileTypes?: string[]; - readonly name?: string; - readonly firstLineMatch?: string; - } - export interface IRawRepositoryMap { - [name: string]: IRawRule; - $self: IRawRule; - $base: IRawRule; - } - export type IRawRepository = IRawRepositoryMap & ILocatable; - export interface IRawRule extends ILocatable { - id?: number; - readonly include?: string; - readonly name?: string; - readonly contentName?: string; - readonly match?: string; - readonly captures?: IRawCaptures; - readonly begin?: string; - readonly beginCaptures?: IRawCaptures; - readonly end?: string; - readonly endCaptures?: IRawCaptures; - readonly while?: string; - readonly whileCaptures?: IRawCaptures; - readonly patterns?: IRawRule[]; - readonly repository?: IRawRepository; - readonly applyEndPatternLast?: boolean; - } - export interface IRawCapturesMap { - [captureId: string]: IRawRule; - } - export type IRawCaptures = IRawCapturesMap & ILocatable; - export interface IOnigLib { - createOnigScanner(sources: string[]): OnigScanner; - createOnigString(sources: string): OnigString; - } - export interface IOnigCaptureIndex { - start: number; - end: number; - length: number; - } - export interface IOnigMatch { - index: number; - captureIndices: IOnigCaptureIndex[]; - scanner: OnigScanner; - } - export interface OnigScanner { - findNextMatchSync(string: string | OnigString, startPosition: number): IOnigMatch; - } - export interface OnigString { - readonly content: string; - readonly dispose?: () => void; - } - - -} diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts index aecdba4bd23..19fa1197cad 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts @@ -174,7 +174,7 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget { private readonly _notificationService: INotificationService; private readonly _model: ITextModel; private readonly _domNode: HTMLElement; - private readonly _grammar: Promise; + private readonly _grammar: Promise; constructor( editor: IActiveCodeEditor, @@ -212,7 +212,12 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget { dom.clearNode(this._domNode); this._domNode.appendChild(document.createTextNode(nls.localize('inspectTMScopesWidget.loading', "Loading..."))); this._grammar.then( - (grammar) => this._compute(grammar, position), + (grammar) => { + if (!grammar) { + throw new Error(`Could not find grammar for language!`); + } + this._compute(grammar, position); + }, (err) => { this._notificationService.warn(err); setTimeout(() => { diff --git a/src/vs/workbench/contrib/themes/test/electron-browser/themes.test.contribution.ts b/src/vs/workbench/contrib/themes/test/electron-browser/themes.test.contribution.ts index 392488ad4e7..d9e0970f7ca 100644 --- a/src/vs/workbench/contrib/themes/test/electron-browser/themes.test.contribution.ts +++ b/src/vs/workbench/contrib/themes/test/electron-browser/themes.test.contribution.ts @@ -216,6 +216,9 @@ class Snapper { public captureSyntaxTokens(fileName: string, content: string): Promise { const modeId = this.modeService.getModeIdByFilepathOrFirstLine(URI.file(fileName)); return this.textMateService.createGrammar(modeId!).then((grammar) => { + if (!grammar) { + return []; + } let lines = content.split(/\r\n|\r|\n/); let result = this._tokenize(grammar, lines); diff --git a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts index dc9222e4b02..0b4e3be1f52 100644 --- a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts +++ b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts @@ -218,6 +218,9 @@ export abstract class AbstractTextMateService extends Disposable implements ITex return null; } const r = await grammarFactory.createGrammar(languageId); + if (!r.grammar) { + return null; + } const tokenization = new TMTokenization(r.grammar, r.initialState, r.containsEmbeddedLanguages); tokenization.onDidEncounterLanguage((languageId) => { if (!this._encounteredLanguages[languageId]) { @@ -314,7 +317,7 @@ export abstract class AbstractTextMateService extends Disposable implements ITex return true; } - public async createGrammar(modeId: string): Promise { + public async createGrammar(modeId: string): Promise { const grammarFactory = await this._getOrCreateGrammarFactory(); const { grammar } = await grammarFactory.createGrammar(this._modeService.getLanguageIdentifier(modeId)!.id); return grammar; diff --git a/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts b/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts index 1ea68789895..38e3101c34b 100644 --- a/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts +++ b/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts @@ -18,7 +18,7 @@ interface ITMGrammarFactoryHost { export interface ICreateGrammarResult { languageId: LanguageId; - grammar: IGrammar; + grammar: IGrammar | null; initialState: StackElement; containsEmbeddedLanguages: boolean; } diff --git a/src/vs/workbench/services/textMate/common/textMateService.ts b/src/vs/workbench/services/textMate/common/textMateService.ts index 2b61bb56cb8..0ff72dadff2 100644 --- a/src/vs/workbench/services/textMate/common/textMateService.ts +++ b/src/vs/workbench/services/textMate/common/textMateService.ts @@ -14,7 +14,7 @@ export interface ITextMateService { onDidEncounterLanguage: Event; - createGrammar(modeId: string): Promise; + createGrammar(modeId: string): Promise; } // -------------- Types "liberated" from vscode-textmate due to usage in /common/ diff --git a/yarn.lock b/yarn.lock index d9bdce0cd9f..1ec9dbf0ebb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9072,10 +9072,10 @@ vscode-sqlite3@4.0.9: dependencies: nan "^2.14.0" -vscode-textmate@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.3.0.tgz#6e1f0f273d84148cfa1e9c7ed85bd16c974f9f61" - integrity sha512-MhEZ3hvxOVuYGsrRzW/PZLDR2VdtG2+V6TIKPvmE9JT+RAq/OtPlrFd1+ZQwBefoHEhjRNuRJ0OktcFezuxPmg== +vscode-textmate@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.4.0.tgz#14032afeb50152e8f53258c95643e555f2948305" + integrity sha512-dFpm2eK0HwEjeFSD1DDh3j0q47bDSVuZt20RiJWxGqjtm73Wu2jip3C2KaZI3dQx/fSeeXCr/uEN4LNaNj7Ytw== dependencies: oniguruma "^7.2.0" From 9c8b2f2f08e03e93a79c6a643b9b43edb4e41a86 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 21 Nov 2019 08:52:50 +0100 Subject: [PATCH 150/246] remove leftover console.log --- .../workbench/browser/parts/editor/noTabsTitleControl.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 8f3af39d0ae..115bfb381ce 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -89,10 +89,7 @@ export class NoTabsTitleControl extends TitleControl { EventHelper.stop(e, false); // delayed to let the onTitleClick() come first which can cause a focus change which can close quick open - setTimeout(() => { - console.log('quick open show'); - this.quickOpenService.show(); - }); + setTimeout(() => this.quickOpenService.show()); } private onTitleDoubleClick(e: MouseEvent): void { @@ -117,9 +114,7 @@ export class NoTabsTitleControl extends TitleControl { // gesture tap should open the quick open // editorGroupView will focus on the editor again when there are mouse/pointer/touch down events // we need to wait a bit as `GesureEvent.Tap` is generated from `touchstart` and then `touchend` evnets, which are not an atom event. - setTimeout(() => { - this.quickOpenService.show(); - }, 50); + setTimeout(() => this.quickOpenService.show(), 50); } } From 485e856c2fe2b804cc0bfa62bb811ca4e100fd9e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 09:02:10 +0100 Subject: [PATCH 151/246] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 30b567bb9d5..b142de115e0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.41.0", - "distro": "b802f3a3df7a3504acd5cec8d4dde868402f6714", + "distro": "799709c0ffd2940f3ecc323aae4a83a1177a61b3", "author": { "name": "Microsoft Corporation" }, From ca8af6b41d997062f363a686a2c343367209d8dd Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 09:10:53 +0100 Subject: [PATCH 152/246] Remove unnecessary telemetry --- .../services/keybinding/browser/keybindingService.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 3867576370d..5b113505bd1 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -193,13 +193,6 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { } }); this._register(this.userKeybindings.onDidChange(() => { - type CustomKeybindingsChangedClassification = { - keyCount: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true } - }; - - this._telemetryService.publicLog2<{ keyCount: number }, CustomKeybindingsChangedClassification>('customKeybindingsChanged', { - keyCount: this.userKeybindings.keybindings.length - }); this.updateResolver({ source: KeybindingSource.User, keybindings: this.userKeybindings.keybindings From 8e1f429d857b7b33818fc6d53be3be4b209552ad Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 09:16:42 +0100 Subject: [PATCH 153/246] Move __GDPR__FRAGMENT__ out of .d.ts (#83421) --- src/typings/native-keymap.d.ts | 24 +------------------ .../keybinding/browser/keybindingService.ts | 22 +++++++++++++++++ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/typings/native-keymap.d.ts b/src/typings/native-keymap.d.ts index 716a74e38b8..8526b115ec4 100644 --- a/src/typings/native-keymap.d.ts +++ b/src/typings/native-keymap.d.ts @@ -42,28 +42,12 @@ declare module 'native-keymap' { export function getKeyMap(): IKeyboardMapping; - /* __GDPR__FRAGMENT__ - "IKeyboardLayoutInfo" : { - "name" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "id": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "text": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ export interface IWindowsKeyboardLayoutInfo { name: string; id: string; text: string; } - /* __GDPR__FRAGMENT__ - "IKeyboardLayoutInfo" : { - "model" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "layout": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "variant": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "options": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "rules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ export interface ILinuxKeyboardLayoutInfo { model: string; layout: string; @@ -72,12 +56,6 @@ declare module 'native-keymap' { rules: string; } - /* __GDPR__FRAGMENT__ - "IKeyboardLayoutInfo" : { - "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "lang": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ export interface IMacKeyboardLayoutInfo { id: string; lang: string; @@ -90,4 +68,4 @@ declare module 'native-keymap' { export function onDidChangeKeyboardLayout(callback: () => void): void; export function isISOKeyboard(): boolean; -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 5b113505bd1..2c0becf3008 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -222,6 +222,28 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { })); let data = this.keymapService.getCurrentKeyboardLayout(); + /* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "name" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "id": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "text": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + /* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "model" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "layout": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "variant": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "options": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "rules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + /* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "lang": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ /* __GDPR__ "keyboardLayout" : { "currentKeyboardLayout": { "${inline}": [ "${IKeyboardLayoutInfo}" ] } From e7e562da3c2a2acbbc20d80b44f9baa30f184673 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 21 Nov 2019 09:28:10 +0100 Subject: [PATCH 154/246] remove data uri from git --- extensions/git/src/commands.ts | 63 +++++++--------------------------- 1 file changed, 12 insertions(+), 51 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 46e13bd0b8a..432e16b8231 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -295,10 +295,10 @@ export class CommandCenter { } } else { if (resource.type !== Status.DELETED_BY_THEM) { - left = await this.getLeftResource(resource); + left = this.getLeftResource(resource); } - right = await this.getRightResource(resource); + right = this.getRightResource(resource); } const title = this.getTitle(resource); @@ -330,79 +330,40 @@ export class CommandCenter { } } - private async getURI(uri: Uri, ref: string): Promise { - // const repository = this.model.getRepository(uri); - - // if (!repository) { - return toGitUri(uri, ref); - // } - - // try { - // let gitRef = ref; - - // if (gitRef === '~') { - // const uriString = uri.toString(); - // const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); - // gitRef = indexStatus ? '' : 'HEAD'; - // } - - // const { size, object } = await repository.getObjectDetails(gitRef, uri.fsPath); - // const { mimetype } = await repository.detectObjectType(object); - - // if (mimetype === 'text/plain') { - // return toGitUri(uri, ref); - // } - - // if (size > 1000000) { // 1 MB - // return Uri.parse(`data:;label:${path.basename(uri.fsPath)};description:${gitRef},`); - // } - - // if (ImageMimetypes.indexOf(mimetype) > -1) { - // const contents = await repository.buffer(gitRef, uri.fsPath); - // return Uri.parse(`data:${mimetype};label:${path.basename(uri.fsPath)};description:${gitRef};size:${size};base64,${contents.toString('base64')}`); - // } - - // return Uri.parse(`data:;label:${path.basename(uri.fsPath)};description:${gitRef},`); - - // } catch (err) { - // return toGitUri(uri, ref); - // } - } - - private async getLeftResource(resource: Resource): Promise { + private getLeftResource(resource: Resource): Uri | undefined { switch (resource.type) { case Status.INDEX_MODIFIED: case Status.INDEX_RENAMED: case Status.INDEX_ADDED: - return this.getURI(resource.original, 'HEAD'); + return toGitUri(resource.original, 'HEAD'); case Status.MODIFIED: case Status.UNTRACKED: - return this.getURI(resource.resourceUri, '~'); + return toGitUri(resource.resourceUri, '~'); case Status.DELETED_BY_THEM: - return this.getURI(resource.resourceUri, ''); + return toGitUri(resource.resourceUri, ''); } return undefined; } - private async getRightResource(resource: Resource): Promise { + private getRightResource(resource: Resource): Uri | undefined { switch (resource.type) { case Status.INDEX_MODIFIED: case Status.INDEX_ADDED: case Status.INDEX_COPIED: case Status.INDEX_RENAMED: - return this.getURI(resource.resourceUri, ''); + return toGitUri(resource.resourceUri, ''); case Status.INDEX_DELETED: case Status.DELETED: - return this.getURI(resource.resourceUri, 'HEAD'); + return toGitUri(resource.resourceUri, 'HEAD'); case Status.DELETED_BY_US: - return this.getURI(resource.resourceUri, '~3'); + return toGitUri(resource.resourceUri, '~3'); case Status.DELETED_BY_THEM: - return this.getURI(resource.resourceUri, '~2'); + return toGitUri(resource.resourceUri, '~2'); case Status.MODIFIED: case Status.UNTRACKED: @@ -771,7 +732,7 @@ export class CommandCenter { return; } - const HEAD = await this.getLeftResource(resource); + const HEAD = this.getLeftResource(resource); const basename = path.basename(resource.resourceUri.fsPath); const title = `${basename} (HEAD)`; From ee54b6ea61ddc34a6a4922a4ea78161f135d0825 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 21 Nov 2019 09:28:50 +0100 Subject: [PATCH 155/246] #85216 always ask sync options --- .../workbench/contrib/userDataSync/browser/userDataSync.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index edfc6b5bcf5..fa271edc179 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -27,7 +27,6 @@ import { IEditorInput } from 'vs/workbench/common/editor'; import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { FalseContext } from 'vs/platform/contextkey/common/contextkeys'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { isWeb } from 'vs/base/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -60,7 +59,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @IHistoryService private readonly historyService: IHistoryService, @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, @IDialogService private readonly dialogService: IDialogService, - @IStorageService private readonly storageService: IStorageService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IInstantiationService instantiationService: IInstantiationService, ) { @@ -174,10 +172,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } await this.signIn(); } - if (this.storageService.getBoolean('userDataSync.isFirstTime', StorageScope.GLOBAL, true)) { - await this.configureSyncOptions(); - this.storageService.store('userDataSync.isFirstTime', false, StorageScope.GLOBAL); - } + await this.configureSyncOptions(); await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, true); this.notificationService.info(localize('Sync Started', "Sync Started.")); } From b63e626066cd3a2ab1872bba6e614d87e4de6ac4 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 09:38:15 +0100 Subject: [PATCH 156/246] Remove .d.ts from typings folder (#83421) --- package.json | 2 +- src/typings/native-keymap.d.ts | 71 ---------------------------------- yarn.lock | 8 ++-- 3 files changed, 5 insertions(+), 76 deletions(-) delete mode 100644 src/typings/native-keymap.d.ts diff --git a/package.json b/package.json index b142de115e0..25fa119f4e1 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "jschardet": "2.1.1", "keytar": "^4.11.0", "native-is-elevated": "0.4.1", - "native-keymap": "2.0.0", + "native-keymap": "2.1.0", "native-watchdog": "1.2.0", "node-pty": "^0.10.0-beta2", "onigasm-umd": "^2.2.4", diff --git a/src/typings/native-keymap.d.ts b/src/typings/native-keymap.d.ts deleted file mode 100644 index 8526b115ec4..00000000000 --- a/src/typings/native-keymap.d.ts +++ /dev/null @@ -1,71 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'native-keymap' { - - export interface IWindowsKeyMapping { - vkey: string; - value: string; - withShift: string; - withAltGr: string; - withShiftAltGr: string; - } - export interface IWindowsKeyboardMapping { - [code: string]: IWindowsKeyMapping; - } - export interface ILinuxKeyMapping { - value: string; - withShift: string; - withAltGr: string; - withShiftAltGr: string; - } - export interface ILinuxKeyboardMapping { - [code: string]: ILinuxKeyMapping; - } - export interface IMacKeyMapping { - value: string; - withShift: string; - withAltGr: string; - withShiftAltGr: string; - valueIsDeadKey: boolean; - withShiftIsDeadKey: boolean; - withAltGrIsDeadKey: boolean; - withShiftAltGrIsDeadKey: boolean; - } - export interface IMacKeyboardMapping { - [code: string]: IMacKeyMapping; - } - - export type IKeyboardMapping = IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping; - - export function getKeyMap(): IKeyboardMapping; - - export interface IWindowsKeyboardLayoutInfo { - name: string; - id: string; - text: string; - } - - export interface ILinuxKeyboardLayoutInfo { - model: string; - layout: string; - variant: string; - options: string; - rules: string; - } - - export interface IMacKeyboardLayoutInfo { - id: string; - lang: string; - } - - export type IKeyboardLayoutInfo = IWindowsKeyboardLayoutInfo | ILinuxKeyboardLayoutInfo | IMacKeyboardLayoutInfo; - - export function getCurrentKeyboardLayout(): IKeyboardLayoutInfo; - - export function onDidChangeKeyboardLayout(callback: () => void): void; - - export function isISOKeyboard(): boolean; -} diff --git a/yarn.lock b/yarn.lock index 1ec9dbf0ebb..3c842a0f8f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5706,10 +5706,10 @@ native-is-elevated@0.4.1: resolved "https://registry.yarnpkg.com/native-is-elevated/-/native-is-elevated-0.4.1.tgz#f6391aafb13441f5b949b39ae0b466b06e7f3986" integrity sha512-2vBXCXCXYKLDjP0WzrXs/AFjDb2njPR31EbGiZ1mR2fMJg211xClK1Xm19RXve35kvAL4dBKOFGCMIyc2+pPsw== -native-keymap@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-2.0.0.tgz#7491ba8f9cc75bd6ada7e754dadb7716c793a3e3" - integrity sha512-KIlDZp0yKaHaGIkEVdlYN3QIaZICXwG1qh/oeBeQdM8TwAi90IAZlAD57qsNDkEvIJIzerCzb5jYYQAdHGBgYg== +native-keymap@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-2.1.0.tgz#f3a92e647ac021fe552587b0020f8132efb03078" + integrity sha512-a5VYhjMqxe+HK5VzJM8yIcJOKkeuMSKYfmS0p7VEKSc7hM0F5IPsq7XO8KtwAgV8PJhfQVgAgyQmK8u/MQQ0aw== native-watchdog@1.2.0: version "1.2.0" From 4235c7f076535b4e6fc3ffa23e87c0d8905b4f72 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 21 Nov 2019 09:58:10 +0100 Subject: [PATCH 157/246] Fix Go To Definition smoketest Fixes #85276 --- test/automation/src/editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/automation/src/editor.ts b/test/automation/src/editor.ts index 907f614d245..97a8b5214a2 100644 --- a/test/automation/src/editor.ts +++ b/test/automation/src/editor.ts @@ -40,7 +40,7 @@ export class Editor { async gotoDefinition(filename: string, term: string, line: number): Promise { await this.clickOnTerm(filename, term, line); - await this.commands.runCommand('Go to Implementation'); + await this.commands.runCommand('Go to Implementations'); } async peekDefinition(filename: string, term: string, line: number): Promise { From 2ac71e9a480b91b43b7def3e58655ee8cc4f6bef Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 09:44:37 +0100 Subject: [PATCH 158/246] Switched to fixed deps for vscode-textmate --- package.json | 2 +- remote/package.json | 2 +- remote/web/package.json | 2 +- remote/web/yarn.lock | 2 +- remote/yarn.lock | 2 +- yarn.lock | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 25fa119f4e1..20a7bd7ace9 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "vscode-proxy-agent": "^0.5.2", "vscode-ripgrep": "^1.5.7", "vscode-sqlite3": "4.0.9", - "vscode-textmate": "^4.4.0", + "vscode-textmate": "4.4.0", "xterm": "4.3.0-beta17", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", diff --git a/remote/package.json b/remote/package.json index 94e15be6512..3cca8979334 100644 --- a/remote/package.json +++ b/remote/package.json @@ -19,7 +19,7 @@ "vscode-nsfw": "1.2.8", "vscode-proxy-agent": "^0.5.2", "vscode-ripgrep": "^1.5.7", - "vscode-textmate": "^4.4.0", + "vscode-textmate": "4.4.0", "xterm": "4.3.0-beta17", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", diff --git a/remote/web/package.json b/remote/web/package.json index 3c8b489c130..20e2b4043e9 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -4,7 +4,7 @@ "dependencies": { "onigasm-umd": "^2.2.4", "semver-umd": "^5.5.3", - "vscode-textmate": "^4.4.0", + "vscode-textmate": "4.4.0", "xterm": "4.3.0-beta17", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index a09ab549ac8..befd242ac50 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -24,7 +24,7 @@ semver-umd@^5.5.3: resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.3.tgz#b64d7a2d4f5a717b369d56e31940a38e47e34d1e" integrity sha512-HOnQrn2iKnVe/xlqCTzMXQdvSz3rPbD0DmQXYuQ+oK1dpptGFfPghonQrx5JHl2O7EJwDqtQnjhE7ME23q6ngw== -vscode-textmate@^4.4.0: +vscode-textmate@4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.4.0.tgz#14032afeb50152e8f53258c95643e555f2948305" integrity sha512-dFpm2eK0HwEjeFSD1DDh3j0q47bDSVuZt20RiJWxGqjtm73Wu2jip3C2KaZI3dQx/fSeeXCr/uEN4LNaNj7Ytw== diff --git a/remote/yarn.lock b/remote/yarn.lock index 3d08be2e5d0..9ac042918df 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -399,7 +399,7 @@ vscode-ripgrep@^1.5.7: resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.7.tgz#acb6b548af488a4bca5d0f1bb5faf761343289ce" integrity sha512-/Vsz/+k8kTvui0q3O74pif9FK0nKopgFTiGNVvxicZANxtSA8J8gUE9GQ/4dpi7D/2yI/YVORszwVskFbz46hQ== -vscode-textmate@^4.4.0: +vscode-textmate@4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.4.0.tgz#14032afeb50152e8f53258c95643e555f2948305" integrity sha512-dFpm2eK0HwEjeFSD1DDh3j0q47bDSVuZt20RiJWxGqjtm73Wu2jip3C2KaZI3dQx/fSeeXCr/uEN4LNaNj7Ytw== diff --git a/yarn.lock b/yarn.lock index 3c842a0f8f6..17e685192f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9072,7 +9072,7 @@ vscode-sqlite3@4.0.9: dependencies: nan "^2.14.0" -vscode-textmate@^4.4.0: +vscode-textmate@4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.4.0.tgz#14032afeb50152e8f53258c95643e555f2948305" integrity sha512-dFpm2eK0HwEjeFSD1DDh3j0q47bDSVuZt20RiJWxGqjtm73Wu2jip3C2KaZI3dQx/fSeeXCr/uEN4LNaNj7Ytw== From 6ae16bde2ec8ce6fd242fc93cd003a5e58dd156d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 09:51:17 +0100 Subject: [PATCH 159/246] Remove .d.ts from typings folder (#83421) --- package.json | 2 +- remote/package.json | 2 +- remote/yarn.lock | 8 ++++---- src/typings/native-watchdog.d.ts | 17 ----------------- yarn.lock | 8 ++++---- 5 files changed, 10 insertions(+), 27 deletions(-) delete mode 100644 src/typings/native-watchdog.d.ts diff --git a/package.json b/package.json index 20a7bd7ace9..b6b1b123f64 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "keytar": "^4.11.0", "native-is-elevated": "0.4.1", "native-keymap": "2.1.0", - "native-watchdog": "1.2.0", + "native-watchdog": "1.3.0", "node-pty": "^0.10.0-beta2", "onigasm-umd": "^2.2.4", "semver-umd": "^5.5.3", diff --git a/remote/package.json b/remote/package.json index 3cca8979334..798d355724c 100644 --- a/remote/package.json +++ b/remote/package.json @@ -10,7 +10,7 @@ "https-proxy-agent": "^2.2.3", "iconv-lite": "0.5.0", "jschardet": "2.1.1", - "native-watchdog": "1.2.0", + "native-watchdog": "1.3.0", "node-pty": "^0.10.0-beta2", "onigasm-umd": "^2.2.4", "semver-umd": "^5.5.3", diff --git a/remote/yarn.lock b/remote/yarn.lock index 9ac042918df..6bdc67d3b65 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -261,10 +261,10 @@ nan@^2.10.0, nan@^2.14.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== -native-watchdog@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.2.0.tgz#9c710093ac6e9e60b19517cb1ef4ac9d7c997395" - integrity sha512-jOOoA3PLSxt1adaeuEW7ymV9cApZiDxn4y4iFs7b4sP73EG+5Lsz+OgUNFcGMyrtznTw6ZvlLcilIN4jeAIgaQ== +native-watchdog@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.3.0.tgz#88cee94c9dc766b85c8506eda14c8bd8c9618e27" + integrity sha512-WOjGRNGkYZ5MXsntcvCYrKtSYMaewlbCFplbcUVo9bE80LPVt8TAVFHYWB8+a6fWCGYheq21+Wtt6CJrUaCJhw== node-addon-api@1.6.2: version "1.6.2" diff --git a/src/typings/native-watchdog.d.ts b/src/typings/native-watchdog.d.ts deleted file mode 100644 index 88119b314c1..00000000000 --- a/src/typings/native-watchdog.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'native-watchdog' { - - /** - * Start monitoring for a certain pid to exist. - * If the process indicated by pid ceases to execute, - * the current process will exit in 6 seconds with exit code 87 - */ - export function start(pid: number): void; - - export function exit(exitCode: number): void; - -} diff --git a/yarn.lock b/yarn.lock index 17e685192f2..ef6fc958089 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5711,10 +5711,10 @@ native-keymap@2.1.0: resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-2.1.0.tgz#f3a92e647ac021fe552587b0020f8132efb03078" integrity sha512-a5VYhjMqxe+HK5VzJM8yIcJOKkeuMSKYfmS0p7VEKSc7hM0F5IPsq7XO8KtwAgV8PJhfQVgAgyQmK8u/MQQ0aw== -native-watchdog@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.2.0.tgz#9c710093ac6e9e60b19517cb1ef4ac9d7c997395" - integrity sha512-jOOoA3PLSxt1adaeuEW7ymV9cApZiDxn4y4iFs7b4sP73EG+5Lsz+OgUNFcGMyrtznTw6ZvlLcilIN4jeAIgaQ== +native-watchdog@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.3.0.tgz#88cee94c9dc766b85c8506eda14c8bd8c9618e27" + integrity sha512-WOjGRNGkYZ5MXsntcvCYrKtSYMaewlbCFplbcUVo9bE80LPVt8TAVFHYWB8+a6fWCGYheq21+Wtt6CJrUaCJhw== natural-compare@^1.4.0: version "1.4.0" From c2a19905fb4c5dbbd0427db34b167d3605cdf443 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 10:01:08 +0100 Subject: [PATCH 160/246] Remove .d.ts from typings folder (#83421) --- package.json | 2 +- remote/package.json | 2 +- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 ++++---- remote/yarn.lock | 8 ++++---- src/typings/onigasm-umd.d.ts | 33 --------------------------------- yarn.lock | 8 ++++---- 7 files changed, 15 insertions(+), 48 deletions(-) delete mode 100644 src/typings/onigasm-umd.d.ts diff --git a/package.json b/package.json index b6b1b123f64..54d5f896186 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "native-keymap": "2.1.0", "native-watchdog": "1.3.0", "node-pty": "^0.10.0-beta2", - "onigasm-umd": "^2.2.4", + "onigasm-umd": "2.2.5", "semver-umd": "^5.5.3", "spdlog": "^0.11.1", "sudo-prompt": "9.1.1", diff --git a/remote/package.json b/remote/package.json index 798d355724c..fa35af0ae93 100644 --- a/remote/package.json +++ b/remote/package.json @@ -12,7 +12,7 @@ "jschardet": "2.1.1", "native-watchdog": "1.3.0", "node-pty": "^0.10.0-beta2", - "onigasm-umd": "^2.2.4", + "onigasm-umd": "2.2.5", "semver-umd": "^5.5.3", "spdlog": "^0.11.1", "vscode-minimist": "^1.2.2", diff --git a/remote/web/package.json b/remote/web/package.json index 20e2b4043e9..6cf60157138 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -2,7 +2,7 @@ "name": "vscode-web", "version": "0.0.0", "dependencies": { - "onigasm-umd": "^2.2.4", + "onigasm-umd": "2.2.5", "semver-umd": "^5.5.3", "vscode-textmate": "4.4.0", "xterm": "4.3.0-beta17", diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index befd242ac50..124cf2e3725 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -7,10 +7,10 @@ nan@^2.14.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== -onigasm-umd@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.4.tgz#27ee87f7496c66ad40cebfbc0d418c19bb7db5ec" - integrity sha512-N9VqCUhl9KBuzm47vcK8T/xUnbYylIhMN45Rwltlo1sZc3QUDda6SxIlyVB8r0SJQwURv8JOHjyXjjCriGvzRg== +onigasm-umd@2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.5.tgz#f104247334a543accd3f8d641a4d99b3d908d6a1" + integrity sha512-R3qD7hq6i2bBklF+QyjqZl/G4fe7GwtukI28YLH2vuiatqx52tb9vpg2sxwemKc3nF76SgkeyOKJLchBmTm0Aw== oniguruma@^7.2.0: version "7.2.0" diff --git a/remote/yarn.lock b/remote/yarn.lock index 6bdc67d3b65..07620b8134f 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -283,10 +283,10 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -onigasm-umd@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.4.tgz#27ee87f7496c66ad40cebfbc0d418c19bb7db5ec" - integrity sha512-N9VqCUhl9KBuzm47vcK8T/xUnbYylIhMN45Rwltlo1sZc3QUDda6SxIlyVB8r0SJQwURv8JOHjyXjjCriGvzRg== +onigasm-umd@2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.5.tgz#f104247334a543accd3f8d641a4d99b3d908d6a1" + integrity sha512-R3qD7hq6i2bBklF+QyjqZl/G4fe7GwtukI28YLH2vuiatqx52tb9vpg2sxwemKc3nF76SgkeyOKJLchBmTm0Aw== oniguruma@^7.2.0: version "7.2.0" diff --git a/src/typings/onigasm-umd.d.ts b/src/typings/onigasm-umd.d.ts deleted file mode 100644 index 151cecebfdd..00000000000 --- a/src/typings/onigasm-umd.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module "onigasm-umd" { - - function loadWASM(data: string | ArrayBuffer): Promise; - - class OnigString { - constructor(content: string); - readonly content: string; - readonly dispose?: () => void; - } - - class OnigScanner { - constructor(patterns: string[]); - findNextMatchSync(string: string | OnigString, startPosition: number): IOnigMatch; - } - - export interface IOnigCaptureIndex { - index: number - start: number - end: number - length: number - } - - export interface IOnigMatch { - index: number - captureIndices: IOnigCaptureIndex[] - scanner: OnigScanner - } -} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index ef6fc958089..31726b4dfef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6068,10 +6068,10 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -onigasm-umd@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.4.tgz#27ee87f7496c66ad40cebfbc0d418c19bb7db5ec" - integrity sha512-N9VqCUhl9KBuzm47vcK8T/xUnbYylIhMN45Rwltlo1sZc3QUDda6SxIlyVB8r0SJQwURv8JOHjyXjjCriGvzRg== +onigasm-umd@2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.5.tgz#f104247334a543accd3f8d641a4d99b3d908d6a1" + integrity sha512-R3qD7hq6i2bBklF+QyjqZl/G4fe7GwtukI28YLH2vuiatqx52tb9vpg2sxwemKc3nF76SgkeyOKJLchBmTm0Aw== oniguruma@^7.2.0: version "7.2.0" From 770877f0bc1733145da8ec727c861076e3731938 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 10:05:07 +0100 Subject: [PATCH 161/246] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 54d5f896186..e8fc3358ae3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.41.0", - "distro": "799709c0ffd2940f3ecc323aae4a83a1177a61b3", + "distro": "4d4d7ee0ae1852d22b319de4ccfdc53e864398c0", "author": { "name": "Microsoft Corporation" }, From acd7df14592fe8a209160220815b5980c72f909f Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 10:14:31 +0100 Subject: [PATCH 162/246] Use ILogService instead of console (#84283) --- .../api/common/extHostExtensionActivator.ts | 17 ++++---- .../api/common/extHostExtensionService.ts | 40 +++++++++++-------- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionActivator.ts b/src/vs/workbench/api/common/extHostExtensionActivator.ts index bc2edc0c2af..efa750e4b65 100644 --- a/src/vs/workbench/api/common/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/common/extHostExtensionActivator.ts @@ -9,6 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtensionActivationError, MissingDependencyError } from 'vs/workbench/services/extensions/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); @@ -182,7 +183,13 @@ export class ExtensionsActivator { */ private readonly _alreadyActivatedEvents: { [activationEvent: string]: boolean; }; - constructor(registry: ExtensionDescriptionRegistry, resolvedExtensions: ExtensionIdentifier[], hostExtensions: ExtensionIdentifier[], host: IExtensionsActivatorHost) { + constructor( + registry: ExtensionDescriptionRegistry, + resolvedExtensions: ExtensionIdentifier[], + hostExtensions: ExtensionIdentifier[], + host: IExtensionsActivatorHost, + @ILogService private readonly _logService: ILogService + ) { this._registry = registry; this._resolvedExtensionsSet = new Set(); resolvedExtensions.forEach((extensionId) => this._resolvedExtensionsSet.add(ExtensionIdentifier.toKey(extensionId))); @@ -308,7 +315,6 @@ export class ExtensionsActivator { } private _activateExtensions(extensions: ActivationIdAndReason[]): Promise { - // console.log('_activateExtensions: ', extensions.map(p => p.id.value)); if (extensions.length === 0) { return Promise.resolve(undefined); } @@ -335,9 +341,6 @@ export class ExtensionsActivator { const green = Object.keys(greenMap).map(id => greenMap[id]); - // console.log('greenExtensions: ', green.map(p => p.id.value)); - // console.log('redExtensions: ', red.map(p => p.id.value)); - if (red.length === 0) { // Finally reached only leafs! return Promise.all(green.map((p) => this._activateExtension(p.id, p.reason))).then(_ => undefined); @@ -362,8 +365,8 @@ export class ExtensionsActivator { const newlyActivatingExtension = this._host.actualActivateExtension(extensionId, reason).then(undefined, (err) => { this._host.onExtensionActivationError(extensionId, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionId.value, err.message)); - console.error('Activating extension `' + extensionId.value + '` failed: ', err.message); - console.log('Here is the error stack: ', err.stack); + this._logService.error(`Activating extension ${extensionId.value} failed due to an error:`); + this._logService.error(err); // Treat the extension as being empty return new FailedExtension(err); }).then((x: ActivatedExtension) => { diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index b5ce835e074..a3b5ed0057d 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -133,20 +133,26 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio const hostExtensions = new Set(); this._initData.hostExtensions.forEach((extensionId) => hostExtensions.add(ExtensionIdentifier.toKey(extensionId))); - this._activator = new ExtensionsActivator(this._registry, this._initData.resolvedExtensions, this._initData.hostExtensions, { - onExtensionActivationError: (extensionId: ExtensionIdentifier, error: ExtensionActivationError): void => { - this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, error); - }, + this._activator = new ExtensionsActivator( + this._registry, + this._initData.resolvedExtensions, + this._initData.hostExtensions, + { + onExtensionActivationError: (extensionId: ExtensionIdentifier, error: ExtensionActivationError): void => { + this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, error); + }, - actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise => { - if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) { - await this._mainThreadExtensionsProxy.$activateExtension(extensionId, reason); - return new HostExtension(); + actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise => { + if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) { + await this._mainThreadExtensionsProxy.$activateExtension(extensionId, reason); + return new HostExtension(); + } + const extensionDescription = this._registry.getExtensionDescription(extensionId)!; + return this._activateExtension(extensionDescription, reason); } - const extensionDescription = this._registry.getExtensionDescription(extensionId)!; - return this._activateExtension(extensionDescription, reason); - } - }); + }, + this._logService + ); this._extensionPathIndex = null; this._resolvers = Object.create(null); this._started = false; @@ -405,7 +411,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio // Handle "eager" activation extensions private _handleEagerExtensions(): Promise { this._activateByEvent('*', true).then(undefined, (err) => { - console.error(err); + this._logService.error(err); }); this._disposables.add(this._extHostWorkspace.onDidChangeWorkspace((e) => this._handleWorkspaceContainsEagerExtensions(e.added))); @@ -467,7 +473,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio // the file was found return ( this._activateById(extensionId, { startup: true, extensionId, activationEvent: `workspaceContains:${fileName}` }) - .then(undefined, err => console.error(err)) + .then(undefined, err => this._logService.error(err)) ); } } @@ -488,7 +494,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio const timer = setTimeout(async () => { tokenSource.cancel(); this._activateById(extensionId, { startup: true, extensionId, activationEvent: `workspaceContainsTimeout:${globPatterns.join(',')}` }) - .then(undefined, err => console.error(err)); + .then(undefined, err => this._logService.error(err)); }, AbstractExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT); let exists: boolean = false; @@ -496,7 +502,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio exists = await searchP; } catch (err) { if (!errors.isPromiseCanceledError(err)) { - console.error(err); + this._logService.error(err); } } @@ -507,7 +513,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio // a file was found matching one of the glob patterns return ( this._activateById(extensionId, { startup: true, extensionId, activationEvent: `workspaceContains:${globPatterns.join(',')}` }) - .then(undefined, err => console.error(err)) + .then(undefined, err => this._logService.error(err)) ); } From abce0e8d4cfa101df616405312aa731501f29618 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 10:36:42 +0100 Subject: [PATCH 163/246] Remove that ui extensions can run on both local and remote sides (#85036) --- .../services/extensions/electron-browser/extensionService.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index bdcbe53192d..f4665c503b9 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -514,13 +514,9 @@ export class ExtensionService extends AbstractExtensionService implements IExten const pickRunningLocation = (extension: IExtensionDescription): RunningLocation => { for (const extensionKind of getExtensionKind(extension, this._productService, this._configurationService)) { if (extensionKind === 'ui') { - // a ui extension can run on both sides for now... if (isInstalledLocally.has(ExtensionIdentifier.toKey(extension.identifier))) { return RunningLocation.Local; } - if (isInstalledRemotely.has(ExtensionIdentifier.toKey(extension.identifier))) { - return RunningLocation.Remote; - } } else if (extensionKind === 'workspace') { if (isInstalledRemotely.has(ExtensionIdentifier.toKey(extension.identifier))) { return RunningLocation.Remote; From 1d2efa96c1587b92ae9c72e336a083068febdffb Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 21 Nov 2019 10:50:01 +0100 Subject: [PATCH 164/246] workbench theme service: provide tokenColorMap --- .../browser/standaloneThemeServiceImpl.ts | 9 - .../test/browser/standaloneLanguages.test.ts | 7 +- src/vs/platform/theme/common/themeService.ts | 5 - .../common/tokenClassificationRegistry.ts | 86 +------ .../theme/test/common/testThemeService.ts | 9 - .../services/themes/common/colorThemeData.ts | 209 +++++++++++++++++- .../themes/common/workbenchThemeService.ts | 11 + 7 files changed, 220 insertions(+), 116 deletions(-) diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 61cf4274a0d..692fd2c2958 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -14,7 +14,6 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { Registry } from 'vs/platform/registry/common/platform'; import { ColorIdentifier, Extensions, IColorRegistry } from 'vs/platform/theme/common/colorRegistry'; import { Extensions as ThemingExtensions, ICssStyleCollector, IIconTheme, IThemingRegistry } from 'vs/platform/theme/common/themeService'; -import { TokenStyle, TokenClassification, ProbeScope } from 'vs/platform/theme/common/tokenClassificationRegistry'; const VS_THEME_NAME = 'vs'; const VS_DARK_THEME_NAME = 'vs-dark'; @@ -130,14 +129,6 @@ class StandaloneTheme implements IStandaloneTheme { } return this._tokenTheme; } - - getTokenStyle(classification: TokenClassification, useDefault?: boolean | undefined): TokenStyle | undefined { - return undefined; - } - - resolveScopes(scopes: ProbeScope[]): TokenStyle | undefined { - return undefined; - } } function isBuiltinTheme(themeName: string): themeName is BuiltinTheme { diff --git a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts index 3cb4e474b1d..456d1652078 100644 --- a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts +++ b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts @@ -54,13 +54,10 @@ suite('TokenizationSupport2Adapter', () => { defines: (color: ColorIdentifier): boolean => { throw new Error('Not implemented'); - }, - - getTokenStyle: () => undefined, - resolveScopes: () => undefined - + } }; } + public getIconTheme(): IIconTheme { return { hasFileIcons: false, diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index 8117fd486a3..42e93300a85 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -10,7 +10,6 @@ import * as platform from 'vs/platform/registry/common/platform'; import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; import { Event, Emitter } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { TokenStyle, TokenClassification, ProbeScope } from 'vs/platform/theme/common/tokenClassificationRegistry'; export const IThemeService = createDecorator('themeService'); @@ -60,10 +59,6 @@ export interface ITheme { * default color will be used. */ defines(color: ColorIdentifier): boolean; - - getTokenStyle(classification: TokenClassification, useDefault?: boolean): TokenStyle | undefined; - - resolveScopes(scopes: ProbeScope[]): TokenStyle | undefined; } export interface IIconTheme { diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index 2ec489ae46f..47148a9837d 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -142,9 +142,9 @@ export interface ITokenClassificationRegistry { getTokenModifiers(): TokenTypeOrModifierContribution[]; /** - * Resolves a token classification against the given rules and default rules from the registry. + * The styling rules to used when a schema does not define any styling rules. */ - resolveTokenStyle(classification: TokenClassification, themingRules: TokenStylingRule[] | undefined, customThemingRules: TokenStylingRule[], theme: ITheme): TokenStyle | undefined; + getTokenStylingDefaultRules(): TokenStylingDefaultRule[]; /** * JSON schema for an object to assign styling to token classifications @@ -271,87 +271,13 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { return Object.keys(this.tokenModifierById).map(id => this.tokenModifierById[id]); } - public resolveTokenStyle(classification: TokenClassification, themingRules: TokenStylingRule[] | undefined, customThemingRules: TokenStylingRule[], theme: ITheme): TokenStyle | undefined { - let result: any = { - foreground: undefined, - bold: undefined, - underline: undefined, - italic: undefined - }; - let score = { - foreground: -1, - bold: -1, - underline: -1, - italic: -1 - }; - - function _processStyle(matchScore: number, style: TokenStyle) { - if (style.foreground && score.foreground <= matchScore) { - score.foreground = matchScore; - result.foreground = style.foreground; - } - for (let p of ['bold', 'underline', 'italic']) { - const property = p as keyof TokenStyle; - const info = style[property]; - if (info !== undefined) { - if (score[property] <= matchScore) { - score[property] = matchScore; - result[property] = info; - } - } - } - } - if (themingRules === undefined) { - for (const rule of this.tokenStylingDefaultRules) { - const matchScore = match(rule, classification); - if (matchScore >= 0) { - let style = theme.resolveScopes(rule.defaults.scopesToProbe); - if (!style) { - style = this.resolveTokenStyleValue(rule.defaults[theme.type], theme); - } - if (style) { - _processStyle(matchScore, style); - } - } - } - } else { - for (const rule of themingRules) { - const matchScore = match(rule, classification); - if (matchScore >= 0) { - _processStyle(matchScore, rule.value); - } - } - } - for (const rule of customThemingRules) { - const matchScore = match(rule, classification); - if (matchScore >= 0) { - _processStyle(matchScore, rule.value); - } - } - return TokenStyle.fromData(result); - } - - /** - * @param tokenStyleValue Resolve a tokenStyleValue in the context of a theme - */ - private resolveTokenStyleValue(tokenStyleValue: TokenStyleValue | null, theme: ITheme): TokenStyle | undefined { - if (tokenStyleValue === null) { - return undefined; - } else if (typeof tokenStyleValue === 'string') { - const classification = this.getTokenClassificationFromString(tokenStyleValue); - if (classification) { - return theme.getTokenStyle(classification); - } - } else if (typeof tokenStyleValue === 'object') { - return tokenStyleValue; - } - return undefined; - } - public getTokenStylingSchema(): IJSONSchema { return this.tokenStylingSchema; } + public getTokenStylingDefaultRules(): TokenStylingDefaultRule[] { + return this.tokenStylingDefaultRules; + } public toString() { let sorter = (a: string, b: string) => { @@ -368,7 +294,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { } -function match(themeSelector: TokenStylingRule | TokenStylingDefaultRule, classification: TokenClassification): number { +export function matchTokenStylingRule(themeSelector: TokenStylingRule | TokenStylingDefaultRule, classification: TokenClassification): number { const selectorType = themeSelector.classification.type; if (selectorType !== TOKEN_TYPE_WILDCARD_NUM && selectorType !== classification.type) { return -1; diff --git a/src/vs/platform/theme/test/common/testThemeService.ts b/src/vs/platform/theme/test/common/testThemeService.ts index b9279dc39ee..fd49d393a34 100644 --- a/src/vs/platform/theme/test/common/testThemeService.ts +++ b/src/vs/platform/theme/test/common/testThemeService.ts @@ -6,7 +6,6 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IThemeService, ITheme, DARK, IIconTheme } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; -import { TokenStyle, TokenClassification, ProbeScope } from 'vs/platform/theme/common/tokenClassificationRegistry'; export class TestTheme implements ITheme { @@ -24,14 +23,6 @@ export class TestTheme implements ITheme { defines(color: string): boolean { throw new Error('Method not implemented.'); } - - getTokenStyle(classification: TokenClassification, useDefault?: boolean | undefined): TokenStyle | undefined { - throw new Error('Method not implemented.'); - } - - resolveScopes(scopes: ProbeScope[]): TokenStyle | undefined { - throw new Error('Method not implemented.'); - } } export class TestIconTheme implements IIconTheme { diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index 4a1b760b4e2..a6572751d5f 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -19,9 +19,10 @@ import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; import { URI } from 'vs/base/common/uri'; import { parse as parsePList } from 'vs/workbench/services/themes/common/plistParser'; import { startsWith } from 'vs/base/common/strings'; -import { TokenStyle, TokenClassification, ProbeScope, TokenStylingRule, getTokenClassificationRegistry } from 'vs/platform/theme/common/tokenClassificationRegistry'; +import { TokenStyle, TokenClassification, ProbeScope, TokenStylingRule, getTokenClassificationRegistry, TokenStyleValue, matchTokenStylingRule } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { MatcherWithPriority, Matcher, createMatchers } from 'vs/workbench/services/themes/common/textMateScopeMatcher'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { FontStyle, ColorId, MetadataConsts } from 'vs/editor/common/modes'; let colorRegistry = Registry.as(ColorRegistryExtensions.ColorContribution); @@ -60,6 +61,8 @@ export class ColorThemeData implements IColorTheme { private themeTokenScopeMatchers: Matcher[] | undefined; private customTokenScopeMatchers: Matcher[] | undefined; + private tokenColorIndex: TokenColorIndex | undefined = undefined; // created on demand + private constructor(id: string, label: string, settingsId: string) { this.id = id; this.label = label; @@ -114,19 +117,141 @@ export class ColorThemeData implements IColorTheme { return color; } - public getTokenStyle(tokenClassification: TokenClassification, useDefault?: boolean): TokenStyle | undefined { - // todo: cache results - return tokenClassificationRegistry.resolveTokenStyle(tokenClassification, this.tokenStylingRules, this.customTokenStylingRules, this); + public getTokenStyle(classification: TokenClassification, useDefault?: boolean): TokenStyle | undefined { + let result: any = { + foreground: undefined, + bold: undefined, + underline: undefined, + italic: undefined + }; + let score = { + foreground: -1, + bold: -1, + underline: -1, + italic: -1 + }; + + function _processStyle(matchScore: number, style: TokenStyle) { + if (style.foreground && score.foreground <= matchScore) { + score.foreground = matchScore; + result.foreground = style.foreground; + } + for (let p of ['bold', 'underline', 'italic']) { + const property = p as keyof TokenStyle; + const info = style[property]; + if (info !== undefined) { + if (score[property] <= matchScore) { + score[property] = matchScore; + result[property] = info; + } + } + } + } + if (this.tokenStylingRules === undefined) { + for (const rule of tokenClassificationRegistry.getTokenStylingDefaultRules()) { + const matchScore = matchTokenStylingRule(rule, classification); + if (matchScore >= 0) { + let style = this.resolveScopes(rule.defaults.scopesToProbe); + if (!style && useDefault !== false) { + style = this.resolveTokenStyleValue(rule.defaults[this.type]); + } + if (style) { + _processStyle(matchScore, style); + } + } + } + } else { + for (const rule of this.tokenStylingRules) { + const matchScore = matchTokenStylingRule(rule, classification); + if (matchScore >= 0) { + _processStyle(matchScore, rule.value); + } + } + } + for (const rule of this.customTokenStylingRules) { + const matchScore = matchTokenStylingRule(rule, classification); + if (matchScore >= 0) { + _processStyle(matchScore, rule.value); + } + } + return TokenStyle.fromData(result); + + } + + /** + * @param tokenStyleValue Resolve a tokenStyleValue in the context of a theme + */ + private resolveTokenStyleValue(tokenStyleValue: TokenStyleValue | null): TokenStyle | undefined { + if (tokenStyleValue === null) { + return undefined; + } else if (typeof tokenStyleValue === 'string') { + const classification = tokenClassificationRegistry.getTokenClassificationFromString(tokenStyleValue); + if (classification) { + return this.getTokenStyle(classification); + } + } else if (typeof tokenStyleValue === 'object') { + return tokenStyleValue; + } + return undefined; + } + + private getTokenColorIndex(): TokenColorIndex { + // collect all colors that tokens can have + if (!this.tokenColorIndex) { + const index = new TokenColorIndex(); + this.tokenColors.forEach(rule => { + index.add(rule.settings.foreground); + index.add(rule.settings.background); + }); + + if (this.tokenStylingRules) { + this.tokenStylingRules.forEach(r => index.add(r.value.foreground)); + } else { + tokenClassificationRegistry.getTokenStylingDefaultRules().forEach(r => { + const defaultColor = r.defaults[this.type]; + if (defaultColor && typeof defaultColor === 'object') { + index.add(defaultColor.foreground); + } + }); + } + this.customTokenStylingRules.forEach(r => index.add(r.value.foreground)); + + this.tokenColorIndex = index; + } + return this.tokenColorIndex; + } + + public get tokenColorMap(): string[] { + return this.getTokenColorIndex().asArray(); + } + + public getTokenStyleMetadata(classification: TokenClassification, useDefault?: boolean): number { + const style = this.getTokenStyle(classification, useDefault); + let fontStyle = FontStyle.NotSet; + let foreground = 0; + if (style) { + if (style.bold === false && style.underline === false && style.italic === false) { + fontStyle = FontStyle.None; + } else { + if (style.bold) { + fontStyle |= FontStyle.Bold; + } + if (style.underline) { + fontStyle |= FontStyle.Underline; + } + if (style.italic) { + fontStyle |= FontStyle.Italic; + } + } + foreground = this.getTokenColorIndex().get(style.foreground); + } + return toMetadata(fontStyle, foreground, 0); } public getDefault(colorId: ColorIdentifier): Color | undefined { return colorRegistry.resolveDefaultColor(colorId, this); } - public getDefaultTokenStyle(tokenClassification: TokenClassification): TokenStyle | undefined { - return tokenClassificationRegistry.resolveTokenStyle(tokenClassification, undefined, [], this); - } - public resolveScopes(scopes: ProbeScope[]): TokenStyle | undefined { if (!this.themeTokenScopeMatchers) { @@ -177,6 +302,8 @@ export class ColorThemeData implements IColorTheme { if (types.isObject(themeSpecificColors)) { this.overwriteCustomColors(themeSpecificColors); } + + this.tokenColorIndex = undefined; } private overwriteCustomColors(colors: IColorCustomizations) { @@ -209,6 +336,8 @@ export class ColorThemeData implements IColorTheme { if (types.isObject(themeSpecificColors)) { readCustomTokenStyleRules(themeSpecificColors, this.tokenStylingRules); } + + this.tokenColorIndex = undefined; } private addCustomTokenColors(customTokenColors: ITokenColorCustomizations) { @@ -250,6 +379,7 @@ export class ColorThemeData implements IColorTheme { } this.themeTokenColors = []; this.themeTokenScopeMatchers = undefined; + this.tokenColorIndex = undefined; const result = { colors: {}, @@ -587,3 +717,66 @@ function readCustomTokenStyleRules(tokenStylingRuleSection: IExperimentalTokenSt function isTokenColorizationSetting(style: any): style is ITokenColorizationSetting { return style && (style.foreground || style.fontStyle); } + + +class TokenColorIndex { + + private _lastColorId: number; + private _id2color: string[]; + private _color2id: { [color: string]: number; }; + + constructor() { + this._lastColorId = 0; + this._id2color = []; + this._color2id = Object.create(null); + } + + public add(color: string | Color | undefined | null): number { + if (color === null || color === undefined) { + return 0; + } + color = normalizeColorForIndex(color); + + let value = this._color2id[color]; + if (value) { + return value; + } + value = ++this._lastColorId; + this._color2id[color] = value; + this._id2color[value] = color; + return value; + } + + public get(color: string | Color | undefined): number { + if (color === undefined) { + return 0; + } + color = normalizeColorForIndex(color); + let value = this._color2id[color]; + if (value) { + return value; + } + console.log(`Color ${color} not in index.`); + return 0; + } + + public asArray(): string[] { + return this._id2color.slice(0); + } + +} + +function normalizeColorForIndex(color: string | Color): string { + if (typeof color !== 'string') { + color = Color.Format.CSS.formatHexA(color, true); + } + return color.toUpperCase(); +} + +function toMetadata(fontStyle: FontStyle, foreground: ColorId | number, background: ColorId | number) { + return ( + (fontStyle << MetadataConsts.FONT_STYLE_OFFSET) + | (foreground << MetadataConsts.FOREGROUND_OFFSET) + | (background << MetadataConsts.BACKGROUND_OFFSET) + ) >>> 0; +} diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 9937ca11be9..884d31c80c6 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -8,6 +8,7 @@ import { Event } from 'vs/base/common/event'; import { Color } from 'vs/base/common/color'; import { ITheme, IThemeService, IIconTheme } from 'vs/platform/theme/common/themeService'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { TokenClassification } from 'vs/platform/theme/common/tokenClassificationRegistry'; export const IWorkbenchThemeService = createDecorator('themeService'); @@ -32,6 +33,16 @@ export interface IColorTheme extends ITheme { readonly description?: string; readonly isLoaded: boolean; readonly tokenColors: ITextMateThemingRule[]; + + /** + * Returns the token style for a given classification. The result uses the MetadataConsts format + */ + getTokenStyleMetadata(classification: TokenClassification): number; + + /** + * List of all colors used with tokens. getTokenStyleMetadata references the colors by index into this list. + */ + readonly tokenColorMap: string[]; } export interface IColorMap { From 64ca1bb8bafc1ae894254003b5559ccf8287dd47 Mon Sep 17 00:00:00 2001 From: Robo Date: Thu, 21 Nov 2019 01:56:54 -0800 Subject: [PATCH 165/246] chore: Bump electron@6.1.5 (#85266) --- .yarnrc | 2 +- cgmanifest.json | 4 ++-- src/typings/electron.d.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.yarnrc b/.yarnrc index 7f3fa82972f..288e7393abd 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "6.1.4" +target "6.1.5" runtime "electron" diff --git a/cgmanifest.json b/cgmanifest.json index 3d6e8a21080..bc3086c3100 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "a5b474e8248803f54efc2c2c724c3322590c4fda" + "commitHash": "6f62f91822a80192cb711c604f1a8f1a176f328d" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "6.1.4" + "version": "6.1.5" }, { "component": { diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index e4c3221c6eb..369817dcd12 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Electron 6.1.4 +// Type definitions for Electron 6.1.5 // Project: http://electronjs.org/ // Definitions by: The Electron Team // Definitions: https://github.com/electron/electron-typescript-definitions From f24f483428f592a262dc73d68a3f05745c562b31 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 21 Nov 2019 10:59:38 +0100 Subject: [PATCH 166/246] rename user data sync settings --- .../userDataSync/common/extensionsSync.ts | 6 ++-- .../userDataSync/common/settingsSync.ts | 6 ++-- .../userDataSync/common/userDataSync.ts | 30 +++++++++---------- .../common/userDataSyncService.ts | 4 +-- .../preferences/browser/settingsLayout.ts | 6 ++-- .../browser/relauncher.contribution.ts | 1 - .../userDataSync/browser/userDataSync.ts | 6 ++-- 7 files changed, 29 insertions(+), 30 deletions(-) diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 72537329e7f..720d0d86ccc 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -69,7 +69,7 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser } async sync(): Promise { - if (!this.configurationService.getValue('configurationSync.enableExtensions')) { + if (!this.configurationService.getValue('sync.enableExtensions')) { this.logService.trace('Extensions: Skipping synchronizing extensions as it is disabled.'); return false; } @@ -108,7 +108,7 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser return this.replaceQueue.queue(async () => { const remoteData = await this.userDataSyncStoreService.read(ExtensionsSynchroniser.EXTERNAL_USER_DATA_EXTENSIONS_KEY, null); const remoteExtensions: ISyncExtension[] = remoteData.content ? JSON.parse(remoteData.content) : []; - const ignoredExtensions = this.configurationService.getValue('configurationSync.extensionsToIgnore') || []; + const ignoredExtensions = this.configurationService.getValue('sync.ignoredExtensions') || []; const removedExtensions = remoteExtensions.filter(e => !ignoredExtensions.some(id => areSameExtensions({ id }, e.identifier)) && areSameExtensions(e.identifier, identifier)); if (removedExtensions.length) { for (const removedExtension of removedExtensions) { @@ -162,7 +162,7 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser * - Update remote with those local extension which are newly added or updated or removed and untouched in remote. */ private merge(localExtensions: ISyncExtension[], remoteExtensions: ISyncExtension[] | null, lastSyncExtensions: ISyncExtension[] | null): { added: ISyncExtension[], removed: IExtensionIdentifier[], updated: ISyncExtension[], remote: ISyncExtension[] | null } { - const ignoredExtensions = this.configurationService.getValue('configurationSync.extensionsToIgnore') || []; + const ignoredExtensions = this.configurationService.getValue('sync.ignoredExtensions') || []; // First time sync if (!remoteExtensions) { this.logService.info('Extensions: Remote extensions does not exist. Synchronizing extensions for the first time.'); diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 03d97cc1418..1a757524dd6 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -80,7 +80,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { } async sync(_continue?: boolean): Promise { - if (!this.configurationService.getValue('configurationSync.enableSettings')) { + if (!this.configurationService.getValue('sync.enableSettings')) { this.logService.trace('Settings: Skipping synchronizing settings as it is disabled.'); return false; } @@ -252,10 +252,10 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { if (settingsContent) { const setting = parse(settingsContent); if (setting) { - value = setting['configurationSync.settingsToIgnore']; + value = setting['sync.ignoredSettings']; } } else { - value = this.configurationService.getValue('configurationSync.settingsToIgnore'); + value = this.configurationService.getValue('sync.ignoredSettings'); } const added: string[] = [], removed: string[] = []; if (Array.isArray(value)) { diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index c90c1cc0a22..5771808a1c9 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -20,48 +20,48 @@ const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store'; export const DEFAULT_IGNORED_SETTINGS = [ CONFIGURATION_SYNC_STORE_KEY, - 'configurationSync.enable', - 'configurationSync.enableSettings', - 'configurationSync.enableExtensions', + 'sync.enable', + 'sync.enableSettings', + 'sync.enableExtensions', ]; export function registerConfiguration(): IDisposable { const ignoredSettingsSchemaId = 'vscode://schemas/ignoredSettings'; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ - id: 'configurationSync', + id: 'sync', order: 30, - title: localize('configurationSync', "Configuration Sync"), + title: localize('sync', "Sync"), type: 'object', properties: { - 'configurationSync.enable': { + 'sync.enable': { type: 'boolean', - description: localize('configurationSync.enable', "When enabled, synchronizes configuration that includes Settings and Extensions."), + description: localize('sync.enable', "Enable synchronization."), default: false, scope: ConfigurationScope.APPLICATION }, - 'configurationSync.enableSettings': { + 'sync.enableSettings': { type: 'boolean', - description: localize('configurationSync.enableSettings', "When enabled settings are synchronized while synchronizing configuration."), + description: localize('sync.enableSettings', "Enable synchronizing settings."), default: true, scope: ConfigurationScope.APPLICATION, }, - 'configurationSync.enableExtensions': { + 'sync.enableExtensions': { type: 'boolean', - description: localize('configurationSync.enableExtensions', "When enabled extensions are synchronized while synchronizing configuration."), + description: localize('sync.enableExtensions', "Enable synchronizing extensions."), default: true, scope: ConfigurationScope.APPLICATION, }, - 'configurationSync.extensionsToIgnore': { + 'sync.ignoredExtensions': { 'type': 'array', - description: localize('configurationSync.extensionsToIgnore', "Configure extensions to be ignored while syncing."), + description: localize('sync.ignoredExtensions', "Configure extensions to be ignored while synchronizing."), 'default': [], 'scope': ConfigurationScope.APPLICATION, uniqueItems: true }, - 'configurationSync.settingsToIgnore': { + 'sync.ignoredSettings': { 'type': 'array', - description: localize('configurationSync.settingsToIgnore', "Configure settings to be ignored while syncing. \nDefault Ignored Settings:\n\n{0}", DEFAULT_IGNORED_SETTINGS.sort().map(setting => `- ${setting}`).join('\n')), + description: localize('sync.ignoredSettings', "Configure settings to be ignored while synchronizing. \nDefault Ignored Settings:\n\n{0}", DEFAULT_IGNORED_SETTINGS.sort().map(setting => `- ${setting}`).join('\n')), 'default': [], 'scope': ConfigurationScope.APPLICATION, $ref: ignoredSettingsSchemaId, diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 6ccfeac0d85..91eed22298e 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -137,7 +137,7 @@ export class UserDataAutoSync extends Disposable { super(); this.updateEnablement(false); this._register(Event.any(authTokenService.onDidChangeStatus, userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true))); - this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('configurationSync.enable'))(() => this.updateEnablement(true))); + this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('sync.enable'))(() => this.updateEnablement(true))); // Sync immediately if there is a local change. this._register(Event.debounce(this.userDataSyncService.onDidChangeLocal, () => undefined, 500)(() => this.sync(false))); @@ -178,7 +178,7 @@ export class UserDataAutoSync extends Disposable { } private isSyncEnabled(): boolean { - return this.configurationService.getValue('configurationSync.enable') + return this.configurationService.getValue('sync.enable') && this.userDataSyncService.status !== SyncStatus.Uninitialized && this.authTokenService.status === AuthTokenStatus.SignedIn; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts index fff4ef6068b..fa530078f92 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts @@ -202,9 +202,9 @@ export const tocData: ITOCEntry = { settings: ['telemetry.*'] }, { - id: 'application/configurationSync', - label: localize('configuration sync', "Configuration Sync"), - settings: ['configurationSync.*'] + id: 'application/sync', + label: localize('sync', "Sync"), + settings: ['sync.*'] } ] } diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index d5447f83cfc..a152b50423d 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -26,7 +26,6 @@ interface IConfiguration extends IWindowsConfiguration { telemetry: { enableCrashReporter: boolean }; workbench: { list: { horizontalScrolling: boolean } }; debug: { console: { wordWrap: boolean } }; - configurationSync: { enableAuth: boolean }; } export class SettingsChangeRelauncher extends Disposable implements IWorkbenchContribution { diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index fa271edc179..c52091d5fa4 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -38,7 +38,7 @@ const SYNC_PUSH_DARK_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbenc export class UserDataSyncWorkbenchContribution extends Disposable implements IWorkbenchContribution { - private static readonly ENABLEMENT_SETTING = 'configurationSync.enable'; + private static readonly ENABLEMENT_SETTING = 'sync.enable'; private readonly userDataSyncStore: IUserDataSyncStore | undefined; private readonly syncStatusContext: IContextKey; @@ -187,10 +187,10 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo quickPick.canSelectMany = true; quickPick.ignoreFocusOut = true; const items = [{ - id: 'configurationSync.enableSettings', + id: 'sync.settings', label: localize('user settings', "User Settings") }, { - id: 'configurationSync.enableExtensions', + id: 'sync.enableExtensions', label: localize('extensions', "Extensions") }]; quickPick.items = items; From f820a9a9e4f3a4a9bf898cc7dcf7556b5b259739 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 21 Nov 2019 11:14:17 +0100 Subject: [PATCH 167/246] fixes #83906 --- src/vs/workbench/contrib/debug/browser/repl.ts | 6 +++--- src/vs/workbench/contrib/debug/common/replModel.ts | 4 ++-- .../workbench/contrib/debug/test/browser/debugModel.test.ts | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index b0394820606..4ff31849ccd 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -809,13 +809,13 @@ class ReplDelegate extends CachedListVirtualDelegate { const config = this.configurationService.getValue('debug'); if (!config.console.wordWrap) { - return Math.ceil(1.4 * config.console.fontSize); + return this.estimateHeight(element, true); } return super.getHeight(element); } - protected estimateHeight(element: IReplElement): number { + protected estimateHeight(element: IReplElement, ignoreValueLength = false): number { const config = this.configurationService.getValue('debug'); const rowHeight = Math.ceil(1.4 * config.console.fontSize); const countNumberOfLines = (str: string) => Math.max(1, (str && str.match(/\r\n|\n/g) || []).length); @@ -825,7 +825,7 @@ class ReplDelegate extends CachedListVirtualDelegate { // For every 30 characters increase the number of lines needed if (hasValue(element)) { let value = element.value; - let valueRows = countNumberOfLines(value) + Math.floor(value.length / 30); + let valueRows = countNumberOfLines(value) + (ignoreValueLength ? 0 : Math.floor(value.length / 30)); return valueRows * rowHeight; } diff --git a/src/vs/workbench/contrib/debug/common/replModel.ts b/src/vs/workbench/contrib/debug/common/replModel.ts index 4083fafaa6c..bc0f77b86d2 100644 --- a/src/vs/workbench/contrib/debug/common/replModel.ts +++ b/src/vs/workbench/contrib/debug/common/replModel.ts @@ -10,7 +10,7 @@ import { ExpressionContainer } from 'vs/workbench/contrib/debug/common/debugMode import { isString, isUndefinedOrNull, isObject } from 'vs/base/common/types'; import { basenameOrAuthority } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { endsWith, startsWith } from 'vs/base/common/strings'; +import { endsWith } from 'vs/base/common/strings'; import { generateUuid } from 'vs/base/common/uuid'; import { Emitter } from 'vs/base/common/event'; @@ -134,7 +134,7 @@ export class ReplModel { if (typeof data === 'string') { const previousElement = this.replElements.length ? this.replElements[this.replElements.length - 1] : undefined; - if (!startsWith(data, '\n') && previousElement instanceof SimpleReplElement && previousElement.severity === sev && !endsWith(previousElement.value, '\n') && !endsWith(previousElement.value, '\r\n')) { + if (previousElement instanceof SimpleReplElement && previousElement.severity === sev && !endsWith(previousElement.value, '\n') && !endsWith(previousElement.value, '\r\n')) { previousElement.value += data; this._onDidChangeElements.fire(); } else { diff --git a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts index a7256a2cd44..9198304bfab 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts @@ -498,13 +498,11 @@ suite('Debug - Model', () => { repl.appendToRepl(session, '3\n4', severity.Info); repl.appendToRepl(session, '5\n', severity.Info); repl.appendToRepl(session, '6', severity.Info); - repl.appendToRepl(session, '\n7', severity.Info); elements = repl.getReplElements(); - assert.equal(elements.length, 4); + assert.equal(elements.length, 3); assert.equal(elements[0], '1\n'); assert.equal(elements[1], '23\n45\n'); assert.equal(elements[2], '6'); - assert.equal(elements[3], '\n7'); }); test('repl merging', () => { From 9a54d769da53dff0c613276157f3a0eadef358e8 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 21 Nov 2019 12:09:44 +0100 Subject: [PATCH 168/246] colortheme: style metadata tests --- .../themes/browser/workbenchThemeService.ts | 2 + .../services/themes/common/colorThemeData.ts | 109 +++++++++++------- .../tokenStyleResolving.test.ts | 30 ++++- 3 files changed, 96 insertions(+), 45 deletions(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 046764e60c3..df7c4cfd196 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -359,6 +359,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { const themeData = data; return themeData.ensureLoaded(this.extensionResourceLoaderService).then(_ => { if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { + this.currentColorTheme.clearCaches(); // the loaded theme is identical to the perisisted theme. Don't need to send an event. this.currentColorTheme = themeData; themeData.setCustomColors(this.colorCustomizations); @@ -415,6 +416,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } addClasses(this.container, newTheme.id); + this.currentColorTheme.clearCaches(); this.currentColorTheme = newTheme; if (!this.themingParticipantChangeListener) { this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(_ => this.updateDynamicCSSRules(this.currentColorTheme)); diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index a6572751d5f..f539067684e 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -61,6 +61,7 @@ export class ColorThemeData implements IColorTheme { private themeTokenScopeMatchers: Matcher[] | undefined; private customTokenScopeMatchers: Matcher[] | undefined; + private textMateThemingRules: ITextMateThemingRule[] | undefined = undefined; // created on demand private tokenColorIndex: TokenColorIndex | undefined = undefined; // created on demand private constructor(id: string, label: string, settingsId: string) { @@ -71,38 +72,41 @@ export class ColorThemeData implements IColorTheme { } get tokenColors(): ITextMateThemingRule[] { - const result: ITextMateThemingRule[] = []; + if (!this.textMateThemingRules) { + const result: ITextMateThemingRule[] = []; - // the default rule (scope empty) is always the first rule. Ignore all other default rules. - const foreground = this.getColor(editorForeground) || this.getDefault(editorForeground)!; - const background = this.getColor(editorBackground) || this.getDefault(editorBackground)!; - result.push({ - settings: { - foreground: Color.Format.CSS.formatHexA(foreground), - background: Color.Format.CSS.formatHexA(background) - } - }); - - let hasDefaultTokens = false; - - function addRule(rule: ITextMateThemingRule) { - if (rule.scope && rule.settings) { - if (rule.scope === 'token.info-token') { - hasDefaultTokens = true; + // the default rule (scope empty) is always the first rule. Ignore all other default rules. + const foreground = this.getColor(editorForeground) || this.getDefault(editorForeground)!; + const background = this.getColor(editorBackground) || this.getDefault(editorBackground)!; + result.push({ + settings: { + foreground: Color.Format.CSS.formatHexA(foreground, true), + background: Color.Format.CSS.formatHexA(background, true) + } + }); + + let hasDefaultTokens = false; + + function addRule(rule: ITextMateThemingRule) { + if (rule.scope && rule.settings) { + if (rule.scope === 'token.info-token') { + hasDefaultTokens = true; + } + result.push(rule); } - result.push(rule); } - } - this.themeTokenColors.forEach(addRule); - // Add the custom colors after the theme colors - // so that they will override them - this.customTokenColors.forEach(addRule); + this.themeTokenColors.forEach(addRule); + // Add the custom colors after the theme colors + // so that they will override them + this.customTokenColors.forEach(addRule); - if (!hasDefaultTokens) { - defaultThemeColors[this.type].forEach(addRule); + if (!hasDefaultTokens) { + defaultThemeColors[this.type].forEach(addRule); + } + this.textMateThemingRules = result; } - return result; + return this.textMateThemingRules; } public getColor(colorId: ColorIdentifier, useDefault?: boolean): Color | undefined { @@ -227,21 +231,17 @@ export class ColorThemeData implements IColorTheme { public getTokenStyleMetadata(classification: TokenClassification, useDefault?: boolean): number { const style = this.getTokenStyle(classification, useDefault); - let fontStyle = FontStyle.NotSet; + let fontStyle = FontStyle.None; let foreground = 0; if (style) { - if (style.bold === false && style.underline === false && style.italic === false) { - fontStyle = FontStyle.None; - } else { - if (style.bold) { - fontStyle |= FontStyle.Bold; - } - if (style.underline) { - fontStyle |= FontStyle.Underline; - } - if (style.italic) { - fontStyle |= FontStyle.Italic; - } + if (style.bold) { + fontStyle |= FontStyle.Bold; + } + if (style.underline) { + fontStyle |= FontStyle.Underline; + } + if (style.italic) { + fontStyle |= FontStyle.Italic; } foreground = this.getTokenColorIndex().get(style.foreground); } @@ -304,6 +304,7 @@ export class ColorThemeData implements IColorTheme { } this.tokenColorIndex = undefined; + this.textMateThemingRules = undefined; } private overwriteCustomColors(colors: IColorCustomizations) { @@ -326,6 +327,9 @@ export class ColorThemeData implements IColorTheme { if (types.isObject(themeSpecificTokenColors)) { this.addCustomTokenColors(themeSpecificTokenColors); } + + this.tokenColorIndex = undefined; + this.textMateThemingRules = undefined; } public setCustomTokenStyleRules(tokenStylingRules: IExperimentalTokenStyleCustomizations) { @@ -338,6 +342,7 @@ export class ColorThemeData implements IColorTheme { } this.tokenColorIndex = undefined; + this.textMateThemingRules = undefined; } private addCustomTokenColors(customTokenColors: ITokenColorCustomizations) { @@ -380,6 +385,7 @@ export class ColorThemeData implements IColorTheme { this.themeTokenColors = []; this.themeTokenScopeMatchers = undefined; this.tokenColorIndex = undefined; + this.textMateThemingRules = undefined; const result = { colors: {}, @@ -394,6 +400,13 @@ export class ColorThemeData implements IColorTheme { }); } + public clearCaches() { + this.tokenColorIndex = undefined; + this.textMateThemingRules = undefined; + this.themeTokenScopeMatchers = undefined; + this.customTokenScopeMatchers = undefined; + } + toStorageData() { let colorMapData: { [key: string]: string } = {}; for (let key in this.colorMap) { @@ -774,9 +787,17 @@ function normalizeColorForIndex(color: string | Color): string { } function toMetadata(fontStyle: FontStyle, foreground: ColorId | number, background: ColorId | number) { - return ( - (fontStyle << MetadataConsts.FONT_STYLE_OFFSET) - | (foreground << MetadataConsts.FOREGROUND_OFFSET) - | (background << MetadataConsts.BACKGROUND_OFFSET) - ) >>> 0; + const fontStyleBits = fontStyle << MetadataConsts.FONT_STYLE_OFFSET; + const foregroundBits = foreground << MetadataConsts.FOREGROUND_OFFSET; + const backgroundBits = background << MetadataConsts.BACKGROUND_OFFSET; + if ((fontStyleBits & MetadataConsts.FONT_STYLE_MASK) !== fontStyleBits) { + console.log(`Can not express fontStyle ${fontStyle} in metadata`); + } + if ((backgroundBits & MetadataConsts.BACKGROUND_MASK) !== backgroundBits) { + console.log(`Can not express background ${background} in metadata`); + } + if ((foregroundBits & MetadataConsts.FOREGROUND_MASK) !== foregroundBits) { + console.log(`Can not express foreground ${foreground} in metadata`); + } + return (fontStyleBits | foregroundBits | backgroundBits) >>> 0; } diff --git a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts index ab41c917cb8..f1f8ede9db0 100644 --- a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts +++ b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts @@ -16,6 +16,7 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { ExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/electron-browser/extensionResourceLoaderService'; +import { TokenMetadata, FontStyle } from 'vs/editor/common/modes'; let tokenClassificationRegistry = getTokenClassificationRegistry(); @@ -48,13 +49,40 @@ function assertTokenStyle(actual: TokenStyle | undefined | null, expected: Token assert.equal(tokenStyleAsString(actual), tokenStyleAsString(expected), message); } +function assertTokenStyleMetaData(colorIndex: string[], actual: number, expected: TokenStyle | undefined | null, message?: string) { + if (!expected) { + assert.equal(actual, 0); + return; + } + const actualFontStyle = TokenMetadata.getFontStyle(actual); + assert.equal((actualFontStyle & FontStyle.Bold) === FontStyle.Bold, expected.bold === true, 'bold'); + assert.equal((actualFontStyle & FontStyle.Italic) === FontStyle.Italic, expected.italic === true, 'italic'); + assert.equal((actualFontStyle & FontStyle.Underline) === FontStyle.Underline, expected.underline === true, 'underline'); + + const actualForegroundIndex = TokenMetadata.getForeground(actual); + if (expected.foreground) { + assert.equal(actualForegroundIndex, colorIndex.indexOf(Color.Format.CSS.formatHexA(expected.foreground, true).toUpperCase()), 'foreground'); + } else { + assert.equal(actualForegroundIndex, 0, 'foreground'); + } + const actualBackgroundIndex = TokenMetadata.getBackground(actual); + assert.equal(actualBackgroundIndex, 0, 'background'); +} + + function assertTokenStyles(themeData: ColorThemeData, expected: { [qualifiedClassifier: string]: TokenStyle }) { + const colorIndex = themeData.tokenColorMap; + for (let qualifiedClassifier in expected) { const classification = tokenClassificationRegistry.getTokenClassificationFromString(qualifiedClassifier); assert.ok(classification, 'Classification not found'); const tokenStyle = themeData.getTokenStyle(classification!); - assertTokenStyle(tokenStyle, expected[qualifiedClassifier], qualifiedClassifier); + const expectedTokenStyle = expected[qualifiedClassifier]; + assertTokenStyle(tokenStyle, expectedTokenStyle, qualifiedClassifier); + + const tokenStyleMetaData = themeData.getTokenStyleMetadata(classification!); + assertTokenStyleMetaData(colorIndex, tokenStyleMetaData, expectedTokenStyle); } } From d041b6c46099e959d9082f62d4be7f9fbc373bbd Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 21 Nov 2019 12:10:10 +0100 Subject: [PATCH 169/246] use token color map in textmate service --- .../textMate/browser/abstractTextMateService.ts | 14 +++++++++----- .../services/textMate/common/TMGrammarFactory.ts | 4 ++-- .../textMate/electron-browser/textMateService.ts | 12 ++++++------ .../textMate/electron-browser/textMateWorker.ts | 4 ++-- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts index 0b4e3be1f52..a782f0cd7ac 100644 --- a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts +++ b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts @@ -10,6 +10,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import * as resources from 'vs/base/common/resources'; import * as types from 'vs/base/common/types'; +import { equals as equalArray } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; import { IState, ITokenizationSupport, LanguageId, TokenMetadata, TokenizationRegistry, StandardTokenType, LanguageIdentifier } from 'vs/editor/common/modes'; @@ -44,6 +45,7 @@ export abstract class AbstractTextMateService extends Disposable implements ITex private _grammarFactory: TMGrammarFactory | null; private _tokenizersRegistrations: IDisposable[]; protected _currentTheme: IRawTheme | null; + protected _currentTokenColorMap: string[] | null; constructor( @IModeService private readonly _modeService: IModeService, @@ -65,6 +67,7 @@ export abstract class AbstractTextMateService extends Disposable implements ITex this._tokenizersRegistrations = []; this._currentTheme = null; + this._currentTokenColorMap = null; grammarsExtPoint.setHandler((extensions) => { this._grammarDefinitions = null; @@ -245,16 +248,17 @@ export abstract class AbstractTextMateService extends Disposable implements ITex } private _updateTheme(grammarFactory: TMGrammarFactory, colorTheme: IColorTheme, forceUpdate: boolean): void { - if (!forceUpdate && this._currentTheme && AbstractTextMateService.equalsTokenRules(this._currentTheme.settings, colorTheme.tokenColors)) { + if (!forceUpdate && this._currentTheme && this._currentTokenColorMap && AbstractTextMateService.equalsTokenRules(this._currentTheme.settings, colorTheme.tokenColors) && equalArray(this._currentTokenColorMap, colorTheme.tokenColorMap)) { return; } this._currentTheme = { name: colorTheme.label, settings: colorTheme.tokenColors }; - this._doUpdateTheme(grammarFactory, this._currentTheme); + this._currentTokenColorMap = colorTheme.tokenColorMap; + this._doUpdateTheme(grammarFactory, this._currentTheme, this._currentTokenColorMap); } - protected _doUpdateTheme(grammarFactory: TMGrammarFactory, theme: IRawTheme): void { - grammarFactory.setTheme(theme); - let colorMap = AbstractTextMateService._toColorMap(grammarFactory.getColorMap()); + protected _doUpdateTheme(grammarFactory: TMGrammarFactory, theme: IRawTheme, tokenColorMap: string[]): void { + grammarFactory.setTheme(theme, tokenColorMap); + let colorMap = AbstractTextMateService._toColorMap(tokenColorMap); let cssRules = generateTokensCSSForColorMap(colorMap); this._styleElement.innerHTML = cssRules; TokenizationRegistry.setColorMap(colorMap); diff --git a/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts b/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts index 38e3101c34b..0a6c9abffe9 100644 --- a/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts +++ b/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts @@ -102,8 +102,8 @@ export class TMGrammarFactory extends Disposable { return this._languageToScope2[languageId] ? true : false; } - public setTheme(theme: IRawTheme): void { - this._grammarRegistry.setTheme(theme); + public setTheme(theme: IRawTheme, colorMap: string[]): void { + this._grammarRegistry.setTheme(theme, colorMap); } public getColorMap(): string[] { diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts index 6a4a4d4d2e2..f33593da728 100644 --- a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts +++ b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts @@ -206,18 +206,18 @@ export class TextMateService extends AbstractTextMateService { return; } this._workerProxy = proxy; - if (this._currentTheme) { - this._workerProxy.acceptTheme(this._currentTheme); + if (this._currentTheme && this._currentTokenColorMap) { + this._workerProxy.acceptTheme(this._currentTheme, this._currentTokenColorMap); } this._modelService.getModels().forEach((model) => this._onModelAdded(model)); }); } } - protected _doUpdateTheme(grammarFactory: TMGrammarFactory, theme: IRawTheme): void { - super._doUpdateTheme(grammarFactory, theme); - if (this._currentTheme && this._workerProxy) { - this._workerProxy.acceptTheme(this._currentTheme); + protected _doUpdateTheme(grammarFactory: TMGrammarFactory, theme: IRawTheme, colorMap: string[]): void { + super._doUpdateTheme(grammarFactory, theme, colorMap); + if (this._currentTheme && this._currentTokenColorMap && this._workerProxy) { + this._workerProxy.acceptTheme(this._currentTheme, this._currentTokenColorMap); } } diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts b/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts index 6f78679f1fd..6f2725ba9c6 100644 --- a/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts +++ b/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts @@ -185,9 +185,9 @@ export class TextMateWorker { return this._grammarCache[languageId]; } - public acceptTheme(theme: IRawTheme): void { + public acceptTheme(theme: IRawTheme, colorMap: string[]): void { if (this._grammarFactory) { - this._grammarFactory.setTheme(theme); + this._grammarFactory.setTheme(theme, colorMap); } } From 760e07d29ea5fe7368ac73561b4e20844d8f4d63 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 21 Nov 2019 09:44:17 +0100 Subject: [PATCH 170/246] Revert "Extend disposable for MenuService" This reverts commit 674bb6700babeac1aa431335b6d1eab19d63c55a. --- src/vs/platform/actions/common/menuService.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts index 651e6fe27f4..202ee04102f 100644 --- a/src/vs/platform/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { IMenu, IMenuActionOptions, IMenuItem, IMenuService, isIMenuItem, ISubmenuItem, MenuId, MenuItemAction, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, IContextKeyService, IContextKeyChangeEvent } from 'vs/platform/contextkey/common/contextkey'; @@ -27,9 +27,10 @@ export class MenuService implements IMenuService { type MenuItemGroup = [string, Array]; -class Menu extends Disposable implements IMenu { +class Menu implements IMenu { - private readonly _onDidChange = this._register(new Emitter()); + private readonly _onDidChange = new Emitter(); + private readonly _dispoables = new DisposableStore(); private _menuGroups!: MenuItemGroup[]; private _contextKeys!: Set; @@ -39,12 +40,11 @@ class Menu extends Disposable implements IMenu { @ICommandService private readonly _commandService: ICommandService, @IContextKeyService private readonly _contextKeyService: IContextKeyService ) { - super(); this._build(); // rebuild this menu whenever the menu registry reports an // event for this MenuId - this._register(Event.debounce( + this._dispoables.add(Event.debounce( Event.filter(MenuRegistry.onDidChangeMenu, menuId => menuId === this._id), () => { }, 50 @@ -52,13 +52,18 @@ class Menu extends Disposable implements IMenu { // when context keys change we need to check if the menu also // has changed - this._register(Event.debounce( + this._dispoables.add(Event.debounce( this._contextKeyService.onDidChangeContext, (last, event) => last || event.affectsSome(this._contextKeys), 50 )(e => e && this._onDidChange.fire(undefined), this)); } + dispose(): void { + this._dispoables.dispose(); + this._onDidChange.dispose(); + } + private _build(): void { // reset From 8da81e54f4d99a3dd97742f9dd1f7a7de915ce1a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 21 Nov 2019 09:53:19 +0100 Subject: [PATCH 171/246] :lipstick: better strict property init --- src/vs/platform/actions/common/menuService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts index 202ee04102f..62f3041c874 100644 --- a/src/vs/platform/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -32,8 +32,8 @@ class Menu implements IMenu { private readonly _onDidChange = new Emitter(); private readonly _dispoables = new DisposableStore(); - private _menuGroups!: MenuItemGroup[]; - private _contextKeys!: Set; + private _menuGroups: MenuItemGroup[] = []; + private _contextKeys: Set = new Set(); constructor( private readonly _id: MenuId, @@ -67,8 +67,8 @@ class Menu implements IMenu { private _build(): void { // reset - this._menuGroups = []; - this._contextKeys = new Set(); + this._menuGroups.length = 0; + this._contextKeys.clear(); const menuItems = MenuRegistry.getMenuItems(this._id); From 14928afabb3326ac515b5def6695cf94ac1e3124 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 21 Nov 2019 10:49:36 +0100 Subject: [PATCH 172/246] make the internals fit for #9827 --- .../editor/contrib/contextmenu/contextmenu.ts | 26 ++++++++++++++----- src/vs/platform/actions/common/menuService.ts | 5 +++- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/contextmenu/contextmenu.ts b/src/vs/editor/contrib/contextmenu/contextmenu.ts index ea1d5f26b17..6a301ba93e4 100644 --- a/src/vs/editor/contrib/contextmenu/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts @@ -15,7 +15,7 @@ import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/brows import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IMenuService, MenuId, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -23,6 +23,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { ITextModel } from 'vs/editor/common/model'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { ContextSubMenu } from 'vs/base/browser/contextmenu'; export class ContextMenuController implements IEditorContribution { @@ -128,7 +129,7 @@ export class ContextMenuController implements IEditorContribution { } // Find actions available for menu - const menuActions = this._getMenuActions(this._editor.getModel()); + const menuActions = this._getMenuActions(this._editor.getModel(), MenuId.EditorContext); // Show menu if we have actions to show if (menuActions.length > 0) { @@ -136,16 +137,27 @@ export class ContextMenuController implements IEditorContribution { } } - private _getMenuActions(model: ITextModel): ReadonlyArray { + private _getMenuActions(model: ITextModel, menuId: MenuId): IAction[] { const result: IAction[] = []; - let contextMenu = this._menuService.createMenu(MenuId.EditorContext, this._contextKeyService); - const groups = contextMenu.getActions({ arg: model.uri }); - contextMenu.dispose(); + // get menu groups + const menu = this._menuService.createMenu(menuId, this._contextKeyService); + const groups = menu.getActions({ arg: model.uri }); + menu.dispose(); + // translate them into other actions for (let group of groups) { const [, actions] = group; - result.push(...actions); + for (const action of actions) { + if (action instanceof SubmenuItemAction) { + const subActions = this._getMenuActions(model, action.item.submenu); + if (subActions.length > 0) { + result.push(new ContextSubMenu(action.label, subActions)); + } + } else { + result.push(action); + } + } result.push(new Separator()); } result.pop(); // remove last separator diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts index 62f3041c874..29787fb379f 100644 --- a/src/vs/platform/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -111,7 +111,10 @@ class Menu implements IMenu { const activeActions: Array = []; for (const item of items) { if (this._contextKeyService.contextMatchesRules(item.when)) { - const action = isIMenuItem(item) ? new MenuItemAction(item.command, item.alt, options, this._contextKeyService, this._commandService) : new SubmenuItemAction(item); + const action = isIMenuItem(item) + ? new MenuItemAction(item.command, item.alt, options, this._contextKeyService, this._commandService) + : new SubmenuItemAction(item); + activeActions.push(action); } } From 724b433e2ce4b5da897871b01d02e84c5cb21fe9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 21 Nov 2019 12:22:40 +0100 Subject: [PATCH 173/246] simplify how editor commands/actions register menu entries --- .../editor/browser/controller/coreCommands.ts | 6 +- src/vs/editor/browser/editorExtensions.ts | 112 +++++++++++------- src/vs/editor/contrib/clipboard/clipboard.ts | 12 +- .../contrib/codeAction/codeActionCommands.ts | 4 +- src/vs/editor/contrib/comment/comment.ts | 4 +- src/vs/editor/contrib/find/findController.ts | 4 +- src/vs/editor/contrib/format/formatActions.ts | 4 +- .../linesOperations/linesOperations.ts | 10 +- .../editor/contrib/multicursor/multicursor.ts | 14 +-- src/vs/editor/contrib/rename/rename.ts | 2 +- .../editor/contrib/smartSelect/smartSelect.ts | 4 +- .../browser/quickOpen/quickCommand.ts | 2 +- .../browser/quickOpen/quickOutline.ts | 2 +- src/vs/platform/actions/common/actions.ts | 1 + .../browser/callHierarchy.contribution.ts | 2 +- .../debug/browser/debugEditorActions.ts | 6 +- .../browser/actions/expandAbbreviation.ts | 2 +- .../browser/actions/showEmmetCommands.ts | 2 +- .../format/browser/formatActionsMultiple.ts | 4 +- .../quickopen/browser/commandsHandler.ts | 2 +- 20 files changed, 116 insertions(+), 83 deletions(-) diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index 94ed9e159cb..cb20bc72ea6 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -1753,7 +1753,7 @@ registerCommand(new EditorOrNativeTextInputCommand({ kbExpr: null, primary: KeyMod.CtrlCmd | KeyCode.KEY_A }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '1_basic', title: nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), @@ -1771,7 +1771,7 @@ registerCommand(new EditorOrNativeTextInputCommand({ kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.KEY_Z }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '1_do', title: nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), @@ -1792,7 +1792,7 @@ registerCommand(new EditorOrNativeTextInputCommand({ secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z } }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '1_do', title: nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"), diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 2a12d008b5c..408881eb438 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -42,10 +42,10 @@ export interface ICommandKeybindingsOptions extends IKeybindings { kbExpr?: ContextKeyExpr | null; weight: number; } -export interface ICommandMenubarOptions { +export interface ICommandMenuOptions { menuId: MenuId; - group: string; - order: number; + group?: string; + order?: number; when?: ContextKeyExpr; title: string; } @@ -54,36 +54,29 @@ export interface ICommandOptions { precondition: ContextKeyExpr | undefined; kbOpts?: ICommandKeybindingsOptions; description?: ICommandHandlerDescription; - menubarOpts?: ICommandMenubarOptions; + menuOpts?: ICommandMenuOptions | ICommandMenuOptions[]; } export abstract class Command { public readonly id: string; public readonly precondition: ContextKeyExpr | undefined; private readonly _kbOpts: ICommandKeybindingsOptions | undefined; - private readonly _menubarOpts: ICommandMenubarOptions | undefined; + private readonly _menuOpts: ICommandMenuOptions | ICommandMenuOptions[] | undefined; private readonly _description: ICommandHandlerDescription | undefined; constructor(opts: ICommandOptions) { this.id = opts.id; this.precondition = opts.precondition; this._kbOpts = opts.kbOpts; - this._menubarOpts = opts.menubarOpts; + this._menuOpts = opts.menuOpts; this._description = opts.description; } public register(): void { - if (this._menubarOpts) { - MenuRegistry.appendMenuItem(this._menubarOpts.menuId, { - group: this._menubarOpts.group, - command: { - id: this.id, - title: this._menubarOpts.title, - // precondition: this.precondition - }, - when: this._menubarOpts.when, - order: this._menubarOpts.order - }); + if (Array.isArray(this._menuOpts)) { + this._menuOpts.forEach(this._registerMenuItem, this); + } else if (this._menuOpts) { + this._registerMenuItem(this._menuOpts); } if (this._kbOpts) { @@ -119,6 +112,19 @@ export abstract class Command { } } + private _registerMenuItem(item: ICommandMenuOptions): void { + MenuRegistry.appendMenuItem(item.menuId, { + group: item.group, + command: { + id: this.id, + title: item.title, + // precondition: this.precondition + }, + when: item.when, + order: item.order + }); + } + public abstract runCommand(accessor: ServicesAccessor, args: any): void | Promise; } @@ -184,44 +190,70 @@ export abstract class EditorCommand extends Command { //#region EditorAction -export interface IEditorCommandMenuOptions { +export interface IEditorActionContextMenuOptions { group: string; order: number; when?: ContextKeyExpr; } -export interface IActionOptions extends ICommandOptions { +export interface IActionOptions { + id: string; label: string; alias: string; - menuOpts?: IEditorCommandMenuOptions; + description?: ICommandHandlerDescription; + precondition: ContextKeyExpr | undefined; + kbOpts?: ICommandKeybindingsOptions; + contextMenuOpts?: IEditorActionContextMenuOptions; + menuOpts?: Partial | Partial; } + export abstract class EditorAction extends EditorCommand { + private static convertOptions(opts: IActionOptions): ICommandOptions { + + function patch(menu: Partial): ICommandMenuOptions { + if (!menu.title) { + menu.title = opts.label; + } + if (!menu.menuId) { + menu.menuId = MenuId.EditorContext; + } + if (!menu.when) { + menu.when = opts.precondition; + } + return menu; + } + + let menuOpts: ICommandMenuOptions[]; + if (Array.isArray(opts.menuOpts)) { + menuOpts = opts.menuOpts.map(m => patch(m!)); + } else if (opts.menuOpts) { + menuOpts = [patch(opts.menuOpts)]; + } else { + menuOpts = []; + } + + if (opts.contextMenuOpts) { + const contextMenuItem = { + title: opts.label, + when: ContextKeyExpr.and(opts.precondition, opts.contextMenuOpts.when), + menuId: MenuId.EditorContext, + group: opts.contextMenuOpts.group, + order: opts.contextMenuOpts.order + }; + menuOpts.push(contextMenuItem); + } + + opts.menuOpts = menuOpts; + return opts; + } + public readonly label: string; public readonly alias: string; - private readonly menuOpts: IEditorCommandMenuOptions | undefined; constructor(opts: IActionOptions) { - super(opts); + super(EditorAction.convertOptions(opts)); this.label = opts.label; this.alias = opts.alias; - this.menuOpts = opts.menuOpts; - } - - public register(): void { - - if (this.menuOpts) { - MenuRegistry.appendMenuItem(MenuId.EditorContext, { - command: { - id: this.id, - title: this.label - }, - when: ContextKeyExpr.and(this.precondition, this.menuOpts.when), - group: this.menuOpts.group, - order: this.menuOpts.order - }); - } - - super.register(); } public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise { diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index df241dc8643..cb106b53b82 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -77,11 +77,11 @@ class ExecCommandCutAction extends ExecCommandAction { alias: 'Cut', precondition: EditorContextKeys.writable, kbOpts: kbOpts, - menuOpts: { + contextMenuOpts: { group: CLIPBOARD_CONTEXT_MENU_GROUP, order: 1 }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '2_ccp', title: nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), @@ -126,11 +126,11 @@ class ExecCommandCopyAction extends ExecCommandAction { alias: 'Copy', precondition: undefined, kbOpts: kbOpts, - menuOpts: { + contextMenuOpts: { group: CLIPBOARD_CONTEXT_MENU_GROUP, order: 2 }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '2_ccp', title: nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), @@ -175,11 +175,11 @@ class ExecCommandPasteAction extends ExecCommandAction { alias: 'Paste', precondition: EditorContextKeys.writable, kbOpts: kbOpts, - menuOpts: { + contextMenuOpts: { group: CLIPBOARD_CONTEXT_MENU_GROUP, order: 3 }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '2_ccp', title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 20561a76ee9..c51443c95c3 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -265,7 +265,7 @@ export class RefactorAction extends EditorAction { }, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: '1_modification', order: 2, when: ContextKeyExpr.and( @@ -308,7 +308,7 @@ export class SourceAction extends EditorAction { label: nls.localize('source.label', "Source Action..."), alias: 'Source Action...', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), - menuOpts: { + contextMenuOpts: { group: '1_modification', order: 2.1, when: ContextKeyExpr.and( diff --git a/src/vs/editor/contrib/comment/comment.ts b/src/vs/editor/contrib/comment/comment.ts index 5c6118a3f6f..301b00677c4 100644 --- a/src/vs/editor/contrib/comment/comment.ts +++ b/src/vs/editor/contrib/comment/comment.ts @@ -56,7 +56,7 @@ class ToggleCommentLineAction extends CommentLineAction { primary: KeyMod.CtrlCmd | KeyCode.US_SLASH, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '5_insert', title: nls.localize({ key: 'miToggleLineComment', comment: ['&& denotes a mnemonic'] }, "&&Toggle Line Comment"), @@ -112,7 +112,7 @@ class BlockCommentAction extends EditorAction { linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_A }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '5_insert', title: nls.localize({ key: 'miToggleBlockComment', comment: ['&& denotes a mnemonic'] }, "Toggle &&Block Comment"), diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index ae362600757..fbd9bf5500b 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -455,7 +455,7 @@ export class StartFindAction extends EditorAction { primary: KeyMod.CtrlCmd | KeyCode.KEY_F, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '3_find', title: nls.localize({ key: 'miFind', comment: ['&& denotes a mnemonic'] }, "&&Find"), @@ -701,7 +701,7 @@ export class StartFindReplaceAction extends EditorAction { mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_F }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '3_find', title: nls.localize({ key: 'miReplace', comment: ['&& denotes a mnemonic'] }, "&&Replace"), diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index daded6e3e9d..879000c9140 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -219,7 +219,7 @@ class FormatDocumentAction extends EditorAction { linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { when: EditorContextKeys.hasDocumentFormattingProvider, group: '1_modification', order: 1.3 @@ -248,7 +248,7 @@ class FormatSelectionAction extends EditorAction { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F), weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { when: ContextKeyExpr.and(EditorContextKeys.hasDocumentSelectionFormattingProvider, EditorContextKeys.hasNonEmptySelection), group: '1_modification', order: 1.31 diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index b615da8257b..d351f6af863 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -64,7 +64,7 @@ class CopyLinesUpAction extends AbstractCopyLinesAction { linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.UpArrow }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '2_line', title: nls.localize({ key: 'miCopyLinesUp', comment: ['&& denotes a mnemonic'] }, "&&Copy Line Up"), @@ -87,7 +87,7 @@ class CopyLinesDownAction extends AbstractCopyLinesAction { linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.DownArrow }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '2_line', title: nls.localize({ key: 'miCopyLinesDown', comment: ['&& denotes a mnemonic'] }, "Co&&py Line Down"), @@ -105,7 +105,7 @@ export class DuplicateSelectionAction extends EditorAction { label: nls.localize('duplicateSelection', "Duplicate Selection"), alias: 'Duplicate Selection', precondition: EditorContextKeys.writable, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '2_line', title: nls.localize({ key: 'miDuplicateSelection', comment: ['&& denotes a mnemonic'] }, "&&Duplicate Selection"), @@ -178,7 +178,7 @@ class MoveLinesUpAction extends AbstractMoveLinesAction { linux: { primary: KeyMod.Alt | KeyCode.UpArrow }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '2_line', title: nls.localize({ key: 'miMoveLinesUp', comment: ['&& denotes a mnemonic'] }, "Mo&&ve Line Up"), @@ -201,7 +201,7 @@ class MoveLinesDownAction extends AbstractMoveLinesAction { linux: { primary: KeyMod.Alt | KeyCode.DownArrow }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '2_line', title: nls.localize({ key: 'miMoveLinesDown', comment: ['&& denotes a mnemonic'] }, "Move &&Line Down"), diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index 4434040595a..a2044206bdd 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -46,7 +46,7 @@ export class InsertCursorAbove extends EditorAction { }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', title: nls.localize({ key: 'miInsertCursorAbove', comment: ['&& denotes a mnemonic'] }, "&&Add Cursor Above"), @@ -95,7 +95,7 @@ export class InsertCursorBelow extends EditorAction { }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', title: nls.localize({ key: 'miInsertCursorBelow', comment: ['&& denotes a mnemonic'] }, "A&&dd Cursor Below"), @@ -140,7 +140,7 @@ class InsertCursorAtEndOfEachLineSelected extends EditorAction { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_I, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', title: nls.localize({ key: 'miInsertCursorAtEndOfEachLineSelected', comment: ['&& denotes a mnemonic'] }, "Add C&&ursors to Line Ends"), @@ -662,7 +662,7 @@ export class AddSelectionToNextFindMatchAction extends MultiCursorSelectionContr primary: KeyMod.CtrlCmd | KeyCode.KEY_D, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', title: nls.localize({ key: 'miAddSelectionToNextFindMatch', comment: ['&& denotes a mnemonic'] }, "Add &&Next Occurrence"), @@ -682,7 +682,7 @@ export class AddSelectionToPreviousFindMatchAction extends MultiCursorSelectionC label: nls.localize('addSelectionToPreviousFindMatch', "Add Selection To Previous Find Match"), alias: 'Add Selection To Previous Find Match', precondition: undefined, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', title: nls.localize({ key: 'miAddSelectionToPreviousFindMatch', comment: ['&& denotes a mnemonic'] }, "Add P&&revious Occurrence"), @@ -740,7 +740,7 @@ export class SelectHighlightsAction extends MultiCursorSelectionControllerAction primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', title: nls.localize({ key: 'miSelectHighlights', comment: ['&& denotes a mnemonic'] }, "Select All &&Occurrences"), @@ -765,7 +765,7 @@ export class CompatChangeAll extends MultiCursorSelectionControllerAction { primary: KeyMod.CtrlCmd | KeyCode.F2, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: '1_modification', order: 1.2 } diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 8eb67d9fe04..86cd28d8d01 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -238,7 +238,7 @@ export class RenameAction extends EditorAction { primary: KeyCode.F2, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: '1_modification', order: 1.1 } diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index 81b60e73d91..a98044f0ada 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -167,7 +167,7 @@ class GrowSelectionAction extends AbstractSmartSelect { }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '1_basic', title: nls.localize({ key: 'miSmartSelectGrow', comment: ['&& denotes a mnemonic'] }, "&&Expand Selection"), @@ -196,7 +196,7 @@ class ShrinkSelectionAction extends AbstractSmartSelect { }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '1_basic', title: nls.localize({ key: 'miSmartSelectShrink', comment: ['&& denotes a mnemonic'] }, "&&Shrink Selection"), diff --git a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts index b5d2715c808..d00c36ea973 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts @@ -88,7 +88,7 @@ export class QuickCommandAction extends BaseEditorQuickOpenAction { primary: (browser.isIE ? KeyMod.Alt | KeyCode.F1 : KeyCode.F1), weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: 'z_commands', order: 1 } diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts index 8bd72a304a2..22ab5a56c83 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts @@ -121,7 +121,7 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: 'navigation', order: 3 } diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index cc7b367fd5d..99a84647d28 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -65,6 +65,7 @@ export const enum MenuId { DebugWatchContext, DebugToolBar, EditorContext, + EditorContextPeek, EditorTitle, EditorTitleContext, EmptyEditorGroupContext, diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts index aae136b9a2c..eac5e02af3c 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts @@ -160,7 +160,7 @@ registerEditorAction(class extends EditorAction { id: 'editor.showCallHierarchy', label: localize('title', "Peek Call Hierarchy"), alias: 'Peek Call Hierarchy', - menuOpts: { + contextMenuOpts: { group: 'navigation', order: 1.48 }, diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index d053a9d481a..80306b110a5 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -111,7 +111,7 @@ export class RunToCursorAction extends EditorAction { label: RunToCursorAction.LABEL, alias: 'Debug: Run to Cursor', precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus), - menuOpts: { + contextMenuOpts: { group: 'debug', order: 2 } @@ -160,7 +160,7 @@ class SelectionToReplAction extends EditorAction { label: nls.localize('debugEvaluate', "Debug: Evaluate"), alias: 'Debug: Evaluate', precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, EditorContextKeys.editorTextFocus), - menuOpts: { + contextMenuOpts: { group: 'debug', order: 0 } @@ -190,7 +190,7 @@ class SelectionToWatchExpressionsAction extends EditorAction { label: nls.localize('debugAddToWatch', "Debug: Add to Watch"), alias: 'Debug: Add to Watch', precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, EditorContextKeys.editorTextFocus), - menuOpts: { + contextMenuOpts: { group: 'debug', order: 1 } diff --git a/src/vs/workbench/contrib/emmet/browser/actions/expandAbbreviation.ts b/src/vs/workbench/contrib/emmet/browser/actions/expandAbbreviation.ts index 18aa8533f44..b6129b8a6ae 100644 --- a/src/vs/workbench/contrib/emmet/browser/actions/expandAbbreviation.ts +++ b/src/vs/workbench/contrib/emmet/browser/actions/expandAbbreviation.ts @@ -29,7 +29,7 @@ class ExpandAbbreviationAction extends EmmetEditorAction { ), weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '5_insert', title: nls.localize({ key: 'miEmmetExpandAbbreviation', comment: ['&& denotes a mnemonic'] }, "Emmet: E&&xpand Abbreviation"), diff --git a/src/vs/workbench/contrib/emmet/browser/actions/showEmmetCommands.ts b/src/vs/workbench/contrib/emmet/browser/actions/showEmmetCommands.ts index 1845bcd4239..1fe29bd1d93 100644 --- a/src/vs/workbench/contrib/emmet/browser/actions/showEmmetCommands.ts +++ b/src/vs/workbench/contrib/emmet/browser/actions/showEmmetCommands.ts @@ -21,7 +21,7 @@ class ShowEmmetCommandsAction extends EditorAction { label: nls.localize('showEmmetCommands', "Show Emmet Commands"), alias: 'Show Emmet Commands', precondition: EditorContextKeys.writable, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '5_insert', title: nls.localize({ key: 'miShowEmmetCommands', comment: ['&& denotes a mnemonic'] }, "E&&mmet..."), diff --git a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts index 887d6a87d2f..4b171f9aa4c 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts @@ -253,7 +253,7 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction { label: nls.localize('formatDocument.label.multiple', "Format Document With..."), alias: 'Format Document...', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasMultipleDocumentFormattingProvider), - menuOpts: { + contextMenuOpts: { group: '1_modification', order: 1.3 } @@ -284,7 +284,7 @@ registerEditorAction(class FormatSelectionMultipleAction extends EditorAction { label: nls.localize('formatSelection.label.multiple', "Format Selection With..."), alias: 'Format Code...', precondition: ContextKeyExpr.and(ContextKeyExpr.and(EditorContextKeys.writable), EditorContextKeys.hasMultipleDocumentSelectionFormattingProvider), - menuOpts: { + contextMenuOpts: { when: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection), group: '1_modification', order: 1.31 diff --git a/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts b/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts index 66b5f574bdb..9c0dbb1757c 100644 --- a/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts @@ -214,7 +214,7 @@ class CommandPaletteEditorAction extends EditorAction { label: localize('showCommands.label', "Command Palette..."), alias: 'Command Palette', precondition: undefined, - menuOpts: { + contextMenuOpts: { group: 'z_commands', order: 1 } From 17de08a829e56657e44213a70cf69d18f06e74a5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 21 Nov 2019 12:36:03 +0100 Subject: [PATCH 174/246] add "Peek" sub menu, #85218 --- .../editor/contrib/gotoSymbol/goToCommands.ts | 49 ++++++++++++++----- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index 3e5a95069d9..ae759078b26 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -21,7 +21,7 @@ import { PeekContext } from 'vs/editor/contrib/peekView/peekView'; import { ReferencesController } from 'vs/editor/contrib/gotoSymbol/peek/referencesController'; import { ReferencesModel } from 'vs/editor/contrib/gotoSymbol/referencesModel'; import * as nls from 'vs/nls'; -import { MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, ISubmenuItem } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -37,6 +37,14 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ScrollType } from 'vs/editor/common/editorCommon'; import { assertType } from 'vs/base/common/types'; + +MenuRegistry.appendMenuItem(MenuId.EditorContext, { + submenu: MenuId.EditorContextPeek, + title: nls.localize('peek.submenu', "Peek"), + group: 'navigation', + order: 1000 +}); + export interface SymbolNavigationActionConfig { openToSide: boolean; openInPeek: boolean; @@ -229,11 +237,11 @@ registerEditorAction(class GoToDefinitionAction extends DefinitionAction { primary: goToDefinitionKb, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: 'navigation', order: 1.1 }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarGoMenu, group: '4_symbol_nav', order: 2, @@ -293,6 +301,9 @@ registerEditorAction(class PeekDefinitionAction extends DefinitionAction { primary: KeyMod.Alt | KeyCode.F12, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F10 }, weight: KeybindingWeight.EditorContrib + }, + menuOpts: { + menuId: MenuId.EditorContextPeek } }); CommandsRegistry.registerCommandAlias('editor.action.previewDeclaration', PeekDefinitionAction.id); @@ -341,11 +352,11 @@ registerEditorAction(class GoToDeclarationAction extends DeclarationAction { EditorContextKeys.hasDeclarationProvider, EditorContextKeys.isInEmbeddedEditor.toNegated() ), - menuOpts: { + contextMenuOpts: { group: 'navigation', order: 1.3 }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarGoMenu, group: '4_symbol_nav', order: 3, @@ -376,6 +387,9 @@ registerEditorAction(class PeekDeclarationAction extends DeclarationAction { PeekContext.notInPeekEditor, EditorContextKeys.isInEmbeddedEditor.toNegated() ), + menuOpts: { + menuId: MenuId.EditorContextPeek + } }); } }); @@ -426,11 +440,11 @@ registerEditorAction(class GoToTypeDefinitionAction extends TypeDefinitionAction primary: 0, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: 'navigation', order: 1.4 }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarGoMenu, group: '4_symbol_nav', order: 3, @@ -457,7 +471,10 @@ registerEditorAction(class PeekTypeDefinitionAction extends TypeDefinitionAction EditorContextKeys.hasTypeDefinitionProvider, PeekContext.notInPeekEditor, EditorContextKeys.isInEmbeddedEditor.toNegated() - ) + ), + menuOpts: { + menuId: MenuId.EditorContextPeek + } }); } }); @@ -508,13 +525,13 @@ registerEditorAction(class GoToImplementationAction extends ImplementationAction primary: KeyMod.CtrlCmd | KeyCode.F12, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarGoMenu, group: '4_symbol_nav', order: 4, title: nls.localize({ key: 'miGotoImplementation', comment: ['&& denotes a mnemonic'] }, "Go to &&Implementations") }, - menuOpts: { + contextMenuOpts: { group: 'navigation', order: 1.45 } @@ -544,6 +561,9 @@ registerEditorAction(class PeekImplementationAction extends ImplementationAction kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F12, weight: KeybindingWeight.EditorContrib + }, + menuOpts: { + menuId: MenuId.EditorContextPeek } }); } @@ -595,11 +615,11 @@ registerEditorAction(class GoToReferencesAction extends ReferencesAction { primary: KeyMod.Shift | KeyCode.F12, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: 'navigation', order: 1.45 }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarGoMenu, group: '4_symbol_nav', order: 5, @@ -624,7 +644,10 @@ registerEditorAction(class PeekReferencesAction extends ReferencesAction { EditorContextKeys.hasReferenceProvider, PeekContext.notInPeekEditor, EditorContextKeys.isInEmbeddedEditor.toNegated() - ) + ), + menuOpts: { + menuId: MenuId.EditorContextPeek + } }); } }); From 2bc77436b953096db1dcc5560fb2d8e97506154d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 12:41:11 +0100 Subject: [PATCH 175/246] Remove no longer necessary minimap font creator --- .../test/browser/view/minimapFontCreator.html | 25 ---- .../test/browser/view/minimapFontCreator.ts | 119 ------------------ 2 files changed, 144 deletions(-) delete mode 100644 src/vs/editor/test/browser/view/minimapFontCreator.html delete mode 100644 src/vs/editor/test/browser/view/minimapFontCreator.ts diff --git a/src/vs/editor/test/browser/view/minimapFontCreator.html b/src/vs/editor/test/browser/view/minimapFontCreator.html deleted file mode 100644 index 9ddd334797a..00000000000 --- a/src/vs/editor/test/browser/view/minimapFontCreator.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/vs/editor/test/browser/view/minimapFontCreator.ts b/src/vs/editor/test/browser/view/minimapFontCreator.ts deleted file mode 100644 index b064114f722..00000000000 --- a/src/vs/editor/test/browser/view/minimapFontCreator.ts +++ /dev/null @@ -1,119 +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 { RGBA8 } from 'vs/editor/common/core/rgba'; -import { MinimapCharRenderer } from 'vs/editor/browser/viewParts/minimap/minimapCharRenderer'; -import { Constants } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet'; -import { MinimapCharRendererFactory } from 'vs/editor/browser/viewParts/minimap/minimapCharRendererFactory'; - -let sampleData = MinimapCharRendererFactory.createSampleData('monospace'); -let minimapCharRenderer1x = MinimapCharRendererFactory.createFromSampleData(sampleData.data, 1); -let minimapCharRenderer2x = MinimapCharRendererFactory.createFromSampleData(sampleData.data, 2); -let minimapCharRenderer4x = MinimapCharRendererFactory.createFromSampleData(sampleData.data, 4); -let minimapCharRenderer6x = MinimapCharRendererFactory.createFromSampleData(sampleData.data, 6); - -renderImageData(sampleData, 10, 100); -renderMinimapCharRenderer(minimapCharRenderer1x, 400, 1); -renderMinimapCharRenderer(minimapCharRenderer2x, 500, 2); -renderMinimapCharRenderer(minimapCharRenderer4x, 600, 4); -renderMinimapCharRenderer(minimapCharRenderer6x, 750, 8); - -function createFakeImageData(width: number, height: number): ImageData { - return { - width: width, - height: height, - data: new Uint8ClampedArray(width * height * Constants.RGBA_CHANNELS_CNT) - }; -} - -function renderMinimapCharRenderer(minimapCharRenderer: MinimapCharRenderer, y: number, scale: number): void { - let background = new RGBA8(0, 0, 0, 255); - let color = new RGBA8(255, 255, 255, 255); - - { - let x2 = createFakeImageData( - Constants.BASE_CHAR_WIDTH * scale * Constants.CHAR_COUNT, - Constants.BASE_CHAR_HEIGHT * scale - ); - // set the background color - for (let i = 0, len = x2.data.length / 4; i < len; i++) { - x2.data[4 * i + 0] = background.r; - x2.data[4 * i + 1] = background.g; - x2.data[4 * i + 2] = background.b; - x2.data[4 * i + 3] = 255; - } - let dx = 0; - for (let chCode = Constants.START_CH_CODE; chCode <= Constants.END_CH_CODE; chCode++) { - minimapCharRenderer.renderChar(x2, dx, 0, chCode, color, background, false); - dx += Constants.BASE_CHAR_WIDTH * scale; - } - renderImageData(x2, 10, y); - } -} - -(function () { - let r = 'let x2Data = [', - offset = 0; - for (let charIndex = 0; charIndex < Constants.CHAR_COUNT; charIndex++) { - let charCode = charIndex + Constants.START_CH_CODE; - r += '\n\n// ' + String.fromCharCode(charCode); - - for (let i = 0; i < Constants.BASE_CHAR_HEIGHT * 2; i++) { - if (i % 2 === 0) { - r += '\n'; - } - r += (minimapCharRenderer2x as any).charDataNormal[offset] + ','; - offset++; - } - } - r += '\n\n]'; - console.log(r); -})(); - -(function () { - let r = 'let x1Data = [', - offset = 0; - for (let charIndex = 0; charIndex < Constants.CHAR_COUNT; charIndex++) { - let charCode = charIndex + Constants.START_CH_CODE; - r += '\n\n// ' + String.fromCharCode(charCode); - - for (let i = 0; i < Constants.BASE_CHAR_HEIGHT * Constants.BASE_CHAR_WIDTH; i++) { - r += '\n'; - r += (minimapCharRenderer1x as any).charDataNormal[offset] + ','; - offset++; - } - } - r += '\n\n]'; - console.log(r); -})(); - -function renderImageData(imageData: ImageData, left: number, top: number): void { - let output = ''; - let offset = 0; - let PX_SIZE = 15; - for (let i = 0; i < imageData.height; i++) { - for (let j = 0; j < imageData.width; j++) { - let R = imageData.data[offset]; - let G = imageData.data[offset + 1]; - let B = imageData.data[offset + 2]; - let A = imageData.data[offset + 3]; - offset += 4; - - output += `
`; - } - } - - let domNode = document.createElement('div'); - domNode.style.position = 'absolute'; - domNode.style.top = top + 'px'; - domNode.style.left = left + 'px'; - domNode.style.width = imageData.width * PX_SIZE + 'px'; - domNode.style.height = imageData.height * PX_SIZE + 'px'; - domNode.style.border = '1px solid #ccc'; - domNode.style.background = '#000000'; - domNode.innerHTML = output; - document.body.appendChild(domNode); -} From 5ff11212bb881ecaffd94809de4aa2818ff59f85 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 12:55:23 +0100 Subject: [PATCH 176/246] Don't always use the replacement character in minimap When the font scale is small enough, using other ASCII characters for characters outside the supported ASCII range is preferrable. Fixes #84264 --- src/vs/editor/browser/viewParts/minimap/minimap.ts | 2 +- .../editor/browser/viewParts/minimap/minimapCharRenderer.ts | 3 ++- src/vs/editor/browser/viewParts/minimap/minimapCharSheet.ts | 6 +++++- src/vs/editor/test/browser/view/minimapCharRenderer.test.ts | 4 ++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 8e50b1e0e2d..4703c176847 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -1109,7 +1109,7 @@ export class Minimap extends ViewPart { if (renderMinimap === RenderMinimap.Blocks) { minimapCharRenderer.blockRenderChar(target, dx, dy, tokenColor, backgroundColor, useLighterFont); } else { // RenderMinimap.Text - minimapCharRenderer.renderChar(target, dx, dy, charCode, tokenColor, backgroundColor, useLighterFont); + minimapCharRenderer.renderChar(target, dx, dy, charCode, tokenColor, backgroundColor, fontScale, useLighterFont); } dx += charWidth; diff --git a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts index e1136ab6c28..cc8a37fcf6b 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts @@ -32,6 +32,7 @@ export class MinimapCharRenderer { chCode: number, color: RGBA8, backgroundColor: RGBA8, + fontScale: number, useLighterFont: boolean ): void { const charWidth = Constants.BASE_CHAR_WIDTH * this.scale; @@ -42,7 +43,7 @@ export class MinimapCharRenderer { } const charData = useLighterFont ? this.charDataLight : this.charDataNormal; - const charIndex = getCharIndex(chCode); + const charIndex = getCharIndex(chCode, fontScale); const destWidth = target.width * Constants.RGBA_CHANNELS_CNT; diff --git a/src/vs/editor/browser/viewParts/minimap/minimapCharSheet.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharSheet.ts index af7b0eae4f8..cda6dc9e7ee 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimapCharSheet.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimapCharSheet.ts @@ -29,9 +29,13 @@ export const allCharCodes: ReadonlyArray = (() => { return v; })(); -export const getCharIndex = (chCode: number) => { +export const getCharIndex = (chCode: number, fontScale: number) => { chCode -= Constants.START_CH_CODE; if (chCode < 0 || chCode > Constants.CHAR_COUNT) { + if (fontScale <= 2) { + // for smaller scales, we can get away with using any ASCII character... + return (chCode + Constants.CHAR_COUNT) % Constants.CHAR_COUNT; + } return Constants.CHAR_COUNT - 1; // unknown symbol } diff --git a/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts b/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts index f45f5fa1162..56281b80706 100644 --- a/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts +++ b/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts @@ -86,7 +86,7 @@ suite('MinimapCharRenderer', () => { imageData.data[4 * i + 2] = background.b; imageData.data[4 * i + 3] = 255; } - renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, false); + renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 2, false); let actual: number[] = []; for (let i = 0; i < imageData.data.length; i++) { @@ -116,7 +116,7 @@ suite('MinimapCharRenderer', () => { imageData.data[4 * i + 3] = 255; } - renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, false); + renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 1, false); let actual: number[] = []; for (let i = 0; i < imageData.data.length; i++) { From b2e2e78c8819dfbf3c181a0f0591ff62caa15cca Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Thu, 21 Nov 2019 13:16:14 +0100 Subject: [PATCH 177/246] make '${file}' equivalent to TextDocument.fileName --- src/vs/workbench/api/common/extHostDebugService.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 6c1802ec3e4..75bc3851448 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -3,9 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - import * as path from 'vs/base/common/path'; -import { Schemas } from 'vs/base/common/network'; import { URI, UriComponents } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { asPromise } from 'vs/base/common/async'; @@ -982,10 +980,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ getFilePath: (): string | undefined => { const activeEditor = editorService.activeEditor(); if (activeEditor) { - const resource = activeEditor.document.uri; - if (resource.scheme === Schemas.file) { - return path.normalize(resource.fsPath); - } + return path.normalize(activeEditor.document.uri.fsPath); } return undefined; }, From 343f26602ef3b341c70754cf1731d97ba73b36d8 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Thu, 21 Nov 2019 13:16:51 +0100 Subject: [PATCH 178/246] remove obsolete comment --- extensions/vscode-api-tests/src/extension.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/extension.ts b/extensions/vscode-api-tests/src/extension.ts index e662ba6b52a..f8070c4307b 100644 --- a/extensions/vscode-api-tests/src/extension.ts +++ b/extensions/vscode-api-tests/src/extension.ts @@ -3812,7 +3812,7 @@ class MockConfigurationProvider implements vscode.DebugConfigurationProvider { config.type = 'mock'; config.name = 'Launch'; config.request = 'launch'; - config.program = '${file}'; // editor.document.fileName; // '${file}'; + config.program = '${file}'; config.stopOnEntry = true; } } From 5f5a65c441b7fbaf52d05fd5fe961edc07306b7c Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Thu, 21 Nov 2019 13:26:06 +0100 Subject: [PATCH 179/246] prevent vscode-api-tests from running in node.js --- extensions/vscode-api-tests/package.json | 3 ++- extensions/vscode-api-tests/src/extension.ts | 14 ++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 09efe39ca76..8ac6b2806ca 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -7,7 +7,8 @@ "enableProposedApi": true, "private": true, "activationEvents": [ - "onFileSystem:memfs" + "onFileSystem:memfs", + "onDebug" ], "main": "./out/extension", "engines": { diff --git a/extensions/vscode-api-tests/src/extension.ts b/extensions/vscode-api-tests/src/extension.ts index f8070c4307b..f6fa8ebf362 100644 --- a/extensions/vscode-api-tests/src/extension.ts +++ b/extensions/vscode-api-tests/src/extension.ts @@ -18,13 +18,15 @@ const textEncoder = new TextEncoder(); const SCHEME = 'memfs'; export function activate(context: vscode.ExtensionContext) { - const memFs = enableFs(context); - enableProblems(context); - enableSearch(context, memFs); - enableTasks(); - enableDebug(context, memFs); + if (typeof window !== 'undefined') { // do not run under node.js + const memFs = enableFs(context); + enableProblems(context); + enableSearch(context, memFs); + enableTasks(); + enableDebug(context, memFs); - vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`memfs:/sample-folder/large.ts`)); + vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`memfs:/sample-folder/large.ts`)); + } } function enableFs(context: vscode.ExtensionContext): MemFS { From a6b869d4bd13588dff2a473e8e626d594f4798e7 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 14:55:01 +0100 Subject: [PATCH 180/246] Allow to run our unit tests with mocha (where document.queryCommandSupported is not defined) --- src/vs/base/browser/canIUse.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/canIUse.ts b/src/vs/base/browser/canIUse.ts index 9329acfad6a..cf8211e919e 100644 --- a/src/vs/base/browser/canIUse.ts +++ b/src/vs/base/browser/canIUse.ts @@ -19,7 +19,7 @@ export const BrowserFeatures = { clipboard: { writeText: ( platform.isNative - || document.queryCommandSupported('copy') + || (document.queryCommandSupported && document.queryCommandSupported('copy')) || !!(navigator && navigator.clipboard && navigator.clipboard.writeText) ), readText: ( From ca40ac378e2826506d898025da6a446642a8f066 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Nov 2019 14:58:38 +0100 Subject: [PATCH 181/246] Fixes #84998: Overtype after a backslash if the character is not a quote --- .../common/controller/cursorTypeOperations.ts | 5 ++-- .../test/browser/controller/cursor.test.ts | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index ba0be3d5317..7940b058b48 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -459,9 +459,10 @@ export class TypeOperations { return false; } - // Do not over-type after a backslash + // Do not over-type quotes after a backslash + const chIsQuote = isQuote(ch); const beforeCharacter = position.column > 2 ? lineText.charCodeAt(position.column - 2) : CharCode.Null; - if (beforeCharacter === CharCode.Backslash) { + if (beforeCharacter === CharCode.Backslash && chIsQuote) { return false; } diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index cabab834266..454ce0ac3c9 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -4936,6 +4936,35 @@ suite('autoClosingPairs', () => { mode.dispose(); }); + test('issue #84998: Overtyping Brackets doesn\'t work after backslash', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + '' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + + cursor.setSelections('test', [new Selection(1, 1, 1, 1)]); + + cursorCommand(cursor, H.Type, { text: '\\' }, 'keyboard'); + assert.equal(model.getValue(), '\\'); + + cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); + assert.equal(model.getValue(), '\\()'); + + cursorCommand(cursor, H.Type, { text: 'abc' }, 'keyboard'); + assert.equal(model.getValue(), '\\(abc)'); + + cursorCommand(cursor, H.Type, { text: '\\' }, 'keyboard'); + assert.equal(model.getValue(), '\\(abc\\)'); + + cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + assert.equal(model.getValue(), '\\(abc\\)'); + }); + mode.dispose(); + }); + test('issue #2773: Accents (´`¨^, others?) are inserted in the wrong position (Mac)', () => { let mode = new AutoClosingMode(); usingCursor({ From 1b5afdf5c34081a3133fb0a9dc4ce175e68715ba Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 21 Nov 2019 10:21:02 +0100 Subject: [PATCH 182/246] refactor --- extensions/git/src/main.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 0c34f4e0f3f..80435de391a 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -21,7 +21,7 @@ import { GitProtocolHandler } from './protocolHandler'; import { GitExtensionImpl } from './api/extension'; import * as path from 'path'; import * as fs from 'fs'; -import { createIPCServer } from './ipc/ipcServer'; +import { createIPCServer, IIPCServer } from './ipc/ipcServer'; const deactivateTasks: { (): Promise; }[] = []; @@ -34,18 +34,24 @@ export async function deactivate(): Promise { async function createModel(context: ExtensionContext, outputChannel: OutputChannel, telemetryReporter: TelemetryReporter, disposables: Disposable[]): Promise { const pathHint = workspace.getConfiguration('git').get('path'); const info = await findGit(pathHint, path => outputChannel.appendLine(localize('looking', "Looking for git in: {0}", path))); + let env: any = {}; + let ipc: IIPCServer | undefined; try { - const ipc = await createIPCServer(); + ipc = await createIPCServer(); disposables.push(ipc); env = { ...env, ...ipc.getEnv() }; + } catch { + // noop + } + if (ipc) { const askpass = new Askpass(ipc); disposables.push(askpass); env = { ...env, ...askpass.getEnv() }; - } catch { - env = Askpass.getDisabledEnv(); + } else { + env = { ...env, ...Askpass.getDisabledEnv() }; } const git = new Git({ gitPath: info.path, version: info.version, env }); From db5b58c44246e0ffceca0779bf697a0bdf883bd9 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 21 Nov 2019 10:24:23 +0100 Subject: [PATCH 183/246] isolate askpass --- extensions/git/src/{ => askpass}/askpass-empty.sh | 0 extensions/git/src/{ => askpass}/askpass-main.ts | 2 +- extensions/git/src/{ => askpass}/askpass.sh | 0 extensions/git/src/{ => askpass}/askpass.ts | 4 ++-- extensions/git/src/main.ts | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename extensions/git/src/{ => askpass}/askpass-empty.sh (100%) rename extensions/git/src/{ => askpass}/askpass-main.ts (96%) rename extensions/git/src/{ => askpass}/askpass.sh (100%) rename extensions/git/src/{ => askpass}/askpass.ts (93%) diff --git a/extensions/git/src/askpass-empty.sh b/extensions/git/src/askpass/askpass-empty.sh similarity index 100% rename from extensions/git/src/askpass-empty.sh rename to extensions/git/src/askpass/askpass-empty.sh diff --git a/extensions/git/src/askpass-main.ts b/extensions/git/src/askpass/askpass-main.ts similarity index 96% rename from extensions/git/src/askpass-main.ts rename to extensions/git/src/askpass/askpass-main.ts index 685fc08ef86..c2674d28705 100644 --- a/extensions/git/src/askpass-main.ts +++ b/extensions/git/src/askpass/askpass-main.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; import * as nls from 'vscode-nls'; -import { IPCClient } from './ipc/ipcClient'; +import { IPCClient } from '../ipc/ipcClient'; const localize = nls.loadMessageBundle(); diff --git a/extensions/git/src/askpass.sh b/extensions/git/src/askpass/askpass.sh similarity index 100% rename from extensions/git/src/askpass.sh rename to extensions/git/src/askpass/askpass.sh diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass/askpass.ts similarity index 93% rename from extensions/git/src/askpass.ts rename to extensions/git/src/askpass/askpass.ts index dd7e84c2e03..994a32514e5 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass/askpass.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { window, InputBoxOptions } from 'vscode'; -import { IDisposable } from './util'; +import { IDisposable } from '../util'; import * as path from 'path'; -import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; +import { IIPCHandler, IIPCServer } from '../ipc/ipcServer'; export interface AskpassEnvironment { GIT_ASKPASS: string; diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 80435de391a..37cb8e5377c 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -13,7 +13,7 @@ import { CommandCenter } from './commands'; import { GitContentProvider } from './contentProvider'; import { GitFileSystemProvider } from './fileSystemProvider'; import { GitDecorations } from './decorationProvider'; -import { Askpass } from './askpass'; +import { Askpass } from './askpass/askpass'; import { toDisposable, filterEvent, eventToPromise } from './util'; import TelemetryReporter from 'vscode-extension-telemetry'; import { GitExtension } from './api/git'; From 15b09b951a6fc5e5546ac8b8d57463a6c89bc41e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 21 Nov 2019 14:59:20 +0100 Subject: [PATCH 184/246] fix ordering #85218 --- src/vs/editor/contrib/gotoSymbol/goToCommands.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index ae759078b26..2c7b9cccefb 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -303,7 +303,8 @@ registerEditorAction(class PeekDefinitionAction extends DefinitionAction { weight: KeybindingWeight.EditorContrib }, menuOpts: { - menuId: MenuId.EditorContextPeek + menuId: MenuId.EditorContextPeek, + order: 2 } }); CommandsRegistry.registerCommandAlias('editor.action.previewDeclaration', PeekDefinitionAction.id); @@ -388,7 +389,8 @@ registerEditorAction(class PeekDeclarationAction extends DeclarationAction { EditorContextKeys.isInEmbeddedEditor.toNegated() ), menuOpts: { - menuId: MenuId.EditorContextPeek + menuId: MenuId.EditorContextPeek, + order: 3 } }); } @@ -473,7 +475,8 @@ registerEditorAction(class PeekTypeDefinitionAction extends TypeDefinitionAction EditorContextKeys.isInEmbeddedEditor.toNegated() ), menuOpts: { - menuId: MenuId.EditorContextPeek + menuId: MenuId.EditorContextPeek, + order: 4 } }); } @@ -563,7 +566,8 @@ registerEditorAction(class PeekImplementationAction extends ImplementationAction weight: KeybindingWeight.EditorContrib }, menuOpts: { - menuId: MenuId.EditorContextPeek + menuId: MenuId.EditorContextPeek, + order: 5 } }); } @@ -646,7 +650,8 @@ registerEditorAction(class PeekReferencesAction extends ReferencesAction { EditorContextKeys.isInEmbeddedEditor.toNegated() ), menuOpts: { - menuId: MenuId.EditorContextPeek + menuId: MenuId.EditorContextPeek, + order: 6 } }); } From 772bc18acbddad78159c3e92044328293503e428 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 21 Nov 2019 15:49:50 +0100 Subject: [PATCH 185/246] debug config manager: more asyn await fixes #83334 --- .../browser/debugConfigurationManager.ts | 195 +++++++++--------- 1 file changed, 102 insertions(+), 93 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 0c4a83d2cb4..eb1eb008da0 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -13,7 +13,6 @@ import * as resources from 'vs/base/common/resources'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ITextModel } from 'vs/editor/common/model'; import { IEditor } from 'vs/workbench/common/editor'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -35,6 +34,7 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { CancellationToken } from 'vs/base/common/cancellation'; import { withUndefinedAsNull } from 'vs/base/common/types'; +import { sequence } from 'vs/base/common/async'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { first } from 'vs/base/common/arrays'; @@ -65,7 +65,6 @@ export class ConfigurationManager implements IConfigurationManager { @IInstantiationService private readonly instantiationService: IInstantiationService, @ICommandService private readonly commandService: ICommandService, @IStorageService private readonly storageService: IStorageService, - @ILifecycleService lifecycleService: ILifecycleService, @IExtensionService private readonly extensionService: IExtensionService, @IContextKeyService contextKeyService: IContextKeyService, @IHistoryService historyService: IHistoryService @@ -75,7 +74,7 @@ export class ConfigurationManager implements IConfigurationManager { this.debuggers = []; this.toDispose = []; this.initLaunches(); - this.registerListeners(lifecycleService); + this.registerListeners(); const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); const previousSelectedLaunch = this.launches.filter(l => l.uri.toString() === previousSelectedRoot).pop(); this.debugConfigurationTypeContext = CONTEXT_DEBUG_CONFIGURATION_TYPE.bindTo(contextKeyService); @@ -191,31 +190,31 @@ export class ConfigurationManager implements IConfigurationManager { return providers.length > 0; } - resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise { - return this.activateDebuggers('onDebugResolve', type).then(() => { - // pipe the config through the promises sequentially. Append at the end the '*' types - const providers = this.configProviders.filter(p => p.type === type && p.resolveDebugConfiguration) - .concat(this.configProviders.filter(p => p.type === '*' && p.resolveDebugConfiguration)); + async resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, config: IConfig, token: CancellationToken): Promise { + await this.activateDebuggers('onDebugResolve', type); + // pipe the config through the promises sequentially. Append at the end the '*' types + const providers = this.configProviders.filter(p => p.type === type && p.resolveDebugConfiguration) + .concat(this.configProviders.filter(p => p.type === '*' && p.resolveDebugConfiguration)); - return providers.reduce((promise, provider) => { - return promise.then(config => { - if (config) { - return provider.resolveDebugConfiguration!(folderUri, config, token); - } else { - return Promise.resolve(config); - } - }); - }, Promise.resolve(debugConfiguration)); - }); + let result: IConfig | null | undefined = config; + await sequence(providers.map(provider => async () => { + // If any provider returned undefined or null make sure to respect that and do not pass the result to more resolver + if (result) { + result = await provider.resolveDebugConfiguration!(folderUri, result, token); + } + })); + + return result; } - provideDebugConfigurations(folderUri: uri | undefined, type: string, token: CancellationToken): Promise { - return this.activateDebuggers('onDebugInitialConfigurations') - .then(() => Promise.all(this.configProviders.filter(p => p.type === type && p.provideDebugConfigurations).map(p => p.provideDebugConfigurations!(folderUri, token))) - .then(results => results.reduce((first, second) => first.concat(second), []))); + async provideDebugConfigurations(folderUri: uri | undefined, type: string, token: CancellationToken): Promise { + await this.activateDebuggers('onDebugInitialConfigurations'); + const results = await Promise.all(this.configProviders.filter(p => p.type === type && p.provideDebugConfigurations).map(p => p.provideDebugConfigurations!(folderUri, token))); + + return results.reduce((first, second) => first.concat(second), []); } - private registerListeners(lifecycleService: ILifecycleService): void { + private registerListeners(): void { debuggersExtPoint.setHandler((extensions, delta) => { delta.added.forEach(added => { added.value.forEach(rawAdapter => { @@ -389,57 +388,54 @@ export class ConfigurationManager implements IConfigurationManager { return this.debuggers.filter(dbg => strings.equalsIgnoreCase(dbg.type, type)).pop(); } - guessDebugger(type?: string): Promise { + async guessDebugger(type?: string): Promise { if (type) { const adapter = this.getDebugger(type); return Promise.resolve(adapter); } const activeTextEditorWidget = this.editorService.activeTextEditorWidget; - let candidates: Promise | undefined; + let candidates: Debugger[] | undefined; if (isCodeEditor(activeTextEditorWidget)) { const model = activeTextEditorWidget.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 Promise.resolve(adapters[0]); + return adapters[0]; } if (adapters.length > 1) { - candidates = Promise.resolve(adapters); + candidates = adapters; } } if (!candidates) { - candidates = this.activateDebuggers('onDebugInitialConfigurations').then(() => this.debuggers.filter(dbg => dbg.hasInitialConfiguration() || dbg.hasConfigurationProvider())); + await this.activateDebuggers('onDebugInitialConfigurations'); + candidates = this.debuggers.filter(dbg => dbg.hasInitialConfiguration() || dbg.hasConfigurationProvider()); } - return candidates.then(debuggers => { - debuggers.sort((first, second) => first.label.localeCompare(second.label)); - const picks = debuggers.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; - }); - }); + 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; + }); } - activateDebuggers(activationEvent: string, debugType?: string): Promise { - const thenables: Promise[] = [ + async activateDebuggers(activationEvent: string, debugType?: string): Promise { + const promises: Promise[] = [ this.extensionService.activateByEvent(activationEvent), this.extensionService.activateByEvent('onDebug') ]; if (debugType) { - thenables.push(this.extensionService.activateByEvent(`${activationEvent}:${debugType}`)); + promises.push(this.extensionService.activateByEvent(`${activationEvent}:${debugType}`)); } - return Promise.all(thenables).then(_ => { - return undefined; - }); + await Promise.all(promises); } private setSelectedLaunchName(selectedName: string | undefined): void { @@ -536,54 +532,57 @@ class Launch extends AbstractLaunch implements ILaunch { return this.configurationService.inspect('launch', { resource: this.workspace.uri }).workspaceFolder; } - openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string, token?: CancellationToken): Promise<{ editor: IEditor | null, created: boolean }> { + async openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string, token?: CancellationToken): Promise<{ editor: IEditor | null, created: boolean }> { const resource = this.uri; let created = false; - - return this.fileService.readFile(resource).then(content => content.value, err => { + let content = ''; + try { + const fileContent = await this.fileService.readFile(resource); + content = fileContent.value.toString(); + } catch { // launch.json not found: create one by collecting launch configs from debugConfigProviders - return this.configurationManager.guessDebugger(type).then(adapter => { - if (adapter) { - return this.configurationManager.provideDebugConfigurations(this.workspace.uri, adapter.type, token || CancellationToken.None).then(initialConfigs => { - return adapter.getInitialConfigurationContent(initialConfigs); - }); - } else { - return ''; - } - }).then(content => { - - if (!content) { - return ''; - } + const adapter = await this.configurationManager.guessDebugger(type); + if (adapter) { + const initialConfigs = await this.configurationManager.provideDebugConfigurations(this.workspace.uri, adapter.type, token || CancellationToken.None); + content = await adapter.getInitialConfigurationContent(initialConfigs); + } + if (content) { created = true; // pin only if config file is created #8727 - return this.textFileService.write(resource, content).then(() => content); - }); - }).then(content => { - if (!content) { - return { editor: null, created: false }; - } - const contentValue = content.toString(); - const index = contentValue.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`); - let startLineNumber = 1; - for (let i = 0; i < index; i++) { - if (contentValue.charAt(i) === '\n') { - startLineNumber++; + try { + await this.textFileService.write(resource, content); + } catch (error) { + throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error.message)); } } - const selection = startLineNumber > 1 ? { startLineNumber, startColumn: 4 } : undefined; + } - return Promise.resolve(this.editorService.openEditor({ - resource, - options: { - selection, - preserveFocus, - pinned: created, - revealIfVisible: true - }, - }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor: withUndefinedAsNull(editor), created }))); - }, (error: Error) => { - throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error.message)); + if (content === '') { + return { editor: null, created: false }; + } + + const index = content.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`); + let startLineNumber = 1; + for (let i = 0; i < index; i++) { + if (content.charAt(i) === '\n') { + startLineNumber++; + } + } + const selection = startLineNumber > 1 ? { startLineNumber, startColumn: 4 } : undefined; + + const editor = await this.editorService.openEditor({ + resource, + options: { + selection, + preserveFocus, + pinned: created, + revealIfVisible: true + }, + }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + + return ({ + editor: withUndefinedAsNull(editor), + created }); } } @@ -613,11 +612,17 @@ class WorkspaceLaunch extends AbstractLaunch implements ILaunch { return this.configurationService.inspect('launch').workspace; } - openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Promise<{ editor: IEditor | null, created: boolean }> { - return this.editorService.openEditor({ + async openConfigFile(sideBySide: boolean, preserveFocus: boolean): Promise<{ editor: IEditor | null, created: boolean }> { + + const editor = await this.editorService.openEditor({ resource: this.contextService.getWorkspace().configuration!, options: { preserveFocus } - }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor: withUndefinedAsNull(editor), created: false })); + }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + + return ({ + editor: withUndefinedAsNull(editor), + created: false + }); } } @@ -650,7 +655,11 @@ class UserLaunch extends AbstractLaunch implements ILaunch { return this.configurationService.inspect('launch').user; } - openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Promise<{ editor: IEditor | null, created: boolean }> { - return this.preferencesService.openGlobalSettings(false, { preserveFocus }).then(editor => ({ editor: withUndefinedAsNull(editor), created: false })); + async openConfigFile(_: boolean, preserveFocus: boolean): Promise<{ editor: IEditor | null, created: boolean }> { + const editor = await this.preferencesService.openGlobalSettings(false, { preserveFocus }); + return ({ + editor: withUndefinedAsNull(editor), + created: false + }); } } From 320413335b73378f0698f0661d36fe8f2bc2b681 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 21 Nov 2019 15:59:39 +0100 Subject: [PATCH 186/246] fix caches --- .../services/themes/common/colorThemeData.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index f539067684e..230e44d700f 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -55,7 +55,7 @@ export class ColorThemeData implements IColorTheme { private colorMap: IColorMap = {}; private customColorMap: IColorMap = {}; - private tokenStylingRules: TokenStylingRule[] | undefined = undefined; + private tokenStylingRules: TokenStylingRule[] | undefined = undefined; // undefined if the theme has no tokenStylingRules section private customTokenStylingRules: TokenStylingRule[] = []; private themeTokenScopeMatchers: Matcher[] | undefined; @@ -305,6 +305,7 @@ export class ColorThemeData implements IColorTheme { this.tokenColorIndex = undefined; this.textMateThemingRules = undefined; + this.customTokenScopeMatchers = undefined; } private overwriteCustomColors(colors: IColorCustomizations) { @@ -318,7 +319,7 @@ export class ColorThemeData implements IColorTheme { public setCustomTokenColors(customTokenColors: ITokenColorCustomizations) { this.customTokenColors = []; - this.customTokenScopeMatchers = undefined; + // first add the non-theme specific settings this.addCustomTokenColors(customTokenColors); @@ -330,6 +331,7 @@ export class ColorThemeData implements IColorTheme { this.tokenColorIndex = undefined; this.textMateThemingRules = undefined; + this.customTokenScopeMatchers = undefined; } public setCustomTokenStyleRules(tokenStylingRules: IExperimentalTokenStyleCustomizations) { @@ -342,7 +344,6 @@ export class ColorThemeData implements IColorTheme { } this.tokenColorIndex = undefined; - this.textMateThemingRules = undefined; } private addCustomTokenColors(customTokenColors: ITokenColorCustomizations) { @@ -383,9 +384,7 @@ export class ColorThemeData implements IColorTheme { return Promise.resolve(undefined); } this.themeTokenColors = []; - this.themeTokenScopeMatchers = undefined; - this.tokenColorIndex = undefined; - this.textMateThemingRules = undefined; + this.clearCaches(); const result = { colors: {}, From ff72b6103a82585ac2a6c9476a3261e055749f47 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 21 Nov 2019 12:09:44 +0100 Subject: [PATCH 187/246] colortheme: style metadata tests --- .../themes/browser/workbenchThemeService.ts | 2 + .../services/themes/common/colorThemeData.ts | 109 +++++++++++------- .../tokenStyleResolving.test.ts | 30 ++++- 3 files changed, 96 insertions(+), 45 deletions(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 046764e60c3..df7c4cfd196 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -359,6 +359,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { const themeData = data; return themeData.ensureLoaded(this.extensionResourceLoaderService).then(_ => { if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { + this.currentColorTheme.clearCaches(); // the loaded theme is identical to the perisisted theme. Don't need to send an event. this.currentColorTheme = themeData; themeData.setCustomColors(this.colorCustomizations); @@ -415,6 +416,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } addClasses(this.container, newTheme.id); + this.currentColorTheme.clearCaches(); this.currentColorTheme = newTheme; if (!this.themingParticipantChangeListener) { this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(_ => this.updateDynamicCSSRules(this.currentColorTheme)); diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index a6572751d5f..f539067684e 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -61,6 +61,7 @@ export class ColorThemeData implements IColorTheme { private themeTokenScopeMatchers: Matcher[] | undefined; private customTokenScopeMatchers: Matcher[] | undefined; + private textMateThemingRules: ITextMateThemingRule[] | undefined = undefined; // created on demand private tokenColorIndex: TokenColorIndex | undefined = undefined; // created on demand private constructor(id: string, label: string, settingsId: string) { @@ -71,38 +72,41 @@ export class ColorThemeData implements IColorTheme { } get tokenColors(): ITextMateThemingRule[] { - const result: ITextMateThemingRule[] = []; + if (!this.textMateThemingRules) { + const result: ITextMateThemingRule[] = []; - // the default rule (scope empty) is always the first rule. Ignore all other default rules. - const foreground = this.getColor(editorForeground) || this.getDefault(editorForeground)!; - const background = this.getColor(editorBackground) || this.getDefault(editorBackground)!; - result.push({ - settings: { - foreground: Color.Format.CSS.formatHexA(foreground), - background: Color.Format.CSS.formatHexA(background) - } - }); - - let hasDefaultTokens = false; - - function addRule(rule: ITextMateThemingRule) { - if (rule.scope && rule.settings) { - if (rule.scope === 'token.info-token') { - hasDefaultTokens = true; + // the default rule (scope empty) is always the first rule. Ignore all other default rules. + const foreground = this.getColor(editorForeground) || this.getDefault(editorForeground)!; + const background = this.getColor(editorBackground) || this.getDefault(editorBackground)!; + result.push({ + settings: { + foreground: Color.Format.CSS.formatHexA(foreground, true), + background: Color.Format.CSS.formatHexA(background, true) + } + }); + + let hasDefaultTokens = false; + + function addRule(rule: ITextMateThemingRule) { + if (rule.scope && rule.settings) { + if (rule.scope === 'token.info-token') { + hasDefaultTokens = true; + } + result.push(rule); } - result.push(rule); } - } - this.themeTokenColors.forEach(addRule); - // Add the custom colors after the theme colors - // so that they will override them - this.customTokenColors.forEach(addRule); + this.themeTokenColors.forEach(addRule); + // Add the custom colors after the theme colors + // so that they will override them + this.customTokenColors.forEach(addRule); - if (!hasDefaultTokens) { - defaultThemeColors[this.type].forEach(addRule); + if (!hasDefaultTokens) { + defaultThemeColors[this.type].forEach(addRule); + } + this.textMateThemingRules = result; } - return result; + return this.textMateThemingRules; } public getColor(colorId: ColorIdentifier, useDefault?: boolean): Color | undefined { @@ -227,21 +231,17 @@ export class ColorThemeData implements IColorTheme { public getTokenStyleMetadata(classification: TokenClassification, useDefault?: boolean): number { const style = this.getTokenStyle(classification, useDefault); - let fontStyle = FontStyle.NotSet; + let fontStyle = FontStyle.None; let foreground = 0; if (style) { - if (style.bold === false && style.underline === false && style.italic === false) { - fontStyle = FontStyle.None; - } else { - if (style.bold) { - fontStyle |= FontStyle.Bold; - } - if (style.underline) { - fontStyle |= FontStyle.Underline; - } - if (style.italic) { - fontStyle |= FontStyle.Italic; - } + if (style.bold) { + fontStyle |= FontStyle.Bold; + } + if (style.underline) { + fontStyle |= FontStyle.Underline; + } + if (style.italic) { + fontStyle |= FontStyle.Italic; } foreground = this.getTokenColorIndex().get(style.foreground); } @@ -304,6 +304,7 @@ export class ColorThemeData implements IColorTheme { } this.tokenColorIndex = undefined; + this.textMateThemingRules = undefined; } private overwriteCustomColors(colors: IColorCustomizations) { @@ -326,6 +327,9 @@ export class ColorThemeData implements IColorTheme { if (types.isObject(themeSpecificTokenColors)) { this.addCustomTokenColors(themeSpecificTokenColors); } + + this.tokenColorIndex = undefined; + this.textMateThemingRules = undefined; } public setCustomTokenStyleRules(tokenStylingRules: IExperimentalTokenStyleCustomizations) { @@ -338,6 +342,7 @@ export class ColorThemeData implements IColorTheme { } this.tokenColorIndex = undefined; + this.textMateThemingRules = undefined; } private addCustomTokenColors(customTokenColors: ITokenColorCustomizations) { @@ -380,6 +385,7 @@ export class ColorThemeData implements IColorTheme { this.themeTokenColors = []; this.themeTokenScopeMatchers = undefined; this.tokenColorIndex = undefined; + this.textMateThemingRules = undefined; const result = { colors: {}, @@ -394,6 +400,13 @@ export class ColorThemeData implements IColorTheme { }); } + public clearCaches() { + this.tokenColorIndex = undefined; + this.textMateThemingRules = undefined; + this.themeTokenScopeMatchers = undefined; + this.customTokenScopeMatchers = undefined; + } + toStorageData() { let colorMapData: { [key: string]: string } = {}; for (let key in this.colorMap) { @@ -774,9 +787,17 @@ function normalizeColorForIndex(color: string | Color): string { } function toMetadata(fontStyle: FontStyle, foreground: ColorId | number, background: ColorId | number) { - return ( - (fontStyle << MetadataConsts.FONT_STYLE_OFFSET) - | (foreground << MetadataConsts.FOREGROUND_OFFSET) - | (background << MetadataConsts.BACKGROUND_OFFSET) - ) >>> 0; + const fontStyleBits = fontStyle << MetadataConsts.FONT_STYLE_OFFSET; + const foregroundBits = foreground << MetadataConsts.FOREGROUND_OFFSET; + const backgroundBits = background << MetadataConsts.BACKGROUND_OFFSET; + if ((fontStyleBits & MetadataConsts.FONT_STYLE_MASK) !== fontStyleBits) { + console.log(`Can not express fontStyle ${fontStyle} in metadata`); + } + if ((backgroundBits & MetadataConsts.BACKGROUND_MASK) !== backgroundBits) { + console.log(`Can not express background ${background} in metadata`); + } + if ((foregroundBits & MetadataConsts.FOREGROUND_MASK) !== foregroundBits) { + console.log(`Can not express foreground ${foreground} in metadata`); + } + return (fontStyleBits | foregroundBits | backgroundBits) >>> 0; } diff --git a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts index ab41c917cb8..f1f8ede9db0 100644 --- a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts +++ b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts @@ -16,6 +16,7 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { ExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/electron-browser/extensionResourceLoaderService'; +import { TokenMetadata, FontStyle } from 'vs/editor/common/modes'; let tokenClassificationRegistry = getTokenClassificationRegistry(); @@ -48,13 +49,40 @@ function assertTokenStyle(actual: TokenStyle | undefined | null, expected: Token assert.equal(tokenStyleAsString(actual), tokenStyleAsString(expected), message); } +function assertTokenStyleMetaData(colorIndex: string[], actual: number, expected: TokenStyle | undefined | null, message?: string) { + if (!expected) { + assert.equal(actual, 0); + return; + } + const actualFontStyle = TokenMetadata.getFontStyle(actual); + assert.equal((actualFontStyle & FontStyle.Bold) === FontStyle.Bold, expected.bold === true, 'bold'); + assert.equal((actualFontStyle & FontStyle.Italic) === FontStyle.Italic, expected.italic === true, 'italic'); + assert.equal((actualFontStyle & FontStyle.Underline) === FontStyle.Underline, expected.underline === true, 'underline'); + + const actualForegroundIndex = TokenMetadata.getForeground(actual); + if (expected.foreground) { + assert.equal(actualForegroundIndex, colorIndex.indexOf(Color.Format.CSS.formatHexA(expected.foreground, true).toUpperCase()), 'foreground'); + } else { + assert.equal(actualForegroundIndex, 0, 'foreground'); + } + const actualBackgroundIndex = TokenMetadata.getBackground(actual); + assert.equal(actualBackgroundIndex, 0, 'background'); +} + + function assertTokenStyles(themeData: ColorThemeData, expected: { [qualifiedClassifier: string]: TokenStyle }) { + const colorIndex = themeData.tokenColorMap; + for (let qualifiedClassifier in expected) { const classification = tokenClassificationRegistry.getTokenClassificationFromString(qualifiedClassifier); assert.ok(classification, 'Classification not found'); const tokenStyle = themeData.getTokenStyle(classification!); - assertTokenStyle(tokenStyle, expected[qualifiedClassifier], qualifiedClassifier); + const expectedTokenStyle = expected[qualifiedClassifier]; + assertTokenStyle(tokenStyle, expectedTokenStyle, qualifiedClassifier); + + const tokenStyleMetaData = themeData.getTokenStyleMetadata(classification!); + assertTokenStyleMetaData(colorIndex, tokenStyleMetaData, expectedTokenStyle); } } From 5b70dcfb8ee31f0caa163577c7693ba0f06ff7f4 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 21 Nov 2019 12:10:10 +0100 Subject: [PATCH 188/246] use token color map in textmate service --- .../textMate/browser/abstractTextMateService.ts | 14 +++++++++----- .../services/textMate/common/TMGrammarFactory.ts | 4 ++-- .../textMate/electron-browser/textMateService.ts | 12 ++++++------ .../textMate/electron-browser/textMateWorker.ts | 4 ++-- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts index 0b4e3be1f52..a782f0cd7ac 100644 --- a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts +++ b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts @@ -10,6 +10,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import * as resources from 'vs/base/common/resources'; import * as types from 'vs/base/common/types'; +import { equals as equalArray } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; import { IState, ITokenizationSupport, LanguageId, TokenMetadata, TokenizationRegistry, StandardTokenType, LanguageIdentifier } from 'vs/editor/common/modes'; @@ -44,6 +45,7 @@ export abstract class AbstractTextMateService extends Disposable implements ITex private _grammarFactory: TMGrammarFactory | null; private _tokenizersRegistrations: IDisposable[]; protected _currentTheme: IRawTheme | null; + protected _currentTokenColorMap: string[] | null; constructor( @IModeService private readonly _modeService: IModeService, @@ -65,6 +67,7 @@ export abstract class AbstractTextMateService extends Disposable implements ITex this._tokenizersRegistrations = []; this._currentTheme = null; + this._currentTokenColorMap = null; grammarsExtPoint.setHandler((extensions) => { this._grammarDefinitions = null; @@ -245,16 +248,17 @@ export abstract class AbstractTextMateService extends Disposable implements ITex } private _updateTheme(grammarFactory: TMGrammarFactory, colorTheme: IColorTheme, forceUpdate: boolean): void { - if (!forceUpdate && this._currentTheme && AbstractTextMateService.equalsTokenRules(this._currentTheme.settings, colorTheme.tokenColors)) { + if (!forceUpdate && this._currentTheme && this._currentTokenColorMap && AbstractTextMateService.equalsTokenRules(this._currentTheme.settings, colorTheme.tokenColors) && equalArray(this._currentTokenColorMap, colorTheme.tokenColorMap)) { return; } this._currentTheme = { name: colorTheme.label, settings: colorTheme.tokenColors }; - this._doUpdateTheme(grammarFactory, this._currentTheme); + this._currentTokenColorMap = colorTheme.tokenColorMap; + this._doUpdateTheme(grammarFactory, this._currentTheme, this._currentTokenColorMap); } - protected _doUpdateTheme(grammarFactory: TMGrammarFactory, theme: IRawTheme): void { - grammarFactory.setTheme(theme); - let colorMap = AbstractTextMateService._toColorMap(grammarFactory.getColorMap()); + protected _doUpdateTheme(grammarFactory: TMGrammarFactory, theme: IRawTheme, tokenColorMap: string[]): void { + grammarFactory.setTheme(theme, tokenColorMap); + let colorMap = AbstractTextMateService._toColorMap(tokenColorMap); let cssRules = generateTokensCSSForColorMap(colorMap); this._styleElement.innerHTML = cssRules; TokenizationRegistry.setColorMap(colorMap); diff --git a/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts b/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts index 38e3101c34b..0a6c9abffe9 100644 --- a/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts +++ b/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts @@ -102,8 +102,8 @@ export class TMGrammarFactory extends Disposable { return this._languageToScope2[languageId] ? true : false; } - public setTheme(theme: IRawTheme): void { - this._grammarRegistry.setTheme(theme); + public setTheme(theme: IRawTheme, colorMap: string[]): void { + this._grammarRegistry.setTheme(theme, colorMap); } public getColorMap(): string[] { diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts index 6a4a4d4d2e2..f33593da728 100644 --- a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts +++ b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts @@ -206,18 +206,18 @@ export class TextMateService extends AbstractTextMateService { return; } this._workerProxy = proxy; - if (this._currentTheme) { - this._workerProxy.acceptTheme(this._currentTheme); + if (this._currentTheme && this._currentTokenColorMap) { + this._workerProxy.acceptTheme(this._currentTheme, this._currentTokenColorMap); } this._modelService.getModels().forEach((model) => this._onModelAdded(model)); }); } } - protected _doUpdateTheme(grammarFactory: TMGrammarFactory, theme: IRawTheme): void { - super._doUpdateTheme(grammarFactory, theme); - if (this._currentTheme && this._workerProxy) { - this._workerProxy.acceptTheme(this._currentTheme); + protected _doUpdateTheme(grammarFactory: TMGrammarFactory, theme: IRawTheme, colorMap: string[]): void { + super._doUpdateTheme(grammarFactory, theme, colorMap); + if (this._currentTheme && this._currentTokenColorMap && this._workerProxy) { + this._workerProxy.acceptTheme(this._currentTheme, this._currentTokenColorMap); } } diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts b/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts index 6f78679f1fd..6f2725ba9c6 100644 --- a/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts +++ b/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts @@ -185,9 +185,9 @@ export class TextMateWorker { return this._grammarCache[languageId]; } - public acceptTheme(theme: IRawTheme): void { + public acceptTheme(theme: IRawTheme, colorMap: string[]): void { if (this._grammarFactory) { - this._grammarFactory.setTheme(theme); + this._grammarFactory.setTheme(theme, colorMap); } } From 3b0571d8c2b24a17ca253294d7ecd58650eace40 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 21 Nov 2019 15:59:05 +0100 Subject: [PATCH 189/246] cleanup denodeify from git --- extensions/git/src/git.ts | 14 ++++++-------- extensions/git/src/ipc/ipcServer.ts | 6 ++---- extensions/git/src/util.ts | 19 ++++--------------- 3 files changed, 12 insertions(+), 27 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index a710a539c29..99ab788410e 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; +import { promises as fs, exists } from 'fs'; import * as path from 'path'; import * as os from 'os'; import * as cp from 'child_process'; @@ -11,7 +11,7 @@ import * as which from 'which'; import { EventEmitter } from 'events'; import iconv = require('iconv-lite'); import * as filetype from 'file-type'; -import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter } from './util'; +import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter } from './util'; import { CancellationToken, Progress } from 'vscode'; import { URI } from 'vscode-uri'; import { detectEncoding } from './encoding'; @@ -22,8 +22,6 @@ import { StringDecoder } from 'string_decoder'; // https://github.com/microsoft/vscode/issues/65693 const MAX_CLI_LENGTH = 30000; -const readfile = denodeify(fs.readFile); - export interface IGit { path: string; version: string; @@ -350,7 +348,7 @@ export class Git { let folderPath = path.join(parentPath, folderName); let count = 1; - while (count < 20 && await new Promise(c => fs.exists(folderPath, c))) { + while (count < 20 && await new Promise(c => exists(folderPath, c))) { folderName = `${baseFolderName}-${count++}`; folderPath = path.join(parentPath, folderName); } @@ -1830,7 +1828,7 @@ export class Repository { const mergeMsgPath = path.join(this.repositoryRoot, '.git', 'MERGE_MSG'); try { - const raw = await readfile(mergeMsgPath, 'utf8'); + const raw = await fs.readFile(mergeMsgPath, 'utf8'); return raw.trim(); } catch { return undefined; @@ -1854,7 +1852,7 @@ export class Repository { templatePath = path.join(this.repositoryRoot, templatePath); } - const raw = await readfile(templatePath, 'utf8'); + const raw = await fs.readFile(templatePath, 'utf8'); return raw.trim(); } catch (err) { @@ -1879,7 +1877,7 @@ export class Repository { const gitmodulesPath = path.join(this.root, '.gitmodules'); try { - const gitmodulesRaw = await readfile(gitmodulesPath, 'utf8'); + const gitmodulesRaw = await fs.readFile(gitmodulesPath, 'utf8'); return parseGitmodules(gitmodulesRaw); } catch (err) { if (/ENOENT/.test(err.message)) { diff --git a/extensions/git/src/ipc/ipcServer.ts b/extensions/git/src/ipc/ipcServer.ts index 39d2b59e6f8..332490896cc 100644 --- a/extensions/git/src/ipc/ipcServer.ts +++ b/extensions/git/src/ipc/ipcServer.ts @@ -4,15 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vscode'; -import { denodeify, toDisposable } from '../util'; +import { toDisposable } from '../util'; import * as path from 'path'; import * as http from 'http'; import * as os from 'os'; import * as fs from 'fs'; import * as crypto from 'crypto'; -const randomBytes = denodeify(crypto.randomBytes); - function getIPCHandlePath(nonce: string): string { if (process.platform === 'win32') { return `\\\\.\\pipe\\vscode-git-ipc-${nonce}-sock`; @@ -31,7 +29,7 @@ export interface IIPCHandler { export async function createIPCServer(): Promise { const server = http.createServer(); - const buffer = await randomBytes(20); + const buffer = await new Promise((c, e) => crypto.randomBytes(20, (err, buf) => err ? e(err) : c(buf))); const nonce = buffer.toString('hex'); const ipcHandlePath = getIPCHandlePath(nonce); diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 0722cb16fbd..3110168343b 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -6,7 +6,7 @@ import { Event } from 'vscode'; import { dirname, sep } from 'path'; import { Readable } from 'stream'; -import * as fs from 'fs'; +import { promises as fs, createReadStream } from 'fs'; import * as byline from 'byline'; export function log(...args: any[]): void { @@ -140,25 +140,14 @@ export function groupBy(arr: T[], fn: (el: T) => string): { [key: string]: T[ }, Object.create(null)); } -export function denodeify(fn: Function): (a: A, b: B, c: C) => Promise; -export function denodeify(fn: Function): (a: A, b: B) => Promise; -export function denodeify(fn: Function): (a: A) => Promise; -export function denodeify(fn: Function): (...args: any[]) => Promise; -export function denodeify(fn: Function): (...args: any[]) => Promise { - return (...args) => new Promise((c, e) => fn(...args, (err: any, r: any) => err ? e(err) : c(r))); -} - -export function nfcall(fn: Function, ...args: any[]): Promise { - return new Promise((c, e) => fn(...args, (err: any, r: any) => err ? e(err) : c(r))); -} export async function mkdirp(path: string, mode?: number): Promise { const mkdir = async () => { try { - await nfcall(fs.mkdir, path, mode); + await fs.mkdir(path, mode); } catch (err) { if (err.code === 'EEXIST') { - const stat = await nfcall(fs.stat, path); + const stat = await fs.stat(path); if (stat.isDirectory()) { return; @@ -232,7 +221,7 @@ export function find(array: T[], fn: (t: T) => boolean): T | undefined { export async function grep(filename: string, pattern: RegExp): Promise { return new Promise((c, e) => { - const fileStream = fs.createReadStream(filename, { encoding: 'utf8' }); + const fileStream = createReadStream(filename, { encoding: 'utf8' }); const stream = byline(fileStream); stream.on('data', (line: string) => { if (pattern.test(line)) { From f4f006216850a9cc82b4a0d530a6e85a96af4a5e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 21 Nov 2019 16:41:28 +0100 Subject: [PATCH 190/246] git input box shouldn't handle comments related to #84201 --- extensions/git/package.json | 6 ------ extensions/git/package.nls.json | 1 - extensions/git/src/commands.ts | 3 ++- extensions/git/src/git.ts | 20 +++++--------------- extensions/git/src/repository.ts | 14 +------------- 5 files changed, 8 insertions(+), 36 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 9d7086dbaad..d801ec64f97 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1608,12 +1608,6 @@ "default": "mixed", "description": "%config.untrackedChanges%", "scope": "resource" - }, - "git.restoreCommitTemplateComments": { - "type": "boolean", - "scope": "resource", - "default": true, - "description": "%config.restoreCommitTemplateComments%" } } }, diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index c4821f5decd..5c357b40eb0 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -139,7 +139,6 @@ "config.untrackedChanges.mixed": "All changes, tracked and untracked, appear together and behave equally.", "config.untrackedChanges.separate": "Untracked changes appear separately in the Source Control view. They are also excluded from several actions.", "config.untrackedChanges.hidden": "Untracked changes are hidden and excluded from several actions.", - "config.restoreCommitTemplateComments": "Controls whether to restore commit template comments in the commit input box.", "colors.added": "Color for added resources.", "colors.modified": "Color for modified resources.", "colors.deleted": "Color for deleted resources.", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 432e16b8231..5dd981bbd9c 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1406,6 +1406,7 @@ export class CommandCenter { const message = repository.inputBox.value; const getCommitMessage = async () => { let _message: string | undefined = message; + if (!_message) { let value: string | undefined = undefined; @@ -1430,7 +1431,7 @@ export class CommandCenter { }); } - return _message ? repository.cleanUpCommitEditMessage(_message) : _message; + return _message; }; const didCommit = await this.smartCommit(repository, getCommitMessage, opts); diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 99ab788410e..193907a15bd 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1810,18 +1810,9 @@ export class Repository { } } - cleanupCommitEditMessage(message: string): string { - // If the message is a single line starting with whitespace followed by `#`, just allow it. - if (/^\s*#[^\n]*$/.test(message)) { - return message; - } - - // Else, remove all lines starting with whitespace followed by `#`. - // TODO: Support core.commentChar - return message.replace(/^(\s*#)(.*)$(\n?)/gm, (_, prefix, content, suffix) => { - // https://github.com/microsoft/vscode/issues/84201#issuecomment-552834814 - return /^\d/.test(content) ? `${prefix}${content}${suffix}` : ''; - }).trim(); + // TODO: Support core.commentChar + stripCommitMessageComments(message: string): string { + return message.replace(/^\s*#.*$\n?/gm, '').trim(); } async getMergeMessage(): Promise { @@ -1829,7 +1820,7 @@ export class Repository { try { const raw = await fs.readFile(mergeMsgPath, 'utf8'); - return raw.trim(); + return this.stripCommitMessageComments(raw); } catch { return undefined; } @@ -1853,8 +1844,7 @@ export class Repository { } const raw = await fs.readFile(templatePath, 'utf8'); - return raw.trim(); - + return this.stripCommitMessageComments(raw); } catch (err) { return ''; } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 5f3c9bee464..4edc79262d2 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -844,15 +844,7 @@ export class Repository implements Disposable { return mergeMessage; } - let template = await this.repository.getCommitTemplate(); - - const config = workspace.getConfiguration('git', Uri.file(this.root)); - - if (!config.get('restoreCommitTemplateComments')) { - template = this.cleanUpCommitEditMessage(template); - } - - return template; + return await this.repository.getCommitTemplate(); } getConfigs(): Promise<{ key: string; value: string; }[]> { @@ -1286,10 +1278,6 @@ export class Repository implements Disposable { return await this.run(Operation.GetCommitTemplate, async () => this.repository.getCommitTemplate()); } - cleanUpCommitEditMessage(editMessage: string): string { - return this.repository.cleanupCommitEditMessage(editMessage); - } - async ignore(files: Uri[]): Promise { return await this.run(Operation.Ignore, async () => { const ignoreFile = `${this.repository.root}${path.sep}.gitignore`; From 2f5120fe3482d9967ff49b57b98b6e8d5dc00908 Mon Sep 17 00:00:00 2001 From: Waterpolymer Date: Thu, 21 Nov 2019 10:57:55 -0500 Subject: [PATCH 191/246] Reverted '-' to '*' and removed space --- README.md | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index bde4fa2e82c..0de5f2f71da 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Visual Studio Code - Open Source ("Code - OSS") - [![Build Status](https://dev.azure.com/vscode/VSCode/_apis/build/status/VS%20Code?branchName=master)](https://dev.azure.com/vscode/VSCode/_build/latest?definitionId=12) [![Feature Requests](https://img.shields.io/github/issues/Microsoft/vscode/feature-request.svg)](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) [![Bugs](https://img.shields.io/github/issues/Microsoft/vscode/bug.svg)](https://github.com/Microsoft/vscode/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3Abug) @@ -27,27 +26,27 @@ Visual Studio Code is updated monthly with new features and bug fixes. You can d There are many ways in which you can participate in the project, for example: -- [Submit bugs and feature requests](https://github.com/microsoft/vscode/issues), and help us verify as they are checked in -- Review [source code changes](https://github.com/microsoft/vscode/pulls) -- Review the [documentation](https://github.com/microsoft/vscode-docs) and make pull requests for anything from typos to new content +* [Submit bugs and feature requests](https://github.com/microsoft/vscode/issues), and help us verify as they are checked in +* Review [source code changes](https://github.com/microsoft/vscode/pulls) +* Review the [documentation](https://github.com/microsoft/vscode-docs) and make pull requests for anything from typos to new content If you are interested in fixing issues and contributing directly to the code base, please see the document [How to Contribute](https://github.com/Microsoft/vscode/wiki/How-to-Contribute), which covers the following: -- [How to build and run from source](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#build-and-run) -- [The development workflow, including debugging and running tests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#debugging) -- [Coding guidelines](https://github.com/Microsoft/vscode/wiki/Coding-Guidelines) -- [Submitting pull requests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#pull-requests) -- [Finding an issue to work on](https://github.com/microsoft/vscode/wiki/How-to-Contribute#where-to-contribute) -- [Contributing to translations](https://aka.ms/vscodeloc) +* [How to build and run from source](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#build-and-run) +* [The development workflow, including debugging and running tests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#debugging) +* [Coding guidelines](https://github.com/Microsoft/vscode/wiki/Coding-Guidelines) +* [Submitting pull requests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#pull-requests) +* [Finding an issue to work on](https://github.com/microsoft/vscode/wiki/How-to-Contribute#where-to-contribute) +* [Contributing to translations](https://aka.ms/vscodeloc) ## Feedback -- Ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/vscode) -- [Request a new feature](CONTRIBUTING.md) -- Up vote [popular feature requests](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) -- [File an issue](https://github.com/Microsoft/vscode/issues) -- Follow [@code](https://twitter.com/code) and let us know what you think! +* Ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/vscode) +* [Request a new feature](CONTRIBUTING.md) +* Up vote [popular feature requests](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) +* [File an issue](https://github.com/Microsoft/vscode/issues) +* Follow [@code](https://twitter.com/code) and let us know what you think! ## Related Projects From 8f02213d4e6cbd917c0079bad2ddd536a3484e2f Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 21 Nov 2019 17:09:20 +0100 Subject: [PATCH 192/246] debug: restart frame on hover #84045 --- .../contrib/debug/browser/callStackView.ts | 16 ++++++++++++++-- .../contrib/debug/browser/media/debugViewlet.css | 5 +++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index ef83c73203a..f7fd358b9f7 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -385,6 +385,7 @@ interface IStackFrameTemplateData { fileName: HTMLElement; lineNumber: HTMLElement; label: HighlightedLabel; + actionBar: ActionBar; } class SessionsRenderer implements ITreeRenderer { @@ -481,8 +482,9 @@ class StackFramesRenderer implements ITreeRenderer, index: number, data: IStackFrameTemplateData): void { @@ -490,6 +492,8 @@ class StackFramesRenderer implements ITreeRenderer { + return stackFrame.restart(); + }); + data.actionBar.push(action, { icon: true, label: false }); + } } disposeTemplate(templateData: IStackFrameTemplateData): void { - // noop + templateData.actionBar.dispose(); } } diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index 185b8187d41..d4e91e65cd1 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -148,6 +148,11 @@ display: none; } +.debug-viewlet .debug-call-stack .monaco-list-row:hover .stack-frame.has-actions .file, +.debug-viewlet .debug-call-stack .monaco-list-row.focused .stack-frame.has-actions .file { + display: none; +} + .debug-viewlet .debug-call-stack .monaco-list-row .monaco-action-bar { display: none; } From c1146debe657147bda6f9442435c677a109325b6 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 21 Nov 2019 17:17:21 +0100 Subject: [PATCH 193/246] scm: hash repo pane ids --- src/vs/workbench/contrib/scm/browser/repositoryPane.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index ab1d61454b2..e4fa1369aed 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -53,6 +53,7 @@ import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/th import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { Hasher } from 'vs/base/common/hash'; type TreeElement = ISCMResourceGroup | IResourceNode | ISCMResource; @@ -963,7 +964,10 @@ export class RepositoryViewDescriptor implements IViewDescriptor { constructor(readonly repository: ISCMRepository, readonly hideByDefault: boolean) { const repoId = repository.provider.rootUri ? repository.provider.rootUri.toString() : `#${RepositoryViewDescriptor.counter++}`; - this.id = `scm:repository:${repository.provider.label}:${repoId}`; + const hasher = new Hasher(); + hasher.hash(repository.provider.label); + hasher.hash(repoId); + this.id = `scm:repository:${hasher.value}`; this.name = repository.provider.rootUri ? basename(repository.provider.rootUri) : repository.provider.label; this.ctorDescriptor = { ctor: RepositoryPane, arguments: [repository] }; From 687bb7c070cdcdc0e3af6f97c161ced8da2f9a13 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 21 Nov 2019 08:47:41 -0800 Subject: [PATCH 194/246] Update codicons: add debug stack reload and debug activity bar alternative --- .../ui/codiconLabel/codicon/codicon.css | 9 ++++++--- .../ui/codiconLabel/codicon/codicon.ttf | Bin 47104 -> 47404 bytes 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css index 4dfc8f69330..2e259eda4f2 100644 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css +++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css @@ -5,7 +5,7 @@ @font-face { font-family: "codicon"; - src: url("./codicon.ttf?6e26276ebddb23b7baa20bf099ba8332") format("truetype"); + src: url("./codicon.ttf?c4e66586cd3ad4acc55fc456c0760dec") format("truetype"); } .codicon[class*='codicon-'] { @@ -401,5 +401,8 @@ .codicon-debug-breakpoint-stackframe-focused:before { content: "\eb8b" } .codicon-debug-breakpoint-unsupported:before { content: "\eb8c" } .codicon-symbol-string:before { content: "\eb8d" } -.codicon-debug-reverse-continue:before { content: "\f101" } -.codicon-debug-step-back:before { content: "\f102" } +.codicon-debug-reverse-continue:before { content: "\eb8e" } +.codicon-debug-step-back:before { content: "\eb8f" } +.codicon-debug-restart-frame:before { content: "\eb90" } +.codicon-debug-alternate:before { content: "\eb91" } +.codicon-debug-alt:before { content: "\f101" } diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf index a8ad5305b9440a2b054f59ce7657892883f26aa8..a51c284681e086082aecd8d267d334a25b9bce8f 100644 GIT binary patch delta 4697 zcmXxn34GJ#83*v+o1{sbr0JQqv;}(KEv2Ogr6(;_5f~yORs~w_(iVh*<&bcRh!t^F zWau2~hJxsvbxfI4=F|yuOsk?Iq9P)`FCri+A}jm-c|J=&-~W@Ox$|6Eb;h>)oNaSz z;0r)?1DG_uy?MdBw3Fum=Vl<{-0ZfcvnCC2BmnV8fbOn2Gn;2bZ5a45Z~Hlw&fyg} zrXJ?`mnm&dduP|0X_qha@s0zI-EH%yH=mgrxfO_82iPXJH+L|!+2b|??3@OKy?y`7sWrJ!ye<6{*E)4-Smh7!U;(ee$Cu{! z|36;l3AgbN2!_qNmM7jv<@O9xOG7d+j+eub(X(9*QL6VH)nV`HSHZp2LAR>nzAMiF z+uN{TIhV)i2JC%s*tYY2zAKOE)EjiS-fza4&1Y55L1%8DiT|_VA-s=waR=T|9=r&{ zclR9%RVt3*B)-NBq~I0Yf^Iy4Y~{vv2vsR)#}pjIpYcaLr=oBdmS7c%@H3{V5S+uW zaT(t$rGk|m|HT?yP(gfMb~q3OCtQ3rA$)zF78!Vr!Klw&w5Fap)6>DlC}4>VvD z8gVs7V+_Xf&HoDHF#*?OB5uGW+=$7z2~D^ew_+-q(SqriiCLJ9Ihc!9%tISGFrOdu zZCHp!=)_{&jxH?aZI)ptKVS!*#8cRbr?DH);92|$&!ZPFU=RL+m#`OqWj6f{uVNqmj@R%H zypA{VPrQZwIDmiQZTuVm!8_=~d-wo{@F70J$2g2na0H*?C_ZCuR(*jlaU5Ua1ipdB zDSV65ID@k|k00ek(h9s$Em8Q~FhRRe~ zDqH1X8y?1Q@hB=$g<8~MBF~q`SWUW1umYf$ z3WPE)6RZm8<%0DAy+W{3psmk@SToQo1*-=-AXrDxs|227TrF5z&})Ezbzqr6uN5pf zXtpE^tUKN*ScK5)1HCEWX0+}^taRue!J3DDK(P9u9~8L2xJ9U-o=e-N2=)x5XOfWQO^juIHB;81~a362+-V8KBH6CyZr zVBCVk2gV~fhG4vc0|_Qna5TaA1XzV8j4zi5ShZ`y1qT;Qgy0B+i4+`WFj0bI4JKM} zz`^(hM;%Oz;LwAK6&!ysaRS>J2M9c7eO&Q^(-9^?a8AM`3QkOzfr7IWW{}_%g&8b3 zPhpY-Co4>{;EaVy5uCO#se*GCCXE6-a0J7o3l3wL48gGslPNf$VX_2AHB7eP(1yto z9N#dxf`c3;PjIBedWm;zxfjR7-+7l;!drciM9!wePJ$5jsS;d2V5$Wd5ttgm zl?0|%a5;gg6I@eZMhY%0F!h3~3rqu_-!$;uf5Sh6u|`! zW~$&S2Qy7@se@@2T<>671o|1L3&p21Gla5P^*@srNI4j13FTy*EtHpWj!>bDbA|FT zwhF}_V&(}I&e$eY1Y^5Uk&GQeMKjK~@cUPOo>?GN4C8G=#WF4w*6$^=NT>mfokGPk zE*2_*afwigjJFFlkg-dsL5xd<8q642#sjG&#^pjKGp-OSjqwhl(iv9@mBGjtZ&8_y ztAxs8TrE^K;~K&J8D_2ECJnPr1h`bg+$p$T!>kuvv|;WNT)AO32rl0+cMH@qZWLU| zVeS!J&0)F)ta7_oa9xMlB)GW4+=s!|0o>tXHVba^ILj=tpsZ&~+@e5^dzyQl`w{nU z_sj0L+(+En6Y0tDjP%U%tni%j^m`k zqcDUehE;}jgl!Ak8+I(*7hW2^F~S`&GNLjvq;#et zbz0{DYrazZ{G{cuMF=Iu>@yxs|WCe<{HfJ5nj?d1? zo}9fU`(XBo9A8dB&W4;lxg&EQ&$H#t%G;XvO1>{YC%-iRctJzKjDq)v#1GjpME6poiSbC)N zLRoa#;c{23 zRYg^8Rfnr%s++2}R3ELe)r_g>s@YU?w6>u3`Px&pm+HKAiFJ8(>*@~FogY~{a&Nu2 zzPf%{{hsr2pSa?NZmxey`7G;q!f;cMjm4ndjoAi?){Ywlztf zoq%cwaOc#&ySx|*El_4 zo4b4|u(g^`us6+}-@N~Fb_38^0|a(GwL7Ua9g5GTHNm&d>JJA8zH<6*p``e7%&K_|XN zmP+8e^ELj1DM;k|G7+orN2Du%+=2iVkNYtR2hfeb;AItz$Iy&Nk&Rz)uj-5QScCuJ z0LuM8TX+Ub(o52n2s5kiF(Y!Y&4(|b1;|h?*o{J z`B;F3c#zLogvEFWOYjIhSc+CG!*Z;^O0?l|tVTPYKnI?j&5M5uYvIK@tjE*Xh(F+2 zJcrGA9)H3X{25#E0=Dtve-SU?6>P_=cnv%7SNsjH;|=V>-|;5i!rSP2>DnPkZpbAnUDpZB3a225@#NN>u|@8aCh= z{2rT7gkqGU4CSa$eQ*QD_%gfvy6gJclm|{`16=;rk@pS)bgi&*QP&A(3-lDhyn&u7 zm_g9f1ak>`y1+Y(GX(PsdZu8eLDviB9Q5Q_JP^|ldbVI9Lf@ZLPIwTrZ)5f!6b)XD3H$hpkU%dFA})L`>Z3< z6VF@L3ni8V^b*0sfPO@dYNE(K`$39G-y8N z&2a{Me&P8_fqNO-1d9;bdR4@7gkCL}+tBTTB?|qdV6j45j}h4T^ix9lF|HLXWoWNp zQA4i-;;aMf8`{bW!q1HBh4S&9cD_9_kGEbBtaNDW<0aNS^ftljhwc*W1JExDlysjA zcv`U~Kpzrp4$vP8wh3q}1&Iv<`eVV?0ex7oi9mlM*iN922sRezPX${H^ig58Sba>e z?LZ#~Jl2672>LU@UIcwYuq#1-F4&)-PYQM_=r06&7W65>?gjm&U>}1%E!feZzY^?i z&}Rg@oCn&2}1V@wnOM|1e+eT7HpBwMzC2zpA&4G(BBF+Q0VUj zTPgJS7A7sbD)f25{t9g+46)NfUli=Q&_4)vU+5o&HRtqC!kTT`$`N93hPD!z*rlN_ z3HEE~%YvPoMRSD*ViSk9GLNpmva38NHg;$$n~5zR`d7hb4{c>LvF)?D3Jw4mX0wHF zE)3h7g+l?xM{qpA_zDjv1xz2onE~S`I6YukY8K8B7=OWu0@F`$w!qj0rwoikaNfW; z1t$+ofZz;*aS5u4^9v?SaFW4<3(ho{2*K$F z6Dc_7V4?&k9!!6M=d8ELEwGs}T5ur3#0ZW?m{`Ff2@@wcE@1`;4o;YO!4V3RAUI56 z5{0!@nIyph3zJM99yn`ZQUs?iOse4gg&8O~iDA+NXEIE>;BMOj3xw*=xKJoJ#|77Im{kI0jH?A#aF}+13dSb{*K%Co zhi>75jte&azr+JD#@iY--;-;fXrE_qwXd~5Yk$q&WAC+JbwoLe9pfE)9EY90PPa3| zImy}T>~tP=>VVpSj({C5KUbn_v}=Z|!`0>5@46b87FZqF6u2pHZ%|awhM+^i?%;93 zox!I--mkKGx2HEwO(o&l)?+6SDB zkBTph-x&W|{K16$gj*BlC2UEwC3Ym9P6|oNPnwXlC~0fb)#TLVDajj?kEb|ON>Un9 zHl>_SwWrQY-8HadU{6|Hnx`qPGwt=XuhWy$C#5&1Z%W@iC}L36phJVR2Co_XV@7Jm z*o;jXuV?HXGH=L7LoN=TGPE-@B6DG8PnIjIFsmu+-C?Q2T8DKFJDBaCU7g*Ky*c~% z@T}pr!}sTu<+SCTACWeqcEqk+p(Rsw*~D9H{iKtg2jIxutS%m91)MRYz6VsBxp#jN0FQ r!mYx(hh=v9bk7puG8L|b?9%Nta8?<-aPm=wPOb_J)o From b24775ae6dd719148a299d325466dbec3db0c4cf Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 21 Nov 2019 08:56:06 -0800 Subject: [PATCH 195/246] #84045: Add "debug-restart-frame" icon --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 2 +- src/vs/workbench/contrib/debug/browser/debugToolBar.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index f7fd358b9f7..20029b0ec3b 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -513,7 +513,7 @@ class StackFramesRenderer implements ITreeRenderer { + const action = new Action('debug.callStack.restartFrame', nls.localize('restartFrame', "Restart Frame"), 'codicon-debug-restart-frame', true, () => { return stackFrame.restart(); }); data.actionBar.push(action, { icon: true, label: false }); diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index cf629a37d01..4d8c423dbce 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -377,7 +377,7 @@ registerThemingParticipant((theme, collector) => { const debugIconRestartColor = theme.getColor(debugIconRestartForeground); if (debugIconRestartColor) { - collector.addRule(`.monaco-workbench .codicon-debug-restart { color: ${debugIconRestartColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-debug-restart, .monaco-workbench .codicon-debug-restart-frame { color: ${debugIconRestartColor} !important; }`); } const debugIconStepOverColor = theme.getColor(debugIconStepOverForeground); From 1ca17cd262c53f632a56ccadf92a3d38ee49cfd0 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 21 Nov 2019 09:07:08 -0800 Subject: [PATCH 196/246] Update debug icon to use alternate version --- src/vs/workbench/contrib/debug/browser/debug.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 7b74dcb4912..67ba00c4cd6 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -83,7 +83,7 @@ Registry.as(ViewletExtensions.Viewlets).registerViewlet(Viewlet DebugViewlet, VIEWLET_ID, nls.localize('debug', "Debug"), - 'codicon-debug', + 'codicon-debug-alt', 3 )); From ca855d5fe63338f6996a93f1c90a1a5af8358926 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 21 Nov 2019 11:42:35 -0800 Subject: [PATCH 197/246] Update "Go to file" icons to use icon font --- .../browser/menuEntryActionViewItem.ts | 2 +- .../browser/preferences.contribution.ts | 33 ++++--------------- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 05efd4bd424..77d930aa7fd 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -147,7 +147,7 @@ export class MenuEntryActionViewItem extends ActionViewItem { @INotificationService protected _notificationService: INotificationService, @IContextMenuService _contextMenuService: IContextMenuService ) { - super(undefined, _action, { icon: !!(_action.class || _action.item.iconLocation), label: !_action.class && !_action.item.iconLocation }); + super(undefined, _action, { icon: !!(_action.class || _action.item.iconLocation || _action.item.iconClassName), label: !_action.class && !_action.item.iconLocation && !_action.item.iconClassName }); this._altKey = AlternativeKeyEmitter.getInstance(_contextMenuService); } diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index cd52141866d..6d38111b94b 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -39,7 +39,6 @@ import { ExplorerRootContext, ExplorerFolderContext } from 'vs/workbench/contrib import { ILabelService } from 'vs/platform/label/common/label'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; Registry.as(EditorExtensions.Editors).registerEditor( EditorDescriptor.create( @@ -368,8 +367,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -const PREFERENCES_EDITOR_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/preferences/browser/media/preferences-editor-light.svg`)); -const PREFERENCES_EDITOR_DARK_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/preferences/browser/media/preferences-editor-dark.svg`)); class PreferencesActionsContribution extends Disposable implements IWorkbenchContribution { constructor( @@ -384,10 +381,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon command: { id: OpenGlobalKeybindingsAction.ID, title: OpenGlobalKeybindingsAction.LABEL, - iconLocation: { - light: PREFERENCES_EDITOR_LIGHT_ICON_URI, - dark: PREFERENCES_EDITOR_DARK_ICON_URI - } + iconClassName: 'codicon-go-to-file' }, when: ResourceContextKey.Resource.isEqualTo(environmentService.keybindingsResource.toString()), group: 'navigation', @@ -400,10 +394,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon command: { id: commandId, title: OpenSettings2Action.LABEL, - iconLocation: { - light: PREFERENCES_EDITOR_LIGHT_ICON_URI, - dark: PREFERENCES_EDITOR_DARK_ICON_URI - } + iconClassName: 'codicon-go-to-file' }, when: ResourceContextKey.Resource.isEqualTo(environmentService.settingsResource.toString()), group: 'navigation', @@ -441,10 +432,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon command: { id: commandId, title: OpenSettings2Action.LABEL, - iconLocation: { - light: PREFERENCES_EDITOR_LIGHT_ICON_URI, - dark: PREFERENCES_EDITOR_DARK_ICON_URI - } + iconClassName: 'codicon-go-to-file' }, when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.workspaceSettingsResource!.toString()), WorkbenchStateContext.isEqualTo('workspace')), group: 'navigation', @@ -469,10 +457,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon command: { id: commandId, title: OpenSettings2Action.LABEL, - iconLocation: { - light: PREFERENCES_EDITOR_LIGHT_ICON_URI, - dark: PREFERENCES_EDITOR_DARK_ICON_URI - } + iconClassName: 'codicon-go-to-file' }, when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.getFolderSettingsResource(folder.uri)!.toString())), group: 'navigation', @@ -536,10 +521,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: OpenGlobalKeybindingsFileAction.ID, title: OpenGlobalKeybindingsFileAction.LABEL, - iconLocation: { - light: PREFERENCES_EDITOR_LIGHT_ICON_URI, - dark: PREFERENCES_EDITOR_DARK_ICON_URI - } + iconClassName: 'codicon-go-to-file' }, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR), group: 'navigation', @@ -820,10 +802,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, title: nls.localize('openSettingsJson', "Open Settings (JSON)"), - iconLocation: { - dark: PREFERENCES_EDITOR_DARK_ICON_URI, - light: PREFERENCES_EDITOR_LIGHT_ICON_URI - } + iconClassName: 'codicon-go-to-file' }, group: 'navigation', order: 1, From 28909fd2adb3e4f2cf93659d26b451b3b24f6351 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 21 Nov 2019 20:45:13 +0100 Subject: [PATCH 198/246] test-SemanticColoringProvider --- extensions/vscode-colorize-tests/package.json | 8 +++ .../src/colorizerTestMain.ts | 60 +++++++++++++++++++ .../src/typings/ref.d.ts | 4 +- extensions/vscode-colorize-tests/yarn.lock | 5 ++ 4 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 extensions/vscode-colorize-tests/src/colorizerTestMain.ts diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json index 6045e073d02..c9834b05549 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -5,12 +5,20 @@ "publisher": "vscode", "license": "MIT", "private": true, + "activationEvents": [ + "onLanguage:json" + ], + "main": "./out/colorizerTestMain", + "enableProposedApi": true, "engines": { "vscode": "*" }, "scripts": { "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-colorize-tests ./tsconfig.json" }, + "dependencies": { + "jsonc-parser": "2.2.0" + }, "devDependencies": { "@types/node": "^12.11.7", "mocha-junit-reporter": "^1.17.0", diff --git a/extensions/vscode-colorize-tests/src/colorizerTestMain.ts b/extensions/vscode-colorize-tests/src/colorizerTestMain.ts new file mode 100644 index 00000000000..b18343005ee --- /dev/null +++ b/extensions/vscode-colorize-tests/src/colorizerTestMain.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as jsoncParser from 'jsonc-parser'; + +export function activate(context: vscode.ExtensionContext): any { + + const tokenModifiers = ['static', 'abstract', 'deprecated']; + const tokenTypes = ['strings', 'types', 'structs', 'classes', 'functions', 'variables']; + const legend = new vscode.SemanticColoringLegend(tokenTypes, tokenModifiers); + + /* + * A certain token (at index `i` is encoded using 5 uint32 integers): + * - at index `5*i` - `deltaLine`: token line number, relative to `SemanticColoringArea.line` + * - at index `5*i+1` - `startCharacter`: token start character offset inside the line (inclusive) + * - at index `5*i+2` - `endCharacter`: token end character offset inside the line (exclusive) + * - at index `5*i+3` - `tokenType`: will be looked up in `SemanticColoringLegend.tokenTypes` + * - at index `5*i+4` - `tokenModifiers`: each set bit will be looked up in `SemanticColoringLegend.tokenModifiers` + */ + + const semanticHighlightProvider: vscode.SemanticColoringProvider = { + provideSemanticColoring(document: vscode.TextDocument): vscode.ProviderResult { + const result: number[] = []; + + const visitor: jsoncParser.JSONVisitor = { + onObjectProperty: (property: string, _offset: number, length: number, startLine: number, startCharacter: number) => { + result.push(startLine); + result.push(startCharacter); + result.push(startCharacter + length); + + + const segments = property.split('.'); + let tokenType = legend.tokenTypes.indexOf(segments[0]); + if (tokenType === -1) { + tokenType = 0; + } + result.push(tokenType); + + let tokenModifiers = 0; + for (let i = 1; i < segments.length; i++) { + const index = legend.tokenTypes.indexOf(segments[0]); + if (index !== -1) { + tokenModifiers = tokenModifiers | 1 << index; + } + } + result.push(tokenModifiers); + } + }; + jsoncParser.visit(document.getText(), visitor); + return new vscode.SemanticColoring([new vscode.SemanticColoringArea(0, new Uint32Array(result))]); + } + }; + + + context.subscriptions.push(vscode.languages.registerSemanticColoringProvider({ pattern: 'color-test.json' }, semanticHighlightProvider, legend)); + +} diff --git a/extensions/vscode-colorize-tests/src/typings/ref.d.ts b/extensions/vscode-colorize-tests/src/typings/ref.d.ts index a45a0c6353f..a17099ac50c 100644 --- a/extensions/vscode-colorize-tests/src/typings/ref.d.ts +++ b/extensions/vscode-colorize-tests/src/typings/ref.d.ts @@ -3,5 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/// +/// /// -/// + diff --git a/extensions/vscode-colorize-tests/yarn.lock b/extensions/vscode-colorize-tests/yarn.lock index 23ac03a3948..c6b3fdb4313 100644 --- a/extensions/vscode-colorize-tests/yarn.lock +++ b/extensions/vscode-colorize-tests/yarn.lock @@ -1042,6 +1042,11 @@ json3@3.3.2: resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= +jsonc-parser@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.0.tgz#f206f87f9d49d644b7502052c04e82dd6392e9ef" + integrity sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA== + jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" From 0df48c6af799f8df0ae5fddc6c3e73cd0c40fa8c Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 21 Nov 2019 12:02:20 -0800 Subject: [PATCH 199/246] fixes #85284 --- src/vs/workbench/contrib/search/browser/searchPanel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchPanel.ts b/src/vs/workbench/contrib/search/browser/searchPanel.ts index 54cf8e628d1..559f023dee7 100644 --- a/src/vs/workbench/contrib/search/browser/searchPanel.ts +++ b/src/vs/workbench/contrib/search/browser/searchPanel.ts @@ -31,7 +31,7 @@ export class SearchPanel extends Panel { } create(parent: HTMLElement): void { - dom.addClasses(parent, 'monaco-panel-view', 'search-panel'); + dom.addClasses(parent, 'monaco-pane-view', 'search-panel'); this.searchView.render(); dom.append(parent, this.searchView.element); this.searchView.setExpanded(true); From f965609a87e86ca5034d62fe25a8eb16dca60ad3 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 21 Nov 2019 13:34:19 -0800 Subject: [PATCH 200/246] Fix #85336, adjust tree padding for file icons --- src/vs/workbench/browser/parts/views/media/views.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/views/media/views.css b/src/vs/workbench/browser/parts/views/media/views.css index fb062a7a3af..43fa0eb55da 100644 --- a/src/vs/workbench/browser/parts/views/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -32,7 +32,7 @@ .file-icon-themable-tree.hide-arrows .monaco-tl-twistie:not(.force-twistie) { background-image: none !important; width: 0 !important; - margin-right: 0 !important; + padding-right: 0 !important; visibility: hidden; } From 1ef501369f1982795730ecd0b8f1cc2243bd10ea Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 21 Nov 2019 09:31:50 -0800 Subject: [PATCH 201/246] exclude system status bar from visual viewport in standalone mode. --- src/vs/base/browser/dom.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index ba6ea61595a..ee0abdc87b8 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -506,7 +506,13 @@ export function getClientArea(element: HTMLElement): Dimension { // If visual view port exits and it's on mobile, it should be used instead of window innerWidth / innerHeight, or document.body.clientWidth / document.body.clientHeight if (platform.isIOS && (window).visualViewport) { - return new Dimension((window).visualViewport.width, (window).visualViewport.height); + const width = (window).visualViewport.height; + const height = (window).visualViewport.height - ( + browser.isStandalone + ? 20 // in PWA mode, the system statusbar is counted into the visual viewport. 20 is the default size of iOS statusbar. + : 0 // in non PWA mode, the visual viewport is awalys smaller than the brower tab (which doesn't include system statusbar, browser header, etc) + ); + return new Dimension(width, height); } // Try innerWidth / innerHeight From 6b21839efdaaa74c55bd56e95e86322f2d02005a Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 21 Nov 2019 14:48:33 -0800 Subject: [PATCH 202/246] Update lightbulb gesture clicking. --- src/vs/base/browser/dom.ts | 15 ++++++++++++--- .../editor/browser/controller/pointerHandler.ts | 4 ++++ .../editor/contrib/codeAction/lightBulbWidget.ts | 5 ++++- src/vs/workbench/browser/style.ts | 9 +++++++-- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index ee0abdc87b8..7557be2bac3 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -267,6 +267,12 @@ export let addStandardDisposableListener: IAddStandardDisposableListenerSignatur return addDisposableListener(node, type, wrapHandler, useCapture); }; +export let addStandardDisposableGenericMouseDownListner = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable { + let wrapHandler = _wrapAsStandardMouseEvent(handler); + + return addDisposableGenericMouseDownListner(node, wrapHandler, useCapture); +}; + export function addDisposableGenericMouseDownListner(node: EventTarget, handler: (event: any) => void, useCapture?: boolean): IDisposable { return addDisposableListener(node, platform.isIOS && BrowserFeatures.pointerEvents ? EventType.POINTER_DOWN : EventType.MOUSE_DOWN, handler, useCapture); } @@ -506,11 +512,14 @@ export function getClientArea(element: HTMLElement): Dimension { // If visual view port exits and it's on mobile, it should be used instead of window innerWidth / innerHeight, or document.body.clientWidth / document.body.clientHeight if (platform.isIOS && (window).visualViewport) { - const width = (window).visualViewport.height; + const width = (window).visualViewport.width; const height = (window).visualViewport.height - ( browser.isStandalone - ? 20 // in PWA mode, the system statusbar is counted into the visual viewport. 20 is the default size of iOS statusbar. - : 0 // in non PWA mode, the visual viewport is awalys smaller than the brower tab (which doesn't include system statusbar, browser header, etc) + // in PWA mode, the visual viewport always includes the safe-area-inset-bottom (which is for the home indicator) + // even when you are using the onscreen monitor, the visual viewport will include the area between system statusbar and the onscreen keyboard + // plus the area between onscreen keyboard and the bottom bezel, which is 20px on iOS. + ? (20 + 8) // body margin is 8px + : 0 ); return new Dimension(width, height); } diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index f1a997e1a4d..46894f1be15 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -226,6 +226,10 @@ export class PointerEventHandler extends MouseHandler { } private onTap(event: GestureEvent): void { + if (!event.initialTarget || !this.viewHelper.linesContentDomNode.contains(event.initialTarget)) { + return; + } + event.preventDefault(); this.viewHelper.focusTextArea(); const target = this._createMouseTarget(new EditorMouseEvent(event, this.viewHelper.viewDomNode), false); diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 5512a63a60f..e01343ff2b5 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -17,6 +17,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { editorLightBulbForeground, editorLightBulbAutoFixForeground } from 'vs/platform/theme/common/colorRegistry'; +import { Gesture } from 'vs/base/browser/touch'; namespace LightBulbState { @@ -71,7 +72,9 @@ export class LightBulbWidget extends Disposable implements IContentWidget { this.hide(); } })); - this._register(dom.addStandardDisposableListener(this._domNode, 'mousedown', e => { + + Gesture.ignoreTarget(this._domNode); + this._register(dom.addStandardDisposableGenericMouseDownListner(this._domNode, e => { if (this.state.type !== LightBulbState.Type.Showing) { return; } diff --git a/src/vs/workbench/browser/style.ts b/src/vs/workbench/browser/style.ts index d38931efc44..ea118c2511a 100644 --- a/src/vs/workbench/browser/style.ts +++ b/src/vs/workbench/browser/style.ts @@ -8,9 +8,9 @@ import 'vs/css!./media/style'; import { registerThemingParticipant, ITheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; import { iconForeground, foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; import { WORKBENCH_BACKGROUND, TITLE_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme'; -import { isWeb } from 'vs/base/common/platform'; +import { isWeb, isIOS } from 'vs/base/common/platform'; import { createMetaElement } from 'vs/base/browser/dom'; -import { isSafari } from 'vs/base/browser/browser'; +import { isSafari, isStandalone } from 'vs/base/browser/browser'; registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { @@ -184,4 +184,9 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { } `); } + + // Update body background color to ensure the home indicator area looks similar to the workbench + if (isIOS && isStandalone) { + collector.addRule(`body { background-color: ${workbenchBackground}; }`); + } }); From 9197d42531103ca6c07a9766819bcb6c2ee2bfc1 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 21 Nov 2019 15:38:54 -0800 Subject: [PATCH 203/246] 4px margin for home indicator --- src/vs/base/browser/dom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 7557be2bac3..e8a190d2a7d 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -518,7 +518,7 @@ export function getClientArea(element: HTMLElement): Dimension { // in PWA mode, the visual viewport always includes the safe-area-inset-bottom (which is for the home indicator) // even when you are using the onscreen monitor, the visual viewport will include the area between system statusbar and the onscreen keyboard // plus the area between onscreen keyboard and the bottom bezel, which is 20px on iOS. - ? (20 + 8) // body margin is 8px + ? (20 + 4) // + 4px for body margin : 0 ); return new Dimension(width, height); From 5534051e276539983d54cc1063c7508f3209a78c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 21 Nov 2019 11:14:00 -0800 Subject: [PATCH 204/246] codeActionWidget -> codeActionMenu --- .../codeAction/{codeActionWidget.ts => codeActionMenu.ts} | 2 +- src/vs/editor/contrib/codeAction/codeActionUi.ts | 6 +++--- .../codeAction/test/codeActionKeybindingResolver.test.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/vs/editor/contrib/codeAction/{codeActionWidget.ts => codeActionMenu.ts} (99%) diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionMenu.ts similarity index 99% rename from src/vs/editor/contrib/codeAction/codeActionWidget.ts rename to src/vs/editor/contrib/codeAction/codeActionMenu.ts index 4e85aa712d2..a170112a977 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionMenu.ts @@ -43,7 +43,7 @@ export interface CodeActionShowOptions { readonly includeDisabledActions: boolean; } -export class CodeActionWidget extends Disposable { +export class CodeActionMenu extends Disposable { private _visible: boolean = false; private readonly _showingActions = this._register(new MutableDisposable()); diff --git a/src/vs/editor/contrib/codeAction/codeActionUi.ts b/src/vs/editor/contrib/codeAction/codeActionUi.ts index 61f1a9b29dd..a62e1c67b76 100644 --- a/src/vs/editor/contrib/codeAction/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/codeActionUi.ts @@ -16,13 +16,13 @@ import { MessageController } from 'vs/editor/contrib/message/messageController'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { CodeActionsState } from './codeActionModel'; -import { CodeActionWidget, CodeActionShowOptions } from './codeActionWidget'; +import { CodeActionMenu, CodeActionShowOptions } from './codeActionMenu'; import { LightBulbWidget } from './lightBulbWidget'; import { CodeActionAutoApply, CodeActionTrigger } from './types'; export class CodeActionUi extends Disposable { - private readonly _codeActionWidget: Lazy; + private readonly _codeActionWidget: Lazy; private readonly _lightBulbWidget: Lazy; private readonly _activeCodeActions = this._register(new MutableDisposable()); @@ -39,7 +39,7 @@ export class CodeActionUi extends Disposable { super(); this._codeActionWidget = new Lazy(() => { - return this._register(new CodeActionWidget(this._editor, contextMenuService, keybindingService, { + return this._register(new CodeActionMenu(this._editor, contextMenuService, keybindingService, { onSelectCodeAction: async (action) => { this.delegate.applyCodeAction(action, /* retrigger */ true); } diff --git a/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts index 44314a90860..9a939303ece 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts @@ -8,7 +8,7 @@ import { ChordKeybinding, KeyCode, SimpleKeybinding } from 'vs/base/common/keyCo import { OperatingSystem } from 'vs/base/common/platform'; import { refactorCommandId, organizeImportsCommandId } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; -import { CodeActionKeybindingResolver } from 'vs/editor/contrib/codeAction/codeActionWidget'; +import { CodeActionKeybindingResolver } from 'vs/editor/contrib/codeAction/codeActionMenu'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; From 7d179d04aee96928fe2a41683943b416e942d169 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 21 Nov 2019 11:28:33 -0800 Subject: [PATCH 205/246] Use InstantiationService --- src/vs/editor/contrib/codeAction/codeActionCommands.ts | 2 +- src/vs/editor/contrib/codeAction/codeActionMenu.ts | 4 ++-- src/vs/editor/contrib/codeAction/codeActionUi.ts | 10 ++++------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index c51443c95c3..8185eca6b17 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -106,7 +106,7 @@ export class QuickFixController extends Disposable implements IEditorContributio } } } - }, contextMenuService, keybindingService)) + }, this._instantiationService)) ); } diff --git a/src/vs/editor/contrib/codeAction/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/codeActionMenu.ts index a170112a977..c2cdc9c4f0f 100644 --- a/src/vs/editor/contrib/codeAction/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/codeActionMenu.ts @@ -52,9 +52,9 @@ export class CodeActionMenu extends Disposable { constructor( private readonly _editor: ICodeEditor, - private readonly _contextMenuService: IContextMenuService, - keybindingService: IKeybindingService, private readonly _delegate: CodeActionWidgetDelegate, + @IContextMenuService private readonly _contextMenuService: IContextMenuService, + @IKeybindingService keybindingService: IKeybindingService, ) { super(); diff --git a/src/vs/editor/contrib/codeAction/codeActionUi.ts b/src/vs/editor/contrib/codeAction/codeActionUi.ts index a62e1c67b76..5c34b32fe7f 100644 --- a/src/vs/editor/contrib/codeAction/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/codeActionUi.ts @@ -13,12 +13,11 @@ import { IPosition } from 'vs/editor/common/core/position'; import { CodeAction } from 'vs/editor/common/modes'; import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; import { MessageController } from 'vs/editor/contrib/message/messageController'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { CodeActionsState } from './codeActionModel'; import { CodeActionMenu, CodeActionShowOptions } from './codeActionMenu'; import { LightBulbWidget } from './lightBulbWidget'; import { CodeActionAutoApply, CodeActionTrigger } from './types'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class CodeActionUi extends Disposable { @@ -33,13 +32,12 @@ export class CodeActionUi extends Disposable { private readonly delegate: { applyCodeAction: (action: CodeAction, regtriggerAfterApply: boolean) => Promise }, - @IContextMenuService contextMenuService: IContextMenuService, - @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService instantiationService: IInstantiationService, ) { super(); this._codeActionWidget = new Lazy(() => { - return this._register(new CodeActionMenu(this._editor, contextMenuService, keybindingService, { + return this._register(instantiationService.createInstance(CodeActionMenu, this._editor, { onSelectCodeAction: async (action) => { this.delegate.applyCodeAction(action, /* retrigger */ true); } @@ -47,7 +45,7 @@ export class CodeActionUi extends Disposable { }); this._lightBulbWidget = new Lazy(() => { - const widget = this._register(new LightBulbWidget(this._editor, quickFixActionId, preferredFixActionId, keybindingService)); + const widget = this._register(instantiationService.createInstance(LightBulbWidget, this._editor, quickFixActionId, preferredFixActionId)); this._register(widget.onClick(e => this.showCodeActionList(e.actions, e, { includeDisabledActions: false }))); return widget; }); From f775aea9ad90528671a8708d942193bd39cb1a75 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 21 Nov 2019 13:51:16 -0800 Subject: [PATCH 206/246] Remove mime extension point --- .../workbench/contrib/customEditor/browser/extensionPoint.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/extensionPoint.ts b/src/vs/workbench/contrib/customEditor/browser/extensionPoint.ts index 5e169d1b2c4..2a126dabefe 100644 --- a/src/vs/workbench/contrib/customEditor/browser/extensionPoint.ts +++ b/src/vs/workbench/contrib/customEditor/browser/extensionPoint.ts @@ -53,10 +53,6 @@ const webviewEditorsContribution: IJSONSchema = { type: 'string', description: nls.localize('contributes.selector.filenamePattern', 'Glob that the custom editor is enabled for.'), }, - mime: { - type: 'string', - description: nls.localize('contributes.selector.mime', 'Glob that matches the mime type of a data uri resource.'), - } } } }, From 7d120c2d3e7c2c773cd9d2c4e26232e8aa9e3c88 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 21 Nov 2019 15:48:16 -0800 Subject: [PATCH 207/246] Use chaining --- src/vs/workbench/api/common/extHostWebview.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 2f48b4e01ea..3b717043577 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -449,18 +449,12 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { $undoEdits(handle: WebviewPanelHandle, edits: string[]): void { const panel = this.getWebviewPanel(handle); - if (!panel) { - return; - } - panel._undoEdits(edits); + panel?._undoEdits(edits); } $redoEdits(handle: WebviewPanelHandle, edits: string[]): void { const panel = this.getWebviewPanel(handle); - if (!panel) { - return; - } - panel._redoEdits(edits); + panel?._redoEdits(edits); } async $onSave(handle: WebviewPanelHandle): Promise { From e4fc57dcd20a135c79aa3b4ffdca6b996bc8cd0e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 21 Nov 2019 15:49:03 -0800 Subject: [PATCH 208/246] Don't convert json edit data tostring --- src/vs/workbench/api/common/extHostWebview.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 3b717043577..3cf879271d3 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -243,7 +243,7 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa this._capabilities = capabilities; if (capabilities.editingCapability) { this._register(capabilities.editingCapability.onEdit(edit => { - this._proxy.$onEdit(this._handle, JSON.stringify(edit)); + this._proxy.$onEdit(this._handle, edit); })); } } From 00bc7c68dd749356f499504332a1a6e132686605 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 21 Nov 2019 20:09:44 -0800 Subject: [PATCH 209/246] Documentation for webview editors --- src/vs/vscode.proposed.d.ts | 43 +++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 7f26e5267b9..ce948daac9b 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1209,7 +1209,7 @@ declare module 'vscode' { //#region Custom editors, mjbvz /** - * + * Defines how a webview editor interacts with VS Code. */ interface WebviewEditorCapabilities { /** @@ -1224,26 +1224,36 @@ declare module 'vscode' { * * @return Thenable that signals the save is complete. */ - rename?(newResource: Uri): Thenable; + // rename?(newResource: Uri): Thenable; + /** + * Controls the editing functionality of a webview editor. This allows the webview editor to hook into standard + * editor events such as `undo` or `save`. + * + * WebviewEditors that do not have `editingCapability` are considered to be readonly. Users can still interact + * with readonly editors, but these editors will not integrate with VS Code's standard editor functionality. + */ readonly editingCapability?: WebviewEditorEditingCapability; } + /** + * Defines the editing functionality of a webview editor. This allows the webview editor to hook into standard + * editor events such as `undo` or `save`. + */ interface WebviewEditorEditingCapability { /** * Persist the resource. + * + * Extensions should persist the resource + * + * @return Thenable signaling that the save has completed. */ save(): Thenable; /** - * Called when the editor exits. - */ - hotExit(hotExitPath: Uri): Thenable; - - /** - * Signal to VS Code that an edit has occurred. + * Event triggered by extensions to signal to VS Code that an edit has occurred. * - * Edits must be a json serilizable object. + * The edit must be a json serializable object. */ readonly onEdit: Event; @@ -1269,14 +1279,16 @@ declare module 'vscode' { export interface WebviewEditorProvider { /** - * Fills out a `WebviewEditor` for a given resource. + * Resolve a webview editor for a given resource. + * + * To resolve a webview editor, a provider must fill in its initial html content and hook up all + * the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`. * * @param input Information about the resource being resolved. * @param webview Webview being resolved. The provider should take ownership of this webview. * * @return Thenable to a `WebviewEditorCapabilities` indicating that the webview editor has been resolved. * The `WebviewEditorCapabilities` defines how the custom editor interacts with VS Code. - * **❗️Note**: `WebviewEditorCapabilities` is not actually implemented... yet! */ resolveWebviewEditor( input: { @@ -1287,6 +1299,15 @@ declare module 'vscode' { } namespace window { + /** + * Register a new provider for webview editors of a given type. + * + * @param viewType Type of the webview editor provider. + * @param provider Resolves webview editors. + * @param options Content settings for a webview panels the provider is given. + * + * @return Disposable that unregisters the `WebviewEditorProvider`. + */ export function registerWebviewEditorProvider( viewType: string, provider: WebviewEditorProvider, From 599c076d91be1374cf51004cec610f3bcaf4c9cd Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 22 Nov 2019 07:51:52 +0100 Subject: [PATCH 210/246] Fix path --- extensions/git/extension.webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/extension.webpack.config.js b/extensions/git/extension.webpack.config.js index 5efa2052e88..bf6953d3183 100644 --- a/extensions/git/extension.webpack.config.js +++ b/extensions/git/extension.webpack.config.js @@ -13,6 +13,6 @@ module.exports = withDefaults({ context: __dirname, entry: { main: './src/main.ts', - ['askpass-main']: './src/askpass-main.ts' + ['askpass-main']: './src/askpass/askpass-main.ts' } }); From 8fc9c48827330fb43fbbb83a675e1b84e7278cb4 Mon Sep 17 00:00:00 2001 From: Robo Date: Thu, 21 Nov 2019 23:26:17 -0800 Subject: [PATCH 211/246] Rebuild with new electronv6.1.5 (#85357) From a88a14d617c90398d5ed81b4031f028713da5890 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 21 Nov 2019 16:24:06 +0100 Subject: [PATCH 212/246] simplify how alternative commands are defined, #73081 --- .../editor/contrib/gotoSymbol/goToCommands.ts | 48 ++++++------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index 2c7b9cccefb..a0f70710925 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -49,15 +49,16 @@ export interface SymbolNavigationActionConfig { openToSide: boolean; openInPeek: boolean; muteMessage: boolean; + alternativeCommand?: string; } abstract class SymbolNavigationAction extends EditorAction { - private readonly _configuration: SymbolNavigationActionConfig; + private readonly _config: SymbolNavigationActionConfig; constructor(configuration: SymbolNavigationActionConfig, opts: IActionOptions) { super(opts); - this._configuration = configuration; + this._config = configuration; } run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { @@ -83,11 +84,11 @@ abstract class SymbolNavigationAction extends EditorAction { alert(references.ariaMessage); const referenceCount = references.references.length; - const altAction = references.referenceAt(model.uri, pos) && editor.getAction(this._getAlternativeCommand()); + const altAction = references.referenceAt(model.uri, pos) && editor.getAction(this._config.alternativeCommand || ''); if (referenceCount === 0) { // no result -> show message - if (!this._configuration.muteMessage) { + if (!this._config.muteMessage) { const info = model.getWordAtPosition(pos); MessageController.get(editor).showMessage(this._getNoResultFoundMessage(info), pos); } @@ -115,20 +116,18 @@ abstract class SymbolNavigationAction extends EditorAction { protected abstract _getNoResultFoundMessage(info: IWordAtPosition | null): string; - protected abstract _getAlternativeCommand(): string; - protected abstract _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues; private async _onResult(editorService: ICodeEditorService, symbolNavService: ISymbolNavigationService, editor: IActiveCodeEditor, model: ReferencesModel): Promise { const gotoLocation = this._getGoToPreference(editor); - if (this._configuration.openInPeek || (gotoLocation === 'peek' && model.references.length > 1)) { + if (this._config.openInPeek || (gotoLocation === 'peek' && model.references.length > 1)) { this._openInPeek(editor, model); } else { const next = model.firstReference()!; const peek = model.references.length > 1 && gotoLocation === 'gotoAndPeek'; - const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide, !peek); + const targetEditor = await this._openReference(editor, editorService, next, this._config.openToSide, !peek); if (peek && targetEditor) { this._openInPeek(targetEditor, model); } else { @@ -182,7 +181,7 @@ abstract class SymbolNavigationAction extends EditorAction { private _openInPeek(target: ICodeEditor, model: ReferencesModel) { let controller = ReferencesController.get(target); if (controller && target.hasModel()) { - controller.toggleWidget(target.getSelection(), createCancelablePromise(_ => Promise.resolve(model)), this._configuration.openInPeek); + controller.toggleWidget(target.getSelection(), createCancelablePromise(_ => Promise.resolve(model)), this._config.openInPeek); } else { model.dispose(); } @@ -203,10 +202,6 @@ export class DefinitionAction extends SymbolNavigationAction { : nls.localize('generic.noResults', "No definition found"); } - protected _getAlternativeCommand(): string { - return 'editor.action.goToReferences'; - } - protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleDefinitions; } @@ -224,7 +219,8 @@ registerEditorAction(class GoToDefinitionAction extends DefinitionAction { super({ openToSide: false, openInPeek: false, - muteMessage: false + muteMessage: false, + alternativeCommand: 'editor.action.goToReferences' }, { id: GoToDefinitionAction.id, label: nls.localize('actions.goToDecl.label', "Go to Definition"), @@ -327,10 +323,6 @@ class DeclarationAction extends SymbolNavigationAction { : nls.localize('decl.generic.noResults', "No declaration found"); } - protected _getAlternativeCommand(): string { - return 'editor.action.goToReferences'; - } - protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleDeclarations; } @@ -344,7 +336,8 @@ registerEditorAction(class GoToDeclarationAction extends DeclarationAction { super({ openToSide: false, openInPeek: false, - muteMessage: false + muteMessage: false, + alternativeCommand: 'editor.action.goToReferences' }, { id: GoToDeclarationAction.id, label: nls.localize('actions.goToDeclaration.label', "Go to Declaration"), @@ -412,10 +405,6 @@ class TypeDefinitionAction extends SymbolNavigationAction { : nls.localize('goToTypeDefinition.generic.noResults', "No type definition found"); } - protected _getAlternativeCommand(): string { - return 'editor.action.goToReferences'; - } - protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleTypeDefinitions; } @@ -429,7 +418,8 @@ registerEditorAction(class GoToTypeDefinitionAction extends TypeDefinitionAction super({ openToSide: false, openInPeek: false, - muteMessage: false + muteMessage: false, + alternativeCommand: 'editor.action.goToReferences' }, { id: GoToTypeDefinitionAction.ID, label: nls.localize('actions.goToTypeDefinition.label', "Go to Type Definition"), @@ -498,10 +488,6 @@ class ImplementationAction extends SymbolNavigationAction { : nls.localize('goToImplementation.generic.noResults', "No implementation found"); } - protected _getAlternativeCommand(): string { - return ''; - } - protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleImplementations; } @@ -589,10 +575,6 @@ class ReferencesAction extends SymbolNavigationAction { : nls.localize('references.noGeneric', "No references found"); } - protected _getAlternativeCommand(): string { - return ''; - } - protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleReferences; } @@ -694,8 +676,6 @@ class GenericGoToLocationAction extends SymbolNavigationAction { protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return this._gotoMultipleBehaviour ?? editor.getOption(EditorOption.gotoLocation).multipleReferences; } - - protected _getAlternativeCommand() { return ''; } } CommandsRegistry.registerCommand({ From e23cbe05f3ecc7da813446ade5e29aca7f9c3e6b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 22 Nov 2019 10:06:58 +0100 Subject: [PATCH 213/246] fix pattern --- extensions/vscode-colorize-tests/src/colorizerTestMain.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/vscode-colorize-tests/src/colorizerTestMain.ts b/extensions/vscode-colorize-tests/src/colorizerTestMain.ts index b18343005ee..52d30eb44b1 100644 --- a/extensions/vscode-colorize-tests/src/colorizerTestMain.ts +++ b/extensions/vscode-colorize-tests/src/colorizerTestMain.ts @@ -7,6 +7,7 @@ import * as vscode from 'vscode'; import * as jsoncParser from 'jsonc-parser'; export function activate(context: vscode.ExtensionContext): any { + console.log('activates'); const tokenModifiers = ['static', 'abstract', 'deprecated']; const tokenTypes = ['strings', 'types', 'structs', 'classes', 'functions', 'variables']; @@ -24,6 +25,7 @@ export function activate(context: vscode.ExtensionContext): any { const semanticHighlightProvider: vscode.SemanticColoringProvider = { provideSemanticColoring(document: vscode.TextDocument): vscode.ProviderResult { const result: number[] = []; + console.log('provideSemanticColoring'); const visitor: jsoncParser.JSONVisitor = { onObjectProperty: (property: string, _offset: number, length: number, startLine: number, startCharacter: number) => { @@ -55,6 +57,6 @@ export function activate(context: vscode.ExtensionContext): any { }; - context.subscriptions.push(vscode.languages.registerSemanticColoringProvider({ pattern: 'color-test.json' }, semanticHighlightProvider, legend)); + context.subscriptions.push(vscode.languages.registerSemanticColoringProvider({ pattern: '**/color-test.json' }, semanticHighlightProvider, legend)); } From c8dd9fc554bff905570b426ba0d835b7fc66a60b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 22 Nov 2019 11:19:35 +0100 Subject: [PATCH 214/246] Use inspect-port=0 for automatic extension host profiling, #85328 --- .../services/extensions/electron-browser/extensionHost.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 87de5701125..ce56c37a418 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -158,6 +158,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { '--nolazy', (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + portNumber ]; + } else { + opts.execArgv = ['--inspect-port=0']; } const crashReporterOptions = undefined; // TODO@electron pass this in as options to the extension host after verifying this actually works From 266f78096edcbc3e616d96a539bf2c10e153ccd7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 22 Nov 2019 11:36:13 +0100 Subject: [PATCH 215/246] remove smoke test for find ref, rename, goto, and peek, #85377 --- test/smoke/src/areas/editor/editor.test.ts | 36 ---------------------- 1 file changed, 36 deletions(-) diff --git a/test/smoke/src/areas/editor/editor.test.ts b/test/smoke/src/areas/editor/editor.test.ts index 469d5a179b1..8832f070225 100644 --- a/test/smoke/src/areas/editor/editor.test.ts +++ b/test/smoke/src/areas/editor/editor.test.ts @@ -15,24 +15,6 @@ export function setup() { await app.workbench.quickopen.waitForQuickOpenElements(names => names.length >= 6); }); - it(`finds 'All References' to 'app'`, async function () { - const app = this.app as Application; - await app.workbench.quickopen.openFile('www'); - - const references = await app.workbench.editor.findReferences('www', 'app', 7); - - await references.waitForReferencesCountInTitle(3); - await references.waitForReferencesCount(3); - await references.close(); - }); - - it(`renames local 'app' variable`, async function () { - const app = this.app as Application; - await app.workbench.quickopen.openFile('www'); - await app.workbench.editor.rename('www', 7, 'app', 'newApp'); - await app.workbench.editor.waitForEditorContents('www', contents => contents.indexOf('newApp') > -1); - }); - // it('folds/unfolds the code correctly', async function () { // await app.workbench.quickopen.openFile('www'); @@ -48,23 +30,5 @@ export function setup() { // await app.workbench.editor.waitUntilShown(4); // await app.workbench.editor.waitUntilShown(5); // }); - - it(`verifies that 'Go To Definition' works`, async function () { - const app = this.app as Application; - await app.workbench.quickopen.openFile('app.js'); - - await app.workbench.editor.gotoDefinition('app.js', 'app', 14); - - await app.workbench.editor.waitForHighlightingLine('app.js', 11); - }); - - it(`verifies that 'Peek Definition' works`, async function () { - const app = this.app as Application; - await app.workbench.quickopen.openFile('app.js'); - - const peek = await app.workbench.editor.peekDefinition('app.js', 'app', 14); - - await peek.waitForFile('app.js'); - }); }); } From 636ac1f31fffc1bb087b7e4f45748366f2799b2a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 22 Nov 2019 11:50:36 +0100 Subject: [PATCH 216/246] restore insertHighlight-setting, #85376 --- src/vs/editor/common/config/editorOptions.ts | 11 ++++++ .../suggest/suggestRangeHighlighter.ts | 38 ++++++++++--------- src/vs/monaco.d.ts | 4 ++ 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 6baacc81469..3b3869e054a 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2354,6 +2354,10 @@ export interface ISuggestOptions { * Overwrite word ends on accept. Default to false. */ insertMode?: 'insert' | 'replace'; + /** + * Show a highlight when suggestion replaces or keep text after the cursor. Defaults to true. + */ + insertHighlight?: boolean; /** * Enable graceful matching. Defaults to true. */ @@ -2487,6 +2491,7 @@ class EditorSuggest extends BaseEditorOption this._highlight(this._currentItem!)); - } + if (opts.insertHighlight) { + if (!this._shiftKeyListener) { + this._shiftKeyListener = shiftKey.event(() => this._highlight(this._currentItem!)); + } - const info = this._controller.getOverwriteInfo(item, shiftKey.isPressed); - const position = this._controller.editor.getPosition()!; + const info = this._controller.getOverwriteInfo(item, shiftKey.isPressed); + const position = this._controller.editor.getPosition()!; - if (opts.insertMode === 'insert' && info.overwriteAfter > 0) { - // wants inserts but got replace-mode -> highlight AFTER range - newDeco = [{ - range: new Range(position.lineNumber, position.column, position.lineNumber, position.column + info.overwriteAfter), - options: { inlineClassName: 'suggest-insertMode-goes' } - }]; - - } else if (opts.insertMode === 'replace' && info.overwriteAfter === 0) { - // want replace but likely got insert -> highlight AFTER range - const wordInfo = this._controller.editor.getModel()?.getWordAtPosition(position); - if (wordInfo && wordInfo.endColumn > position.column) { + if (opts.insertMode === 'insert' && info.overwriteAfter > 0) { + // wants inserts but got replace-mode -> highlight AFTER range newDeco = [{ - range: new Range(position.lineNumber, position.column, position.lineNumber, wordInfo.endColumn), - options: { inlineClassName: 'suggest-insertMode-stays' } + range: new Range(position.lineNumber, position.column, position.lineNumber, position.column + info.overwriteAfter), + options: { inlineClassName: 'suggest-insertMode-goes' } }]; + + } else if (opts.insertMode === 'replace' && info.overwriteAfter === 0) { + // want replace but likely got insert -> highlight AFTER range + const wordInfo = this._controller.editor.getModel()?.getWordAtPosition(position); + if (wordInfo && wordInfo.endColumn > position.column) { + newDeco = [{ + range: new Range(position.lineNumber, position.column, position.lineNumber, wordInfo.endColumn), + options: { inlineClassName: 'suggest-insertMode-stays' } + }]; + } } } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 5baa27de579..3b858f566f8 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3397,6 +3397,10 @@ declare namespace monaco.editor { * Overwrite word ends on accept. Default to false. */ insertMode?: 'insert' | 'replace'; + /** + * Show a highlight when suggestion replaces or keep text after the cursor. Defaults to true. + */ + insertHighlight?: boolean; /** * Enable graceful matching. Defaults to true. */ From 7c5d94547bbab9c3e642a526a8b723a7fa1d53c1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 21 Nov 2019 16:24:06 +0100 Subject: [PATCH 217/246] simplify how alternative commands are defined, #73081 --- .../editor/contrib/gotoSymbol/goToCommands.ts | 48 ++++++------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index 2c7b9cccefb..a0f70710925 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -49,15 +49,16 @@ export interface SymbolNavigationActionConfig { openToSide: boolean; openInPeek: boolean; muteMessage: boolean; + alternativeCommand?: string; } abstract class SymbolNavigationAction extends EditorAction { - private readonly _configuration: SymbolNavigationActionConfig; + private readonly _config: SymbolNavigationActionConfig; constructor(configuration: SymbolNavigationActionConfig, opts: IActionOptions) { super(opts); - this._configuration = configuration; + this._config = configuration; } run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { @@ -83,11 +84,11 @@ abstract class SymbolNavigationAction extends EditorAction { alert(references.ariaMessage); const referenceCount = references.references.length; - const altAction = references.referenceAt(model.uri, pos) && editor.getAction(this._getAlternativeCommand()); + const altAction = references.referenceAt(model.uri, pos) && editor.getAction(this._config.alternativeCommand || ''); if (referenceCount === 0) { // no result -> show message - if (!this._configuration.muteMessage) { + if (!this._config.muteMessage) { const info = model.getWordAtPosition(pos); MessageController.get(editor).showMessage(this._getNoResultFoundMessage(info), pos); } @@ -115,20 +116,18 @@ abstract class SymbolNavigationAction extends EditorAction { protected abstract _getNoResultFoundMessage(info: IWordAtPosition | null): string; - protected abstract _getAlternativeCommand(): string; - protected abstract _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues; private async _onResult(editorService: ICodeEditorService, symbolNavService: ISymbolNavigationService, editor: IActiveCodeEditor, model: ReferencesModel): Promise { const gotoLocation = this._getGoToPreference(editor); - if (this._configuration.openInPeek || (gotoLocation === 'peek' && model.references.length > 1)) { + if (this._config.openInPeek || (gotoLocation === 'peek' && model.references.length > 1)) { this._openInPeek(editor, model); } else { const next = model.firstReference()!; const peek = model.references.length > 1 && gotoLocation === 'gotoAndPeek'; - const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide, !peek); + const targetEditor = await this._openReference(editor, editorService, next, this._config.openToSide, !peek); if (peek && targetEditor) { this._openInPeek(targetEditor, model); } else { @@ -182,7 +181,7 @@ abstract class SymbolNavigationAction extends EditorAction { private _openInPeek(target: ICodeEditor, model: ReferencesModel) { let controller = ReferencesController.get(target); if (controller && target.hasModel()) { - controller.toggleWidget(target.getSelection(), createCancelablePromise(_ => Promise.resolve(model)), this._configuration.openInPeek); + controller.toggleWidget(target.getSelection(), createCancelablePromise(_ => Promise.resolve(model)), this._config.openInPeek); } else { model.dispose(); } @@ -203,10 +202,6 @@ export class DefinitionAction extends SymbolNavigationAction { : nls.localize('generic.noResults', "No definition found"); } - protected _getAlternativeCommand(): string { - return 'editor.action.goToReferences'; - } - protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleDefinitions; } @@ -224,7 +219,8 @@ registerEditorAction(class GoToDefinitionAction extends DefinitionAction { super({ openToSide: false, openInPeek: false, - muteMessage: false + muteMessage: false, + alternativeCommand: 'editor.action.goToReferences' }, { id: GoToDefinitionAction.id, label: nls.localize('actions.goToDecl.label', "Go to Definition"), @@ -327,10 +323,6 @@ class DeclarationAction extends SymbolNavigationAction { : nls.localize('decl.generic.noResults', "No declaration found"); } - protected _getAlternativeCommand(): string { - return 'editor.action.goToReferences'; - } - protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleDeclarations; } @@ -344,7 +336,8 @@ registerEditorAction(class GoToDeclarationAction extends DeclarationAction { super({ openToSide: false, openInPeek: false, - muteMessage: false + muteMessage: false, + alternativeCommand: 'editor.action.goToReferences' }, { id: GoToDeclarationAction.id, label: nls.localize('actions.goToDeclaration.label', "Go to Declaration"), @@ -412,10 +405,6 @@ class TypeDefinitionAction extends SymbolNavigationAction { : nls.localize('goToTypeDefinition.generic.noResults', "No type definition found"); } - protected _getAlternativeCommand(): string { - return 'editor.action.goToReferences'; - } - protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleTypeDefinitions; } @@ -429,7 +418,8 @@ registerEditorAction(class GoToTypeDefinitionAction extends TypeDefinitionAction super({ openToSide: false, openInPeek: false, - muteMessage: false + muteMessage: false, + alternativeCommand: 'editor.action.goToReferences' }, { id: GoToTypeDefinitionAction.ID, label: nls.localize('actions.goToTypeDefinition.label', "Go to Type Definition"), @@ -498,10 +488,6 @@ class ImplementationAction extends SymbolNavigationAction { : nls.localize('goToImplementation.generic.noResults', "No implementation found"); } - protected _getAlternativeCommand(): string { - return ''; - } - protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleImplementations; } @@ -589,10 +575,6 @@ class ReferencesAction extends SymbolNavigationAction { : nls.localize('references.noGeneric', "No references found"); } - protected _getAlternativeCommand(): string { - return ''; - } - protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleReferences; } @@ -694,8 +676,6 @@ class GenericGoToLocationAction extends SymbolNavigationAction { protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return this._gotoMultipleBehaviour ?? editor.getOption(EditorOption.gotoLocation).multipleReferences; } - - protected _getAlternativeCommand() { return ''; } } CommandsRegistry.registerCommand({ From f75c9b8bf0faee38b18126285f358297f9caebdb Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 22 Nov 2019 11:58:06 +0100 Subject: [PATCH 218/246] connect ModelSemanticColoring to Theme --- .../common/services/modelServiceImpl.ts | 59 +++++++++++-------- .../standalone/browser/standaloneServices.ts | 6 +- .../browser/standaloneThemeServiceImpl.ts | 8 +++ .../test/browser/standaloneLanguages.test.ts | 8 ++- src/vs/platform/theme/common/themeService.ts | 10 ++++ .../theme/test/common/testThemeService.ts | 8 +++ .../terminalColorRegistry.test.ts | 4 +- .../services/themes/common/colorThemeData.ts | 6 +- .../themes/common/workbenchThemeService.ts | 11 ---- .../tokenStyleResolving.test.ts | 13 ++-- 10 files changed, 86 insertions(+), 47 deletions(-) diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 6fbe5274a87..e5c133c4573 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -14,7 +14,7 @@ import { Range } from 'vs/editor/common/core/range'; import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model'; import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel'; import { IModelLanguageChangedEvent, IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; -import { LanguageIdentifier, SemanticColoringProviderRegistry, SemanticColoringProvider, SemanticColoring, FontStyle, MetadataConsts } from 'vs/editor/common/modes'; +import { LanguageIdentifier, SemanticColoringProviderRegistry, SemanticColoringProvider, SemanticColoring, SemanticColoringLegend } from 'vs/editor/common/modes'; import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry'; import { ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -23,6 +23,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { RunOnceScheduler } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { SparseEncodedTokens, MultilineTokens2 } from 'vs/editor/common/model/tokensStore'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; function MODEL_ID(resource: URI): string { return resource.toString(); @@ -118,7 +119,8 @@ export class ModelServiceImpl extends Disposable implements IModelService { constructor( @IConfigurationService configurationService: IConfigurationService, - @ITextResourcePropertiesService resourcePropertiesService: ITextResourcePropertiesService + @ITextResourcePropertiesService resourcePropertiesService: ITextResourcePropertiesService, + @IThemeService themeService: IThemeService ) { super(); this._configurationService = configurationService; @@ -129,7 +131,7 @@ export class ModelServiceImpl extends Disposable implements IModelService { this._configurationServiceSubscription = this._configurationService.onDidChangeConfiguration(e => this._updateModelOptions()); this._updateModelOptions(); - this._register(new SemanticColoringFeature(this)); + this._register(new SemanticColoringFeature(this, themeService)); } private static _readModelOptions(config: IRawConfig, isForSimpleWidget: boolean): ITextModelCreationOptions { @@ -440,11 +442,11 @@ export interface ILineSequence { class SemanticColoringFeature extends Disposable { private _watchers: Record; - constructor(modelService: IModelService) { + constructor(modelService: IModelService, themeService: IThemeService) { super(); this._watchers = Object.create(null); this._register(modelService.onModelAdded((model) => { - this._watchers[model.uri.toString()] = new ModelSemanticColoring(model); + this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService); })); this._register(modelService.onModelRemoved((model) => { this._watchers[model.uri.toString()].dispose(); @@ -460,8 +462,9 @@ class ModelSemanticColoring extends Disposable { private readonly _fetchSemanticTokens: RunOnceScheduler; private _currentResponse: SemanticColoring | null; private _currentRequestCancellationTokenSource: CancellationTokenSource | null; + private _themeService: IThemeService; - constructor(model: ITextModel) { + constructor(model: ITextModel, themeService: IThemeService) { super(); this._isDisposed = false; @@ -469,6 +472,7 @@ class ModelSemanticColoring extends Disposable { this._fetchSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchSemanticTokensNow(), 500)); this._currentResponse = null; this._currentRequestCancellationTokenSource = null; + this._themeService = themeService; this._register(this._model.onDidChangeContent(e => this._fetchSemanticTokens.schedule())); this._register(SemanticColoringProviderRegistry.onDidChange(e => this._fetchSemanticTokens.schedule())); @@ -509,16 +513,16 @@ class ModelSemanticColoring extends Disposable { request.then((res) => { this._currentRequestCancellationTokenSource = null; contentChangeListener.dispose(); - this._setSemanticTokens(res || null, pendingChanges); + this._setSemanticTokens(res || null, provider.getLegend(), pendingChanges); }, (err) => { errors.onUnexpectedError(err); this._currentRequestCancellationTokenSource = null; contentChangeListener.dispose(); - this._setSemanticTokens(null, pendingChanges); + this._setSemanticTokens(null, provider.getLegend(), pendingChanges); }); } - private _setSemanticTokens(tokens: SemanticColoring | null, pendingChanges: IModelContentChangedEvent[]): void { + private _setSemanticTokens(tokens: SemanticColoring | null, legend: SemanticColoringLegend, pendingChanges: IModelContentChangedEvent[]): void { if (this._currentResponse) { this._currentResponse.dispose(); this._currentResponse = null; @@ -540,30 +544,37 @@ class ModelSemanticColoring extends Disposable { for (const area of this._currentResponse.areas) { const srcTokens = area.data; const tokenCount = srcTokens.length / 5; - const destTokens = new Uint32Array(4 * tokenCount); + let destTokens = new Uint32Array(4 * tokenCount); + let destOffset = 0; for (let i = 0; i < tokenCount; i++) { const srcOffset = 5 * i; const deltaLine = srcTokens[srcOffset]; const startCharacter = srcTokens[srcOffset + 1]; const endCharacter = srcTokens[srcOffset + 2]; - // const tokenType = srcTokens[srcOffset + 3]; - // const tokenModifiers = srcTokens[srcOffset + 4]; - // TODO@semantic: map here tokenType and tokenModifiers to metadata + const tokenTypeIndex = srcTokens[srcOffset + 3]; + const tokenType = legend.tokenTypes[tokenTypeIndex]; - const fontStyle = FontStyle.Italic | FontStyle.Bold | FontStyle.Underline; - const foregroundColorId = 3; - const metadata = ( - (fontStyle << MetadataConsts.FONT_STYLE_OFFSET) - | (foregroundColorId << MetadataConsts.FOREGROUND_OFFSET) - ) >>> 0; + const tokenModifierSet = srcTokens[srcOffset + 4]; + let tokenModifiers: string[] = []; + for (let modifierIndex = 0; tokenModifierSet !== 0 && modifierIndex < legend.tokenModifiers.length; modifierIndex++) { + if (tokenModifierSet & 1) { + tokenModifiers.push(legend.tokenTypes[modifierIndex]); + } + } - const destOffset = 4 * i; - destTokens[destOffset] = deltaLine; - destTokens[destOffset + 1] = startCharacter; - destTokens[destOffset + 2] = endCharacter; - destTokens[destOffset + 3] = metadata; + const metadata = this._themeService.getTheme().getTokenStyleMetadata(tokenType, tokenModifiers); + if (metadata !== undefined) { + destTokens[destOffset] = deltaLine; + destTokens[destOffset + 1] = startCharacter; + destTokens[destOffset + 2] = endCharacter; + destTokens[destOffset + 3] = metadata; + destOffset += 4; + } } + if (destOffset !== destTokens.length) { + destTokens = destTokens.subarray(0, destOffset); + } const tokens = new MultilineTokens2(area.line, new SparseEncodedTokens(destTokens)); result.push(tokens); } diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index f03b1e1415e..41fadf14389 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -144,12 +144,12 @@ export module StaticServices { export const modeService = define(IModeService, (o) => new ModeServiceImpl()); - export const modelService = define(IModelService, (o) => new ModelServiceImpl(configurationService.get(o), resourcePropertiesService.get(o))); + export const standaloneThemeService = define(IStandaloneThemeService, () => new StandaloneThemeServiceImpl()); + + export const modelService = define(IModelService, (o) => new ModelServiceImpl(configurationService.get(o), resourcePropertiesService.get(o), standaloneThemeService.get(o))); export const markerDecorationsService = define(IMarkerDecorationsService, (o) => new MarkerDecorationsService(modelService.get(o), markerService.get(o))); - export const standaloneThemeService = define(IStandaloneThemeService, () => new StandaloneThemeServiceImpl()); - export const codeEditorService = define(ICodeEditorService, (o) => new StandaloneCodeEditorServiceImpl(standaloneThemeService.get(o))); export const editorProgressService = define(IEditorProgressService, () => new SimpleEditorProgressService()); diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 692fd2c2958..445bf8a2528 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -129,6 +129,14 @@ class StandaloneTheme implements IStandaloneTheme { } return this._tokenTheme; } + + public getTokenStyleMetadata(type: string, modifiers: string[]): number | undefined { + return undefined; + } + + public get tokenColorMap(): string[] { + return []; + } } function isBuiltinTheme(themeName: string): themeName is BuiltinTheme { diff --git a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts index 456d1652078..12bfb828dff 100644 --- a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts +++ b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts @@ -54,7 +54,13 @@ suite('TokenizationSupport2Adapter', () => { defines: (color: ColorIdentifier): boolean => { throw new Error('Not implemented'); - } + }, + + getTokenStyleMetadata: (type: string, modifiers: string[]): number | undefined => { + return undefined; + }, + + tokenColorMap: [] }; } diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index 42e93300a85..f2b0f08a256 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -59,6 +59,16 @@ export interface ITheme { * default color will be used. */ defines(color: ColorIdentifier): boolean; + + /** + * Returns the token style for a given classification. The result uses the MetadataConsts format + */ + getTokenStyleMetadata(type: string, modifiers: string[]): number | undefined; + + /** + * List of all colors used with tokens. getTokenStyleMetadata references the colors by index into this list. + */ + readonly tokenColorMap: string[]; } export interface IIconTheme { diff --git a/src/vs/platform/theme/test/common/testThemeService.ts b/src/vs/platform/theme/test/common/testThemeService.ts index fd49d393a34..8df0303f8bb 100644 --- a/src/vs/platform/theme/test/common/testThemeService.ts +++ b/src/vs/platform/theme/test/common/testThemeService.ts @@ -23,6 +23,14 @@ export class TestTheme implements ITheme { defines(color: string): boolean { throw new Error('Method not implemented.'); } + + getTokenStyleMetadata(type: string, modifiers: string[]): number | undefined { + return undefined; + } + + get tokenColorMap(): string[] { + return []; + } } export class TestIconTheme implements IIconTheme { diff --git a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts index 10cad170f83..65fcaa47fe2 100644 --- a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts @@ -20,8 +20,8 @@ function getMockTheme(type: ThemeType): ITheme { type: type, getColor: (colorId: ColorIdentifier): Color | undefined => themingRegistry.resolveDefaultColor(colorId, theme), defines: () => true, - getTokenStyle: () => undefined, - resolveScopes: () => undefined + getTokenStyleMetadata: () => undefined, + tokenColorMap: [] }; return theme; diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index 6297355467d..cf284cc3835 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -229,7 +229,11 @@ export class ColorThemeData implements IColorTheme { return this.getTokenColorIndex().asArray(); } - public getTokenStyleMetadata(classification: TokenClassification, useDefault?: boolean): number { + public getTokenStyleMetadata(type: string, modifiers: string[], useDefault?: boolean): number | undefined { + const classification = tokenClassificationRegistry.getTokenClassification(type, modifiers); + if (!classification) { + return undefined; + } const style = this.getTokenStyle(classification, useDefault); let fontStyle = FontStyle.None; let foreground = 0; diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 884d31c80c6..9937ca11be9 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -8,7 +8,6 @@ import { Event } from 'vs/base/common/event'; import { Color } from 'vs/base/common/color'; import { ITheme, IThemeService, IIconTheme } from 'vs/platform/theme/common/themeService'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { TokenClassification } from 'vs/platform/theme/common/tokenClassificationRegistry'; export const IWorkbenchThemeService = createDecorator('themeService'); @@ -33,16 +32,6 @@ export interface IColorTheme extends ITheme { readonly description?: string; readonly isLoaded: boolean; readonly tokenColors: ITextMateThemingRule[]; - - /** - * Returns the token style for a given classification. The result uses the MetadataConsts format - */ - getTokenStyleMetadata(classification: TokenClassification): number; - - /** - * List of all colors used with tokens. getTokenStyleMetadata references the colors by index into this list. - */ - readonly tokenColorMap: string[]; } export interface IColorMap { diff --git a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts index f1f8ede9db0..6f179e407e0 100644 --- a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts +++ b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts @@ -49,9 +49,9 @@ function assertTokenStyle(actual: TokenStyle | undefined | null, expected: Token assert.equal(tokenStyleAsString(actual), tokenStyleAsString(expected), message); } -function assertTokenStyleMetaData(colorIndex: string[], actual: number, expected: TokenStyle | undefined | null, message?: string) { - if (!expected) { - assert.equal(actual, 0); +function assertTokenStyleMetaData(colorIndex: string[], actual: number | undefined, expected: TokenStyle | undefined | null, message?: string) { + if (!expected || !actual) { + assert.equal(actual, expected); return; } const actualFontStyle = TokenMetadata.getFontStyle(actual); @@ -74,14 +74,17 @@ function assertTokenStyles(themeData: ColorThemeData, expected: { [qualifiedClas const colorIndex = themeData.tokenColorMap; for (let qualifiedClassifier in expected) { - const classification = tokenClassificationRegistry.getTokenClassificationFromString(qualifiedClassifier); + const modifiers = qualifiedClassifier.split('.'); + const type = modifiers.shift()!; + + const classification = tokenClassificationRegistry.getTokenClassification(type, modifiers); assert.ok(classification, 'Classification not found'); const tokenStyle = themeData.getTokenStyle(classification!); const expectedTokenStyle = expected[qualifiedClassifier]; assertTokenStyle(tokenStyle, expectedTokenStyle, qualifiedClassifier); - const tokenStyleMetaData = themeData.getTokenStyleMetadata(classification!); + const tokenStyleMetaData = themeData.getTokenStyleMetadata(type, modifiers); assertTokenStyleMetaData(colorIndex, tokenStyleMetaData, expectedTokenStyle); } } From e465e920796e8fc2544187c1a51dad9b1c1b7680 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 22 Nov 2019 12:01:53 +0100 Subject: [PATCH 219/246] Revert "simplify how alternative commands are defined, #73081" This reverts commit a88a14d617c90398d5ed81b4031f028713da5890. --- .../editor/contrib/gotoSymbol/goToCommands.ts | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index a0f70710925..2c7b9cccefb 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -49,16 +49,15 @@ export interface SymbolNavigationActionConfig { openToSide: boolean; openInPeek: boolean; muteMessage: boolean; - alternativeCommand?: string; } abstract class SymbolNavigationAction extends EditorAction { - private readonly _config: SymbolNavigationActionConfig; + private readonly _configuration: SymbolNavigationActionConfig; constructor(configuration: SymbolNavigationActionConfig, opts: IActionOptions) { super(opts); - this._config = configuration; + this._configuration = configuration; } run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { @@ -84,11 +83,11 @@ abstract class SymbolNavigationAction extends EditorAction { alert(references.ariaMessage); const referenceCount = references.references.length; - const altAction = references.referenceAt(model.uri, pos) && editor.getAction(this._config.alternativeCommand || ''); + const altAction = references.referenceAt(model.uri, pos) && editor.getAction(this._getAlternativeCommand()); if (referenceCount === 0) { // no result -> show message - if (!this._config.muteMessage) { + if (!this._configuration.muteMessage) { const info = model.getWordAtPosition(pos); MessageController.get(editor).showMessage(this._getNoResultFoundMessage(info), pos); } @@ -116,18 +115,20 @@ abstract class SymbolNavigationAction extends EditorAction { protected abstract _getNoResultFoundMessage(info: IWordAtPosition | null): string; + protected abstract _getAlternativeCommand(): string; + protected abstract _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues; private async _onResult(editorService: ICodeEditorService, symbolNavService: ISymbolNavigationService, editor: IActiveCodeEditor, model: ReferencesModel): Promise { const gotoLocation = this._getGoToPreference(editor); - if (this._config.openInPeek || (gotoLocation === 'peek' && model.references.length > 1)) { + if (this._configuration.openInPeek || (gotoLocation === 'peek' && model.references.length > 1)) { this._openInPeek(editor, model); } else { const next = model.firstReference()!; const peek = model.references.length > 1 && gotoLocation === 'gotoAndPeek'; - const targetEditor = await this._openReference(editor, editorService, next, this._config.openToSide, !peek); + const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide, !peek); if (peek && targetEditor) { this._openInPeek(targetEditor, model); } else { @@ -181,7 +182,7 @@ abstract class SymbolNavigationAction extends EditorAction { private _openInPeek(target: ICodeEditor, model: ReferencesModel) { let controller = ReferencesController.get(target); if (controller && target.hasModel()) { - controller.toggleWidget(target.getSelection(), createCancelablePromise(_ => Promise.resolve(model)), this._config.openInPeek); + controller.toggleWidget(target.getSelection(), createCancelablePromise(_ => Promise.resolve(model)), this._configuration.openInPeek); } else { model.dispose(); } @@ -202,6 +203,10 @@ export class DefinitionAction extends SymbolNavigationAction { : nls.localize('generic.noResults', "No definition found"); } + protected _getAlternativeCommand(): string { + return 'editor.action.goToReferences'; + } + protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleDefinitions; } @@ -219,8 +224,7 @@ registerEditorAction(class GoToDefinitionAction extends DefinitionAction { super({ openToSide: false, openInPeek: false, - muteMessage: false, - alternativeCommand: 'editor.action.goToReferences' + muteMessage: false }, { id: GoToDefinitionAction.id, label: nls.localize('actions.goToDecl.label', "Go to Definition"), @@ -323,6 +327,10 @@ class DeclarationAction extends SymbolNavigationAction { : nls.localize('decl.generic.noResults', "No declaration found"); } + protected _getAlternativeCommand(): string { + return 'editor.action.goToReferences'; + } + protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleDeclarations; } @@ -336,8 +344,7 @@ registerEditorAction(class GoToDeclarationAction extends DeclarationAction { super({ openToSide: false, openInPeek: false, - muteMessage: false, - alternativeCommand: 'editor.action.goToReferences' + muteMessage: false }, { id: GoToDeclarationAction.id, label: nls.localize('actions.goToDeclaration.label', "Go to Declaration"), @@ -405,6 +412,10 @@ class TypeDefinitionAction extends SymbolNavigationAction { : nls.localize('goToTypeDefinition.generic.noResults', "No type definition found"); } + protected _getAlternativeCommand(): string { + return 'editor.action.goToReferences'; + } + protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleTypeDefinitions; } @@ -418,8 +429,7 @@ registerEditorAction(class GoToTypeDefinitionAction extends TypeDefinitionAction super({ openToSide: false, openInPeek: false, - muteMessage: false, - alternativeCommand: 'editor.action.goToReferences' + muteMessage: false }, { id: GoToTypeDefinitionAction.ID, label: nls.localize('actions.goToTypeDefinition.label', "Go to Type Definition"), @@ -488,6 +498,10 @@ class ImplementationAction extends SymbolNavigationAction { : nls.localize('goToImplementation.generic.noResults', "No implementation found"); } + protected _getAlternativeCommand(): string { + return ''; + } + protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleImplementations; } @@ -575,6 +589,10 @@ class ReferencesAction extends SymbolNavigationAction { : nls.localize('references.noGeneric', "No references found"); } + protected _getAlternativeCommand(): string { + return ''; + } + protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return editor.getOption(EditorOption.gotoLocation).multipleReferences; } @@ -676,6 +694,8 @@ class GenericGoToLocationAction extends SymbolNavigationAction { protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { return this._gotoMultipleBehaviour ?? editor.getOption(EditorOption.gotoLocation).multipleReferences; } + + protected _getAlternativeCommand() { return ''; } } CommandsRegistry.registerCommand({ From 92735e12af51db4ce412aecf4d27856c02ef11f8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 22 Nov 2019 12:07:09 +0100 Subject: [PATCH 220/246] add alternative command settings, #73081 --- src/vs/editor/common/config/editorOptions.ts | 46 +++++++++++++++++-- .../editor/contrib/gotoSymbol/goToCommands.ts | 24 +++++----- src/vs/monaco.d.ts | 8 ++-- 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 3b3869e054a..68c3ab1a604 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1300,15 +1300,20 @@ export type GoToLocationValues = 'peek' | 'gotoAndPeek' | 'goto'; * Configuration options for go to location */ export interface IGotoLocationOptions { - /** - * Control how goto-command work when having multiple results. - */ + multiple?: GoToLocationValues; + multipleDefinitions?: GoToLocationValues; multipleTypeDefinitions?: GoToLocationValues; multipleDeclarations?: GoToLocationValues; multipleImplementations?: GoToLocationValues; multipleReferences?: GoToLocationValues; + + alternativeDefinitionCommand?: string; + alternativeTypeDefinitionCommand?: string; + alternativeDeclarationCommand?: string; + alternativeImplementationCommand?: string; + alternativeReferenceCommand?: string; } export type GoToLocationOptions = Readonly>; @@ -1323,6 +1328,11 @@ class EditorGoToLocation extends BaseEditorOption(input.multipleDeclarations, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleImplementations: input.multipleImplementations ?? EditorStringEnumOption.stringSet(input.multipleImplementations, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleReferences: input.multipleReferences ?? EditorStringEnumOption.stringSet(input.multipleReferences, 'peek', ['peek', 'gotoAndPeek', 'goto']), + alternativeDefinitionCommand: EditorStringOption.string(input.alternativeDefinitionCommand, this.defaultValue.alternativeDefinitionCommand), + alternativeTypeDefinitionCommand: EditorStringOption.string(input.alternativeTypeDefinitionCommand, this.defaultValue.alternativeTypeDefinitionCommand), + alternativeDeclarationCommand: EditorStringOption.string(input.alternativeDeclarationCommand, this.defaultValue.alternativeDeclarationCommand), + alternativeImplementationCommand: EditorStringOption.string(input.alternativeImplementationCommand, this.defaultValue.alternativeImplementationCommand), + alternativeReferenceCommand: EditorStringOption.string(input.alternativeReferenceCommand, this.defaultValue.alternativeReferenceCommand), }; } } diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index 2c7b9cccefb..328eb593053 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -83,7 +83,7 @@ abstract class SymbolNavigationAction extends EditorAction { alert(references.ariaMessage); const referenceCount = references.references.length; - const altAction = references.referenceAt(model.uri, pos) && editor.getAction(this._getAlternativeCommand()); + const altAction = references.referenceAt(model.uri, pos) && editor.getAction(this._getAlternativeCommand(editor)); if (referenceCount === 0) { // no result -> show message @@ -115,7 +115,7 @@ abstract class SymbolNavigationAction extends EditorAction { protected abstract _getNoResultFoundMessage(info: IWordAtPosition | null): string; - protected abstract _getAlternativeCommand(): string; + protected abstract _getAlternativeCommand(editor: IActiveCodeEditor): string; protected abstract _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues; @@ -203,8 +203,8 @@ export class DefinitionAction extends SymbolNavigationAction { : nls.localize('generic.noResults', "No definition found"); } - protected _getAlternativeCommand(): string { - return 'editor.action.goToReferences'; + protected _getAlternativeCommand(editor: IActiveCodeEditor): string { + return editor.getOption(EditorOption.gotoLocation).alternativeDefinitionCommand; } protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { @@ -327,8 +327,8 @@ class DeclarationAction extends SymbolNavigationAction { : nls.localize('decl.generic.noResults', "No declaration found"); } - protected _getAlternativeCommand(): string { - return 'editor.action.goToReferences'; + protected _getAlternativeCommand(editor: IActiveCodeEditor): string { + return editor.getOption(EditorOption.gotoLocation).alternativeDeclarationCommand; } protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { @@ -412,8 +412,8 @@ class TypeDefinitionAction extends SymbolNavigationAction { : nls.localize('goToTypeDefinition.generic.noResults', "No type definition found"); } - protected _getAlternativeCommand(): string { - return 'editor.action.goToReferences'; + protected _getAlternativeCommand(editor: IActiveCodeEditor): string { + return editor.getOption(EditorOption.gotoLocation).alternativeTypeDefinitionCommand; } protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { @@ -498,8 +498,8 @@ class ImplementationAction extends SymbolNavigationAction { : nls.localize('goToImplementation.generic.noResults', "No implementation found"); } - protected _getAlternativeCommand(): string { - return ''; + protected _getAlternativeCommand(editor: IActiveCodeEditor): string { + return editor.getOption(EditorOption.gotoLocation).alternativeImplementationCommand; } protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { @@ -589,8 +589,8 @@ class ReferencesAction extends SymbolNavigationAction { : nls.localize('references.noGeneric', "No references found"); } - protected _getAlternativeCommand(): string { - return ''; + protected _getAlternativeCommand(editor: IActiveCodeEditor): string { + return editor.getOption(EditorOption.gotoLocation).alternativeReferenceCommand; } protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 3b858f566f8..74d50c5031b 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3066,15 +3066,17 @@ declare namespace monaco.editor { * Configuration options for go to location */ export interface IGotoLocationOptions { - /** - * Control how goto-command work when having multiple results. - */ multiple?: GoToLocationValues; multipleDefinitions?: GoToLocationValues; multipleTypeDefinitions?: GoToLocationValues; multipleDeclarations?: GoToLocationValues; multipleImplementations?: GoToLocationValues; multipleReferences?: GoToLocationValues; + alternativeDefinitionCommand?: string; + alternativeTypeDefinitionCommand?: string; + alternativeDeclarationCommand?: string; + alternativeImplementationCommand?: string; + alternativeReferenceCommand?: string; } export type GoToLocationOptions = Readonly>; From 01cc5003514f7d689b269c440f8c026303b9dd57 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 22 Nov 2019 12:12:37 +0100 Subject: [PATCH 221/246] alternative action cannot be action itself, #73081 --- src/vs/editor/contrib/gotoSymbol/goToCommands.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index 328eb593053..f2d7748031a 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -34,7 +34,7 @@ import { EditorOption, GoToLocationValues } from 'vs/editor/common/config/editor import { isStandalone } from 'vs/base/browser/browser'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ScrollType } from 'vs/editor/common/editorCommon'; +import { ScrollType, IEditorAction } from 'vs/editor/common/editorCommon'; import { assertType } from 'vs/base/common/types'; @@ -82,8 +82,15 @@ abstract class SymbolNavigationAction extends EditorAction { alert(references.ariaMessage); + let altAction: IEditorAction | null | undefined; + if (references.referenceAt(model.uri, pos)) { + const altActionId = this._getAlternativeCommand(editor); + if (altActionId !== this.id) { + altAction = editor.getAction(altActionId); + } + } + const referenceCount = references.references.length; - const altAction = references.referenceAt(model.uri, pos) && editor.getAction(this._getAlternativeCommand(editor)); if (referenceCount === 0) { // no result -> show message From da30588bedcdfae054bcee01efc8c5358e20f1d9 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 22 Nov 2019 12:15:42 +0100 Subject: [PATCH 222/246] fix compile errors --- src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts | 3 ++- src/vs/editor/test/common/services/modelService.test.ts | 3 ++- .../electron-browser/api/mainThreadDocumentsAndEditors.test.ts | 3 ++- .../test/electron-browser/api/mainThreadEditors.test.ts | 3 ++- .../test/electron-browser/quickopen.perf.integrationTest.ts | 3 ++- .../test/electron-browser/textsearch.perf.integrationTest.ts | 3 ++- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts index f0eedb8324d..b161849905b 100644 --- a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts @@ -17,6 +17,7 @@ import { provideSelectionRanges } from 'vs/editor/contrib/smartSelect/smartSelec import { CancellationToken } from 'vs/base/common/cancellation'; import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSelections'; import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/modelService.test'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; class MockJSMode extends MockMode { @@ -45,7 +46,7 @@ suite('SmartSelect', () => { setup(() => { const configurationService = new TestConfigurationService(); - modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService)); + modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService()); mode = new MockJSMode(); }); diff --git a/src/vs/editor/test/common/services/modelService.test.ts b/src/vs/editor/test/common/services/modelService.test.ts index 0a28a285b38..8b72a3493c3 100644 --- a/src/vs/editor/test/common/services/modelService.test.ts +++ b/src/vs/editor/test/common/services/modelService.test.ts @@ -16,6 +16,7 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; const GENERATE_TESTS = false; @@ -27,7 +28,7 @@ suite('ModelService', () => { configService.setUserConfiguration('files', { 'eol': '\n' }); configService.setUserConfiguration('files', { 'eol': '\r\n' }, URI.file(platform.isWindows ? 'c:\\myroot' : '/myroot')); - modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService)); + modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService()); }); teardown(() => { diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts index 483d634e6ea..f8d30bbaf7f 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts @@ -20,6 +20,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IFileService } from 'vs/platform/files/common/files'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; suite('MainThreadDocumentsAndEditors', () => { @@ -42,7 +43,7 @@ suite('MainThreadDocumentsAndEditors', () => { deltas.length = 0; const configService = new TestConfigurationService(); configService.setUserConfiguration('editor', { 'detectIndentation': false }); - modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService)); + modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService()); codeEditorService = new TestCodeEditorService(); textFileService = new class extends mock() { isDirty() { return false; } diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts index 35fde4c61de..eba10c94c0d 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -27,6 +27,7 @@ import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/se import { IReference, ImmortalReference } from 'vs/base/common/lifecycle'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { LabelService } from 'vs/workbench/services/label/common/labelService'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; suite('MainThreadEditors', () => { @@ -41,7 +42,7 @@ suite('MainThreadEditors', () => { setup(() => { const configService = new TestConfigurationService(); - modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService)); + modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService()); const codeEditorService = new TestCodeEditorService(); movedResources.clear(); diff --git a/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts index e8161804b96..bd24a27bebe 100644 --- a/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts @@ -30,6 +30,7 @@ import { LocalSearchService } from 'vs/workbench/services/search/node/searchServ import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { TestContextService, TestEditorGroupsService, TestEditorService, TestEnvironmentService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; namespace Timer { export interface ITimerEvent { @@ -73,7 +74,7 @@ suite.skip('QuickOpen performance (integration)', () => { [ITelemetryService, telemetryService], [IConfigurationService, configurationService], [ITextResourcePropertiesService, textResourcePropertiesService], - [IModelService, new ModelServiceImpl(configurationService, textResourcePropertiesService)], + [IModelService, new ModelServiceImpl(configurationService, textResourcePropertiesService, new TestThemeService())], [IWorkspaceContextService, new TestContextService(testWorkspace(URI.file(testWorkspacePath)))], [IEditorService, new TestEditorService()], [IEditorGroupsService, new TestEditorGroupsService()], diff --git a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts index 76aa1c7f8f2..d4eb6ee2728 100644 --- a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts @@ -34,6 +34,7 @@ import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { NullLogService, ILogService } from 'vs/platform/log/common/log'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; declare var __dirname: string; @@ -63,7 +64,7 @@ suite.skip('TextSearch performance (integration)', () => { [ITelemetryService, telemetryService], [IConfigurationService, configurationService], [ITextResourcePropertiesService, textResourcePropertiesService], - [IModelService, new ModelServiceImpl(configurationService, textResourcePropertiesService)], + [IModelService, new ModelServiceImpl(configurationService, textResourcePropertiesService, new TestThemeService())], [IWorkspaceContextService, new TestContextService(testWorkspace(URI.file(testWorkspacePath)))], [IEditorService, new TestEditorService()], [IEditorGroupsService, new TestEditorGroupsService()], From a456e0c04311c64cf3e344f3289a6b63763ef14c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 22 Nov 2019 12:28:21 +0100 Subject: [PATCH 223/246] fix tokenStyleResolving test --- .../themes/test/electron-browser/tokenStyleResolving.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts index 6f179e407e0..566feeda03b 100644 --- a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts +++ b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts @@ -50,8 +50,8 @@ function assertTokenStyle(actual: TokenStyle | undefined | null, expected: Token } function assertTokenStyleMetaData(colorIndex: string[], actual: number | undefined, expected: TokenStyle | undefined | null, message?: string) { - if (!expected || !actual) { - assert.equal(actual, expected); + if (expected === undefined || expected === null || actual === undefined) { + assert.equal(actual, expected, message); return; } const actualFontStyle = TokenMetadata.getFontStyle(actual); From d2408cb8171950eb70c5baca0b12827595408900 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 22 Nov 2019 12:32:00 +0100 Subject: [PATCH 224/246] remove getTokenClassificationFromString --- .../theme/common/tokenClassificationRegistry.ts | 13 ++----------- .../services/themes/common/colorThemeData.ts | 6 ++++-- .../electron-browser/tokenStyleResolving.test.ts | 3 +-- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index 47148a9837d..3fa3e0c9c46 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -109,7 +109,6 @@ export interface ITokenClassificationRegistry { */ registerTokenModifier(id: string, description: string): void; - getTokenClassificationFromString(str: TokenClassificationString): TokenClassification | undefined; getTokenClassification(type: string, modifiers: string[]): TokenClassification | undefined; getTokenStylingRule(classification: TokenClassification, value: TokenStyle): TokenStylingRule; @@ -168,6 +167,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { private tokenStylingSchema: IJSONSchema & { properties: IJSONSchemaMap } = { type: 'object', properties: {}, + additionalProperties: getStylingSchemeEntry(), definitions: { style: { type: 'object', @@ -236,15 +236,6 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { return { type: tokenTypeDesc.num, modifiers: allModifierBits }; } - public getTokenClassificationFromString(str: TokenClassificationString): TokenClassification | undefined { - const parts = str.split('.'); - const type = parts.shift(); - if (type) { - return this.getTokenClassification(type, parts); - } - return undefined; - } - public getTokenStylingRule(classification: TokenClassification, value: TokenStyle): TokenStylingRule { return { classification, matchScore: getTokenStylingScore(classification), value }; } @@ -374,7 +365,7 @@ function getTokenStylingScore(classification: TokenClassification) { return bitCount(classification.modifiers) + ((classification.type !== TOKEN_TYPE_WILDCARD_NUM) ? 1 : 0); } -function getStylingSchemeEntry(description: string, deprecationMessage?: string): IJSONSchema { +function getStylingSchemeEntry(description?: string, deprecationMessage?: string): IJSONSchema { return { description, deprecationMessage, diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index cf284cc3835..aca8cb6d688 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -189,7 +189,8 @@ export class ColorThemeData implements IColorTheme { if (tokenStyleValue === null) { return undefined; } else if (typeof tokenStyleValue === 'string') { - const classification = tokenClassificationRegistry.getTokenClassificationFromString(tokenStyleValue); + const [type, ...modifiers] = tokenStyleValue.split('.'); + const classification = tokenClassificationRegistry.getTokenClassification(type, modifiers); if (classification) { return this.getTokenStyle(classification); } @@ -713,7 +714,8 @@ function getTokenStyle(foreground: string | undefined, fontStyle: string | undef function readCustomTokenStyleRules(tokenStylingRuleSection: IExperimentalTokenStyleCustomizations, result: TokenStylingRule[] = []) { for (let key in tokenStylingRuleSection) { if (key[0] !== '[') { - const classification = tokenClassificationRegistry.getTokenClassificationFromString(key); + const [type, ...modifiers] = key.split('.'); + const classification = tokenClassificationRegistry.getTokenClassification(type, modifiers); if (classification) { const settings = tokenStylingRuleSection[key]; let style: TokenStyle | undefined; diff --git a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts index 566feeda03b..b256164b7b4 100644 --- a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts +++ b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts @@ -74,8 +74,7 @@ function assertTokenStyles(themeData: ColorThemeData, expected: { [qualifiedClas const colorIndex = themeData.tokenColorMap; for (let qualifiedClassifier in expected) { - const modifiers = qualifiedClassifier.split('.'); - const type = modifiers.shift()!; + const [type, ...modifiers] = qualifiedClassifier.split('.'); const classification = tokenClassificationRegistry.getTokenClassification(type, modifiers); assert.ok(classification, 'Classification not found'); From a78363abd0fcbca5f8e1d4e7543cafa8500e9952 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 22 Nov 2019 12:33:22 +0100 Subject: [PATCH 225/246] fix missing entries from global go menu --- src/vs/editor/browser/editorExtensions.ts | 55 ++++++++----------- .../editor/contrib/gotoSymbol/goToCommands.ts | 15 +++-- 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 408881eb438..18bd6370f27 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -44,8 +44,8 @@ export interface ICommandKeybindingsOptions extends IKeybindings { } export interface ICommandMenuOptions { menuId: MenuId; - group?: string; - order?: number; + group: string; + order: number; when?: ContextKeyExpr; title: string; } @@ -194,53 +194,42 @@ export interface IEditorActionContextMenuOptions { group: string; order: number; when?: ContextKeyExpr; + menuId?: MenuId; } -export interface IActionOptions { - id: string; +export interface IActionOptions extends ICommandOptions { label: string; alias: string; - description?: ICommandHandlerDescription; - precondition: ContextKeyExpr | undefined; - kbOpts?: ICommandKeybindingsOptions; - contextMenuOpts?: IEditorActionContextMenuOptions; - menuOpts?: Partial | Partial; + contextMenuOpts?: IEditorActionContextMenuOptions | IEditorActionContextMenuOptions[]; } export abstract class EditorAction extends EditorCommand { private static convertOptions(opts: IActionOptions): ICommandOptions { - function patch(menu: Partial): ICommandMenuOptions { - if (!menu.title) { - menu.title = opts.label; - } - if (!menu.menuId) { - menu.menuId = MenuId.EditorContext; - } - if (!menu.when) { - menu.when = opts.precondition; - } - return menu; - } - let menuOpts: ICommandMenuOptions[]; if (Array.isArray(opts.menuOpts)) { - menuOpts = opts.menuOpts.map(m => patch(m!)); + menuOpts = opts.menuOpts; } else if (opts.menuOpts) { - menuOpts = [patch(opts.menuOpts)]; + menuOpts = [opts.menuOpts]; } else { menuOpts = []; } - if (opts.contextMenuOpts) { - const contextMenuItem = { - title: opts.label, - when: ContextKeyExpr.and(opts.precondition, opts.contextMenuOpts.when), - menuId: MenuId.EditorContext, - group: opts.contextMenuOpts.group, - order: opts.contextMenuOpts.order - }; - menuOpts.push(contextMenuItem); + function withDefaults(item: Partial): ICommandMenuOptions { + if (!item.menuId) { + item.menuId = MenuId.EditorContext; + } + if (!item.title) { + item.title = opts.label; + } + item.when = ContextKeyExpr.and(opts.precondition, item.when); + return item; + } + + if (Array.isArray(opts.contextMenuOpts)) { + menuOpts.push(...opts.contextMenuOpts.map(withDefaults)); + } else if (opts.contextMenuOpts) { + menuOpts.push(withDefaults(opts.contextMenuOpts)); } opts.menuOpts = menuOpts; diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index f2d7748031a..86f229ecc2f 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -309,8 +309,9 @@ registerEditorAction(class PeekDefinitionAction extends DefinitionAction { linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F10 }, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { menuId: MenuId.EditorContextPeek, + group: 'peek', order: 2 } }); @@ -395,8 +396,9 @@ registerEditorAction(class PeekDeclarationAction extends DeclarationAction { PeekContext.notInPeekEditor, EditorContextKeys.isInEmbeddedEditor.toNegated() ), - menuOpts: { + contextMenuOpts: { menuId: MenuId.EditorContextPeek, + group: 'peek', order: 3 } }); @@ -481,8 +483,9 @@ registerEditorAction(class PeekTypeDefinitionAction extends TypeDefinitionAction PeekContext.notInPeekEditor, EditorContextKeys.isInEmbeddedEditor.toNegated() ), - menuOpts: { + contextMenuOpts: { menuId: MenuId.EditorContextPeek, + group: 'peek', order: 4 } }); @@ -572,8 +575,9 @@ registerEditorAction(class PeekImplementationAction extends ImplementationAction primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F12, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { menuId: MenuId.EditorContextPeek, + group: 'peek', order: 5 } }); @@ -656,8 +660,9 @@ registerEditorAction(class PeekReferencesAction extends ReferencesAction { PeekContext.notInPeekEditor, EditorContextKeys.isInEmbeddedEditor.toNegated() ), - menuOpts: { + contextMenuOpts: { menuId: MenuId.EditorContextPeek, + group: 'peek', order: 6 } }); From 1803c7fd764241ddb6eb4fbe6f123169097b1aad Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 22 Nov 2019 13:52:24 +0100 Subject: [PATCH 226/246] fixes for theming semantic tokens --- extensions/vscode-colorize-tests/src/colorizerTestMain.ts | 8 ++++---- src/vs/editor/common/services/modelServiceImpl.ts | 6 ++++-- .../services/themes/browser/workbenchThemeService.ts | 3 +++ src/vs/workbench/services/themes/common/colorThemeData.ts | 6 +++--- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/extensions/vscode-colorize-tests/src/colorizerTestMain.ts b/extensions/vscode-colorize-tests/src/colorizerTestMain.ts index 52d30eb44b1..6557111c496 100644 --- a/extensions/vscode-colorize-tests/src/colorizerTestMain.ts +++ b/extensions/vscode-colorize-tests/src/colorizerTestMain.ts @@ -34,16 +34,16 @@ export function activate(context: vscode.ExtensionContext): any { result.push(startCharacter + length); - const segments = property.split('.'); - let tokenType = legend.tokenTypes.indexOf(segments[0]); + const [type, ...modifiers] = property.split('.'); + let tokenType = legend.tokenTypes.indexOf(type); if (tokenType === -1) { tokenType = 0; } result.push(tokenType); let tokenModifiers = 0; - for (let i = 1; i < segments.length; i++) { - const index = legend.tokenTypes.indexOf(segments[0]); + for (let i = 0; i < modifiers.length; i++) { + const index = legend.tokenModifiers.indexOf(modifiers[i]); if (index !== -1) { tokenModifiers = tokenModifiers | 1 << index; } diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index e5c133c4573..3793c1ae50e 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -476,6 +476,7 @@ class ModelSemanticColoring extends Disposable { this._register(this._model.onDidChangeContent(e => this._fetchSemanticTokens.schedule())); this._register(SemanticColoringProviderRegistry.onDidChange(e => this._fetchSemanticTokens.schedule())); + this._register(themeService.onThemeChange(_ => this._fetchSemanticTokens.schedule())); this._fetchSemanticTokens.schedule(0); } @@ -554,12 +555,13 @@ class ModelSemanticColoring extends Disposable { const tokenTypeIndex = srcTokens[srcOffset + 3]; const tokenType = legend.tokenTypes[tokenTypeIndex]; - const tokenModifierSet = srcTokens[srcOffset + 4]; + let tokenModifierSet = srcTokens[srcOffset + 4]; let tokenModifiers: string[] = []; for (let modifierIndex = 0; tokenModifierSet !== 0 && modifierIndex < legend.tokenModifiers.length; modifierIndex++) { if (tokenModifierSet & 1) { - tokenModifiers.push(legend.tokenTypes[modifierIndex]); + tokenModifiers.push(legend.tokenModifiers[modifierIndex]); } + tokenModifierSet = tokenModifierSet >> 1; } const metadata = this._themeService.getTheme().getTokenStyleMetadata(tokenType, tokenModifiers); diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index df7c4cfd196..c0172a4cdd8 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -364,10 +364,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.currentColorTheme = themeData; themeData.setCustomColors(this.colorCustomizations); themeData.setCustomTokenColors(this.tokenColorCustomizations); + themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations); return Promise.resolve(themeData); } themeData.setCustomColors(this.colorCustomizations); themeData.setCustomTokenColors(this.tokenColorCustomizations); + themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations); this.updateDynamicCSSRules(themeData); return this.applyTheme(themeData, settingsTarget); }, error => { @@ -380,6 +382,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { await this.currentColorTheme.reload(this.extensionResourceLoaderService); this.currentColorTheme.setCustomColors(this.colorCustomizations); this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations); + this.currentColorTheme.setCustomTokenStyleRules(this.tokenStylesCustomizations); this.updateDynamicCSSRules(this.currentColorTheme); this.applyTheme(this.currentColorTheme, undefined, false); } diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index aca8cb6d688..9639f59e0d1 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -340,12 +340,12 @@ export class ColorThemeData implements IColorTheme { } public setCustomTokenStyleRules(tokenStylingRules: IExperimentalTokenStyleCustomizations) { - this.tokenStylingRules = []; - readCustomTokenStyleRules(tokenStylingRules, this.tokenStylingRules); + this.customTokenStylingRules = []; + readCustomTokenStyleRules(tokenStylingRules, this.customTokenStylingRules); const themeSpecificColors = tokenStylingRules[`[${this.settingsId}]`] as IExperimentalTokenStyleCustomizations; if (types.isObject(themeSpecificColors)) { - readCustomTokenStyleRules(themeSpecificColors, this.tokenStylingRules); + readCustomTokenStyleRules(themeSpecificColors, this.customTokenStylingRules); } this.tokenColorIndex = undefined; From 26ef4cfb96994929c63ba1d49427c38b8c69e39f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 22 Nov 2019 11:22:43 +0100 Subject: [PATCH 227/246] fix sync settings setting --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index c52091d5fa4..2a6b12b2adb 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -187,7 +187,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo quickPick.canSelectMany = true; quickPick.ignoreFocusOut = true; const items = [{ - id: 'sync.settings', + id: 'sync.enableSettings', label: localize('user settings', "User Settings") }, { id: 'sync.enableExtensions', From 816489dac78c90575ef52dcf1b65c4ac0a385be1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 22 Nov 2019 15:30:59 +0100 Subject: [PATCH 228/246] use one style for unexpected outcome, disable by default, #85376 --- src/vs/editor/common/config/editorOptions.ts | 4 ++-- src/vs/editor/contrib/suggest/media/suggest.css | 7 ++----- src/vs/editor/contrib/suggest/suggestRangeHighlighter.ts | 4 ++-- src/vs/monaco.d.ts | 2 +- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 68c3ab1a604..f17391dbe75 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2395,7 +2395,7 @@ export interface ISuggestOptions { */ insertMode?: 'insert' | 'replace'; /** - * Show a highlight when suggestion replaces or keep text after the cursor. Defaults to true. + * Show a highlight when suggestion replaces or keep text after the cursor. Defaults to false. */ insertHighlight?: boolean; /** @@ -2531,7 +2531,7 @@ class EditorSuggest extends BaseEditorOption highlight AFTER range newDeco = [{ range: new Range(position.lineNumber, position.column, position.lineNumber, position.column + info.overwriteAfter), - options: { inlineClassName: 'suggest-insertMode-goes' } + options: { inlineClassName: 'suggest-insert-unexpected' } }]; } else if (opts.insertMode === 'replace' && info.overwriteAfter === 0) { @@ -83,7 +83,7 @@ export class SuggestRangeHighlighter { if (wordInfo && wordInfo.endColumn > position.column) { newDeco = [{ range: new Range(position.lineNumber, position.column, position.lineNumber, wordInfo.endColumn), - options: { inlineClassName: 'suggest-insertMode-stays' } + options: { inlineClassName: 'suggest-insert-unexpected' } }]; } } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 74d50c5031b..996495a26d5 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3400,7 +3400,7 @@ declare namespace monaco.editor { */ insertMode?: 'insert' | 'replace'; /** - * Show a highlight when suggestion replaces or keep text after the cursor. Defaults to true. + * Show a highlight when suggestion replaces or keep text after the cursor. Defaults to false. */ insertHighlight?: boolean; /** From 91f269098868a3729abac1c59035c41420231b77 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 22 Nov 2019 12:59:25 +0100 Subject: [PATCH 229/246] Avoid having providers change their mind about the legend after the fact --- src/vs/editor/common/services/modelServiceImpl.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 3793c1ae50e..aaa20e6038f 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -509,17 +509,18 @@ class ModelSemanticColoring extends Disposable { pendingChanges.push(e); }); + const legend = provider.getLegend(); const request = Promise.resolve(provider.provideSemanticColoring(this._model, this._currentRequestCancellationTokenSource.token)); request.then((res) => { this._currentRequestCancellationTokenSource = null; contentChangeListener.dispose(); - this._setSemanticTokens(res || null, provider.getLegend(), pendingChanges); + this._setSemanticTokens(res || null, legend, pendingChanges); }, (err) => { errors.onUnexpectedError(err); this._currentRequestCancellationTokenSource = null; contentChangeListener.dispose(); - this._setSemanticTokens(null, provider.getLegend(), pendingChanges); + this._setSemanticTokens(null, legend, pendingChanges); }); } From 1c98b5ec12c522176f4b4dc606615951a9da7b8d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 22 Nov 2019 15:59:22 +0100 Subject: [PATCH 230/246] Revert "no "Change All Occurrences" when there is a rename provider" This reverts commit 5c27cdccf8621e212d0113b18f4ef07714c28d3f. --- src/vs/editor/contrib/multicursor/multicursor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index a2044206bdd..a5341c5896b 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -759,7 +759,7 @@ export class CompatChangeAll extends MultiCursorSelectionControllerAction { id: 'editor.action.changeAll', label: nls.localize('changeAll.label', "Change All Occurrences"), alias: 'Change All Occurrences', - precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.editorTextFocus, EditorContextKeys.hasRenameProvider.toNegated()), + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.editorTextFocus), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.CtrlCmd | KeyCode.F2, From 8c6c0232fee7b6aeab7380967a955f59cd5570dd Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 22 Nov 2019 16:23:06 +0100 Subject: [PATCH 231/246] Introduce a cache object for styling per semantic coloring provider --- .../common/services/modelServiceImpl.ts | 82 ++++++++++++++----- 1 file changed, 61 insertions(+), 21 deletions(-) diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index aaa20e6038f..7c5ba7451af 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -441,12 +441,14 @@ export interface ILineSequence { class SemanticColoringFeature extends Disposable { private _watchers: Record; + private _semanticStyling: SemanticStyling; constructor(modelService: IModelService, themeService: IThemeService) { super(); this._watchers = Object.create(null); + this._semanticStyling = new SemanticStyling(themeService); this._register(modelService.onModelAdded((model) => { - this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService); + this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling); })); this._register(modelService.onModelRemoved((model) => { this._watchers[model.uri.toString()].dispose(); @@ -455,24 +457,72 @@ class SemanticColoringFeature extends Disposable { } } +class SemanticStyling { + + private readonly _caches: WeakMap; + + constructor( + private readonly _themeService: IThemeService + ) { + this._caches = new WeakMap(); + } + + public get(provider: SemanticColoringProvider): SemanticColoringProviderStyling { + if (!this._caches.has(provider)) { + this._caches.set(provider, new SemanticColoringProviderStyling(provider.getLegend(), this._themeService)); + } + return this._caches.get(provider)!; + } +} + +const enum Constants { + NO_STYLING = 0b01111111111111111111111111111111 +} + +class SemanticColoringProviderStyling { + + constructor( + private readonly _legend: SemanticColoringLegend, + private readonly _themeService: IThemeService + ) { + } + + public getMetadata(tokenTypeIndex: number, tokenModifierSet: number): number { + const tokenType = this._legend.tokenTypes[tokenTypeIndex]; + const tokenModifiers: string[] = []; + for (let modifierIndex = 0; tokenModifierSet !== 0 && modifierIndex < this._legend.tokenModifiers.length; modifierIndex++) { + if (tokenModifierSet & 1) { + tokenModifiers.push(this._legend.tokenModifiers[modifierIndex]); + } + tokenModifierSet = tokenModifierSet >> 1; + } + + const metadata = this._themeService.getTheme().getTokenStyleMetadata(tokenType, tokenModifiers); + if (typeof metadata === 'undefined') { + return Constants.NO_STYLING; + } + return metadata; + } +} + class ModelSemanticColoring extends Disposable { private _isDisposed: boolean; private readonly _model: ITextModel; + private readonly _semanticStyling: SemanticStyling; private readonly _fetchSemanticTokens: RunOnceScheduler; private _currentResponse: SemanticColoring | null; private _currentRequestCancellationTokenSource: CancellationTokenSource | null; - private _themeService: IThemeService; - constructor(model: ITextModel, themeService: IThemeService) { + constructor(model: ITextModel, themeService: IThemeService, stylingProvider: SemanticStyling) { super(); this._isDisposed = false; this._model = model; + this._semanticStyling = stylingProvider; this._fetchSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchSemanticTokensNow(), 500)); this._currentResponse = null; this._currentRequestCancellationTokenSource = null; - this._themeService = themeService; this._register(this._model.onDidChangeContent(e => this._fetchSemanticTokens.schedule())); this._register(SemanticColoringProviderRegistry.onDidChange(e => this._fetchSemanticTokens.schedule())); @@ -509,22 +559,22 @@ class ModelSemanticColoring extends Disposable { pendingChanges.push(e); }); - const legend = provider.getLegend(); + const styling = this._semanticStyling.get(provider); const request = Promise.resolve(provider.provideSemanticColoring(this._model, this._currentRequestCancellationTokenSource.token)); request.then((res) => { this._currentRequestCancellationTokenSource = null; contentChangeListener.dispose(); - this._setSemanticTokens(res || null, legend, pendingChanges); + this._setSemanticTokens(res || null, styling, pendingChanges); }, (err) => { errors.onUnexpectedError(err); this._currentRequestCancellationTokenSource = null; contentChangeListener.dispose(); - this._setSemanticTokens(null, legend, pendingChanges); + this._setSemanticTokens(null, styling, pendingChanges); }); } - private _setSemanticTokens(tokens: SemanticColoring | null, legend: SemanticColoringLegend, pendingChanges: IModelContentChangedEvent[]): void { + private _setSemanticTokens(tokens: SemanticColoring | null, styling: SemanticColoringProviderStyling, pendingChanges: IModelContentChangedEvent[]): void { if (this._currentResponse) { this._currentResponse.dispose(); this._currentResponse = null; @@ -554,19 +604,9 @@ class ModelSemanticColoring extends Disposable { const startCharacter = srcTokens[srcOffset + 1]; const endCharacter = srcTokens[srcOffset + 2]; const tokenTypeIndex = srcTokens[srcOffset + 3]; - const tokenType = legend.tokenTypes[tokenTypeIndex]; - - let tokenModifierSet = srcTokens[srcOffset + 4]; - let tokenModifiers: string[] = []; - for (let modifierIndex = 0; tokenModifierSet !== 0 && modifierIndex < legend.tokenModifiers.length; modifierIndex++) { - if (tokenModifierSet & 1) { - tokenModifiers.push(legend.tokenModifiers[modifierIndex]); - } - tokenModifierSet = tokenModifierSet >> 1; - } - - const metadata = this._themeService.getTheme().getTokenStyleMetadata(tokenType, tokenModifiers); - if (metadata !== undefined) { + const tokenModifierSet = srcTokens[srcOffset + 4]; + const metadata = styling.getMetadata(tokenTypeIndex, tokenModifierSet); + if (metadata !== Constants.NO_STYLING) { destTokens[destOffset] = deltaLine; destTokens[destOffset + 1] = startCharacter; destTokens[destOffset + 2] = endCharacter; From a153842765bcfebf7243dd593339860c443372b2 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 22 Nov 2019 16:31:59 +0100 Subject: [PATCH 232/246] adjust peek call hierarchy menu --- src/vs/editor/contrib/gotoSymbol/goToCommands.ts | 2 +- .../contrib/callHierarchy/browser/callHierarchy.contribution.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index 86f229ecc2f..cdcad1d5df4 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -42,7 +42,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorContext, { submenu: MenuId.EditorContextPeek, title: nls.localize('peek.submenu', "Peek"), group: 'navigation', - order: 1000 + order: 100 }); export interface SymbolNavigationActionConfig { diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts index eac5e02af3c..57b75c64bf1 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts @@ -162,7 +162,7 @@ registerEditorAction(class extends EditorAction { alias: 'Peek Call Hierarchy', contextMenuOpts: { group: 'navigation', - order: 1.48 + order: 1000 }, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, From 264a740fedee25cfa23ae0e16c509f93b37083d7 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 22 Nov 2019 17:19:42 +0100 Subject: [PATCH 233/246] Cache styling information via a hash table --- .../common/services/modelServiceImpl.ts | 113 +++++++++++++++++- 1 file changed, 108 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 7c5ba7451af..fea2a56cb56 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -446,7 +446,7 @@ class SemanticColoringFeature extends Disposable { constructor(modelService: IModelService, themeService: IThemeService) { super(); this._watchers = Object.create(null); - this._semanticStyling = new SemanticStyling(themeService); + this._semanticStyling = this._register(new SemanticStyling(themeService)); this._register(modelService.onModelAdded((model) => { this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling); })); @@ -457,14 +457,18 @@ class SemanticColoringFeature extends Disposable { } } -class SemanticStyling { +class SemanticStyling extends Disposable { - private readonly _caches: WeakMap; + private _caches: WeakMap; constructor( private readonly _themeService: IThemeService ) { + super(); this._caches = new WeakMap(); + this._register(this._themeService.onThemeChange(() => { + this._caches = new WeakMap(); + })); } public get(provider: SemanticColoringProvider): SemanticColoringProviderStyling { @@ -479,15 +483,112 @@ const enum Constants { NO_STYLING = 0b01111111111111111111111111111111 } +class HashTableEntry { + public readonly tokenTypeIndex: number; + public readonly tokenModifierSet: number; + public readonly metadata: number; + public next: HashTableEntry | null; + + constructor(tokenTypeIndex: number, tokenModifierSet: number, metadata: number) { + this.tokenTypeIndex = tokenTypeIndex; + this.tokenModifierSet = tokenModifierSet; + this.metadata = metadata; + this.next = null; + } +} + +class HashTable { + + private static _SIZES = [3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 2097143]; + + private _elementsCount: number; + private _currentLengthIndex: number; + private _currentLength: number; + private _growCount: number; + private _elements: (HashTableEntry | null)[]; + + constructor() { + this._elementsCount = 0; + this._currentLengthIndex = 0; + this._currentLength = HashTable._SIZES[this._currentLengthIndex]; + this._growCount = Math.round(this._currentLengthIndex + 1 < HashTable._SIZES.length ? 2 / 3 * this._currentLength : 0); + this._elements = []; + HashTable._nullOutEntries(this._elements, this._currentLength); + } + + private static _nullOutEntries(entries: (HashTableEntry | null)[], length: number): void { + for (let i = 0; i < length; i++) { + entries[i] = null; + } + } + + private _hashFunc(tokenTypeIndex: number, tokenModifierSet: number): number { + return ((((tokenTypeIndex << 5) - tokenTypeIndex) + tokenModifierSet) | 0) % this._currentLength; // tokenTypeIndex * 31 + tokenModifierSet, keep as int32 + } + + public get(tokenTypeIndex: number, tokenModifierSet: number): HashTableEntry | null { + const hash = this._hashFunc(tokenTypeIndex, tokenModifierSet); + + let p = this._elements[hash]; + while (p) { + if (p.tokenTypeIndex === tokenTypeIndex && p.tokenModifierSet === tokenModifierSet) { + return p; + } + p = p.next; + } + + return null; + } + + public add(tokenTypeIndex: number, tokenModifierSet: number, metadata: number): void { + this._elementsCount++; + if (this._growCount !== 0 && this._elementsCount >= this._growCount) { + // expand! + const oldElements = this._elements; + + this._currentLengthIndex++; + this._currentLength = HashTable._SIZES[this._currentLengthIndex]; + this._growCount = Math.round(this._currentLengthIndex + 1 < HashTable._SIZES.length ? 2 / 3 * this._currentLength : 0); + this._elements = []; + HashTable._nullOutEntries(this._elements, this._currentLength); + + for (const first of oldElements) { + let p = first; + while (p) { + const oldNext = p.next; + p.next = null; + this._add(p); + p = oldNext; + } + } + } + this._add(new HashTableEntry(tokenTypeIndex, tokenModifierSet, metadata)); + } + + private _add(element: HashTableEntry): void { + const hash = this._hashFunc(element.tokenTypeIndex, element.tokenModifierSet); + element.next = this._elements[hash]; + this._elements[hash] = element; + } +} + class SemanticColoringProviderStyling { + private readonly _hashTable: HashTable; + constructor( private readonly _legend: SemanticColoringLegend, private readonly _themeService: IThemeService ) { + this._hashTable = new HashTable(); } public getMetadata(tokenTypeIndex: number, tokenModifierSet: number): number { + const entry = this._hashTable.get(tokenTypeIndex, tokenModifierSet); + if (entry) { + return entry.metadata; + } + const tokenType = this._legend.tokenTypes[tokenTypeIndex]; const tokenModifiers: string[] = []; for (let modifierIndex = 0; tokenModifierSet !== 0 && modifierIndex < this._legend.tokenModifiers.length; modifierIndex++) { @@ -497,10 +598,12 @@ class SemanticColoringProviderStyling { tokenModifierSet = tokenModifierSet >> 1; } - const metadata = this._themeService.getTheme().getTokenStyleMetadata(tokenType, tokenModifiers); + let metadata = this._themeService.getTheme().getTokenStyleMetadata(tokenType, tokenModifiers); if (typeof metadata === 'undefined') { - return Constants.NO_STYLING; + metadata = Constants.NO_STYLING; } + + this._hashTable.add(tokenTypeIndex, tokenModifierSet, metadata); return metadata; } } From 2e5a68d737fbee31f34932b746fe6af796b10251 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 22 Nov 2019 17:22:17 +0100 Subject: [PATCH 234/246] Clear out existing semantic tokens when the theme changes --- src/vs/editor/common/services/modelServiceImpl.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index fea2a56cb56..07a0874862a 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -629,7 +629,11 @@ class ModelSemanticColoring extends Disposable { this._register(this._model.onDidChangeContent(e => this._fetchSemanticTokens.schedule())); this._register(SemanticColoringProviderRegistry.onDidChange(e => this._fetchSemanticTokens.schedule())); - this._register(themeService.onThemeChange(_ => this._fetchSemanticTokens.schedule())); + this._register(themeService.onThemeChange(_ => { + // clear out existing tokens + this._setSemanticTokens(null, null, []); + this._fetchSemanticTokens.schedule(); + })); this._fetchSemanticTokens.schedule(0); } @@ -677,7 +681,7 @@ class ModelSemanticColoring extends Disposable { }); } - private _setSemanticTokens(tokens: SemanticColoring | null, styling: SemanticColoringProviderStyling, pendingChanges: IModelContentChangedEvent[]): void { + private _setSemanticTokens(tokens: SemanticColoring | null, styling: SemanticColoringProviderStyling | null, pendingChanges: IModelContentChangedEvent[]): void { if (this._currentResponse) { this._currentResponse.dispose(); this._currentResponse = null; @@ -690,7 +694,7 @@ class ModelSemanticColoring extends Disposable { return; } this._currentResponse = tokens; - if (!this._currentResponse) { + if (!this._currentResponse || !styling) { this._model.setSemanticTokens(null); return; } From 4b6011db2653b9797fc08a5735154ba27a32bdb0 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 22 Nov 2019 17:30:42 +0100 Subject: [PATCH 235/246] Add workarounds for bad tests --- .../common/services/modelServiceImpl.ts | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 07a0874862a..3b9734e0a16 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -466,9 +466,12 @@ class SemanticStyling extends Disposable { ) { super(); this._caches = new WeakMap(); - this._register(this._themeService.onThemeChange(() => { - this._caches = new WeakMap(); - })); + if (this._themeService) { + // workaround for tests which use undefined... :/ + this._register(this._themeService.onThemeChange(() => { + this._caches = new WeakMap(); + })); + } } public get(provider: SemanticColoringProvider): SemanticColoringProviderStyling { @@ -629,11 +632,14 @@ class ModelSemanticColoring extends Disposable { this._register(this._model.onDidChangeContent(e => this._fetchSemanticTokens.schedule())); this._register(SemanticColoringProviderRegistry.onDidChange(e => this._fetchSemanticTokens.schedule())); - this._register(themeService.onThemeChange(_ => { - // clear out existing tokens - this._setSemanticTokens(null, null, []); - this._fetchSemanticTokens.schedule(); - })); + if (themeService) { + // workaround for tests which use undefined... :/ + this._register(themeService.onThemeChange(_ => { + // clear out existing tokens + this._setSemanticTokens(null, null, []); + this._fetchSemanticTokens.schedule(); + })); + } this._fetchSemanticTokens.schedule(0); } From 109fab829c935e9ea6f6bd851e40ad4933b68c77 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 22 Nov 2019 17:37:02 +0100 Subject: [PATCH 236/246] #85178 emit the right visible indices --- src/vs/workbench/browser/parts/views/views.ts | 29 +++++++----- .../test/browser/parts/views/views.test.ts | 47 +++++++++++++++++++ 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 8eceaffcb90..7d2297562e1 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -432,24 +432,27 @@ export class ContributableViewsModel extends Disposable { const toRemove: { index: number, viewDescriptor: IViewDescriptor; }[] = []; const toAdd: { index: number, viewDescriptor: IViewDescriptor, size?: number, collapsed: boolean; }[] = []; - const previous = this._viewDescriptors; - this._viewDescriptors = viewDescriptors.sort(this.compareViewDescriptors.bind(this)); - - for (let index = 0; index < previous.length; index++) { - const previousViewDescriptor = previous[index]; - if (this.isViewDescriptorVisible(previousViewDescriptor) && this._viewDescriptors.every(viewDescriptor => viewDescriptor.id !== previousViewDescriptor.id)) { - toRemove.push({ index, viewDescriptor: previousViewDescriptor }); - } - } + viewDescriptors = viewDescriptors.sort(this.compareViewDescriptors.bind(this)); for (let index = 0; index < this._viewDescriptors.length; index++) { - const viewDescriptor = this._viewDescriptors[index]; - if (this.isViewDescriptorVisible(viewDescriptor) && previous.every(previousViewDescriptor => previousViewDescriptor.id !== viewDescriptor.id)) { - const state = this.viewStates.get(viewDescriptor.id)!; - toAdd.push({ index, viewDescriptor, size: state.size, collapsed: !!state.collapsed }); + const currentViewDescriptor = this._viewDescriptors[index]; + if (this.isViewDescriptorVisible(currentViewDescriptor) && viewDescriptors.every(viewDescriptor => viewDescriptor.id !== currentViewDescriptor.id)) { + const { visibleIndex } = this.find(currentViewDescriptor.id); + toRemove.push({ index: visibleIndex, viewDescriptor: currentViewDescriptor }); } } + const previous = this._viewDescriptors; + this._viewDescriptors = viewDescriptors.slice(0); + for (let i = 0; i < this._viewDescriptors.length; i++) { + const viewDescriptor = this._viewDescriptors[i]; + if (this.isViewDescriptorVisible(viewDescriptor) && previous.every(previousViewDescriptor => previousViewDescriptor.id !== viewDescriptor.id)) { + const { visibleIndex, state } = this.find(viewDescriptor.id); + toAdd.push({ index: visibleIndex, viewDescriptor, size: state.size, collapsed: !!state.collapsed }); + } + } + + if (toRemove.length) { this._onDidRemove.fire(toRemove); } diff --git a/src/vs/workbench/test/browser/parts/views/views.test.ts b/src/vs/workbench/test/browser/parts/views/views.test.ts index 82e6bd8c59c..dbfe6851bae 100644 --- a/src/vs/workbench/test/browser/parts/views/views.test.ts +++ b/src/vs/workbench/test/browser/parts/views/views.test.ts @@ -296,6 +296,52 @@ suite('ContributableViewsModel', () => { assert.equal(seq.elements.length, 0); }); + test('view states and when contexts multiple views', async function () { + const viewStates = new Map(); + viewStates.set('view1', { visibleGlobal: false, collapsed: false, visibleWorkspace: undefined }); + const model = new ContributableViewsModel(container, viewsService, viewStates); + const seq = new ViewDescriptorSequence(model); + + assert.equal(model.visibleViewDescriptors.length, 0); + assert.equal(seq.elements.length, 0); + + const view1: IViewDescriptor = { + id: 'view1', + ctorDescriptor: null!, + name: 'Test View 1', + when: ContextKeyExpr.equals('showview', true) + }; + const view2: IViewDescriptor = { + id: 'view2', + ctorDescriptor: null!, + name: 'Test View 2', + }; + const view3: IViewDescriptor = { + id: 'view3', + ctorDescriptor: null!, + name: 'Test View 3', + when: ContextKeyExpr.equals('showview', true) + }; + + ViewsRegistry.registerViews([view1, view2, view3], container); + assert.deepEqual(model.visibleViewDescriptors, [view2], 'Only view2 should be visible'); + assert.deepEqual(seq.elements, [view2]); + + const key = contextKeyService.createKey('showview', false); + assert.deepEqual(model.visibleViewDescriptors, [view2], 'Only view2 should be visible'); + assert.deepEqual(seq.elements, [view2]); + + key.set(true); + await new Promise(c => setTimeout(c, 30)); + assert.deepEqual(model.visibleViewDescriptors, [view2, view3], 'view3 should be visible'); + assert.deepEqual(seq.elements, [view2, view3]); + + key.set(false); + await new Promise(c => setTimeout(c, 30)); + assert.deepEqual(model.visibleViewDescriptors, [view2], 'Only view2 should be visible'); + assert.deepEqual(seq.elements, [view2]); + }); + test('remove event is not triggered if view was hidden and removed', async function () { const model = new ContributableViewsModel(container, viewsService); const seq = new ViewDescriptorSequence(model); @@ -324,4 +370,5 @@ suite('ContributableViewsModel', () => { await new Promise(c => setTimeout(c, 30)); assert.ok(!target.called, 'remove event should not be called since it is already hidden'); }); + }); From f17ce5f528cc5891194644ffe9324e2d5b31d699 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 22 Nov 2019 17:56:03 +0100 Subject: [PATCH 237/246] :lipstick: --- src/vs/workbench/browser/parts/views/views.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 7d2297562e1..2b35bf2b70c 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -429,21 +429,21 @@ export class ContributableViewsModel extends Disposable { } } - const toRemove: { index: number, viewDescriptor: IViewDescriptor; }[] = []; - const toAdd: { index: number, viewDescriptor: IViewDescriptor, size?: number, collapsed: boolean; }[] = []; - viewDescriptors = viewDescriptors.sort(this.compareViewDescriptors.bind(this)); + const toRemove: { index: number, viewDescriptor: IViewDescriptor; }[] = []; for (let index = 0; index < this._viewDescriptors.length; index++) { - const currentViewDescriptor = this._viewDescriptors[index]; - if (this.isViewDescriptorVisible(currentViewDescriptor) && viewDescriptors.every(viewDescriptor => viewDescriptor.id !== currentViewDescriptor.id)) { - const { visibleIndex } = this.find(currentViewDescriptor.id); - toRemove.push({ index: visibleIndex, viewDescriptor: currentViewDescriptor }); + const previousViewDescriptor = this._viewDescriptors[index]; + if (this.isViewDescriptorVisible(previousViewDescriptor) && viewDescriptors.every(viewDescriptor => viewDescriptor.id !== previousViewDescriptor.id)) { + const { visibleIndex } = this.find(previousViewDescriptor.id); + toRemove.push({ index: visibleIndex, viewDescriptor: previousViewDescriptor }); } } const previous = this._viewDescriptors; this._viewDescriptors = viewDescriptors.slice(0); + + const toAdd: { index: number, viewDescriptor: IViewDescriptor, size?: number, collapsed: boolean; }[] = []; for (let i = 0; i < this._viewDescriptors.length; i++) { const viewDescriptor = this._viewDescriptors[i]; if (this.isViewDescriptorVisible(viewDescriptor) && previous.every(previousViewDescriptor => previousViewDescriptor.id !== viewDescriptor.id)) { @@ -452,7 +452,6 @@ export class ContributableViewsModel extends Disposable { } } - if (toRemove.length) { this._onDidRemove.fire(toRemove); } From 17ce94c11ed0feb2a492a1ccf3646893b84e36d5 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 22 Nov 2019 18:02:49 +0100 Subject: [PATCH 238/246] remove unused focusBody --- src/vs/workbench/contrib/files/browser/views/emptyView.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/views/emptyView.ts b/src/vs/workbench/contrib/files/browser/views/emptyView.ts index d1b2af81a5f..d5d9984613c 100644 --- a/src/vs/workbench/contrib/files/browser/views/emptyView.ts +++ b/src/vs/workbench/contrib/files/browser/views/emptyView.ts @@ -135,12 +135,7 @@ export class EmptyView extends ViewletPane { } focus(): void { - this.focusBody(); + this.button.element.focus(); } - focusBody(): void { - if (this.button) { - this.button.element.focus(); - } - } } From 6b78be028629ac52ba980ace1d60f077d9493c9b Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Fri, 22 Nov 2019 09:42:04 -0800 Subject: [PATCH 239/246] Use local server for auth so that a completion page can be shown. --- src/vs/platform/auth/common/auth.css | 100 ++++++++++ src/vs/platform/auth/common/auth.html | 35 ++++ src/vs/platform/auth/common/auth.ts | 2 +- src/vs/platform/auth/common/authTokenIpc.ts | 5 +- .../auth/electron-browser/authServer.ts | 162 ++++++++++++++++ .../auth/electron-browser/authTokenService.ts | 178 +++++++++--------- .../electron-browser/authTokenService.ts | 16 +- 7 files changed, 392 insertions(+), 106 deletions(-) create mode 100644 src/vs/platform/auth/common/auth.css create mode 100644 src/vs/platform/auth/common/auth.html create mode 100644 src/vs/platform/auth/electron-browser/authServer.ts diff --git a/src/vs/platform/auth/common/auth.css b/src/vs/platform/auth/common/auth.css new file mode 100644 index 00000000000..e87a6372763 --- /dev/null +++ b/src/vs/platform/auth/common/auth.css @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +html { + height: 100%; +} + +body { + box-sizing: border-box; + min-height: 100%; + margin: 0; + padding: 15px 30px; + display: flex; + flex-direction: column; + color: white; + font-family: "Segoe UI","Helvetica Neue","Helvetica",Arial,sans-serif; + background-color: #373277; +} + +.branding { + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGRlZnM+PHN0eWxlPi5pY29uLWNhbnZhcy10cmFuc3BhcmVudHtmaWxsOiNmNmY2ZjY7b3BhY2l0eTowO30uaWNvbi13aGl0ZXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5CcmFuZFZpc3VhbFN0dWRpb0NvZGUyMDE3UlRXXzI0eF93aGl0ZV8yNHg8L3RpdGxlPjxwYXRoIGNsYXNzPSJpY29uLWNhbnZhcy10cmFuc3BhcmVudCIgZD0iTTI0LDBWMjRIMFYwWiIvPjxwYXRoIGNsYXNzPSJpY29uLXdoaXRlIiBkPSJNMjQsMi41VjIxLjVMMTgsMjQsMCwxOC41di0uNTYxbDE4LDEuNTQ1VjBaTTEsMTMuMTExLDQuMzg1LDEwLDEsNi44ODlsMS40MTgtLjgyN0w1Ljg1Myw4LjY1LDEyLDNsMywxLjQ1NlYxNS41NDRMMTIsMTcsNS44NTMsMTEuMzUsMi40MTksMTMuOTM5Wk03LjY0NCwxMCwxMiwxMy4yODNWNi43MTdaIi8+PC9zdmc+"); + background-size: 24px; + background-repeat: no-repeat; + background-position: left 50%; + padding-left: 36px; + font-size: 20px; + letter-spacing: -0.04rem; + font-weight: 400; + color: white; + text-decoration: none; +} + +.message-container { + flex-grow: 1; + display: flex; + align-items: center; + justify-content: center; + margin: 0 30px; +} + +.message { + font-weight: 300; + font-size: 1.3rem; +} + +body.error .message { + display: none; +} + +body.error .error-message { + display: block; +} + +.error-message { + display: none; + font-weight: 300; + font-size: 1.3rem; +} + +.error-text { + color: red; + font-size: 1rem; +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI Light"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.svg#web") format("svg"); + font-weight: 200 +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI Semilight"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.svg#web") format("svg"); + font-weight: 300 +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.svg#web") format("svg"); + font-weight: 400 +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI Semibold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.svg#web") format("svg"); + font-weight: 600 +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI Bold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.svg#web") format("svg"); + font-weight: 700 +} diff --git a/src/vs/platform/auth/common/auth.html b/src/vs/platform/auth/common/auth.html new file mode 100644 index 00000000000..8fe3e50e7b7 --- /dev/null +++ b/src/vs/platform/auth/common/auth.html @@ -0,0 +1,35 @@ + + + + + + + Azure Account - Sign In + + + + + + Visual Studio Code + +
+
+ You are signed in now and can close this page. +
+
+ An error occurred while signing in: +
+
+
+ + + diff --git a/src/vs/platform/auth/common/auth.ts b/src/vs/platform/auth/common/auth.ts index 039949e9035..81af0bbf0a4 100644 --- a/src/vs/platform/auth/common/auth.ts +++ b/src/vs/platform/auth/common/auth.ts @@ -26,6 +26,6 @@ export interface IAuthTokenService { getToken(): Promise; refreshToken(): Promise; - login(callbackUri?: URI): Promise; + login(): Promise; logout(): Promise; } diff --git a/src/vs/platform/auth/common/authTokenIpc.ts b/src/vs/platform/auth/common/authTokenIpc.ts index 99a2111750c..eff088c1114 100644 --- a/src/vs/platform/auth/common/authTokenIpc.ts +++ b/src/vs/platform/auth/common/authTokenIpc.ts @@ -22,11 +22,8 @@ export class AuthTokenChannel implements IServerChannel { switch (command) { case '_getInitialStatus': return Promise.resolve(this.service.status); case 'getToken': return this.service.getToken(); - case 'exchangeCodeForToken': - this.service._onDidGetCallback.fire(args); - return Promise.resolve(); case 'refreshToken': return this.service.refreshToken(); - case 'login': return this.service.login(args); + case 'login': return this.service.login(); case 'logout': return this.service.logout(); } throw new Error('Invalid call'); diff --git a/src/vs/platform/auth/electron-browser/authServer.ts b/src/vs/platform/auth/electron-browser/authServer.ts new file mode 100644 index 00000000000..b2f227114fd --- /dev/null +++ b/src/vs/platform/auth/electron-browser/authServer.ts @@ -0,0 +1,162 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as http from 'http'; +import * as url from 'url'; +import * as fs from 'fs'; +import * as net from 'net'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; + +interface Deferred { + resolve: (result: T | Promise) => void; + reject: (reason: any) => void; +} + +export function createTerminateServer(server: http.Server) { + const sockets: Record = {}; + let socketCount = 0; + server.on('connection', socket => { + const id = socketCount++; + sockets[id] = socket; + socket.on('close', () => { + delete sockets[id]; + }); + }); + return async () => { + const result = new Promise(resolve => server.close(resolve)); + for (const id in sockets) { + sockets[id].destroy(); + } + return result; + }; +} + +export async function startServer(server: http.Server): Promise { + let portTimer: NodeJS.Timer; + + function cancelPortTimer() { + clearTimeout(portTimer); + } + + const port = new Promise((resolve, reject) => { + portTimer = setTimeout(() => { + reject(new Error('Timeout waiting for port')); + }, 5000); + + server.on('listening', () => { + const address = server.address(); + if (typeof address === 'string') { + resolve(address); + } else { + resolve(address.port.toString()); + } + }); + + server.on('error', err => { + reject(err); + }); + + server.on('close', () => { + reject(new Error('Closed')); + }); + + server.listen(0); + }); + + port.then(cancelPortTimer, cancelPortTimer); + return port; +} + +function sendFile(res: http.ServerResponse, filepath: string, contentType: string) { + fs.readFile(filepath, (err, body) => { + if (err) { + console.error(err); + } else { + res.writeHead(200, { + 'Content-Length': body.length, + 'Content-Type': contentType + }); + res.end(body); + } + }); +} + +async function callback(nonce: string, reqUrl: url.Url): Promise { + const query = reqUrl.query; + if (!query || typeof query === 'string') { + throw new Error('No query received.'); + } + + let error = query.error_description || query.error; + + if (!error) { + const state = (query.state as string) || ''; + const receivedNonce = (state.split(',')[1] || '').replace(/ /g, '+'); + if (receivedNonce !== nonce) { + error = 'Nonce does not match.'; + } + } + + const code = query.code as string; + if (!error && code) { + return code; + } + + throw new Error((error as string) || 'No code received.'); +} + +export function createServer(nonce: string) { + type RedirectResult = { req: http.IncomingMessage; res: http.ServerResponse; } | { err: any; res: http.ServerResponse; }; + let deferredRedirect: Deferred; + const redirectPromise = new Promise((resolve, reject) => deferredRedirect = { resolve, reject }); + + type CodeResult = { code: string; res: http.ServerResponse; } | { err: any; res: http.ServerResponse; }; + let deferredCode: Deferred; + const codePromise = new Promise((resolve, reject) => deferredCode = { resolve, reject }); + + const codeTimer = setTimeout(() => { + deferredCode.reject(new Error('Timeout waiting for code')); + }, 5 * 60 * 1000); + + function cancelCodeTimer() { + clearTimeout(codeTimer); + } + + const server = http.createServer(function (req, res) { + const reqUrl = url.parse(req.url!, /* parseQueryString */ true); + switch (reqUrl.pathname) { + case '/signin': + const receivedNonce = ((reqUrl.query.nonce as string) || '').replace(/ /g, '+'); + if (receivedNonce === nonce) { + deferredRedirect.resolve({ req, res }); + } else { + const err = new Error('Nonce does not match.'); + deferredRedirect.resolve({ err, res }); + } + break; + case '/': + sendFile(res, getPathFromAmdModule(require, '../common/auth.html'), 'text/html; charset=utf-8'); + break; + case '/auth.css': + sendFile(res, getPathFromAmdModule(require, '../common/auth.css'), 'text/css; charset=utf-8'); + break; + case '/callback': + deferredCode.resolve(callback(nonce, reqUrl) + .then(code => ({ code, res }), err => ({ err, res }))); + break; + default: + res.writeHead(404); + res.end(); + break; + } + }); + + codePromise.then(cancelCodeTimer, cancelCodeTimer); + return { + server, + redirectPromise, + codePromise + }; +} diff --git a/src/vs/platform/auth/electron-browser/authTokenService.ts b/src/vs/platform/auth/electron-browser/authTokenService.ts index 14586bab7b0..301a6ae505c 100644 --- a/src/vs/platform/auth/electron-browser/authTokenService.ts +++ b/src/vs/platform/auth/electron-browser/authTokenService.ts @@ -8,10 +8,11 @@ import * as https from 'https'; import { Event, Emitter } from 'vs/base/common/event'; import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { shell } from 'electron'; +import { createServer, startServer } from 'vs/platform/auth/electron-browser/authServer'; const SERVICE_NAME = 'VS Code'; const ACCOUNT = 'MyAccount'; @@ -23,14 +24,6 @@ const activeDirectoryResourceId = 'https://management.core.windows.net/'; const clientId = 'aebc6443-996d-45c2-90f0-388ff96faa56'; const tenantId = 'common'; -function parseQuery(uri: URI) { - return uri.query.split('&').reduce((prev: any, current) => { - const queryString = current.split('='); - prev[queryString[0]] = queryString[1]; - return prev; - }, {}); -} - function toQuery(obj: any): string { return Object.keys(obj).map(key => `${key}=${obj[key]}`).join('&'); } @@ -72,34 +65,63 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { }); } - public async login(callbackUri: URI): Promise { + public async login(): Promise { this.setStatus(AuthTokenStatus.SigningIn); + const nonce = generateUuid(); - const port = (callbackUri.authority.match(/:([0-9]*)$/) || [])[1] || (callbackUri.scheme === 'https' || callbackUri.scheme === 'http' ? 443 : 80); - const state = `${callbackUri.scheme},${port},${encodeURIComponent(nonce)},${encodeURIComponent(callbackUri.query)}`; - const signInUrl = `${activeDirectoryEndpointUrl}${tenantId}/oauth2/authorize`; + const { server, redirectPromise, codePromise } = createServer(nonce); - const codeVerifier = toBase64UrlEncoding(crypto.randomBytes(32).toString('base64')); - const codeChallenge = toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64')); + try { + const port = await startServer(server); + shell.openExternal(`http://localhost:${port}/signin?nonce=${encodeURIComponent(nonce)}`); - let uri = URI.parse(signInUrl); - uri = uri.with({ - query: `response_type=code&client_id=${encodeURIComponent(clientId)}&redirect_uri=${redirectUrlAAD}&state=${encodeURIComponent(state)}&resource=${activeDirectoryResourceId}&prompt=select_account&code_challenge_method=S256&code_challenge=${codeChallenge}` - }); + const redirectReq = await redirectPromise; + if ('err' in redirectReq) { + const { err, res } = redirectReq; + res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unkown error')}` }); + res.end(); + throw err; + } - await shell.openExternal(uri.toString(true)); + const host = redirectReq.req.headers.host || ''; + const updatedPortStr = (/^[^:]+:(\d+)$/.exec(Array.isArray(host) ? host[0] : host) || [])[1]; + const updatedPort = updatedPortStr ? parseInt(updatedPortStr, 10) : port; - const timeoutPromise = new Promise((resolve: (value: IToken) => void, reject) => { - const wait = setTimeout(() => { - this.setStatus(AuthTokenStatus.SignedOut); - clearTimeout(wait); - reject('Login timed out.'); - }, 1000 * 60 * 5); - }); + const state = `${updatedPort},${encodeURIComponent(nonce)}`; + const signInUrl = `${activeDirectoryEndpointUrl}${tenantId}/oauth2/authorize`; + + const codeVerifier = toBase64UrlEncoding(crypto.randomBytes(32).toString('base64')); + const codeChallenge = toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64')); + + let uri = URI.parse(signInUrl); + uri = uri.with({ + query: `response_type=code&client_id=${encodeURIComponent(clientId)}&redirect_uri=${redirectUrlAAD}&state=${encodeURIComponent(state)}&resource=${activeDirectoryResourceId}&prompt=select_account&code_challenge_method=S256&code_challenge=${codeChallenge}` + }); + + await redirectReq.res.writeHead(302, { Location: uri.toString(true) }); + redirectReq.res.end(); + + const codeRes = await codePromise; + const res = codeRes.res; + + try { + if ('err' in codeRes) { + throw codeRes.err; + } + const token = await this.exchangeCodeForToken(codeRes.code, codeVerifier); + this.setToken(token); + res.writeHead(302, { Location: '/' }); + res.end(); + } catch (err) { + res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unkown error')}` }); + res.end(); + } + } finally { + setTimeout(() => { + server.close(); + }, 5000); + } - return Promise.race([this.exchangeCodeForToken(clientId, tenantId, codeVerifier, state), timeoutPromise]).then(token => { - this.setToken(token); - }); } public getToken(): Promise { @@ -120,71 +142,55 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { this.setStatus(AuthTokenStatus.SignedIn); } - private async exchangeCodeForToken(clientId: string, tenantId: string, codeVerifier: string, state: string): Promise { - let uriEventListener: IDisposable; + private exchangeCodeForToken(code: string, codeVerifier: string): Promise { return new Promise((resolve: (value: IToken) => void, reject) => { - uriEventListener = this.onDidGetCallback(async (uri: URI) => { - try { - const query = parseQuery(uri); - const code = query.code; + try { + const postData = toQuery({ + grant_type: 'authorization_code', + code: code, + client_id: clientId, + code_verifier: codeVerifier, + redirect_uri: redirectUrlAAD + }); - if (query.state !== state) { - return; + const post = https.request({ + host: 'login.microsoftonline.com', + path: `/${tenantId}/oauth2/token`, + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': postData.length } - - const postData = toQuery({ - grant_type: 'authorization_code', - code: code, - client_id: clientId, - code_verifier: codeVerifier, - redirect_uri: redirectUrlAAD + }, result => { + const buffer: Buffer[] = []; + result.on('data', (chunk: Buffer) => { + buffer.push(chunk); }); - - const post = https.request({ - host: 'login.microsoftonline.com', - path: `/${tenantId}/oauth2/token`, - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': postData.length + result.on('end', () => { + if (result.statusCode === 200) { + const json = JSON.parse(Buffer.concat(buffer).toString()); + resolve({ + expiresIn: json.access_token, + expiresOn: json.expires_on, + accessToken: json.access_token, + refreshToken: json.refresh_token + }); + } else { + reject(new Error('Bad!')); } - }, result => { - const buffer: Buffer[] = []; - result.on('data', (chunk: Buffer) => { - buffer.push(chunk); - }); - result.on('end', () => { - if (result.statusCode === 200) { - const json = JSON.parse(Buffer.concat(buffer).toString()); - resolve({ - expiresIn: json.access_token, - expiresOn: json.expires_on, - accessToken: json.access_token, - refreshToken: json.refresh_token - }); - } else { - reject(new Error('Bad!')); - } - }); }); + }); - post.write(postData); + post.write(postData); - post.end(); - post.on('error', err => { - reject(err); - }); + post.end(); + post.on('error', err => { + reject(err); + }); - } catch (e) { - reject(e); - } - }); - }).then(result => { - uriEventListener.dispose(); - return result; - }).catch(err => { - uriEventListener.dispose(); - throw err; + } catch (e) { + reject(e); + } }); } diff --git a/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts b/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts index 099395471aa..ad7abd58826 100644 --- a/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts +++ b/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts @@ -9,7 +9,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; -import { IURLService } from 'vs/platform/url/common/url'; import { URI } from 'vs/base/common/uri'; export class AuthTokenService extends Disposable implements IAuthTokenService { @@ -27,23 +26,11 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { constructor( @ISharedProcessService sharedProcessService: ISharedProcessService, - @IURLService private readonly urlService: IURLService ) { super(); this.channel = sharedProcessService.getChannel('authToken'); this._register(this.channel.listen('onDidChangeStatus')(status => this.updateStatus(status))); this.channel.call('_getInitialStatus').then(status => this.updateStatus(status)); - - this.urlService.registerHandler(this); - } - - handleURL(uri: URI) { - if (uri.authority === 'vscode.login') { - this.channel.call('exchangeCodeForToken', uri); - return Promise.resolve(true); - } else { - return Promise.resolve(false); - } } getToken(): Promise { @@ -51,8 +38,7 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { } login(): Promise { - const callbackUri = this.urlService.create({ authority: 'vscode.login ' }); - return this.channel.call('login', callbackUri); + return this.channel.call('login'); } refreshToken(): Promise { From 0d25d0a79823802d0c5df42db4d4d40b099c14d5 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Fri, 22 Nov 2019 10:16:38 -0800 Subject: [PATCH 240/246] Fix #47069 --- .../client/src/htmlMain.ts | 12 ++ .../client/src/matchingTag.ts | 147 ++++++++++++++++++ .../html-language-features/package.json | 6 + .../html-language-features/package.nls.json | 3 +- .../server/package.json | 2 +- .../server/src/htmlServerMain.ts | 19 +++ .../server/src/modes/htmlMode.ts | 4 + .../server/src/modes/languageModes.ts | 1 + .../html-language-features/server/yarn.lock | 8 +- 9 files changed, 196 insertions(+), 6 deletions(-) create mode 100644 extensions/html-language-features/client/src/matchingTag.ts diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index 89bdc325721..194935b25fc 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -14,10 +14,14 @@ import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared'; import { activateTagClosing } from './tagClosing'; import TelemetryReporter from 'vscode-extension-telemetry'; import { getCustomDataPathsInAllWorkspaces, getCustomDataPathsFromAllExtensions } from './customData'; +import { activateMatchingTagPosition as activateMatchingTagSelection } from './matchingTag'; namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); } +namespace MatchingTagPositionRequest { + export const type: RequestType = new RequestType('html/matchingTagPosition'); +} interface IPackageInfo { name: string; @@ -84,6 +88,14 @@ export function activate(context: ExtensionContext) { disposable = activateTagClosing(tagRequestor, { html: true, handlebars: true }, 'html.autoClosingTags'); toDispose.push(disposable); + const matchingTagPositionRequestor = (document: TextDocument, position: Position) => { + let param = client.code2ProtocolConverter.asTextDocumentPositionParams(document, position); + return client.sendRequest(MatchingTagPositionRequest.type, param); + }; + + disposable = activateMatchingTagSelection(matchingTagPositionRequestor, { html: true, handlebars: true }, 'html.autoSelectingMatchingTags'); + toDispose.push(disposable); + disposable = client.onTelemetry(e => { if (telemetryReporter) { telemetryReporter.sendTelemetryEvent(e.key, e.data); diff --git a/extensions/html-language-features/client/src/matchingTag.ts b/extensions/html-language-features/client/src/matchingTag.ts new file mode 100644 index 00000000000..589504ce1bb --- /dev/null +++ b/extensions/html-language-features/client/src/matchingTag.ts @@ -0,0 +1,147 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { + window, + workspace, + Disposable, + TextDocument, + Position, + TextEditorSelectionChangeEvent, + Selection, + Range, + WorkspaceEdit +} from 'vscode'; + +export function activateMatchingTagPosition( + matchingTagPositionProvider: (document: TextDocument, position: Position) => Thenable, + supportedLanguages: { [id: string]: boolean }, + configName: string +): Disposable { + let disposables: Disposable[] = []; + + window.onDidChangeTextEditorSelection(event => onDidChangeTextEditorSelection(event), null, disposables); + + let isEnabled = false; + updateEnabledState(); + + window.onDidChangeActiveTextEditor(updateEnabledState, null, disposables); + + function updateEnabledState() { + isEnabled = false; + let editor = window.activeTextEditor; + if (!editor) { + return; + } + let document = editor.document; + if (!supportedLanguages[document.languageId]) { + return; + } + if (!workspace.getConfiguration(undefined, document.uri).get(configName)) { + return; + } + isEnabled = true; + } + + // let prevCursorCount = 0; + let cursorCount = 0; + let inMirrorMode = false; + + function onDidChangeTextEditorSelection(event: TextEditorSelectionChangeEvent) { + if (!isEnabled) { + return; + } + + // prevCursorCount = cursorCount; + cursorCount = event.selections.length; + + if (cursorCount === 1) { + if (event.selections[0].isEmpty) { + matchingTagPositionProvider(event.textEditor.document, event.selections[0].active).then(position => { + if (position && window.activeTextEditor) { + inMirrorMode = true; + const newCursor = new Selection(position.line, position.character, position.line, position.character); + window.activeTextEditor.selections = [...window.activeTextEditor.selections, newCursor]; + } + }); + } + } + + if (cursorCount === 2 && inMirrorMode) { + // Check two cases + if (event.selections[0].isEmpty && event.selections[1].isEmpty) { + const charBeforePrimarySelection = getCharBefore(event.textEditor.document, event.selections[0].anchor); + const charAfterPrimarySelection = getCharAfter(event.textEditor.document, event.selections[0].anchor); + const charBeforeSecondarySelection = getCharBefore(event.textEditor.document, event.selections[1].anchor); + const charAfterSecondarySelection = getCharAfter(event.textEditor.document, event.selections[1].anchor); + + // Exit mirror mode when cursor position no longer mirror + // Unless it's in the case of `<|>` + const charBeforeBothPositionRoughlyEqual = + charBeforePrimarySelection === charBeforeSecondarySelection || + (charBeforePrimarySelection === '/' && charBeforeSecondarySelection === '<') || + (charBeforeSecondarySelection === '/' && charBeforePrimarySelection === '<'); + const charAfterBothPositionRoughlyEqual = + charAfterPrimarySelection === charAfterSecondarySelection || + (charAfterPrimarySelection === ' ' && charAfterSecondarySelection === '>') || + (charAfterSecondarySelection === ' ' && charAfterPrimarySelection === '>'); + + if (!charBeforeBothPositionRoughlyEqual || !charAfterBothPositionRoughlyEqual) { + inMirrorMode = false; + window.activeTextEditor!.selections = [window.activeTextEditor!.selections[0]]; + return; + } else { + // Need to cleanup in the case of
+ if ( + charBeforePrimarySelection === ' ' && + charAfterPrimarySelection === '>' && + charBeforeSecondarySelection === ' ' && + charAfterSecondarySelection === '>' + ) { + inMirrorMode = false; + const cleanupEdit = new WorkspaceEdit(); + + const primaryBeforeSecondary = + event.textEditor.document.offsetAt(event.selections[0].anchor) < + event.textEditor.document.offsetAt(event.selections[1].anchor); + const cleanupRange = primaryBeforeSecondary + ? new Range(event.selections[1].anchor.translate(0, -1), event.selections[1].anchor) + : new Range(event.selections[0].anchor.translate(0, -1), event.selections[0].anchor); + + cleanupEdit.replace(event.textEditor.document.uri, cleanupRange, ''); + window.activeTextEditor!.selections = primaryBeforeSecondary + ? [window.activeTextEditor!.selections[0]] + : [window.activeTextEditor!.selections[1]]; + workspace.applyEdit(cleanupEdit); + } + } + } + } + } + + return Disposable.from(...disposables); +} + +function getCharBefore(document: TextDocument, position: Position) { + const offset = document.offsetAt(position); + if (offset === 0) { + return ''; + } + + return document.getText( + new Range(document.positionAt(offset - 1), position) + ); +} + +function getCharAfter(document: TextDocument, position: Position) { + const offset = document.offsetAt(position); + if (offset === document.getText().length) { + return ''; + } + + return document.getText( + new Range(position, document.positionAt(offset + 1)) + ); +} \ No newline at end of file diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 7c4f83d6b5b..a641eb4ca46 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -161,6 +161,12 @@ "default": true, "description": "%html.autoClosingTags%" }, + "html.autoSelectingMatchingTags": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%html.autoSelectingMatchingTags%" + }, "html.trace.server": { "type": "string", "scope": "window", diff --git a/extensions/html-language-features/package.nls.json b/extensions/html-language-features/package.nls.json index 2cb7e3d1697..4e4d666b9f7 100644 --- a/extensions/html-language-features/package.nls.json +++ b/extensions/html-language-features/package.nls.json @@ -24,5 +24,6 @@ "html.trace.server.desc": "Traces the communication between VS Code and the HTML language server.", "html.validate.scripts": "Controls whether the built-in HTML language support validates embedded scripts.", "html.validate.styles": "Controls whether the built-in HTML language support validates embedded styles.", - "html.autoClosingTags": "Enable/disable autoclosing of HTML tags." + "html.autoClosingTags": "Enable/disable autoclosing of HTML tags.", + "html.autoSelectingMatchingTags": "Enable/disable auto selecting matching HTML tags." } diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 0f3ff9f9497..be9e3fd2a21 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -10,7 +10,7 @@ "main": "./out/htmlServerMain", "dependencies": { "vscode-css-languageservice": "^4.0.3-next.20", - "vscode-html-languageservice": "^3.0.4-next.9", + "vscode-html-languageservice": "^3.0.4-next.10", "vscode-languageserver": "^6.0.0-next.3", "vscode-nls": "^4.1.1", "vscode-uri": "^2.0.3" diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index dcbbdca483b..8964b5ee035 100644 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/htmlServerMain.ts @@ -24,6 +24,9 @@ import { getDataProviders } from './customData'; namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); } +namespace MatchingTagPositionRequest { + export const type: RequestType = new RequestType('html/matchingTagPosition'); +} // Create a connection for the server const connection: IConnection = createConnection(); @@ -485,5 +488,21 @@ connection.onRenameRequest((params, token) => { }, null, `Error while computing rename for ${params.textDocument.uri}`, token); }); +connection.onRequest(MatchingTagPositionRequest.type, (params, token) => { + return runSafe(() => { + const document = documents.get(params.textDocument.uri); + if (document) { + const pos = params.position; + if (pos.character > 0) { + const mode = languageModes.getModeAtPosition(document, Position.create(pos.line, pos.character - 1)); + if (mode && mode.findMatchingTagPosition) { + return mode.findMatchingTagPosition(document, pos); + } + } + } + return null; + }, null, `Error while computing matching tag position for ${params.textDocument.uri}`, token); +}); + // Listen on the connection connection.listen(); diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 9c64fb3a9d7..23304a28ec7 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -81,6 +81,10 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: onDocumentRemoved(document: TextDocument) { htmlDocuments.onDocumentRemoved(document); }, + findMatchingTagPosition(document: TextDocument, position: Position) { + const htmlDocument = htmlDocuments.get(document); + return htmlLanguageService.findMatchingTagPosition(document, position, htmlDocument); + }, dispose() { htmlDocuments.dispose(); } diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index f50287330ea..a1fc67b27e1 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -48,6 +48,7 @@ export interface LanguageMode { findDocumentColors?: (document: TextDocument) => ColorInformation[]; getColorPresentations?: (document: TextDocument, color: Color, range: Range) => ColorPresentation[]; doAutoClose?: (document: TextDocument, position: Position) => string | null; + findMatchingTagPosition?: (document: TextDocument, position: Position) => Position | null; getFoldingRanges?: (document: TextDocument) => FoldingRange[]; onDocumentRemoved(document: TextDocument): void; dispose(): void; diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 439f360ddb5..07ad507e2ff 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -621,10 +621,10 @@ vscode-css-languageservice@^4.0.3-next.20: vscode-nls "^4.1.1" vscode-uri "^2.1.1" -vscode-html-languageservice@^3.0.4-next.9: - version "3.0.4-next.9" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.9.tgz#b27f26c29f3af64fa32eabb7425749f95f64036a" - integrity sha512-9V9G7508ybFcn9gQpuucEZIGv8kKlBMEVD8lFFWwWS1yEonKchsxIGJZFbmSGr/n//2anfya8F8yL5ybKuWIRA== +vscode-html-languageservice@^3.0.4-next.10: + version "3.0.4-next.10" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.10.tgz#da426326833770c51712abb2c7473b9b30bf1cbc" + integrity sha512-8P0QBtMPJ9nDMhW8MF/z+5JGg6rK6UOa9po18KIleNuV0rDHU9CAqDyUjxW0CEfLrHYz6dQdkW12ZTClvQnNHw== dependencies: vscode-languageserver-textdocument "^1.0.0-next.4" vscode-languageserver-types "^3.15.0-next.6" From 4e2cefda4c0ccd2ccdc26641c6aa0c7c5728b5ac Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Fri, 22 Nov 2019 10:21:25 -0800 Subject: [PATCH 241/246] Show notification on successful sign in --- .../workbench/contrib/userDataSync/browser/userDataSync.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 2a6b12b2adb..4e4b029b044 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -46,6 +46,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private readonly badgeDisposable = this._register(new MutableDisposable()); private readonly conflictsWarningDisposable = this._register(new MutableDisposable()); private readonly signInNotificationDisposable = this._register(new MutableDisposable()); + private previousAuthStatus: AuthTokenStatus | undefined; constructor( @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, @@ -86,8 +87,13 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this.authTokenContext.set(status); if (status === AuthTokenStatus.SignedIn) { this.signInNotificationDisposable.clear(); + + if (this.previousAuthStatus === AuthTokenStatus.SigningIn) { + this.notificationService.info(localize('signedIn', "Successfully signed in.")); + } } this.updateBadge(); + this.previousAuthStatus = status; } private onDidChangeSyncStatus(status: SyncStatus) { From 41f177e64e6a49c9554dee93ec9eda51962f4c7d Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Fri, 22 Nov 2019 11:38:17 -0800 Subject: [PATCH 242/246] Extract auth urls to product service --- .../auth/electron-browser/authTokenService.ts | 46 +++++++++++++------ .../platform/product/common/productService.ts | 7 +++ 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/vs/platform/auth/electron-browser/authTokenService.ts b/src/vs/platform/auth/electron-browser/authTokenService.ts index 301a6ae505c..971e8f7a967 100644 --- a/src/vs/platform/auth/electron-browser/authTokenService.ts +++ b/src/vs/platform/auth/electron-browser/authTokenService.ts @@ -13,17 +13,13 @@ import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { shell } from 'electron'; import { createServer, startServer } from 'vs/platform/auth/electron-browser/authServer'; +import { IProductService } from 'vs/platform/product/common/productService'; const SERVICE_NAME = 'VS Code'; const ACCOUNT = 'MyAccount'; -const redirectUrlAAD = 'https://vscode-redirect.azurewebsites.net/'; -const activeDirectoryEndpointUrl = 'https://login.microsoftonline.com/'; const activeDirectoryResourceId = 'https://management.core.windows.net/'; -const clientId = 'aebc6443-996d-45c2-90f0-388ff96faa56'; -const tenantId = 'common'; - function toQuery(obj: any): string { return Object.keys(obj).map(key => `${key}=${obj[key]}`).join('&'); } @@ -54,8 +50,13 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { constructor( @ICredentialsService private readonly credentialsService: ICredentialsService, + @IProductService private readonly productService: IProductService ) { super(); + if (!this.productService.auth) { + return; + } + this.credentialsService.getPassword(SERVICE_NAME, ACCOUNT).then(storedRefreshToken => { if (storedRefreshToken) { this.refresh(storedRefreshToken); @@ -66,6 +67,10 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { } public async login(): Promise { + if (!this.productService.auth) { + throw new Error('Authentication is not configured.'); + } + this.setStatus(AuthTokenStatus.SigningIn); const nonce = generateUuid(); @@ -88,14 +93,13 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { const updatedPort = updatedPortStr ? parseInt(updatedPortStr, 10) : port; const state = `${updatedPort},${encodeURIComponent(nonce)}`; - const signInUrl = `${activeDirectoryEndpointUrl}${tenantId}/oauth2/authorize`; const codeVerifier = toBase64UrlEncoding(crypto.randomBytes(32).toString('base64')); const codeChallenge = toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64')); - let uri = URI.parse(signInUrl); + let uri = URI.parse(this.productService.auth.loginUrl); uri = uri.with({ - query: `response_type=code&client_id=${encodeURIComponent(clientId)}&redirect_uri=${redirectUrlAAD}&state=${encodeURIComponent(state)}&resource=${activeDirectoryResourceId}&prompt=select_account&code_challenge_method=S256&code_challenge=${codeChallenge}` + query: `response_type=code&client_id=${encodeURIComponent(this.productService.auth.clientId)}&redirect_uri=${this.productService.auth.redirectUrl}&state=${encodeURIComponent(state)}&resource=${activeDirectoryResourceId}&prompt=select_account&code_challenge_method=S256&code_challenge=${codeChallenge}` }); await redirectReq.res.writeHead(302, { Location: uri.toString(true) }); @@ -145,17 +149,23 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { private exchangeCodeForToken(code: string, codeVerifier: string): Promise { return new Promise((resolve: (value: IToken) => void, reject) => { try { + if (!this.productService.auth) { + throw new Error('Authentication is not configured.'); + } + const postData = toQuery({ grant_type: 'authorization_code', code: code, - client_id: clientId, + client_id: this.productService.auth?.clientId, code_verifier: codeVerifier, - redirect_uri: redirectUrlAAD + redirect_uri: this.productService.auth?.redirectUrl }); + const tokenUrl = URI.parse(this.productService.auth.tokenUrl); + const post = https.request({ - host: 'login.microsoftonline.com', - path: `/${tenantId}/oauth2/token`, + host: tokenUrl.authority, + path: tokenUrl.path, method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -196,17 +206,23 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { private async refresh(refreshToken: string): Promise { return new Promise((resolve, reject) => { + if (!this.productService.auth) { + throw new Error('Authentication is not configured.'); + } + this.setStatus(AuthTokenStatus.RefreshingToken); const postData = toQuery({ refresh_token: refreshToken, - client_id: clientId, + client_id: this.productService.auth?.clientId, grant_type: 'refresh_token', resource: activeDirectoryResourceId }); + const tokenUrl = URI.parse(this.productService.auth.tokenUrl); + const post = https.request({ - host: 'login.microsoftonline.com', - path: `/${tenantId}/oauth2/token`, + host: tokenUrl.authority, + path: tokenUrl.path, method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index 68199163f02..6db9725704d 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -102,6 +102,13 @@ export interface IProductConfiguration { readonly msftInternalDomains?: string[]; readonly linkProtectionTrustedDomains?: readonly string[]; + + readonly auth?: { + loginUrl: string; + tokenUrl: string; + redirectUrl: string; + clientId: string; + }; } export interface IExeBasedExtensionTip { From 5ff010a97052fa3ed12d507cac6a7766cdb24300 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Fri, 22 Nov 2019 11:47:22 -0800 Subject: [PATCH 243/246] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e8fc3358ae3..1a0b119442e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.41.0", - "distro": "4d4d7ee0ae1852d22b319de4ccfdc53e864398c0", + "distro": "531a9666cfe4c178d84b8540af753fa5ee456f33", "author": { "name": "Microsoft Corporation" }, From 4e3af8a2954e38185261ad35132168f897e49583 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Fri, 22 Nov 2019 13:54:10 -0800 Subject: [PATCH 244/246] Remove textedit when adding space from ending tag --- .../client/src/matchingTag.ts | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/extensions/html-language-features/client/src/matchingTag.ts b/extensions/html-language-features/client/src/matchingTag.ts index 589504ce1bb..e8d9b5c4d80 100644 --- a/extensions/html-language-features/client/src/matchingTag.ts +++ b/extensions/html-language-features/client/src/matchingTag.ts @@ -100,21 +100,18 @@ export function activateMatchingTagPosition( charBeforeSecondarySelection === ' ' && charAfterSecondarySelection === '>' ) { - inMirrorMode = false; - const cleanupEdit = new WorkspaceEdit(); - const primaryBeforeSecondary = event.textEditor.document.offsetAt(event.selections[0].anchor) < event.textEditor.document.offsetAt(event.selections[1].anchor); - const cleanupRange = primaryBeforeSecondary - ? new Range(event.selections[1].anchor.translate(0, -1), event.selections[1].anchor) - : new Range(event.selections[0].anchor.translate(0, -1), event.selections[0].anchor); - cleanupEdit.replace(event.textEditor.document.uri, cleanupRange, ''); - window.activeTextEditor!.selections = primaryBeforeSecondary - ? [window.activeTextEditor!.selections[0]] - : [window.activeTextEditor!.selections[1]]; - workspace.applyEdit(cleanupEdit); + if (primaryBeforeSecondary) { + inMirrorMode = false; + const cleanupEdit = new WorkspaceEdit(); + const cleanupRange = new Range(event.selections[1].anchor.translate(0, -1), event.selections[1].anchor); + cleanupEdit.replace(event.textEditor.document.uri, cleanupRange, ''); + window.activeTextEditor!.selections = [window.activeTextEditor!.selections[0]]; + workspace.applyEdit(cleanupEdit); + } } } } @@ -130,9 +127,7 @@ function getCharBefore(document: TextDocument, position: Position) { return ''; } - return document.getText( - new Range(document.positionAt(offset - 1), position) - ); + return document.getText(new Range(document.positionAt(offset - 1), position)); } function getCharAfter(document: TextDocument, position: Position) { @@ -141,7 +136,5 @@ function getCharAfter(document: TextDocument, position: Position) { return ''; } - return document.getText( - new Range(position, document.positionAt(offset + 1)) - ); -} \ No newline at end of file + return document.getText(new Range(position, document.positionAt(offset + 1))); +} From 84aa1ada9fd7bd44a107851f29e9c2f6cd7c109b Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Fri, 22 Nov 2019 13:59:26 -0800 Subject: [PATCH 245/246] ESC to exit mirror tag position mode --- .../html-language-features/client/src/matchingTag.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/extensions/html-language-features/client/src/matchingTag.ts b/extensions/html-language-features/client/src/matchingTag.ts index e8d9b5c4d80..6816c2c5e32 100644 --- a/extensions/html-language-features/client/src/matchingTag.ts +++ b/extensions/html-language-features/client/src/matchingTag.ts @@ -45,7 +45,7 @@ export function activateMatchingTagPosition( isEnabled = true; } - // let prevCursorCount = 0; + let prevCursorCount = 0; let cursorCount = 0; let inMirrorMode = false; @@ -54,10 +54,13 @@ export function activateMatchingTagPosition( return; } - // prevCursorCount = cursorCount; + prevCursorCount = cursorCount; cursorCount = event.selections.length; if (cursorCount === 1) { + if (inMirrorMode && prevCursorCount === 2) { + return; + } if (event.selections[0].isEmpty) { matchingTagPositionProvider(event.textEditor.document, event.selections[0].active).then(position => { if (position && window.activeTextEditor) { From 56a544b36cfaf48fe7530457839b80f70091af87 Mon Sep 17 00:00:00 2001 From: Jean Pierre Date: Fri, 22 Nov 2019 17:33:33 -0500 Subject: [PATCH 246/246] Don't encode image resource URI twice (#85355) --- extensions/image-preview/src/preview.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index f78061d008e..dd461b519e0 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -240,9 +240,9 @@ class Preview extends Disposable implements vscode.WebviewEditorEditingCapabilit default: // Avoid adding cache busting if there is already a query string if (resource.query) { - return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true)); + return webviewEditor.webview.asWebviewUri(resource).toString(true); } - return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true) + `?version=${version}`); + return webviewEditor.webview.asWebviewUri(resource).with({ query: `version=${version}` }).toString(true); } }