From d7ed37e864c6a21b7012b96598d84b4576d7f0ac Mon Sep 17 00:00:00 2001 From: Fmstrat Date: Thu, 16 Jan 2020 09:39:19 -0500 Subject: [PATCH 0001/1882] add ignoreSubmodules option --- extensions/git/package.json | 6 ++++++ extensions/git/package.nls.json | 1 + extensions/git/src/git.ts | 10 ++++++++-- extensions/git/src/repository.ts | 3 +++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 3e049ddd7e5a9..75aa30835466b 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1512,6 +1512,12 @@ "default": false, "description": "%config.alwaysSignOff%" }, + "git.ignoreSubmodules": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.ignoreSubmodules%" + }, "git.ignoredRepositories": { "type": "array", "items": { diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 5c357b40eb079..a892d87d512cd 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -121,6 +121,7 @@ "config.detectSubmodulesLimit": "Controls the limit of git submodules detected.", "config.alwaysShowStagedChangesResourceGroup": "Always show the Staged Changes resource group.", "config.alwaysSignOff": "Controls the signoff flag for all commits.", + "config.ignoreSubmodules": "Ignore modifications to submodules in the file tree.", "config.ignoredRepositories": "List of git repositories to ignore.", "config.scanRepositories": "List of paths to search for git repositories in.", "config.showProgress": "Controls whether git actions should show progress.", diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index af92f83c1a674..322765dba4c88 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -12,7 +12,7 @@ import { EventEmitter } from 'events'; import iconv = require('iconv-lite'); import * as filetype from 'file-type'; import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter } from './util'; -import { CancellationToken, Progress } from 'vscode'; +import { CancellationToken, Progress, workspace } from 'vscode'; import { URI } from 'vscode-uri'; import { detectEncoding } from './encoding'; import { Ref, RefType, Branch, Remote, GitErrorCodes, LogOptions, Change, Status } from './api/git'; @@ -1619,7 +1619,13 @@ export class Repository { return new Promise<{ status: IFileStatus[]; didHitLimit: boolean; }>((c, e) => { const parser = new GitStatusParser(); const env = { GIT_OPTIONAL_LOCKS: '0' }; - const child = this.stream(['status', '-z', '-u'], { env }); + + const config = workspace.getConfiguration('git'); + const args = ['status', '-z', '-u']; + if (config.get('ignoreSubmodules')) { + args.push('--ignore-submodules'); + } + const child = this.stream(args, { env }); const onExit = (exitCode: number) => { if (exitCode !== 0) { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 47832f9e0f06e..361a35a64b93f 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -731,6 +731,9 @@ export class Repository implements Disposable { const onConfigListenerForUntracked = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.untrackedChanges', root)); onConfigListenerForUntracked(this.updateModelState, this, this.disposables); + const onConfigListenerForIgnoreSubmodules = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.ignoreSubmodules', root)); + onConfigListenerForIgnoreSubmodules(this.updateModelState, this, this.disposables); + this.mergeGroup.hideWhenEmpty = true; this.untrackedGroup.hideWhenEmpty = true; From 2abdb90472470865d7e28cfbdbcd60b9ecf3d64a Mon Sep 17 00:00:00 2001 From: Oliver Larsson Date: Fri, 24 Jan 2020 21:20:44 +0100 Subject: [PATCH 0002/1882] git.pruneOnFetch setting implemented --- extensions/git/package.json | 6 ++++++ extensions/git/package.nls.json | 1 + extensions/git/src/repository.ts | 21 +++++++++++++++++---- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 2ba6515e963f9..ace8e1bd98438 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1548,6 +1548,12 @@ "default": false, "description": "%config.fetchOnPull%" }, + "git.pruneOnFetch": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.pruneOnFetch%" + }, "git.pullTags": { "type": "boolean", "scope": "resource", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 5c357b40eb079..0b269e889dcb5 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -128,6 +128,7 @@ "config.confirmEmptyCommits": "Always confirm the creation of empty commits for the 'Git: Commit Empty' command.", "config.fetchOnPull": "Fetch all branches when pulling or just the current one.", "config.pullTags": "Fetch all tags when pulling.", + "config.pruneOnFetch": "Always prune when fetching.", "config.autoStash": "Stash any changes before pulling and restore them after successful pull.", "config.allowForcePush": "Controls whether force push (with or without lease) is enabled.", "config.useForcePushWithLease": "Controls whether force pushing uses the safer force-with-lease variant.", diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 47832f9e0f06e..79f90cfdaeb09 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1072,21 +1072,34 @@ export class Repository implements Disposable { @throttle async fetchDefault(options: { silent?: boolean } = {}): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch(options)); + await this.fetchFrom({ silent: options.silent }); } @throttle async fetchPrune(): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch({ prune: true })); + await this.fetchFrom({ prune: true }); } @throttle async fetchAll(): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch({ all: true })); + await this.fetchFrom({ all: true }); } async fetch(remote?: string, ref?: string, depth?: number): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch({ remote, ref, depth })); + await this.fetchFrom({ remote, ref, depth }); + } + + private async fetchFrom(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean } = {}): Promise { + await this.run(Operation.Fetch, async () => { + const config = workspace.getConfiguration('git', Uri.file(this.root)); + const prune = config.get('pruneOnFetch'); + + if (prune) { + options.prune = prune; + } + + this.repository.fetch(options); + }); } @throttle From 712ceb8279a0da8652bae92d497e48a887c98684 Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Sat, 25 Jan 2020 23:59:15 -0500 Subject: [PATCH 0003/1882] Fixes #89145 --- .../ui/tree/compressedObjectTreeModel.ts | 6 +- .../contrib/scm/browser/repositoryPane.ts | 82 +++++++++++++------ 2 files changed, 62 insertions(+), 26 deletions(-) diff --git a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts index f1da43550471e..54cb4e1de0063 100644 --- a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts @@ -49,11 +49,11 @@ export function compress(element: ICompressedTreeElement): ITreeElement = children[0]; + if (childElement.incompressible) { break; } + element = childElement; elements.push(element.element); } diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index 81f88f32cd79e..075102cb8dd2d 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -367,20 +367,24 @@ export class SCMTreeKeyboardNavigationLabelProvider implements ICompressibleKeyb } } +function getSCMResourceId(element: TreeElement): string { + if (ResourceTree.isResourceNode(element)) { + const group = element.context; + return `${group.provider.contextValue}/${group.id}/$FOLDER/${element.uri.toString()}`; + } else if (isSCMResource(element)) { + const group = element.resourceGroup; + const provider = group.provider; + return `${provider.contextValue}/${group.id}/${element.sourceUri.toString()}`; + } else { + const provider = element.provider; + return `${provider.contextValue}/${element.id}`; + } +} + class SCMResourceIdentityProvider implements IIdentityProvider { getId(element: TreeElement): string { - if (ResourceTree.isResourceNode(element)) { - const group = element.context; - return `${group.provider.contextValue}/${group.id}/$FOLDER/${element.uri.toString()}`; - } else if (isSCMResource(element)) { - const group = element.resourceGroup; - const provider = group.provider; - return `${provider.contextValue}/${group.id}/${element.sourceUri.toString()}`; - } else { - const provider = element.provider; - return `${provider.contextValue}/${element.id}`; - } + return getSCMResourceId(element); } } @@ -391,19 +395,31 @@ interface IGroupItem { readonly disposable: IDisposable; } -function groupItemAsTreeElement(item: IGroupItem, mode: ViewModelMode): ICompressedTreeElement { +interface IViewState { + readonly expanded: Set; +} + +function groupItemAsTreeElement(item: IGroupItem, mode: ViewModelMode, viewState?: IViewState): ICompressedTreeElement { const children = mode === ViewModelMode.List ? Iterator.map(Iterator.fromArray(item.resources), element => ({ element, incompressible: true })) - : Iterator.map(item.tree.root.children, node => asTreeElement(node, true)); + : Iterator.map(item.tree.root.children, node => asTreeElement(node, true, viewState)); - return { element: item.group, children, incompressible: true, collapsible: true }; + const element = item.group; + const collapsed = viewState ? !viewState.expanded.has(getSCMResourceId(element)) : false; + + return { element, children, incompressible: true, collapsed, collapsible: true }; } -function asTreeElement(node: IResourceNode, forceIncompressible: boolean): ICompressedTreeElement { +function asTreeElement(node: IResourceNode, forceIncompressible: boolean, viewState?: IViewState): ICompressedTreeElement { + const element = (node.childrenCount === 0 && node.element) ? node.element : node; + const collapsed = viewState ? !viewState.expanded.has(getSCMResourceId(element)) : false; + return { - element: (node.childrenCount === 0 && node.element) ? node.element : node, - children: Iterator.map(node.children, node => asTreeElement(node, false)), - incompressible: !!node.element || forceIncompressible + element, + children: Iterator.map(node.children, node => asTreeElement(node, false, viewState)), + incompressible: !!node.element || forceIncompressible, + collapsed, + collapsible: node.childrenCount > 0 }; } @@ -439,6 +455,7 @@ class ViewModel { private visibilityDisposables = new DisposableStore(); private scrollTop: number | undefined; private firstVisible = true; + private viewState: IViewState | undefined; private disposables = new DisposableStore(); constructor( @@ -449,7 +466,7 @@ class ViewModel { @IConfigurationService protected configurationService: IConfigurationService, ) { } - private onDidSpliceGroups({ start, deleteCount, toInsert }: ISplice): void { + private onDidSpliceGroups({ start, deleteCount, toInsert }: ISplice, viewState?: IViewState): void { const itemsToInsert: IGroupItem[] = []; for (const group of toInsert) { @@ -477,7 +494,7 @@ class ViewModel { item.disposable.dispose(); } - this.refresh(); + this.refresh(undefined, viewState); } private onDidSpliceGroup(item: IGroupItem, { start, deleteCount, toInsert }: ISplice): void { @@ -500,7 +517,8 @@ class ViewModel { if (visible) { this.visibilityDisposables = new DisposableStore(); this.groups.onDidSplice(this.onDidSpliceGroups, this, this.visibilityDisposables); - this.onDidSpliceGroups({ start: 0, deleteCount: this.items.length, toInsert: this.groups.elements }); + this.onDidSpliceGroups({ start: 0, deleteCount: this.items.length, toInsert: this.groups.elements }, this.viewState); + this.viewState = undefined; if (typeof this.scrollTop === 'number') { this.tree.scrollTop = this.scrollTop; @@ -510,20 +528,38 @@ class ViewModel { this.editorService.onDidActiveEditorChange(this.onDidActiveEditorChange, this, this.visibilityDisposables); this.onDidActiveEditorChange(); } else { + this.updateViewState(); this.visibilityDisposables.dispose(); this.onDidSpliceGroups({ start: 0, deleteCount: this.items.length, toInsert: [] }); this.scrollTop = this.tree.scrollTop; } } - private refresh(item?: IGroupItem): void { + private refresh(item?: IGroupItem, viewState?: IViewState): void { if (item) { this.tree.setChildren(item.group, groupItemAsTreeElement(item, this.mode).children); } else { - this.tree.setChildren(null, this.items.map(item => groupItemAsTreeElement(item, this.mode))); + this.tree.setChildren(null, this.items.map(item => groupItemAsTreeElement(item, this.mode, viewState))); } } + private updateViewState(): void { + const expanded = new Set(); + const visit = (node: ITreeNode) => { + if (node.element && node.collapsible && !node.collapsed) { + expanded.add(getSCMResourceId(node.element)); + } + + for (const child of node.children) { + visit(child); + } + }; + + visit(this.tree.getNode()); + + this.viewState = { expanded }; + } + private onDidActiveEditorChange(): void { if (!this.configurationService.getValue('scm.autoReveal')) { return; From cdc6c051e5e710e1851effaf1b9ff3f685baea11 Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Mon, 27 Jan 2020 03:36:01 -0500 Subject: [PATCH 0004/1882] Persist scm tree view state between sessions --- .../contrib/scm/browser/repositoryPane.ts | 74 +++++++++++-------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index 075102cb8dd2d..a91fc19e5a496 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -395,24 +395,24 @@ interface IGroupItem { readonly disposable: IDisposable; } -interface IViewState { - readonly expanded: Set; +interface ITreeViewState { + readonly expanded: string[]; } -function groupItemAsTreeElement(item: IGroupItem, mode: ViewModelMode, viewState?: IViewState): ICompressedTreeElement { +function groupItemAsTreeElement(item: IGroupItem, mode: ViewModelMode, viewState?: ITreeViewState): ICompressedTreeElement { const children = mode === ViewModelMode.List ? Iterator.map(Iterator.fromArray(item.resources), element => ({ element, incompressible: true })) : Iterator.map(item.tree.root.children, node => asTreeElement(node, true, viewState)); const element = item.group; - const collapsed = viewState ? !viewState.expanded.has(getSCMResourceId(element)) : false; + const collapsed = viewState ? viewState.expanded.indexOf(getSCMResourceId(element)) === -1 : false; return { element, children, incompressible: true, collapsed, collapsible: true }; } -function asTreeElement(node: IResourceNode, forceIncompressible: boolean, viewState?: IViewState): ICompressedTreeElement { +function asTreeElement(node: IResourceNode, forceIncompressible: boolean, viewState?: ITreeViewState): ICompressedTreeElement { const element = (node.childrenCount === 0 && node.element) ? node.element : node; - const collapsed = viewState ? !viewState.expanded.has(getSCMResourceId(element)) : false; + const collapsed = viewState ? viewState.expanded.indexOf(getSCMResourceId(element)) === -1 : false; return { element, @@ -451,22 +451,27 @@ class ViewModel { this._onDidChangeMode.fire(mode); } + get treeViewState(): ITreeViewState | undefined { + this.updateViewState(); + return this._treeViewState; + } + private items: IGroupItem[] = []; private visibilityDisposables = new DisposableStore(); private scrollTop: number | undefined; private firstVisible = true; - private viewState: IViewState | undefined; private disposables = new DisposableStore(); constructor( private groups: ISequence, private tree: WorkbenchCompressibleObjectTree, private _mode: ViewModelMode, + private _treeViewState: ITreeViewState | undefined, @IEditorService protected editorService: IEditorService, - @IConfigurationService protected configurationService: IConfigurationService, + @IConfigurationService protected configurationService: IConfigurationService ) { } - private onDidSpliceGroups({ start, deleteCount, toInsert }: ISplice, viewState?: IViewState): void { + private onDidSpliceGroups({ start, deleteCount, toInsert }: ISplice): void { const itemsToInsert: IGroupItem[] = []; for (const group of toInsert) { @@ -494,7 +499,7 @@ class ViewModel { item.disposable.dispose(); } - this.refresh(undefined, viewState); + this.refresh(undefined, toInsert.length > 0 ? this._treeViewState : undefined); } private onDidSpliceGroup(item: IGroupItem, { start, deleteCount, toInsert }: ISplice): void { @@ -517,8 +522,7 @@ class ViewModel { if (visible) { this.visibilityDisposables = new DisposableStore(); this.groups.onDidSplice(this.onDidSpliceGroups, this, this.visibilityDisposables); - this.onDidSpliceGroups({ start: 0, deleteCount: this.items.length, toInsert: this.groups.elements }, this.viewState); - this.viewState = undefined; + this.onDidSpliceGroups({ start: 0, deleteCount: this.items.length, toInsert: this.groups.elements }); if (typeof this.scrollTop === 'number') { this.tree.scrollTop = this.scrollTop; @@ -535,19 +539,19 @@ class ViewModel { } } - private refresh(item?: IGroupItem, viewState?: IViewState): void { + private refresh(item?: IGroupItem, treeViewState?: ITreeViewState): void { if (item) { this.tree.setChildren(item.group, groupItemAsTreeElement(item, this.mode).children); } else { - this.tree.setChildren(null, this.items.map(item => groupItemAsTreeElement(item, this.mode, viewState))); + this.tree.setChildren(null, this.items.map(item => groupItemAsTreeElement(item, this.mode, treeViewState))); } } private updateViewState(): void { - const expanded = new Set(); + const expanded: string[] = []; const visit = (node: ITreeNode) => { if (node.element && node.collapsible && !node.collapsed) { - expanded.add(getSCMResourceId(node.element)); + expanded.push(getSCMResourceId(node.element)); } for (const child of node.children) { @@ -557,7 +561,7 @@ class ViewModel { visit(this.tree.getNode()); - this.viewState = { expanded }; + this._treeViewState = { expanded }; } private onDidActiveEditorChange(): void { @@ -883,18 +887,27 @@ export class RepositoryPane extends ViewPane { this._register(this.tree); let mode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewModelMode.List : ViewModelMode.Tree; + let treeViewState: ITreeViewState | undefined; const rootUri = this.repository.provider.rootUri; if (typeof rootUri !== 'undefined') { - const storageMode = this.storageService.get(`scm.repository.viewMode:${rootUri.toString()}`, StorageScope.WORKSPACE) as ViewModelMode; + const raw = this.storageService.get(`scm.repository.viewState:${rootUri.toString()}`, StorageScope.WORKSPACE); + if (raw) { + let data: any; + try { + data = JSON.parse(raw); + } catch (e) { + } - if (typeof storageMode === 'string') { - mode = storageMode; + if (typeof data.mode === 'string') { + mode = data.mode; + } + treeViewState = data.treeViewState; } } - this.viewModel = this.instantiationService.createInstance(ViewModel, this.repository.provider.groups, this.tree, mode); + this.viewModel = this.instantiationService.createInstance(ViewModel, this.repository.provider.groups, this.tree, mode, treeViewState); this._register(this.viewModel); addClass(this.listContainer, 'file-icon-themable-tree'); @@ -910,6 +923,17 @@ export class RepositoryPane extends ViewPane { this._register(this.onDidChangeBodyVisibility(this._onDidChangeVisibility, this)); this.updateActions(); + + this._register(this.storageService.onWillSaveState(() => { + if (typeof rootUri === 'undefined') { + return; + } + + this.storageService.store(`scm.repository.viewState:${rootUri.toString()}`, JSON.stringify({ + mode: this.viewModel.mode, + treeViewState: this.viewModel.treeViewState + }), StorageScope.WORKSPACE); + })); } private updateIndentStyles(theme: IFileIconTheme): void { @@ -921,14 +945,6 @@ export class RepositoryPane extends ViewPane { private onDidChangeMode(): void { this.updateIndentStyles(this.themeService.getFileIconTheme()); - - const rootUri = this.repository.provider.rootUri; - - if (typeof rootUri === 'undefined') { - return; - } - - this.storageService.store(`scm.repository.viewMode:${rootUri.toString()}`, this.viewModel.mode, StorageScope.WORKSPACE); } layoutBody(height: number | undefined = this.cachedHeight, width: number | undefined = this.cachedWidth): void { From f23fcb72f5595ea0192d5a2c5f4eff7d0a47410c Mon Sep 17 00:00:00 2001 From: Mathias Rasmussen Date: Sun, 1 Mar 2020 05:14:00 +0100 Subject: [PATCH 0005/1882] allow git amend message only --- extensions/git/src/commands.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index a27fa783fc044..477021ce2c5b5 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1408,6 +1408,8 @@ export class CommandCenter { // no staged changes and no tracked unstaged changes || (noStagedChanges && smartCommitChanges === 'tracked' && repository.workingTreeGroup.resourceStates.every(r => r.type === Status.UNTRACKED)) ) + // amend allows changing only the commit message + && !opts.amend && !opts.empty ) { window.showInformationMessage(localize('no changes', "There are no changes to commit.")); From 37bca69ff169e7dedcd7d4a5bda81876fc880c12 Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Sun, 1 Mar 2020 22:09:20 -0500 Subject: [PATCH 0006/1882] :lipstick: --- src/vs/workbench/contrib/scm/browser/repositoryPane.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index 2acefed09cc73..0206d0fa72c28 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -457,7 +457,7 @@ class ViewModel { } } - this.refresh(); + this.refresh(undefined, this._treeViewState); this._onDidChangeMode.fire(mode); } @@ -479,7 +479,9 @@ class ViewModel { private _treeViewState: ITreeViewState | undefined, @IEditorService protected editorService: IEditorService, @IConfigurationService protected configurationService: IConfigurationService - ) { } + ) { + this.disposables.add(this.tree.onDidChangeCollapseState(() => this.updateViewState())); + } private onDidSpliceGroups({ start, deleteCount, toInsert }: ISplice): void { const itemsToInsert: IGroupItem[] = []; @@ -509,7 +511,7 @@ class ViewModel { item.disposable.dispose(); } - this.refresh(undefined, toInsert.length > 0 ? this._treeViewState : undefined); + this.refresh(undefined, this._treeViewState); } private onDidSpliceGroup(item: IGroupItem, { start, deleteCount, toInsert }: ISplice): void { @@ -551,7 +553,7 @@ class ViewModel { private refresh(item?: IGroupItem, treeViewState?: ITreeViewState): void { if (item) { - this.tree.setChildren(item.group, groupItemAsTreeElement(item, this.mode).children); + this.tree.setChildren(item.group, groupItemAsTreeElement(item, this.mode, treeViewState).children); } else { this.tree.setChildren(null, this.items.map(item => groupItemAsTreeElement(item, this.mode, treeViewState))); } From bc85a9ffdb3c1644cfd11a610ce846c6ad22cb06 Mon Sep 17 00:00:00 2001 From: Jacob Date: Thu, 2 Apr 2020 17:34:07 +0200 Subject: [PATCH 0007/1882] Added user choice for opening the folder always. --- extensions/git/src/commands.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 451ff28afca5d..68299863db7de 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -514,7 +514,8 @@ export class CommandCenter { let message = localize('proposeopen', "Would you like to open the cloned repository?"); const open = localize('openrepo', "Open"); const openNewWindow = localize('openreponew', "Open in New Window"); - const choices = [open, openNewWindow]; + const openAlways = localize('openrepoalways', "Always open after cloning"); + const choices = [open, openNewWindow, openAlways]; const addToWorkspace = localize('add', "Add to Workspace"); if (workspace.workspaceFolders) { @@ -541,6 +542,9 @@ export class CommandCenter { workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri }); } else if (result === openNewWindow) { commands.executeCommand('vscode.openFolder', uri, true); + } else if (result === openAlways) { + commands.executeCommand('vscode.openFolder', uri); + // will add a command for always option later } } catch (err) { if (/already exists and is not an empty directory/.test(err && err.stderr || '')) { From 412a44e9bce95688dc9bb87c561cc3321217b6b1 Mon Sep 17 00:00:00 2001 From: Dmitry Sharshakov Date: Fri, 3 Apr 2020 12:51:59 +0300 Subject: [PATCH 0008/1882] Git: ask to save unsaved files before stashing --- extensions/git/src/commands.ts | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 451ff28afca5d..c4a591c1e85d8 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2242,6 +2242,44 @@ export class CommandCenter { return; } + const config = workspace.getConfiguration('git', Uri.file(repository.root)); + let promptToSaveFilesBeforeCommit = config.get<'always' | 'staged' | 'never'>('promptToSaveFilesBeforeCommit'); + + // migration + if (promptToSaveFilesBeforeCommit as any === true) { + promptToSaveFilesBeforeCommit = 'always'; + } else if (promptToSaveFilesBeforeCommit as any === false) { + promptToSaveFilesBeforeCommit = 'never'; + } + + if (promptToSaveFilesBeforeCommit !== 'never') { + let documents = workspace.textDocuments + .filter(d => !d.isUntitled && d.isDirty && isDescendant(repository.root, d.uri.fsPath)); + + if (promptToSaveFilesBeforeCommit === 'staged' || repository.indexGroup.resourceStates.length > 0) { + documents = documents + .filter(d => repository.indexGroup.resourceStates.some(s => pathEquals(s.resourceUri.fsPath, d.uri.fsPath))); + } + + if (documents.length > 0) { + const message = documents.length === 1 + ? localize('unsaved stash files single', "The following file has unsaved changes which won't be included in the stash if you proceed: {0}.\n\nWould you like to save it before committing?", path.basename(documents[0].uri.fsPath)) + : localize('unsaved stash files', "There are {0} unsaved files.\n\nWould you like to save them before stashing?", documents.length); + const saveAndStash = localize('save and stash', "Save All & Stash"); + const stash = localize('stash', "Stash Anyway"); + const pick = await window.showWarningMessage(message, { modal: true }, saveAndStash, stash); + + if (pick === saveAndStash) { + await Promise.all(documents.map(d => d.save())); + if (!includeUntracked) { + await repository.add(documents.map(d => d.uri)); + } + } else if (pick !== stash) { + return; // do not stash on cancel + } + } + } + const message = await this.getStashMessage(); if (typeof message === 'undefined') { From 156d5ab2812eb244473f7cded67bb4b68b63af1e Mon Sep 17 00:00:00 2001 From: Jacob Date: Fri, 3 Apr 2020 13:43:50 +0200 Subject: [PATCH 0009/1882] Added setting for opening cloned repository without prompt. #93300 --- extensions/git/package.json | 17 ++++++++ extensions/git/package.nls.json | 5 +++ extensions/git/src/commands.ts | 69 ++++++++++++++++++++------------- 3 files changed, 63 insertions(+), 28 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 75306b8a78512..9eca6c7791c0c 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1467,6 +1467,23 @@ "scope": "resource", "default": "none" }, + "git.promptToOpenClonedRepository": { + "type": "string", + "enum": [ + "currentWindow", + "newWindow", + "noFolderOpened", + "showPrompt" + ], + "enumDescriptions": [ + "%config.promptToOpenClonedRepository.currentWindow%", + "%config.promptToOpenClonedRepository.newWindow%", + "%config.promptToOpenClonedRepository.noFolderOpened%", + "%config.promptToOpenClonedRepository.showPrompt%" + ], + "default": "showPrompt", + "description": "%config.promptToOpenClonedRepository%" + }, "git.showInlineOpenFileAction": { "type": "boolean", "default": true, diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index e163ffad48d3b..3f847c92fee0e 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -116,6 +116,11 @@ "config.postCommitCommand.none": "Don't run any command after a commit.", "config.postCommitCommand.push": "Run 'Git Push' after a successful commit.", "config.postCommitCommand.sync": "Run 'Git Sync' after a successful commit.", + "config.promptToOpenClonedRepository": "Controls whether to show a prompt after cloning a repository.", + "config.promptToOpenClonedRepository.currentWindow": "Open repository in current window.", + "config.promptToOpenClonedRepository.newWindow": "Open repository in new window.", + "config.promptToOpenClonedRepository.noFolderOpened": "Open in current window if no folder is opened. Otherwise show prompt.", + "config.promptToOpenClonedRepository.showPrompt": "Always show prompt to choose action.", "config.showInlineOpenFileAction": "Controls whether to show an inline Open File action in the Git changes view.", "config.showPushSuccessNotification": "Controls whether to show a notification when a push is successful.", "config.inputValidation": "Controls when to show commit message input validation.", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 68299863db7de..f36772e6fd036 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -511,41 +511,54 @@ export class CommandCenter { (progress, token) => this.git.clone(url!, parentPath!, progress, token) ); - let message = localize('proposeopen', "Would you like to open the cloned repository?"); - const open = localize('openrepo', "Open"); - const openNewWindow = localize('openreponew', "Open in New Window"); - const openAlways = localize('openrepoalways', "Always open after cloning"); - const choices = [open, openNewWindow, openAlways]; + let config = workspace.getConfiguration('git'); + let promptToOpenClonedRepository = config.get<'currentWindow' | 'newWindow' | 'noFolderOpened' | 'showPrompt'>('promptToOpenClonedRepository'); - const addToWorkspace = localize('add', "Add to Workspace"); - if (workspace.workspaceFolders) { - message = localize('proposeopen2', "Would you like to open the cloned repository, or add it to the current workspace?"); - choices.push(addToWorkspace); - } + const uri = Uri.file(repositoryPath); - const result = await window.showInformationMessage(message, ...choices); + if (promptToOpenClonedRepository === 'currentWindow') { + commands.executeCommand('vscode.openFolder', uri); + } else if (promptToOpenClonedRepository === 'newWindow') { + commands.executeCommand('vscode.openFolder', uri, true); + } else { + if (promptToOpenClonedRepository === 'noFolderOpened') { + if (!workspace.workspaceFolders) { + commands.executeCommand('vscode.openFolder', uri); + } + } - const openFolder = result === open; - /* __GDPR__ - "clone" : { - "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "openFolder": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } + let message = localize('proposeopen', "Would you like to open the cloned repository?"); + const open = localize('openrepo', "Open"); + const openNewWindow = localize('openreponew', "Open in New Window"); + const choices = [open, openNewWindow]; + + const addToWorkspace = localize('add', "Add to Workspace"); + if (workspace.workspaceFolders) { + message = localize('proposeopen2', "Would you like to open the cloned repository, or add it to the current workspace?"); + choices.push(addToWorkspace); } - */ - this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'success' }, { openFolder: openFolder ? 1 : 0 }); - const uri = Uri.file(repositoryPath); + const result = await window.showInformationMessage(message, ...choices); - if (openFolder) { - commands.executeCommand('vscode.openFolder', uri); - } else if (result === addToWorkspace) { - workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri }); - } else if (result === openNewWindow) { - commands.executeCommand('vscode.openFolder', uri, true); - } else if (result === openAlways) { - commands.executeCommand('vscode.openFolder', uri); - // will add a command for always option later + + const openFolder = result === open; + /* __GDPR__ + "clone" : { + "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "openFolder": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } + } + */ + this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'success' }, { openFolder: openFolder ? 1 : 0 }); + + if (openFolder) { + commands.executeCommand('vscode.openFolder', uri); + } else if (result === addToWorkspace) { + workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri }); + } else if (result === openNewWindow) { + commands.executeCommand('vscode.openFolder', uri, true); + } } + } catch (err) { if (/already exists and is not an empty directory/.test(err && err.stderr || '')) { /* __GDPR__ From 4420bbfbcf7d284eb95132b23b5b82bc01b19a40 Mon Sep 17 00:00:00 2001 From: Nathaniel Palmer Date: Mon, 13 Apr 2020 17:13:39 -0400 Subject: [PATCH 0010/1882] Offer to show git command output on failure --- extensions/git/src/commands.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index e8a696735fc48..b4ec7cc525ba7 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -236,6 +236,7 @@ interface PushOptions { export class CommandCenter { private disposables: Disposable[]; + private lastCommandErrorOutput = ''; constructor( private git: Git, @@ -252,6 +253,13 @@ export class CommandCenter { return commands.registerCommand(commandId, command); } }); + this.disposables.push( + workspace.registerTextDocumentContentProvider('git-output', this) + ); + } + + async provideTextDocumentContent(): Promise { + return this.lastCommandErrorOutput; } @command('git.setLogLevel') @@ -2435,6 +2443,16 @@ export class CommandCenter { const openOutputChannelChoice = localize('open git log', "Open Git Log"); const outputChannel = this.outputChannel as OutputChannel; choices.set(openOutputChannelChoice, () => outputChannel.show()); + const showCommandOutputChoice = localize('show command output', 'Show Command Output'); + if (err.stderr) { + choices.set(showCommandOutputChoice, () => { + this.lastCommandErrorOutput = err.stderr; + const uri = Uri.parse(`git-output://command-error/${err.gitCommand}-${Math.random().toString(16).slice(2, 10)}`); + workspace.openTextDocument(uri).then(doc => { + return window.showTextDocument(doc); + }); + }); + } switch (err.gitErrorCode) { case GitErrorCodes.DirtyWorkTree: From 1243ff76e4e66585418131c634de5a82070a3164 Mon Sep 17 00:00:00 2001 From: Jacob Date: Wed, 22 Apr 2020 14:27:55 +0200 Subject: [PATCH 0011/1882] Changed name of the setting to openAfterClone --- extensions/git/package.json | 12 ++++++------ extensions/git/package.nls.json | 10 +++++----- extensions/git/src/commands.ts | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 7ba43eedaa66b..8d098c504a38f 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1507,7 +1507,7 @@ "scope": "resource", "default": "none" }, - "git.promptToOpenClonedRepository": { + "git.openAfterClone": { "type": "string", "enum": [ "currentWindow", @@ -1516,13 +1516,13 @@ "showPrompt" ], "enumDescriptions": [ - "%config.promptToOpenClonedRepository.currentWindow%", - "%config.promptToOpenClonedRepository.newWindow%", - "%config.promptToOpenClonedRepository.noFolderOpened%", - "%config.promptToOpenClonedRepository.showPrompt%" + "%config.openAfterClone.currentWindow%", + "%config.openAfterClone.newWindow%", + "%config.openAfterClone.noFolderOpened%", + "%config.openAfterClone.showPrompt%" ], "default": "showPrompt", - "description": "%config.promptToOpenClonedRepository%" + "description": "%config.openAfterClone%" }, "git.showInlineOpenFileAction": { "type": "boolean", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 3ef518de24e88..7b0f2a578665e 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -116,11 +116,11 @@ "config.postCommitCommand.none": "Don't run any command after a commit.", "config.postCommitCommand.push": "Run 'Git Push' after a successful commit.", "config.postCommitCommand.sync": "Run 'Git Sync' after a successful commit.", - "config.promptToOpenClonedRepository": "Controls whether to show a prompt after cloning a repository.", - "config.promptToOpenClonedRepository.currentWindow": "Open repository in current window.", - "config.promptToOpenClonedRepository.newWindow": "Open repository in new window.", - "config.promptToOpenClonedRepository.noFolderOpened": "Open in current window if no folder is opened. Otherwise show prompt.", - "config.promptToOpenClonedRepository.showPrompt": "Always show prompt to choose action.", + "config.openAfterClone": "Controls whether to show a prompt after cloning a repository.", + "config.openAfterClone.currentWindow": "Open repository in current window.", + "config.openAfterClone.newWindow": "Open repository in new window.", + "config.openAfterClone.noFolderOpened": "Open in current window if no folder is opened. Otherwise show prompt.", + "config.openAfterClone.showPrompt": "Always show prompt to choose action.", "config.showInlineOpenFileAction": "Controls whether to show an inline Open File action in the Git changes view.", "config.showPushSuccessNotification": "Controls whether to show a notification when a push is successful.", "config.inputValidation": "Controls when to show commit message input validation.", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index b707f75bdc2fc..8e5d5a06e3e97 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -619,16 +619,16 @@ export class CommandCenter { ); let config = workspace.getConfiguration('git'); - let promptToOpenClonedRepository = config.get<'currentWindow' | 'newWindow' | 'noFolderOpened' | 'showPrompt'>('promptToOpenClonedRepository'); + let openAfterClone = config.get<'currentWindow' | 'newWindow' | 'noFolderOpened' | 'showPrompt'>('openAfterClone'); const uri = Uri.file(repositoryPath); - if (promptToOpenClonedRepository === 'currentWindow') { + if (openAfterClone === 'currentWindow') { commands.executeCommand('vscode.openFolder', uri); - } else if (promptToOpenClonedRepository === 'newWindow') { + } else if (openAfterClone === 'newWindow') { commands.executeCommand('vscode.openFolder', uri, true); } else { - if (promptToOpenClonedRepository === 'noFolderOpened') { + if (openAfterClone === 'noFolderOpened') { if (!workspace.workspaceFolders) { commands.executeCommand('vscode.openFolder', uri); } From 1531898fdb10cd559f62669f14afddcb1c179f3e Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 22 Apr 2020 21:22:48 +0200 Subject: [PATCH 0012/1882] avoid loading and twisty set at the same time --- src/vs/base/browser/ui/tree/abstractTree.ts | 12 +++++++++--- src/vs/base/browser/ui/tree/asyncDataTree.ts | 6 ++++-- src/vs/base/browser/ui/tree/objectTree.ts | 5 +++-- src/vs/base/browser/ui/tree/tree.ts | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index a71e4d6ae4842..e5ad99be7c602 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -401,15 +401,21 @@ class TreeRenderer implements IListRenderer } private renderTwistie(node: ITreeNode, templateData: ITreeListTemplateData) { + removeClasses(templateData.twistie, treeItemExpandedIcon.classNames); + + let twistieRendered = false; if (this.renderer.renderTwistie) { - this.renderer.renderTwistie(node.element, templateData.twistie); + twistieRendered = this.renderer.renderTwistie(node.element, templateData.twistie); } if (node.collapsible && (!this.hideTwistiesOfChildlessElements || node.visibleChildrenCount > 0)) { - addClasses(templateData.twistie, treeItemExpandedIcon.classNames, 'collapsible'); + if (!twistieRendered) { + addClasses(templateData.twistie, treeItemExpandedIcon.classNames); + } + addClasses(templateData.twistie, 'collapsible'); toggleClass(templateData.twistie, 'collapsed', node.collapsed); } else { - removeClasses(templateData.twistie, treeItemExpandedIcon.classNames, 'collapsible', 'collapsed'); + removeClasses(templateData.twistie, 'collapsible', 'collapsed'); } if (node.collapsible) { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 1e9600e2bffed..5102e51a76a9a 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -112,10 +112,11 @@ class AsyncDataTreeRenderer implements IT renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { if (element.slow) { addClasses(twistieElement, treeItemLoadingIcon.classNames); + return true; } else { removeClasses(twistieElement, treeItemLoadingIcon.classNames); + return false; } - return false; } disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { @@ -1056,10 +1057,11 @@ class CompressibleAsyncDataTreeRenderer i renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { if (element.slow) { addClasses(twistieElement, treeItemLoadingIcon.classNames); + return true; } else { removeClasses(twistieElement, treeItemLoadingIcon.classNames); + return false; } - return false; } disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index 3864707985367..c0fbecdf73259 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -124,10 +124,11 @@ class CompressibleRenderer, TFilterData, TTemplateDat this.renderer.disposeTemplate(templateData.data); } - renderTwistie?(element: T, twistieElement: HTMLElement): void { + renderTwistie?(element: T, twistieElement: HTMLElement): boolean { if (this.renderer.renderTwistie) { - this.renderer.renderTwistie(element, twistieElement); + return this.renderer.renderTwistie(element, twistieElement); } + return false; } } diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 085bb3d90b742..c17b41fbbed29 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -129,7 +129,7 @@ export interface ITreeModel { } export interface ITreeRenderer extends IListRenderer, TTemplateData> { - renderTwistie?(element: T, twistieElement: HTMLElement): void; + renderTwistie?(element: T, twistieElement: HTMLElement): boolean; onDidChangeTwistieState?: Event; } From 8e8dc25e6597993141a94cb57f4b5def32313389 Mon Sep 17 00:00:00 2001 From: Evan Krause Date: Wed, 6 May 2020 14:43:00 -0700 Subject: [PATCH 0013/1882] Don't focus editor when un-expanded comment is hidden --- .../contrib/comments/browser/commentThreadWidget.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index b99e99ff0ce33..f14e3fa6ffe8b 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -928,9 +928,11 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } hide() { - this._isExpanded = false; - // Focus the container so that the comment editor will be blurred before it is hidden - this.editor.focus(); + if (this._isExpanded) { + this._isExpanded = false; + // Focus the container so that the comment editor will be blurred before it is hidden + this.editor.focus(); + } super.hide(); } From b24cb8b47d4ed3703e5018f6c45f5feee70d2095 Mon Sep 17 00:00:00 2001 From: kingwl Date: Mon, 11 May 2020 19:01:45 +0800 Subject: [PATCH 0014/1882] Add rename by git context menu --- extensions/git/package.json | 13 +++++++++++++ extensions/git/package.nls.json | 1 + extensions/git/src/commands.ts | 21 +++++++++++++++++++++ extensions/git/src/git.ts | 5 +++++ extensions/git/src/repository.ts | 6 ++++++ extensions/types/lib.textEncoder.d.ts | 4 ++-- 6 files changed, 48 insertions(+), 2 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index ea99da5140a39..b0b11e653a7db 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -150,6 +150,12 @@ "category": "Git", "icon": "$(discard)" }, + { + "command": "git.rename", + "title": "%command.rename%", + "category": "Git", + "icon": "$(discard)" + }, { "command": "git.cleanAll", "title": "%command.cleanAll%", @@ -1296,6 +1302,13 @@ "group": "5_copy@2", "when": "config.git.enabled && !git.missing && timelineItem =~ /git:file:commit\\b/" } + ], + "explorer/context": [ + { + "command": "git.rename", + "group": "7_modification", + "when": "config.git.enabled && !git.missing && !explorerResourceIsRoot" + } ] }, "configuration": { diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 39965be4f75c5..eba9ea7eefc1e 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -21,6 +21,7 @@ "command.unstage": "Unstage Changes", "command.unstageAll": "Unstage All Changes", "command.unstageSelectedRanges": "Unstage Selected Ranges", + "command.rename": "Rename (Git)", "command.clean": "Discard Changes", "command.cleanAll": "Discard All Changes", "command.cleanAllTracked": "Discard All Tracked Changes", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index d6eeeec88ba30..48984566cdd57 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -19,6 +19,7 @@ import { grep, isDescendant, pathEquals } from './util'; import { Log, LogLevel } from './log'; import { GitTimelineItem } from './timelineProvider'; import { throttle, debounce } from './decorators'; +import { URI } from 'vscode-uri'; const localize = nls.loadMessageBundle(); @@ -930,6 +931,26 @@ export class CommandCenter { } } + @command('git.rename', { repository: true }) + async rename(repository: Repository, fromUri: URI): Promise { + this.outputChannel.appendLine(`git.rename ${fromUri.fsPath}`); + + const rootPath = workspace.rootPath; + const fromPath = workspace.asRelativePath(fromUri); + const fromBasename = path.basename(fromPath); + const toPath = await window.showInputBox({ + value: fromPath, + valueSelection: [fromPath.length - fromBasename.length, fromPath.length] + }); + if (!toPath?.trim()) { + return; + } + + const fullToPath = path.join(rootPath || '', toPath); + this.outputChannel.appendLine(`git.rename from ${fromPath} to ${fullToPath}`); + await repository.move(fromPath, fullToPath); + } + @command('git.stage') async stage(...resourceStates: SourceControlResourceState[]): Promise { this.outputChannel.appendLine(`git.stage ${resourceStates.length}`); diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 1b5c8d4837162..8323ef3a45915 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1390,6 +1390,11 @@ export class Repository { await this.run(args); } + async move(from: string, to: string): Promise { + const args = ['mv', from, to]; + await this.run(args); + } + async setBranchUpstream(name: string, upstream: string): Promise { const args = ['branch', '--set-upstream-to', upstream, name]; await this.run(args); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 9e85d0fca0c38..7466cfd811230 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -310,6 +310,8 @@ export const enum Operation { Blame = 'Blame', Log = 'Log', LogFile = 'LogFile', + + Move = 'Move' } function isReadOnly(operation: Operation): boolean { @@ -1045,6 +1047,10 @@ export class Repository implements Disposable { await this.run(Operation.RenameBranch, () => this.repository.renameBranch(name)); } + async move(from: string, to: string): Promise { + await this.run(Operation.Move, () => this.repository.move(from, to)); + } + async getBranch(name: string): Promise { return await this.run(Operation.GetBranch, () => this.repository.getBranch(name)); } diff --git a/extensions/types/lib.textEncoder.d.ts b/extensions/types/lib.textEncoder.d.ts index 99a5b2271d630..02e1b4890af9a 100644 --- a/extensions/types/lib.textEncoder.d.ts +++ b/extensions/types/lib.textEncoder.d.ts @@ -7,5 +7,5 @@ // // Proper fix: https://github.com/microsoft/TypeScript/issues/31535 -declare var TextDecoder: typeof import('util').TextDecoder; -declare var TextEncoder: typeof import('util').TextEncoder; +declare let TextDecoder: typeof import('util').TextDecoder; +declare let TextEncoder: typeof import('util').TextEncoder; From 31ee5b96449103913998a073686fe69f1f6e3eef Mon Sep 17 00:00:00 2001 From: kingwl Date: Mon, 11 May 2020 22:57:34 +0800 Subject: [PATCH 0015/1882] fix something --- extensions/git/src/commands.ts | 3 +-- extensions/types/lib.textEncoder.d.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 48984566cdd57..c370133d8d7b2 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -19,7 +19,6 @@ import { grep, isDescendant, pathEquals } from './util'; import { Log, LogLevel } from './log'; import { GitTimelineItem } from './timelineProvider'; import { throttle, debounce } from './decorators'; -import { URI } from 'vscode-uri'; const localize = nls.loadMessageBundle(); @@ -932,7 +931,7 @@ export class CommandCenter { } @command('git.rename', { repository: true }) - async rename(repository: Repository, fromUri: URI): Promise { + async rename(repository: Repository, fromUri: Uri): Promise { this.outputChannel.appendLine(`git.rename ${fromUri.fsPath}`); const rootPath = workspace.rootPath; diff --git a/extensions/types/lib.textEncoder.d.ts b/extensions/types/lib.textEncoder.d.ts index 02e1b4890af9a..99a5b2271d630 100644 --- a/extensions/types/lib.textEncoder.d.ts +++ b/extensions/types/lib.textEncoder.d.ts @@ -7,5 +7,5 @@ // // Proper fix: https://github.com/microsoft/TypeScript/issues/31535 -declare let TextDecoder: typeof import('util').TextDecoder; -declare let TextEncoder: typeof import('util').TextEncoder; +declare var TextDecoder: typeof import('util').TextDecoder; +declare var TextEncoder: typeof import('util').TextEncoder; From 8561cbb8aef51abe906b43db8952dd14a359f60a Mon Sep 17 00:00:00 2001 From: kingwl Date: Tue, 12 May 2020 00:36:28 +0800 Subject: [PATCH 0016/1882] Add force checkout and smart checkout --- extensions/git/src/commands.ts | 69 +++++++++++++++++++++++++++------- extensions/git/src/git.ts | 1 + 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index d6eeeec88ba30..f1e217422ffbf 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -6,7 +6,7 @@ import { lstat, Stats } from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, QuickPick } from 'vscode'; +import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, QuickPick, SourceControl } from 'vscode'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider, RemoteSource } from './api/git'; @@ -2506,18 +2506,7 @@ export class CommandCenter { if (!options.repository) { result = Promise.resolve(method.apply(this, args)); } else { - // try to guess the repository based on the first argument - const repository = this.model.getRepository(args[0]); - let repositoryPromise: Promise; - - if (repository) { - repositoryPromise = Promise.resolve(repository); - } else if (this.model.repositories.length === 1) { - repositoryPromise = Promise.resolve(this.model.repositories[0]); - } else { - repositoryPromise = this.model.pickRepository(); - } - + const repositoryPromise = this.guessRepository(args[0]); result = repositoryPromise.then(repository => { if (!repository) { return Promise.resolve(); @@ -2544,12 +2533,19 @@ export class CommandCenter { const choices = new Map void>(); const openOutputChannelChoice = localize('open git log', "Open Git Log"); + const forceCheckoutChoice = localize('force checkout', "Force Checkout"); + const smartCheckoutChoice = localize('smart checkout', "Smart Checkout"); const outputChannel = this.outputChannel as OutputChannel; choices.set(openOutputChannelChoice, () => outputChannel.show()); switch (err.gitErrorCode) { case GitErrorCodes.DirtyWorkTree: message = localize('clean repo', "Please clean your repository working tree before checkout."); + if (err.gitTreeish) { + options.modal = true; + choices.set(forceCheckoutChoice, () => forceCheckout(err.gitTreeish, args)); + choices.set(smartCheckoutChoice, () => smartCheckout(err.gitTreeish, args)); + } break; case GitErrorCodes.PushRejected: message = localize('cant push', "Can't push refs to remote. Try running 'Pull' first to integrate your changes."); @@ -2612,12 +2608,59 @@ export class CommandCenter { }); }; + const forceCheckout = async (treeish: string, args: any[]) => { + const repo = await this.guessRepository(args[0]); + if (!repo) { + return; + } + + this.outputChannel.appendLine('force checkout: clean all'); + await this.cleanAll(repo); + this.outputChannel.appendLine(`force checkout: checkout ${treeish}`); + await repo.checkout(treeish); + this.outputChannel.appendLine('force checkout: done'); + }; + + const smartCheckout = async (treeish: string, args: any[]) => { + const repo = await this.guessRepository(args[0]); + if (!repo) { + return; + } + + this.outputChannel.appendLine('smart checkout: stash'); + await repo.createStash(); + try { + this.outputChannel.appendLine(`smart checkout: checkout ${treeish}`); + await repo.checkout(treeish); + } finally { + this.outputChannel.appendLine('smart checkout pop stash'); + await repo.popStash(); + } + this.outputChannel.appendLine('smart checkout: done'); + }; + // patch this object, so people can call methods directly (this as any)[key] = result; return result; } + /** + * try to guess the repository based on the first argument + * @param sourceControl + */ + private guessRepository (sourceControl: SourceControl) { + const repository = this.model.getRepository(sourceControl); + + if (repository) { + return Promise.resolve(repository); + } else if (this.model.repositories.length === 1) { + return Promise.resolve(this.model.repositories[0]); + } else { + return this.model.pickRepository(); + } + } + private getSCMResource(uri?: Uri): Resource | undefined { uri = uri ? uri : (window.activeTextEditor && window.activeTextEditor.document.uri); diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 1b5c8d4837162..62ca189e51a4b 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1298,6 +1298,7 @@ export class Repository { } catch (err) { if (/Please,? commit your changes or stash them/.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.DirtyWorkTree; + err.gitTreeish = treeish; } throw err; From 63ccc69f08f27bf5888d1706b80dd1a5a12286f5 Mon Sep 17 00:00:00 2001 From: Borja Zarco Date: Sun, 10 May 2020 23:57:21 -0400 Subject: [PATCH 0017/1882] Fix launch configuration input variable resolution. When resolving launch configuration variables during a debug session, the configuration target was not being specified, always defaulting to reading workspace folder inputs. This made it impossible for user or workspace file launch configurations to use input variables, as the inputs list was never found. This change forwards the launch configuration source to the configurationResolverService, so that it looks for the inputs list in the right place. Forwarding the source fixed single-root workspaces, but multi-root workspaces were skipping the inputs lookup, since they pass an undefined workspace folder... In this case, the workspace folder is not relevant, as the config and inputs are defined in the workspace file, and allowing resolution to continue yields the desired behavior. --- .../browser/debugConfigurationManager.ts | 13 ++++++-- .../workbench/contrib/debug/common/debug.ts | 3 +- .../contrib/debug/common/debugger.ts | 2 +- .../browser/configurationResolverService.ts | 6 ++-- .../configurationResolverService.test.ts | 33 +++++++++++++++++++ 5 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index d0eff031334f8..17c3b1bda8e8d 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -625,8 +625,17 @@ abstract class AbstractLaunch { if (!config || !config.configurations) { return undefined; } - - return config.configurations.find(config => config && config.name === name); + const configuration = config.configurations.find(config => config && config.name === name); + if (configuration) { + if (this instanceof UserLaunch) { + configuration.__configurationTarget = ConfigurationTarget.USER; + } else if (this instanceof WorkspaceLaunch) { + configuration.__configurationTarget = ConfigurationTarget.WORKSPACE; + } else { + configuration.__configurationTarget = ConfigurationTarget.WORKSPACE_FOLDER; + } + } + return configuration; } async getInitialConfigurationContent(folderUri?: uri, type?: string, token?: CancellationToken): Promise { diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 997e8f4df84d5..26df2485c28aa 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -21,7 +21,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IDisposable } from 'vs/base/common/lifecycle'; import { TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks'; import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { CancellationToken } from 'vs/base/common/cancellation'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes'; import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; @@ -512,6 +512,7 @@ export interface IConfig extends IEnvConfig { linux?: IEnvConfig; // internals + __configurationTarget?: ConfigurationTarget; __sessionId?: string; __restart?: any; __autoAttach?: boolean; diff --git a/src/vs/workbench/contrib/debug/common/debugger.ts b/src/vs/workbench/contrib/debug/common/debugger.ts index ca1ee7ebfc3fe..8ede6963b15b9 100644 --- a/src/vs/workbench/contrib/debug/common/debugger.ts +++ b/src/vs/workbench/contrib/debug/common/debugger.ts @@ -107,7 +107,7 @@ export class Debugger implements IDebugger { substituteVariables(folder: IWorkspaceFolder | undefined, config: IConfig): Promise { return this.configurationManager.substituteVariables(this.type, folder, config).then(config => { - return this.configurationResolverService.resolveWithInteractionReplace(folder, config, 'launch', this.variables); + return this.configurationResolverService.resolveWithInteractionReplace(folder, config, 'launch', this.variables, config.__configurationTarget); }); } diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index 42b3431dc41ad..0a35a47cd77c0 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -147,8 +147,8 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR // get all "inputs" let inputs: ConfiguredInput[] = []; - if (folder && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && section) { - let result = this.configurationService.inspect(section, { resource: folder.uri }); + if (this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && section) { + let result = this.configurationService.inspect(section, { resource: folder?.uri }); if (result && (result.userValue || result.workspaceValue || result.workspaceFolderValue)) { switch (target) { case ConfigurationTarget.USER: inputs = (result.userValue)?.inputs; break; @@ -156,7 +156,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR default: inputs = (result.workspaceFolderValue)?.inputs; } } else { - const valueResult = this.configurationService.getValue(section, { resource: folder.uri }); + const valueResult = this.configurationService.getValue(section, { resource: folder?.uri }); if (valueResult) { inputs = valueResult.inputs; } diff --git a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts index 9d1f709f55cad..de7980bef6e0b 100644 --- a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts @@ -443,6 +443,7 @@ suite('Configuration Resolver Service', () => { assert.equal(1, mockCommandService.callCount); }); }); + test('a single prompt input variable', () => { const configuration = { @@ -470,6 +471,7 @@ suite('Configuration Resolver Service', () => { assert.equal(0, mockCommandService.callCount); }); }); + test('a single pick input variable', () => { const configuration = { @@ -497,6 +499,7 @@ suite('Configuration Resolver Service', () => { assert.equal(0, mockCommandService.callCount); }); }); + test('a single command input variable', () => { const configuration = { @@ -524,6 +527,7 @@ suite('Configuration Resolver Service', () => { assert.equal(1, mockCommandService.callCount); }); }); + test('several input variables and command', () => { const configuration = { @@ -553,6 +557,35 @@ suite('Configuration Resolver Service', () => { assert.equal(2, mockCommandService.callCount); }); }); + + test('input variable with undefined workspace folder', () => { + + const configuration = { + 'name': 'Attach to Process', + 'type': 'node', + 'request': 'attach', + 'processId': '${input:input1}', + 'port': 5858, + 'sourceMaps': false, + 'outDir': null + }; + + return configurationResolverService!.resolveWithInteractionReplace(undefined, configuration, 'tasks').then(result => { + + assert.deepEqual(result, { + 'name': 'Attach to Process', + 'type': 'node', + 'request': 'attach', + 'processId': 'resolvedEnterinput1', + 'port': 5858, + 'sourceMaps': false, + 'outDir': null + }); + + assert.equal(0, mockCommandService.callCount); + }); + }); + test('contributed variable', () => { const buildTask = 'npm: compile'; const variable = 'defaultBuildTask'; From 352f231bec54ae53cf56262f80d45d25fc7e3e29 Mon Sep 17 00:00:00 2001 From: Borja Zarco Date: Mon, 11 May 2020 08:03:24 -0400 Subject: [PATCH 0018/1882] Do not define resource override unless folder is defined. --- .../browser/configurationResolverService.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index 0a35a47cd77c0..5b9afb5b0302e 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -11,7 +11,7 @@ import { Schemas } from 'vs/base/common/network'; import { toResource } from 'vs/workbench/common/editor'; import { IStringDictionary, forEach, fromMap } from 'vs/base/common/collections'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationOverrides, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkspaceFolder, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -148,7 +148,8 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR // get all "inputs" let inputs: ConfiguredInput[] = []; if (this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && section) { - let result = this.configurationService.inspect(section, { resource: folder?.uri }); + const overrides: IConfigurationOverrides = folder ? { resource: folder.uri } : {}; + let result = this.configurationService.inspect(section, overrides); if (result && (result.userValue || result.workspaceValue || result.workspaceFolderValue)) { switch (target) { case ConfigurationTarget.USER: inputs = (result.userValue)?.inputs; break; @@ -156,7 +157,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR default: inputs = (result.workspaceFolderValue)?.inputs; } } else { - const valueResult = this.configurationService.getValue(section, { resource: folder?.uri }); + const valueResult = this.configurationService.getValue(section, overrides); if (valueResult) { inputs = valueResult.inputs; } From bed300cd4f326084e74af3e6efd63c769a5c1e0a Mon Sep 17 00:00:00 2001 From: Borja Zarco Date: Thu, 7 May 2020 22:29:20 -0400 Subject: [PATCH 0019/1882] Don't fail when resolving config variables without a `folderUri`. Config variables cannot define a workspace folder, but resolution was using `getFolderUri` and failing when undefined, even though `getConfigurationValue` implementations have logic to handle it. This PR changes the logic to simply forward folderUri to `getConfigurationValue`, which can be undefined. --- src/vs/workbench/api/common/extHostDebugService.ts | 2 +- .../browser/configurationResolverService.ts | 2 +- .../configurationResolver/common/variableResolver.ts | 8 ++++---- .../configurationResolverService.test.ts | 11 +++++++++++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 94132fed5b868..72b1bb63bc723 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -985,7 +985,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ getWorkspaceFolderCount: (): number => { return folders.length; }, - getConfigurationValue: (folderUri: URI, section: string): string | undefined => { + getConfigurationValue: (folderUri: URI | undefined, section: string): string | undefined => { return configurationService.getConfiguration(undefined, folderUri).get(section); }, getExecPath: (): string | undefined => { diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index 42b3431dc41ad..0343dc63dd8eb 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -44,7 +44,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR getWorkspaceFolderCount: (): number => { return workspaceContextService.getWorkspace().folders.length; }, - getConfigurationValue: (folderUri: uri, suffix: string): string | undefined => { + getConfigurationValue: (folderUri: uri | undefined, suffix: string): string | undefined => { return configurationService.getValue(suffix, folderUri ? { resource: folderUri } : {}); }, getExecPath: (): string | undefined => { diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index c29a083e75994..1b832ff39d268 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -18,7 +18,7 @@ import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; export interface IVariableResolveContext { getFolderUri(folderName: string): uri | undefined; getWorkspaceFolderCount(): number; - getConfigurationValue(folderUri: uri, section: string): string | undefined; + getConfigurationValue(folderUri: uri | undefined, section: string): string | undefined; getExecPath(): string | undefined; getFilePath(): string | undefined; getSelectedText(): string | undefined; @@ -162,9 +162,9 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe }; // common error handling for all variables that require an open folder and accept a folder name argument - const getFolderUri = (withArg = true): uri => { + const getFolderUri = (): uri => { - if (withArg && argument) { + if (argument) { const folder = this._context.getFolderUri(argument); if (folder) { return folder; @@ -200,7 +200,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe case 'config': if (argument) { - const config = this._context.getConfigurationValue(getFolderUri(false), argument); + const config = this._context.getConfigurationValue(folderUri, argument); if (types.isUndefinedOrNull(config)) { throw new Error(localize('configNotFound', "'{0}' can not be resolved because setting '{1}' not found.", match, argument)); } diff --git a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts index 9d1f709f55cad..f616e92e6eeb3 100644 --- a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts @@ -206,6 +206,17 @@ suite('Configuration Resolver Service', () => { assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz'); }); + test('substitute configuration variable with undefined workspace folder', () => { + let configurationService: IConfigurationService = new TestConfigurationService({ + editor: { + fontFamily: 'foo' + } + }); + + let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService); + assert.strictEqual(service.resolve(undefined, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz'); + }); + test('substitute many configuration variables', () => { let configurationService: IConfigurationService; configurationService = new TestConfigurationService({ From fc797d2430903cd49c06e97ed92be84bccb4eca8 Mon Sep 17 00:00:00 2001 From: Asif Hasan Date: Wed, 3 Jun 2020 18:00:11 -0500 Subject: [PATCH 0020/1882] fix 97472 --- extensions/git/src/repository.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 44a8858c63d0a..e93d0212a0c11 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1162,11 +1162,12 @@ export class Repository implements Disposable { const fetchOnPull = config.get('fetchOnPull'); const tags = config.get('pullTags'); + // When fetchOnPull is enabled, fetch all branches when pulling if (fetchOnPull) { - await this.repository.pull(rebase, undefined, undefined, { unshallow, tags }); - } else { - await this.repository.pull(rebase, remote, branch, { unshallow, tags }); + await this.repository.fetch({ all: true }); } + + await this.repository.pull(rebase, remote, branch, { unshallow, tags }); }); }); } From df3af97279be7431f8fb6d333572e9f79c8cb80b Mon Sep 17 00:00:00 2001 From: Asif Hasan Date: Wed, 3 Jun 2020 18:23:37 -0500 Subject: [PATCH 0021/1882] fix fetchOnPull behavior for Sync --- extensions/git/src/git.ts | 6 ++++-- extensions/git/src/repository.ts | 13 ++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 66d91da4d7a0c..b95e7009d10f7 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1513,9 +1513,11 @@ export class Repository { await this.run(args); } - async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean } = {}): Promise { + async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean, readonly cancellationToken?: CancellationToken } = {}): Promise { const args = ['fetch']; - const spawnOptions: SpawnOptions = {}; + const spawnOptions: SpawnOptions = { + cancellationToken: options.cancellationToken, + }; if (options.remote) { args.push(options.remote); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index e93d0212a0c11..577eff4fd7ce1 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1225,9 +1225,16 @@ export class Repository implements Disposable { const tags = config.get('pullTags'); const supportCancellation = config.get('supportCancellation'); - const fn = fetchOnPull - ? async (cancellationToken?: CancellationToken) => await this.repository.pull(rebase, undefined, undefined, { tags, cancellationToken }) - : async (cancellationToken?: CancellationToken) => await this.repository.pull(rebase, remoteName, pullBranch, { tags, cancellationToken }); + const fn = async (cancellationToken?: CancellationToken) => { + + // When fetchOnPull is enabled, fetch all branches when pulling + if (fetchOnPull) { + await this.repository.fetch({ all: true, cancellationToken }); + } + + await this.repository.pull(rebase, remoteName, pullBranch, { tags, cancellationToken }); + }; + if (supportCancellation) { const opts: ProgressOptions = { From 3f585d74003b24c59583af529220e8503bc5294a Mon Sep 17 00:00:00 2001 From: kingwl Date: Sat, 9 May 2020 12:35:48 +0800 Subject: [PATCH 0022/1882] Add better support for checkout type config --- extensions/git/package.json | 15 ++---------- extensions/git/package.nls.json | 6 +---- extensions/git/src/commands.ts | 41 +++++++++++++++++++++++---------- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index d33bafbad5fa6..2d056f6a39391 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1399,20 +1399,9 @@ }, "git.checkoutType": { "type": "string", - "enum": [ - "all", - "local", - "tags", - "remote" - ], - "enumDescriptions": [ - "%config.checkoutType.all%", - "%config.checkoutType.local%", - "%config.checkoutType.tags%", - "%config.checkoutType.remote%" - ], + "markdownDescription": "%config.checkoutType%", - "default": "all" + "default": "local,remote,tags" }, "git.ignoreLegacyWarning": { "type": "boolean", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index be817e22df1e3..b1506ed95681c 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -90,11 +90,7 @@ "config.countBadge.all": "Count all changes.", "config.countBadge.tracked": "Count only tracked changes.", "config.countBadge.off": "Turn off counter.", - "config.checkoutType": "Controls what type of branches are listed when running `Checkout to...`.", - "config.checkoutType.all": "Show all references.", - "config.checkoutType.local": "Show only local branches.", - "config.checkoutType.tags": "Show only tags.", - "config.checkoutType.remote": "Show only remote branches.", + "config.checkoutType": "Controls what type of branches (local, remote or tags, split with ',') are listed when running `Checkout to...`.", "config.branchValidationRegex": "A regular expression to validate new branch names.", "config.branchWhitespaceChar": "The character to replace whitespace in new branch names.", "config.ignoreLegacyWarning": "Ignores the legacy Git warning.", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index e60dc39e6a641..9c2e59cf515a7 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -203,18 +203,35 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{ function createCheckoutItems(repository: Repository): CheckoutItem[] { const config = workspace.getConfiguration('git'); - const checkoutType = config.get('checkoutType') || 'all'; - const includeTags = checkoutType === 'all' || checkoutType === 'tags'; - const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; - - const heads = repository.refs.filter(ref => ref.type === RefType.Head) - .map(ref => new CheckoutItem(ref)); - const tags = (includeTags ? repository.refs.filter(ref => ref.type === RefType.Tag) : []) - .map(ref => new CheckoutTagItem(ref)); - const remoteHeads = (includeRemotes ? repository.refs.filter(ref => ref.type === RefType.RemoteHead) : []) - .map(ref => new CheckoutRemoteHeadItem(ref)); - - return [...heads, ...tags, ...remoteHeads]; + const checkoutType = config.get('checkoutType') || 'local,remote,tags'; + const checkoutTypes = checkoutType.trim().split(',').map(type => type.trim()); + + const results: CheckoutItem[] = []; + const invalids = new Set(); + const seens = new Set(); + checkoutTypes.forEach(type => { + if (seens.has(type)) { + return; + } + seens.add(type); + + switch (type) { + case 'local': + results.push(...repository.refs.filter(ref => ref.type === RefType.Head).map(ref => new CheckoutItem(ref))); + break; + case 'remote': + results.push(...repository.refs.filter(ref => ref.type === RefType.RemoteHead).map(ref => new CheckoutRemoteHeadItem(ref))); + break; + case 'tags': + results.push(...repository.refs.filter(ref => ref.type === RefType.Tag).map(ref => new CheckoutTagItem(ref))); + break; + default: + invalids.add(type); + break; + } + }); + + return results; } function sanitizeRemoteName(name: string) { From ae540536b42f83e7503cf0c876488061f9ac34e5 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 5 Jun 2020 17:41:46 +0800 Subject: [PATCH 0023/1882] Rewrite checkout items --- extensions/git/src/commands.ts | 45 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 9c2e59cf515a7..a2b741ad4d37e 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -6,7 +6,7 @@ import { lstat, Stats } from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, QuickPick } from 'vscode'; +import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, QuickPick, debug } from 'vscode'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider, RemoteSource } from './api/git'; @@ -70,6 +70,11 @@ class CheckoutRemoteHeadItem extends CheckoutItem { } } +interface RefTypeAndCheckoutItem { + refType: RefType; + checkoutItemCtor: { new(ref: Ref): CheckoutItem; }; +} + class BranchDeleteItem implements QuickPickItem { private get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); } @@ -203,37 +208,39 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{ function createCheckoutItems(repository: Repository): CheckoutItem[] { const config = workspace.getConfiguration('git'); - const checkoutType = config.get('checkoutType') || 'local,remote,tags'; - const checkoutTypes = checkoutType.trim().split(',').map(type => type.trim()); + const checkoutTypeString = config.get('checkoutType'); + + const checkoutTypeOptions = ['local', 'remote', 'tags']; + const checkoutTypes = checkoutTypeString?.trim().split(',').map(type => type.trim()).filter(type => checkoutTypeOptions.includes(type)); const results: CheckoutItem[] = []; - const invalids = new Set(); const seens = new Set(); - checkoutTypes.forEach(type => { + (checkoutTypes && checkoutTypes.length ? checkoutTypes : checkoutTypeOptions).forEach(type => { if (seens.has(type)) { return; } seens.add(type); - switch (type) { - case 'local': - results.push(...repository.refs.filter(ref => ref.type === RefType.Head).map(ref => new CheckoutItem(ref))); - break; - case 'remote': - results.push(...repository.refs.filter(ref => ref.type === RefType.RemoteHead).map(ref => new CheckoutRemoteHeadItem(ref))); - break; - case 'tags': - results.push(...repository.refs.filter(ref => ref.type === RefType.Tag).map(ref => new CheckoutTagItem(ref))); - break; - default: - invalids.add(type); - break; - } + const { refType, checkoutItemCtor } = getRefTypeAndCheckoutItem(type); + results.push(...repository.refs.filter(ref => ref.type === refType).map(ref => new checkoutItemCtor(ref))); }); return results; } +function getRefTypeAndCheckoutItem(type: string): RefTypeAndCheckoutItem { + switch (type) { + case 'local': + return { refType: RefType.Head, checkoutItemCtor: CheckoutItem }; + case 'remote': + return { refType: RefType.RemoteHead, checkoutItemCtor: CheckoutRemoteHeadItem }; + case 'tags': + return { refType: RefType.Tag, checkoutItemCtor: CheckoutTagItem }; + default: + throw new Error(`Unexpected type: ${type}`); + } +} + function sanitizeRemoteName(name: string) { name = name.trim(); return name && name.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, '-'); From d9d1be4e490f76dbc53a7e25d582add9b2099441 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 5 Jun 2020 22:30:29 +0800 Subject: [PATCH 0024/1882] Avoid debug --- extensions/git/src/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index a2b741ad4d37e..f21aa23f9aec8 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -6,7 +6,7 @@ import { lstat, Stats } from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, QuickPick, debug } from 'vscode'; +import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, QuickPick } from 'vscode'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider, RemoteSource } from './api/git'; From b4c528cbfd2c85eed10f91f4aee9e35864fd4302 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 8 Aug 2020 14:05:51 -0500 Subject: [PATCH 0025/1882] fixes #103281 --- extensions/git/package.json | 6 ++++++ extensions/git/package.nls.json | 1 + extensions/git/src/repository.ts | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 748868778ddf2..ba3e194ad03cd 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1568,6 +1568,12 @@ "description": "%config.enableStatusBarSync%", "scope": "resource" }, + "git.pushTags": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.pushTags%" + }, "git.promptToSaveFilesBeforeCommit": { "type": "string", "enum": [ diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 0e8ea1c649e4a..af825fe317f93 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -111,6 +111,7 @@ "config.discardAllScope": "Controls what changes are discarded by the `Discard all changes` command. `all` discards all changes. `tracked` discards only tracked files. `prompt` shows a prompt dialog every time the action is run.", "config.decorations.enabled": "Controls whether Git contributes colors and badges to the explorer and the open editors view.", "config.enableStatusBarSync": "Controls whether the Git Sync command appears in the status bar.", + "config.pushTags": "Push all tags when synchronizing.", "config.promptToSaveFilesBeforeCommit": "Controls whether Git should check for unsaved files before committing.", "config.promptToSaveFilesBeforeCommit.always": "Check for any unsaved files.", "config.promptToSaveFilesBeforeCommit.staged": "Check only for unsaved staged files.", diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index edebe7e54c192..3662ae6074720 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1225,6 +1225,7 @@ export class Repository implements Disposable { const config = workspace.getConfiguration('git', Uri.file(this.root)); const fetchOnPull = config.get('fetchOnPull'); const tags = config.get('pullTags'); + const pushTags = config.get('pushTags'); const supportCancellation = config.get('supportCancellation'); const fn = fetchOnPull @@ -1252,7 +1253,7 @@ export class Repository implements Disposable { const shouldPush = this.HEAD && (typeof this.HEAD.ahead === 'number' ? this.HEAD.ahead > 0 : true); if (shouldPush) { - await this._push(remoteName, pushBranch); + await this._push(remoteName, pushBranch, false, pushTags); } }); }); From ff8d4feeb4361da1942f02ac040d92e42ecd8dc3 Mon Sep 17 00:00:00 2001 From: "sneakyfish5.sneaky@gmail.com" Date: Sun, 9 Aug 2020 16:48:22 -0500 Subject: [PATCH 0026/1882] Git: Add cherryPick command --- extensions/git/package.json | 9 +++++++++ extensions/git/package.nls.json | 1 + extensions/git/src/commands.ts | 15 +++++++++++++++ extensions/git/src/git.ts | 5 +++++ extensions/git/src/repository.ts | 5 +++++ 5 files changed, 35 insertions(+) diff --git a/extensions/git/package.json b/extensions/git/package.json index 748868778ddf2..767a70a5a685b 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -326,6 +326,11 @@ "title": "%command.pushFollowTagsForce%", "category": "Git" }, + { + "command": "git.cherryPick", + "title": "%command.cherryPick%", + "category": "Git" + }, { "command": "git.addRemote", "title": "%command.addRemote%", @@ -673,6 +678,10 @@ "command": "git.pushWithTagsForce", "when": "config.git.enabled && !git.missing && config.git.allowForcePush && gitOpenRepositoryCount != 0" }, + { + "command": "git.cherryPick", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + }, { "command": "git.addRemote", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 0e8ea1c649e4a..0780ac89b8d75 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -56,6 +56,7 @@ "command.pushToForce": "Push to... (Force)", "command.pushFollowTags": "Push (Follow Tags)", "command.pushFollowTagsForce": "Push (Follow Tags, Force)", + "command.cherryPick": "Cherry Pick...", "command.addRemote": "Add Remote...", "command.removeRemote": "Remove Remote", "command.sync": "Sync", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index f618a8669c27c..a862568e22d97 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2027,6 +2027,21 @@ export class CommandCenter { await this._push(repository, { pushType: PushType.PushFollowTags, forcePush: true }); } + @command('git.cherryPick', { repository: true }) + async cherryPick(repository: Repository): Promise { + const inputCommitHash = await window.showInputBox({ + placeHolder: localize('commit hash', "Commit Hash"), + prompt: localize('provide commit hash', "Please provide the commit hash"), + ignoreFocusOut: true + }); + + if (!inputCommitHash) { + return; + } + + await repository.cherryPick(inputCommitHash); + } + @command('git.pushTo', { repository: true }) async pushTo(repository: Repository): Promise { await this._push(repository, { pushType: PushType.PushTo }); diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index d974c7bf1ca34..9019a8e477292 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1635,6 +1635,11 @@ export class Repository { } } + async cherryPick(commitHash: string): Promise { + const args = ['cherry-pick', commitHash]; + await this.run(args); + } + async blame(path: string): Promise { try { const args = ['blame', sanitizePath(path)]; diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index edebe7e54c192..124b34caf7a84 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -294,6 +294,7 @@ export const enum Operation { Fetch = 'Fetch', Pull = 'Pull', Push = 'Push', + CherryPick = 'CherryPick', Sync = 'Sync', Show = 'Show', Stage = 'Stage', @@ -1195,6 +1196,10 @@ export class Repository implements Disposable { await this.run(Operation.Push, () => this._push(remote, undefined, false, true, forcePushMode)); } + async cherryPick(commitHash: string): Promise { + await this.run(Operation.CherryPick, () => this.repository.cherryPick(commitHash)); + } + async blame(path: string): Promise { return await this.run(Operation.Blame, () => this.repository.blame(path)); } From 11664e62a3c07902d240d9eb80d3e2eb856eee99 Mon Sep 17 00:00:00 2001 From: ae1020 Date: Tue, 18 Aug 2020 08:16:27 -0400 Subject: [PATCH 0027/1882] Make Clicking in Scrollbars Move By Page This changes clicking in the "gutter" area of a scrollbar from jumping to a position in the file proportional to the click, to the more standard behavior of "clicking before the slider does a page up (or left), and clicking after the slider does a page down (or right)". The behavior is requested in #43564 (and 101698, 100446, 87273 70267, 62834, 55139...) It would likely make a reasonable default, since clicking in the source preview right next to the scroll bar already permits jumping to absolute positions. However, this PR could also be amended to make it an option. Documentation of the implementation process is here: https://ae1020.github.io/vscode-scrollbar-go-by-page/ --- .../base/browser/ui/scrollbar/scrollbarState.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts index 48e20a5a03379..57bb729e852d6 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts @@ -189,8 +189,10 @@ export class ScrollbarState { } /** - * Compute a desired `scrollPosition` such that `offset` ends up in the center of the slider. - * `offset` is based on the same coordinate system as the `sliderPosition`. + * Compute a desired `scrollPosition` from if offset is before or after the slider position. + * If offset is before slider, treat as a page up (or left). If after, page down (or right). + * `offset` and `_computedSliderPosition` are based on the same coordinate system. + * `_visibleSize` corresponds to a "page" of lines in the returned coordinate system. */ public getDesiredScrollPositionFromOffset(offset: number): number { if (!this._computedIsNeeded) { @@ -198,8 +200,14 @@ export class ScrollbarState { return 0; } - let desiredSliderPosition = offset - this._arrowSize - this._computedSliderSize / 2; - return Math.round(desiredSliderPosition / this._computedSliderRatio); + let correctedOffset = offset - this._arrowSize; // compensate if has arrows + let desiredScrollPosition = this._scrollPosition; + if (correctedOffset < this._computedSliderPosition) { + desiredScrollPosition -= this._visibleSize; // page up/left + } else { + desiredScrollPosition += this._visibleSize; // page down/right + } + return desiredScrollPosition; } /** From 2fa97c8794662a636e227918a1a328e2f7220441 Mon Sep 17 00:00:00 2001 From: Jensui Date: Wed, 19 Aug 2020 17:36:47 +0300 Subject: [PATCH 0028/1882] feat(extensions): added an option to disable related dependencies when disabling an extension with enabled dependencies --- .../browser/extensionsWorkbenchService.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 996f4bb2fed65..67bf8b3852199 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -28,7 +28,7 @@ import { IURLService, IURLHandler, IOpenURLOptions } from 'vs/platform/url/commo import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import * as resources from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -1050,7 +1050,19 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension for (const extension of extensions) { let dependents = this.getDependentsAfterDisablement(extension, allExtensions, this.local); if (dependents.length) { - return Promise.reject(new Error(this.getDependentsErrorMessage(extension, allExtensions, dependents))); + return new Promise((resolve, reject) => { + this.notificationService.prompt(Severity.Error, this.getDependentsErrorMessage(extension, allExtensions, dependents), [ + { + label: nls.localize('disable dep and dependents', 'Disable all related dependencies'), + run: async () => { + await this.checkAndSetEnablement(dependents, [extension], enablementState); + return resolve(); + } + } + ], { + onCancel: () => reject() + }); + }); } } } From 8b21b331facc4c79b001bf7744f98b2017b6fbd2 Mon Sep 17 00:00:00 2001 From: ae1020 Date: Wed, 19 Aug 2020 20:22:39 -0400 Subject: [PATCH 0029/1882] Amend scrollbarState.test.ts for new positions The test file had calls to getDesiredScrollPositionFromOffset() which do not seem critical to the aspect being tested. This updates the two calls to reflect the new value resulting from movement by page. --- .../base/test/browser/ui/scrollbar/scrollbarState.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts index 5ab03d9cd4ccb..d191083ce4995 100644 --- a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts +++ b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts @@ -18,8 +18,9 @@ suite('ScrollbarState', () => { assert.equal(actual.getSliderSize(), 20); assert.equal(actual.getSliderPosition(), 249); + // 259 is greater than 230 so page down, 32787 + 339 = 33126 + assert.equal(actual.getDesiredScrollPositionFromOffset(259), 33126); - assert.equal(actual.getDesiredScrollPositionFromOffset(259), 32849); actual.setScrollPosition(32849); assert.equal(actual.getArrowSize(), 0); assert.equal(actual.getScrollPosition(), 32849); @@ -41,8 +42,9 @@ suite('ScrollbarState', () => { assert.equal(actual.getSliderSize(), 20); assert.equal(actual.getSliderPosition(), 230); + // 240 + 12 = 252; greater than 230 so page down, 32787 + 339 = 33126 + assert.equal(actual.getDesiredScrollPositionFromOffset(240 + 12), 33126); - assert.equal(actual.getDesiredScrollPositionFromOffset(240 + 12), 32811); actual.setScrollPosition(32811); assert.equal(actual.getArrowSize(), 12); assert.equal(actual.getScrollPosition(), 32811); From aca9ae328801177993dc735a10454779fde36960 Mon Sep 17 00:00:00 2001 From: ae1020 Date: Mon, 31 Aug 2020 12:00:47 -0400 Subject: [PATCH 0030/1882] Option: editor.scrollbar.gutterClickMovesByPage This makes paging behavior by clicking in the scroll bar gutter optional. --- .../browser/ui/scrollbar/abstractScrollbar.ts | 14 +++++++++++++- .../browser/ui/scrollbar/horizontalScrollbar.ts | 3 ++- .../browser/ui/scrollbar/scrollableElement.ts | 4 +++- .../ui/scrollbar/scrollableElementOptions.ts | 6 ++++++ .../base/browser/ui/scrollbar/scrollbarState.ts | 16 +++++++++++++++- .../browser/ui/scrollbar/verticalScrollbar.ts | 3 ++- .../browser/ui/scrollbar/scrollbarState.test.ts | 8 ++++++-- .../viewParts/editorScrollbar/editorScrollbar.ts | 1 + src/vs/editor/common/config/editorOptions.ts | 10 +++++++++- .../viewLayout/editorLayoutProvider.test.ts | 1 + src/vs/monaco.d.ts | 6 ++++++ 11 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts index 23bfdb2c7bf82..5baba5c997fa2 100644 --- a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts @@ -38,12 +38,14 @@ export interface AbstractScrollbarOptions { visibility: ScrollbarVisibility; extraScrollbarClassName: string; scrollable: Scrollable; + gutterClickMovesByPage: boolean; } export abstract class AbstractScrollbar extends Widget { protected _host: ScrollbarHost; protected _scrollable: Scrollable; + protected _gutterClickMovesByPage: boolean; private _lazyRender: boolean; protected _scrollbarState: ScrollbarState; private _visibilityController: ScrollbarVisibilityController; @@ -59,6 +61,7 @@ export abstract class AbstractScrollbar extends Widget { this._lazyRender = opts.lazyRender; this._host = opts.host; this._scrollable = opts.scrollable; + this._gutterClickMovesByPage = opts.gutterClickMovesByPage; this._scrollbarState = opts.scrollbarState; this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'visible scrollbar ' + opts.extraScrollbarClassName, 'invisible scrollbar ' + opts.extraScrollbarClassName)); this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()); @@ -210,7 +213,16 @@ export abstract class AbstractScrollbar extends Widget { offsetX = e.posx - domNodePosition.left; offsetY = e.posy - domNodePosition.top; } - this._setDesiredScrollPositionNow(this._scrollbarState.getDesiredScrollPositionFromOffset(this._mouseDownRelativePosition(offsetX, offsetY))); + + let offset = this._mouseDownRelativePosition(offsetX, offsetY); + let scrollPos: number; + if (this._gutterClickMovesByPage) { + scrollPos = this._scrollbarState.getDesiredScrollPositionFromOffsetPaged(offset); + } else { + scrollPos = this._scrollbarState.getDesiredScrollPositionFromOffsetAbsolute(offset); + } + this._setDesiredScrollPositionNow(scrollPos); + if (e.leftButton) { e.preventDefault(); this._sliderMouseDown(e, () => { /*nothing to do*/ }); diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index 6e7f132e99f83..9686d459bca0b 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -33,7 +33,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { ), visibility: options.horizontal, extraScrollbarClassName: 'horizontal', - scrollable: scrollable + scrollable: scrollable, + gutterClickMovesByPage: options.gutterClickMovesByPage }); if (options.horizontalHasArrows) { diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 00bb9830d54ff..ba3c9b5f6f6f6 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -614,7 +614,9 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme vertical: (typeof opts.vertical !== 'undefined' ? opts.vertical : ScrollbarVisibility.Auto), verticalScrollbarSize: (typeof opts.verticalScrollbarSize !== 'undefined' ? opts.verticalScrollbarSize : 10), verticalHasArrows: (typeof opts.verticalHasArrows !== 'undefined' ? opts.verticalHasArrows : false), - verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0) + verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0), + + gutterClickMovesByPage: (typeof opts.gutterClickMovesByPage !== 'undefined' ? opts.gutterClickMovesByPage : false) }; result.horizontalSliderSize = (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : result.horizontalScrollbarSize); diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts index afb227be73b9f..491986dcc8020 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts @@ -114,6 +114,11 @@ export interface ScrollableElementCreationOptions { * Defaults to false. */ verticalHasArrows?: boolean; + /** + * Scroll gutter clicks move by page vs. jump to position. + * Defaults to false. + */ + gutterClickMovesByPage?: boolean; } export interface ScrollableElementChangeOptions { @@ -146,4 +151,5 @@ export interface ScrollableElementResolvedOptions { verticalScrollbarSize: number; verticalSliderSize: number; verticalHasArrows: boolean; + gutterClickMovesByPage: boolean; } diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts index 57bb729e852d6..0929cd2b29741 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts @@ -188,13 +188,27 @@ export class ScrollbarState { return this._computedSliderPosition; } + /** + * Compute a desired `scrollPosition` such that `offset` ends up in the center of the slider. + * `offset` is based on the same coordinate system as the `sliderPosition`. + */ + public getDesiredScrollPositionFromOffsetAbsolute(offset: number): number { + if (!this._computedIsNeeded) { + // no need for a slider + return 0; + } + + let desiredSliderPosition = offset - this._arrowSize - this._computedSliderSize / 2; + return Math.round(desiredSliderPosition / this._computedSliderRatio); + } + /** * Compute a desired `scrollPosition` from if offset is before or after the slider position. * If offset is before slider, treat as a page up (or left). If after, page down (or right). * `offset` and `_computedSliderPosition` are based on the same coordinate system. * `_visibleSize` corresponds to a "page" of lines in the returned coordinate system. */ - public getDesiredScrollPositionFromOffset(offset: number): number { + public getDesiredScrollPositionFromOffsetPaged(offset: number): number { if (!this._computedIsNeeded) { // no need for a slider return 0; diff --git a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts index 296913a3fd8b5..505dcc7f50617 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -33,7 +33,8 @@ export class VerticalScrollbar extends AbstractScrollbar { ), visibility: options.vertical, extraScrollbarClassName: 'vertical', - scrollable: scrollable + scrollable: scrollable, + gutterClickMovesByPage: options.gutterClickMovesByPage }); if (options.verticalHasArrows) { diff --git a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts index d191083ce4995..ec2d384ede455 100644 --- a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts +++ b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts @@ -18,8 +18,10 @@ suite('ScrollbarState', () => { assert.equal(actual.getSliderSize(), 20); assert.equal(actual.getSliderPosition(), 249); + assert.equal(actual.getDesiredScrollPositionFromOffsetAbsolute(259), 32849); + // 259 is greater than 230 so page down, 32787 + 339 = 33126 - assert.equal(actual.getDesiredScrollPositionFromOffset(259), 33126); + assert.equal(actual.getDesiredScrollPositionFromOffsetPaged(259), 33126); actual.setScrollPosition(32849); assert.equal(actual.getArrowSize(), 0); @@ -42,8 +44,10 @@ suite('ScrollbarState', () => { assert.equal(actual.getSliderSize(), 20); assert.equal(actual.getSliderPosition(), 230); + assert.equal(actual.getDesiredScrollPositionFromOffsetAbsolute(240 + 12), 32811); + // 240 + 12 = 252; greater than 230 so page down, 32787 + 339 = 33126 - assert.equal(actual.getDesiredScrollPositionFromOffset(240 + 12), 33126); + assert.equal(actual.getDesiredScrollPositionFromOffsetPaged(240 + 12), 33126); actual.setScrollPosition(32811); assert.equal(actual.getArrowSize(), 12); diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts index 21193bac130b6..595dd8670bc97 100644 --- a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts +++ b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts @@ -56,6 +56,7 @@ export class EditorScrollbar extends ViewPart { mouseWheelScrollSensitivity: mouseWheelScrollSensitivity, fastScrollSensitivity: fastScrollSensitivity, scrollPredominantAxis: scrollPredominantAxis, + gutterClickMovesByPage: scrollbar.gutterClickMovesByPage, }; this.scrollbar = this._register(new SmoothScrollableElement(linesContent.domNode, scrollbarOptions, this._context.viewLayout.getScrollable())); diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 3000465639075..73f90d11a354d 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2872,6 +2872,11 @@ export interface IEditorScrollbarOptions { * Defaults to `horizontalScrollbarSize`. */ horizontalSliderSize?: number; + /** + * Scroll gutter clicks move by page vs jump to position. + * Defaults to false. + */ + gutterClickMovesByPage?: boolean; } export interface InternalEditorScrollbarOptions { @@ -2887,6 +2892,7 @@ export interface InternalEditorScrollbarOptions { readonly horizontalSliderSize: number; readonly verticalScrollbarSize: number; readonly verticalSliderSize: number; + readonly gutterClickMovesByPage: boolean; } function _scrollbarVisibilityFromString(visibility: string | undefined, defaultValue: ScrollbarVisibility): ScrollbarVisibility { @@ -2917,7 +2923,8 @@ class EditorScrollbar extends BaseEditorOption { horizontalSliderSize: EditorOptions.scrollbar.defaultValue.horizontalSliderSize, verticalScrollbarSize: input.verticalScrollbarWidth, verticalSliderSize: EditorOptions.scrollbar.defaultValue.verticalSliderSize, + gutterClickMovesByPage: EditorOptions.scrollbar.defaultValue.gutterClickMovesByPage, }; options._write(EditorOption.scrollbar, scrollbarOptions); const lineNumbersOptions: InternalEditorRenderLineNumbersOptions = { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index d371020b87326..ed9ca3637d69f 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3652,6 +3652,11 @@ declare namespace monaco.editor { * Defaults to `horizontalScrollbarSize`. */ horizontalSliderSize?: number; + /** + * Scroll gutter clicks move by page vs jump to position. + * Defaults to false. + */ + gutterClickMovesByPage?: boolean; } export interface InternalEditorScrollbarOptions { @@ -3667,6 +3672,7 @@ declare namespace monaco.editor { readonly horizontalSliderSize: number; readonly verticalScrollbarSize: number; readonly verticalSliderSize: number; + readonly gutterClickMovesByPage: boolean; } /** From b955b58eee047560f59e7a49818934ffac03e7d6 Mon Sep 17 00:00:00 2001 From: Nick Rayburn Date: Tue, 1 Sep 2020 15:40:02 -0500 Subject: [PATCH 0031/1882] Add dirSep variable launch.json --- .../services/configurationResolver/common/variableResolver.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 2ad97f9bc3336..7fed78e87e004 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -307,6 +307,9 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe } return match; + case 'dirSep': + return paths.sep; + default: try { return this.resolveFromMap(match, variable, commandValueMapping, undefined); From 22ca0c1e8a751fe0f74495282f1610b981a519dd Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 9 Sep 2020 14:09:04 -0700 Subject: [PATCH 0032/1882] Add providerName option to git.api.getRemoteSources --- extensions/git/src/remoteSource.ts | 228 +++++++++++++++-------------- 1 file changed, 120 insertions(+), 108 deletions(-) diff --git a/extensions/git/src/remoteSource.ts b/extensions/git/src/remoteSource.ts index b736f606e67a6..2f3f493e75ddd 100644 --- a/extensions/git/src/remoteSource.ts +++ b/extensions/git/src/remoteSource.ts @@ -12,122 +12,134 @@ import { throttle, debounce } from './decorators'; const localize = nls.loadMessageBundle(); async function getQuickPickResult(quickpick: QuickPick): Promise { - const result = await new Promise(c => { - quickpick.onDidAccept(() => c(quickpick.selectedItems[0])); - quickpick.onDidHide(() => c(undefined)); - quickpick.show(); - }); - - quickpick.hide(); - return result; + const result = await new Promise(c => { + quickpick.onDidAccept(() => c(quickpick.selectedItems[0])); + quickpick.onDidHide(() => c(undefined)); + quickpick.show(); + }); + + quickpick.hide(); + return result; } class RemoteSourceProviderQuickPick { - private quickpick: QuickPick; - - constructor(private provider: RemoteSourceProvider) { - this.quickpick = window.createQuickPick(); - this.quickpick.ignoreFocusOut = true; - - if (provider.supportsQuery) { - this.quickpick.placeholder = localize('type to search', "Repository name (type to search)"); - this.quickpick.onDidChangeValue(this.onDidChangeValue, this); - } else { - this.quickpick.placeholder = localize('type to filter', "Repository name"); - } - } - - @debounce(300) - private onDidChangeValue(): void { - this.query(); - } - - @throttle - private async query(): Promise { - this.quickpick.busy = true; - - try { - const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || []; - - if (remoteSources.length === 0) { - this.quickpick.items = [{ - label: localize('none found', "No remote repositories found."), - alwaysShow: true - }]; - } else { - this.quickpick.items = remoteSources.map(remoteSource => ({ - label: remoteSource.name, - description: remoteSource.description || (typeof remoteSource.url === 'string' ? remoteSource.url : remoteSource.url[0]), - remoteSource - })); - } - } catch (err) { - this.quickpick.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }]; - console.error(err); - } finally { - this.quickpick.busy = false; - } - } - - async pick(): Promise { - this.query(); - const result = await getQuickPickResult(this.quickpick); - return result?.remoteSource; - } + private quickpick: QuickPick; + + constructor(private provider: RemoteSourceProvider) { + this.quickpick = window.createQuickPick(); + this.quickpick.ignoreFocusOut = true; + + if (provider.supportsQuery) { + this.quickpick.placeholder = localize('type to search', "Repository name (type to search)"); + this.quickpick.onDidChangeValue(this.onDidChangeValue, this); + } else { + this.quickpick.placeholder = localize('type to filter', "Repository name"); + } + } + + @debounce(300) + private onDidChangeValue(): void { + this.query(); + } + + @throttle + private async query(): Promise { + this.quickpick.busy = true; + + try { + const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || []; + + if (remoteSources.length === 0) { + this.quickpick.items = [{ + label: localize('none found', "No remote repositories found."), + alwaysShow: true + }]; + } else { + this.quickpick.items = remoteSources.map(remoteSource => ({ + label: remoteSource.name, + description: remoteSource.description || (typeof remoteSource.url === 'string' ? remoteSource.url : remoteSource.url[0]), + remoteSource + })); + } + } catch (err) { + this.quickpick.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }]; + console.error(err); + } finally { + this.quickpick.busy = false; + } + } + + async pick(): Promise { + this.query(); + const result = await getQuickPickResult(this.quickpick); + return result?.remoteSource; + } } export interface PickRemoteSourceOptions { - readonly providerLabel?: (provider: RemoteSourceProvider) => string; - readonly urlLabel?: string; + readonly providerLabel?: (provider: RemoteSourceProvider) => string; + readonly urlLabel?: string; + readonly providerName?: string; } export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise { - const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>(); - quickpick.ignoreFocusOut = true; - - const providers = model.getRemoteProviders() - .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + (options.providerLabel ? options.providerLabel(provider) : provider.name), alwaysShow: true, provider })); - - quickpick.placeholder = providers.length === 0 - ? localize('provide url', "Provide repository URL") - : localize('provide url or pick', "Provide repository URL or pick a repository source."); - - const updatePicks = (value?: string) => { - if (value) { - quickpick.items = [{ - label: options.urlLabel ?? localize('url', "URL"), - description: value, - alwaysShow: true, - url: value - }, - ...providers]; - } else { - quickpick.items = providers; - } - }; - - quickpick.onDidChangeValue(updatePicks); - updatePicks(); - - const result = await getQuickPickResult(quickpick); - - if (result) { - if (result.url) { - return result.url; - } else if (result.provider) { - const quickpick = new RemoteSourceProviderQuickPick(result.provider); - const remote = await quickpick.pick(); - - if (remote) { - if (typeof remote.url === 'string') { - return remote.url; - } else if (remote.url.length > 0) { - return await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); - } - } - } - } - - return undefined; + const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>(); + quickpick.ignoreFocusOut = true; + + const targetProvider = model.getRemoteProviders().filter(provider => provider.name === options.providerName); + if (targetProvider && targetProvider.length === 1) { + await pickProviderSource(targetProvider[0]); + } else { + const providers = model.getRemoteProviders() + .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + (options.providerLabel ? options.providerLabel(provider) : provider.name), alwaysShow: true, provider })); + + quickpick.placeholder = providers.length === 0 + ? localize('provide url', "Provide repository URL") + : localize('provide url or pick', "Provide repository URL or pick a repository source."); + + const updatePicks = (value?: string) => { + if (value) { + quickpick.items = [{ + label: options.urlLabel ?? localize('url', "URL"), + description: value, + alwaysShow: true, + url: value + }, + ...providers]; + } else { + quickpick.items = providers; + } + }; + + quickpick.onDidChangeValue(updatePicks); + updatePicks(); + + const result = await getQuickPickResult(quickpick); + + if (result) { + if (result.url) { + return result.url; + } else if (result.provider) { + return await pickProviderSource(result.provider); + } + } + } + + return undefined; +} + +async function pickProviderSource(provider: RemoteSourceProvider): Promise { + const quickpick = new RemoteSourceProviderQuickPick(provider); + const remote = await quickpick.pick(); + + if (remote) { + if (typeof remote.url === 'string') { + return remote.url; + } else if (remote.url.length > 0) { + return await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); + } + } + + return undefined; } From 3890d7fba9f9f2d9576f77ef10bf31bc845dd60c Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 9 Sep 2020 14:31:22 -0700 Subject: [PATCH 0033/1882] Formatting --- extensions/git/src/remoteSource.ts | 234 ++++++++++++++--------------- 1 file changed, 117 insertions(+), 117 deletions(-) diff --git a/extensions/git/src/remoteSource.ts b/extensions/git/src/remoteSource.ts index 2f3f493e75ddd..a1acb2b9f98a5 100644 --- a/extensions/git/src/remoteSource.ts +++ b/extensions/git/src/remoteSource.ts @@ -12,134 +12,134 @@ import { throttle, debounce } from './decorators'; const localize = nls.loadMessageBundle(); async function getQuickPickResult(quickpick: QuickPick): Promise { - const result = await new Promise(c => { - quickpick.onDidAccept(() => c(quickpick.selectedItems[0])); - quickpick.onDidHide(() => c(undefined)); - quickpick.show(); - }); - - quickpick.hide(); - return result; + const result = await new Promise(c => { + quickpick.onDidAccept(() => c(quickpick.selectedItems[0])); + quickpick.onDidHide(() => c(undefined)); + quickpick.show(); + }); + + quickpick.hide(); + return result; } class RemoteSourceProviderQuickPick { - private quickpick: QuickPick; - - constructor(private provider: RemoteSourceProvider) { - this.quickpick = window.createQuickPick(); - this.quickpick.ignoreFocusOut = true; - - if (provider.supportsQuery) { - this.quickpick.placeholder = localize('type to search', "Repository name (type to search)"); - this.quickpick.onDidChangeValue(this.onDidChangeValue, this); - } else { - this.quickpick.placeholder = localize('type to filter', "Repository name"); - } - } - - @debounce(300) - private onDidChangeValue(): void { - this.query(); - } - - @throttle - private async query(): Promise { - this.quickpick.busy = true; - - try { - const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || []; - - if (remoteSources.length === 0) { - this.quickpick.items = [{ - label: localize('none found', "No remote repositories found."), - alwaysShow: true - }]; - } else { - this.quickpick.items = remoteSources.map(remoteSource => ({ - label: remoteSource.name, - description: remoteSource.description || (typeof remoteSource.url === 'string' ? remoteSource.url : remoteSource.url[0]), - remoteSource - })); - } - } catch (err) { - this.quickpick.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }]; - console.error(err); - } finally { - this.quickpick.busy = false; - } - } - - async pick(): Promise { - this.query(); - const result = await getQuickPickResult(this.quickpick); - return result?.remoteSource; - } + private quickpick: QuickPick; + + constructor(private provider: RemoteSourceProvider) { + this.quickpick = window.createQuickPick(); + this.quickpick.ignoreFocusOut = true; + + if (provider.supportsQuery) { + this.quickpick.placeholder = localize('type to search', "Repository name (type to search)"); + this.quickpick.onDidChangeValue(this.onDidChangeValue, this); + } else { + this.quickpick.placeholder = localize('type to filter', "Repository name"); + } + } + + @debounce(300) + private onDidChangeValue(): void { + this.query(); + } + + @throttle + private async query(): Promise { + this.quickpick.busy = true; + + try { + const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || []; + + if (remoteSources.length === 0) { + this.quickpick.items = [{ + label: localize('none found', "No remote repositories found."), + alwaysShow: true + }]; + } else { + this.quickpick.items = remoteSources.map(remoteSource => ({ + label: remoteSource.name, + description: remoteSource.description || (typeof remoteSource.url === 'string' ? remoteSource.url : remoteSource.url[0]), + remoteSource + })); + } + } catch (err) { + this.quickpick.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }]; + console.error(err); + } finally { + this.quickpick.busy = false; + } + } + + async pick(): Promise { + this.query(); + const result = await getQuickPickResult(this.quickpick); + return result?.remoteSource; + } } export interface PickRemoteSourceOptions { - readonly providerLabel?: (provider: RemoteSourceProvider) => string; - readonly urlLabel?: string; - readonly providerName?: string; + readonly providerLabel?: (provider: RemoteSourceProvider) => string; + readonly urlLabel?: string; + readonly providerName?: string; } export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise { - const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>(); - quickpick.ignoreFocusOut = true; - - const targetProvider = model.getRemoteProviders().filter(provider => provider.name === options.providerName); - if (targetProvider && targetProvider.length === 1) { - await pickProviderSource(targetProvider[0]); - } else { - const providers = model.getRemoteProviders() - .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + (options.providerLabel ? options.providerLabel(provider) : provider.name), alwaysShow: true, provider })); - - quickpick.placeholder = providers.length === 0 - ? localize('provide url', "Provide repository URL") - : localize('provide url or pick', "Provide repository URL or pick a repository source."); - - const updatePicks = (value?: string) => { - if (value) { - quickpick.items = [{ - label: options.urlLabel ?? localize('url', "URL"), - description: value, - alwaysShow: true, - url: value - }, - ...providers]; - } else { - quickpick.items = providers; - } - }; - - quickpick.onDidChangeValue(updatePicks); - updatePicks(); - - const result = await getQuickPickResult(quickpick); - - if (result) { - if (result.url) { - return result.url; - } else if (result.provider) { - return await pickProviderSource(result.provider); - } - } - } - - return undefined; + const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>(); + quickpick.ignoreFocusOut = true; + + const targetedProvider = model.getRemoteProviders().filter(provider => provider.name === options.providerName); + if (targetedProvider && targetedProvider.length === 1) { + await pickProviderSource(targetedProvider[0]); + } else { + const providers = model.getRemoteProviders() + .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + (options.providerLabel ? options.providerLabel(provider) : provider.name), alwaysShow: true, provider })); + + quickpick.placeholder = providers.length === 0 + ? localize('provide url', "Provide repository URL") + : localize('provide url or pick', "Provide repository URL or pick a repository source."); + + const updatePicks = (value?: string) => { + if (value) { + quickpick.items = [{ + label: options.urlLabel ?? localize('url', "URL"), + description: value, + alwaysShow: true, + url: value + }, + ...providers]; + } else { + quickpick.items = providers; + } + }; + + quickpick.onDidChangeValue(updatePicks); + updatePicks(); + + const result = await getQuickPickResult(quickpick); + + if (result) { + if (result.url) { + return result.url; + } else if (result.provider) { + return await pickProviderSource(result.provider); + } + } + } + + return undefined; } async function pickProviderSource(provider: RemoteSourceProvider): Promise { - const quickpick = new RemoteSourceProviderQuickPick(provider); - const remote = await quickpick.pick(); - - if (remote) { - if (typeof remote.url === 'string') { - return remote.url; - } else if (remote.url.length > 0) { - return await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); - } - } - - return undefined; + const quickpick = new RemoteSourceProviderQuickPick(provider); + const remote = await quickpick.pick(); + + if (remote) { + if (typeof remote.url === 'string') { + return remote.url; + } else if (remote.url.length > 0) { + return await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); + } + } + + return undefined; } From 3cc907a220de728e44245ad8aee478c129b1a2a9 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 10 Sep 2020 14:02:42 -0700 Subject: [PATCH 0034/1882] Fix return --- extensions/git/src/remoteSource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/remoteSource.ts b/extensions/git/src/remoteSource.ts index a1acb2b9f98a5..1ecdc71c25e47 100644 --- a/extensions/git/src/remoteSource.ts +++ b/extensions/git/src/remoteSource.ts @@ -89,7 +89,7 @@ export async function pickRemoteSource(model: Model, options: PickRemoteSourceOp const targetedProvider = model.getRemoteProviders().filter(provider => provider.name === options.providerName); if (targetedProvider && targetedProvider.length === 1) { - await pickProviderSource(targetedProvider[0]); + return await pickProviderSource(targetedProvider[0]); } else { const providers = model.getRemoteProviders() .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + (options.providerLabel ? options.providerLabel(provider) : provider.name), alwaysShow: true, provider })); From 2f1c0213d35ee4d133b66e43ad68e158f5db5c9a Mon Sep 17 00:00:00 2001 From: Jonathan Belcher Date: Wed, 16 Sep 2020 12:36:58 -0400 Subject: [PATCH 0035/1882] Fix composition logic for Firefox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/microsoft/vscode/issues/106392 In Firefox when inserting Emoji's using the macOS emoji input or using the compositor for characters with accents è, ë, î, etc. the character is inserted twice. Additionally, when adding an accented character the base character was also inserted. Examples: 🏅 inserts 🏅🏅 è inserts eèè This change does two things: - It removes `this._onType.fire(typeInput);` from the compositionupdate event listener. If you are in the middle of a composition there is no need to fire off the event. This change fixes the unaccented character from being inserted. `è` no longer inserts `eè`. - In the composition end function `browser.isFirefox` is added to the conditional that resets this._textAreaState to the value of the text area. This is needed as the insert of the composition is handled by L294. The textarea needs to be updated correctly when the compositions ends. Tested in Firefox (Windows 10, Ubuntu, macOS), Safari (macOS), Chrome (macOS), Edge Legacy (Windows 10), Edge (Windows 10) --- src/vs/editor/browser/controller/textAreaInput.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index 7b2868aa70105..cc8178d4927c3 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -274,7 +274,6 @@ export class TextAreaInput extends Disposable { const [newState, typeInput] = deduceComposition(e.data); this._textAreaState = newState; - this._onType.fire(typeInput); this._onCompositionUpdate.fire(e); })); @@ -295,9 +294,9 @@ export class TextAreaInput extends Disposable { this._onType.fire(typeInput); } - // Due to isEdgeOrIE (where the textarea was not cleared initially) and isChrome (the textarea is not updated correctly when composition ends) + // Due to isEdgeOrIE (where the textarea was not cleared initially) and isChrome, isFirefox (the textarea is not updated correctly when composition ends) // we cannot assume the text at the end consists only of the composited text - if (browser.isEdge || browser.isChrome) { + if (browser.isEdge || browser.isChrome || browser.isFirefox) { this._textAreaState = TextAreaState.readFromTextArea(this._textArea); } From 822ca5f07ba82df53a25a07e8da9cc8f6bb68a2a Mon Sep 17 00:00:00 2001 From: Jonathan Belcher Date: Wed, 16 Sep 2020 13:00:36 -0400 Subject: [PATCH 0036/1882] don't declare a variable that isn't used textInput is no longer used. This removes the declaration as it is not used or needed. --- src/vs/editor/browser/controller/textAreaInput.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index cc8178d4927c3..2daf53ee2c397 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -272,7 +272,7 @@ export class TextAreaInput extends Disposable { return; } - const [newState, typeInput] = deduceComposition(e.data); + const [newState] = deduceComposition(e.data); this._textAreaState = newState; this._onCompositionUpdate.fire(e); })); From e1fa9403bd5dd07d7c8727bee135bab5aca60d39 Mon Sep 17 00:00:00 2001 From: turara Date: Mon, 28 Sep 2020 23:42:13 +0900 Subject: [PATCH 0037/1882] Add keybinding shortcut for "Preserve case" replace option Resolves #107208 --- .../base/browser/ui/findinput/replaceInput.ts | 4 +++- src/vs/editor/contrib/find/findController.ts | 20 +++++++++++++++++-- src/vs/editor/contrib/find/findModel.ts | 4 ++++ src/vs/editor/contrib/find/findWidget.ts | 4 ++++ .../browser/find/simpleFindReplaceWidget.ts | 1 + .../search/browser/search.contribution.ts | 11 ++++++++-- .../contrib/search/browser/searchActions.ts | 7 +++++++ .../contrib/search/browser/searchView.ts | 5 +++++ .../contrib/search/browser/searchWidget.ts | 1 + .../contrib/search/common/constants.ts | 1 + 10 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/findinput/replaceInput.ts b/src/vs/base/browser/ui/findinput/replaceInput.ts index cdf5d65798df4..901049b813466 100644 --- a/src/vs/base/browser/ui/findinput/replaceInput.ts +++ b/src/vs/base/browser/ui/findinput/replaceInput.ts @@ -28,6 +28,7 @@ export interface IReplaceInputOptions extends IReplaceInputStyles { readonly flexibleWidth?: boolean; readonly flexibleMaxHeight?: number; + readonly appendPreserveCaseLabel?: string; readonly history?: string[]; } @@ -128,6 +129,7 @@ export class ReplaceInput extends Widget { this.inputValidationErrorBackground = options.inputValidationErrorBackground; this.inputValidationErrorForeground = options.inputValidationErrorForeground; + const appendPreserveCaseLabel = options.appendPreserveCaseLabel || ''; const history = options.history || []; const flexibleHeight = !!options.flexibleHeight; const flexibleWidth = !!options.flexibleWidth; @@ -161,7 +163,7 @@ export class ReplaceInput extends Widget { })); this.preserveCase = this._register(new PreserveCaseCheckbox({ - appendTitle: '', + appendTitle: appendPreserveCaseLabel, isChecked: false, inputActiveOptionBorder: this.inputActiveOptionBorder, inputActiveOptionForeground: this.inputActiveOptionForeground, diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index 7e452f6a6cdb3..80ae3f7644e5e 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -12,7 +12,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution, MultiEditorAction, registerMultiEditorAction } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding, CONTEXT_REPLACE_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; +import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, TogglePreserveCaseKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding, CONTEXT_REPLACE_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; import { FindOptionsWidget } from 'vs/editor/contrib/find/findOptionsWidget'; import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/findState'; import { FindWidget, IFindController } from 'vs/editor/contrib/find/findWidget'; @@ -224,7 +224,9 @@ export class CommonFindController extends Disposable implements IEditorContribut public togglePreserveCase(): void { this._state.change({ preserveCase: !this._state.preserveCase }, false); - this.highlightFindOptions(); + if (!this._state.isRevealed) { + this.highlightFindOptions(); + } } public toggleSearchScope(): void { @@ -858,6 +860,20 @@ registerEditorCommand(new FindCommand({ } })); +registerEditorCommand(new FindCommand({ + id: FIND_IDS.TogglePreserveCaseCommand, + precondition: undefined, + handler: x => x.togglePreserveCase(), + kbOpts: { + weight: KeybindingWeight.EditorContrib + 5, + kbExpr: EditorContextKeys.focus, + primary: TogglePreserveCaseKeybinding.primary, + mac: TogglePreserveCaseKeybinding.mac, + win: TogglePreserveCaseKeybinding.win, + linux: TogglePreserveCaseKeybinding.linux + } +})); + registerEditorCommand(new FindCommand({ id: FIND_IDS.ReplaceOneAction, precondition: CONTEXT_FIND_WIDGET_VISIBLE, diff --git a/src/vs/editor/contrib/find/findModel.ts b/src/vs/editor/contrib/find/findModel.ts index a902f36c6d067..11ec1c635055d 100644 --- a/src/vs/editor/contrib/find/findModel.ts +++ b/src/vs/editor/contrib/find/findModel.ts @@ -47,6 +47,10 @@ export const ToggleSearchScopeKeybinding: IKeybindings = { primary: KeyMod.Alt | KeyCode.KEY_L, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_L } }; +export const TogglePreserveCaseKeybinding: IKeybindings = { + primary: KeyMod.Alt | KeyCode.KEY_P, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_P } +}; export const FIND_IDS = { StartFindAction: 'actions.find', diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 71864ba97f7c6..21fbb0060e02a 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -353,6 +353,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL if (e.matchCase) { this._findInput.setCaseSensitive(this._state.matchCase); } + if (e.preserveCase) { + this._replaceInput.setPreserveCase(this._state.preserveCase); + } if (e.searchScope) { if (this._state.searchScope) { this._toggleSelectionFind.checked = true; @@ -1087,6 +1090,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._replaceInput = this._register(new ContextScopedReplaceInput(null, undefined, { label: NLS_REPLACE_INPUT_LABEL, placeholder: NLS_REPLACE_INPUT_PLACEHOLDER, + appendPreserveCaseLabel: this._keybindingLabelFor(FIND_IDS.TogglePreserveCaseCommand), history: [], flexibleHeight, flexibleWidth, diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts index ad05b40ac5b3a..9b8cf7a37b9e9 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts @@ -140,6 +140,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._findInput.setRegex(this._state.isRegex); this._findInput.setWholeWords(this._state.wholeWord); this._findInput.setCaseSensitive(this._state.matchCase); + this._replaceInput.setPreserveCase(this._state.preserveCase); this.findFirst(); })); diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 70b5e881f3eb4..c0b8a580b54e2 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -10,7 +10,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as platform from 'vs/base/common/platform'; import { dirname } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding } from 'vs/editor/contrib/find/findModel'; +import { ToggleCaseSensitiveKeybinding, TogglePreserveCaseKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding } from 'vs/editor/contrib/find/findModel'; import * as nls from 'vs/nls'; import { ICommandAction, MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; @@ -31,7 +31,7 @@ import { Extensions as ViewExtensions, IViewsRegistry, IViewContainersRegistry, import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition, IExplorerService, VIEWLET_ID as VIEWLET_ID_FILES } from 'vs/workbench/contrib/files/common/files'; 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, ExpandAllAction } from 'vs/workbench/contrib/search/browser/searchActions'; +import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, togglePreserveCaseCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction, ExpandAllAction } from 'vs/workbench/contrib/search/browser/searchActions'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; import { registerContributions as searchWidgetContributions } from 'vs/workbench/contrib/search/browser/searchWidget'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; @@ -642,6 +642,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule(Object.assign({ handler: toggleRegexCommand }, ToggleRegexKeybinding)); +KeybindingsRegistry.registerCommandAndKeybindingRule(Object.assign({ + id: Constants.TogglePreserveCaseId, + weight: KeybindingWeight.WorkbenchContrib, + when: Constants.SearchViewFocusedKey, + handler: togglePreserveCaseCommand +}, TogglePreserveCaseKeybinding)); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.AddCursorsAtSearchResults, weight: KeybindingWeight.WorkbenchContrib, diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 040baac60b984..13534ce365593 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -83,6 +83,13 @@ export const toggleRegexCommand = (accessor: ServicesAccessor) => { } }; +export const togglePreserveCaseCommand = (accessor: ServicesAccessor) => { + const searchView = getSearchView(accessor.get(IViewsService)); + if (searchView) { + searchView.togglePreserveCase(); + } +}; + export class FocusNextInputAction extends Action { static readonly ID = 'search.focus.nextInputBox'; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 2a53fe463566e..bbfa8bcbd639d 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1160,6 +1160,11 @@ export class SearchView extends ViewPane { this.triggerQueryChange(); } + togglePreserveCase(): void { + this.searchWidget.replaceInput.setPreserveCase(!this.searchWidget.replaceInput.getPreserveCase()); + this.triggerQueryChange(); + } + setSearchParameters(args: IFindInFilesArgs = {}): void { if (typeof args.isCaseSensitive === 'boolean') { this.searchWidget.searchInput.setCaseSensitive(args.isCaseSensitive); diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 78d6587b42336..d633afe199fb2 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -393,6 +393,7 @@ export class SearchWidget extends Widget { this.replaceInput = this._register(new ContextScopedReplaceInput(replaceBox, this.contextViewService, { label: nls.localize('label.Replace', 'Replace: Type replace term and press Enter to preview or Escape to cancel'), placeholder: nls.localize('search.replace.placeHolder', "Replace"), + appendPreserveCaseLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.TogglePreserveCaseId), this.keyBindingService), history: options.replaceHistory, flexibleHeight: true, flexibleMaxHeight: SearchWidget.INPUT_MAX_HEIGHT diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index f1e6975f7882c..38e04a3de249c 100644 --- a/src/vs/workbench/contrib/search/common/constants.ts +++ b/src/vs/workbench/contrib/search/common/constants.ts @@ -26,6 +26,7 @@ export const CloseReplaceWidgetActionId = 'closeReplaceInFilesWidget'; export const ToggleCaseSensitiveCommandId = 'toggleSearchCaseSensitive'; export const ToggleWholeWordCommandId = 'toggleSearchWholeWord'; export const ToggleRegexCommandId = 'toggleSearchRegex'; +export const TogglePreserveCaseId = 'toggleSearchPreserveCase'; export const AddCursorsAtSearchResults = 'addCursorsAtSearchResults'; export const RevealInSideBarForSearchResults = 'search.action.revealInSideBar'; From 774336d9a772617255689c059156d5789ea3aec1 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 29 Sep 2020 14:25:44 +0200 Subject: [PATCH 0038/1882] fixes #107670 --- src/vs/workbench/contrib/debug/browser/debugHover.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index aa484dd8c39f3..2bad9d0895b5a 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -298,7 +298,7 @@ export class DebugHoverWidget implements IContentWidget { } private layoutTreeAndContainer(initialLayout: boolean): void { - const scrollBarHeight = 8; + const scrollBarHeight = 10; const treeHeight = Math.min(this.editor.getLayoutInfo().height * 0.7, this.tree.contentHeight + scrollBarHeight); this.treeContainer.style.height = `${treeHeight}px`; this.tree.layout(treeHeight, initialLayout ? 400 : undefined); From 1287f6b14a4c488549ed01f53af11e61b9d5ac73 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 29 Sep 2020 15:13:47 +0200 Subject: [PATCH 0039/1882] remove console.log statement fyi @tyriar --- src/vs/workbench/services/hover/browser/hoverWidget.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/services/hover/browser/hoverWidget.ts b/src/vs/workbench/services/hover/browser/hoverWidget.ts index 3fddc21ed30b0..7d65e2fef387f 100644 --- a/src/vs/workbench/services/hover/browser/hoverWidget.ts +++ b/src/vs/workbench/services/hover/browser/hoverWidget.ts @@ -176,7 +176,6 @@ export class HoverWidget extends Widget { this._y = targetTop; } } else { - console.log('below'); const targetBottom = Math.max(...targetBounds.map(e => e.bottom)); if (targetBottom + this._hover.containerDomNode.clientHeight > window.innerHeight) { console.log(targetBottom, this._hover.containerDomNode.clientHeight, window.innerHeight); From 29a999424bc5d989b2dbc1b2ce60c074789c4140 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 29 Sep 2020 16:42:45 +0200 Subject: [PATCH 0040/1882] use time interval trigger instead of window open, also fix using correct maps --- .../electron-sandbox/extensionTipsService.ts | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts index 785abfcfe8190..125155e486865 100644 --- a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts +++ b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts @@ -16,13 +16,11 @@ import { forEach, IStringDictionary } from 'vs/base/common/collections'; import { IRequestService } from 'vs/platform/request/common/request'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtensionTipsService as BaseExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionTipsService'; -import { timeout } from 'vs/base/common/async'; +import { disposableTimeout, timeout } from 'vs/base/common/async'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { localize } from 'vs/nls'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { Event } from 'vs/base/common/event'; -import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; type ExeExtensionRecommendationsClassification = { extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; @@ -55,7 +53,6 @@ export class ExtensionTipsService extends BaseExtensionTipsService { @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IStorageService private readonly storageService: IStorageService, @IExtensionRecommendationNotificationService private readonly extensionRecommendationNotificationService: IExtensionRecommendationNotificationService, - @INativeHostService private readonly nativeHostMainService: INativeHostService, @IFileService fileService: IFileService, @IProductService productService: IProductService, @IRequestService requestService: IRequestService, @@ -176,8 +173,8 @@ export class ExtensionTipsService extends BaseExtensionTipsService { this.highImportanceTipsByExe.delete(exeName); break; case RecommendationsNotificationResult.TooMany: - // It is skipped. So try to recommend when new window is opened - this._register(Event.once(this.nativeHostMainService.onWindowOpen)(() => this.promptHighImportanceExeBasedTip())); + // Too many notifications. Schedule the prompt after one hour + const disposable = this._register(disposableTimeout(() => { disposable.dispose(); this.promptHighImportanceExeBasedTip(); }, 60 * 60 * 1000 /* 1 hour */)); break; } }); @@ -187,36 +184,44 @@ export class ExtensionTipsService extends BaseExtensionTipsService { * Medium importance tips are prompted once per 7 days */ private promptMediumImportanceExeBasedTip(): void { - if (this.mediumImportanceExecutableTips.size === 0) { + if (this.mediumImportanceTipsByExe.size === 0) { return; } const lastPromptedMediumExeTime = this.getLastPromptedMediumExeTime(); - if ((Date.now() - lastPromptedMediumExeTime) < 7 * 24 * 60 * 60 * 1000) { // 7 Days - // Not 7 days yet. So try to recommend when new window is opened - this._register(Event.once(this.nativeHostMainService.onWindowOpen)(() => this.promptMediumImportanceExeBasedTip())); + const timeSinceLastPrompt = Date.now() - lastPromptedMediumExeTime; + const promptInterval = 7 * 24 * 60 * 60 * 1000; // 7 Days + if (timeSinceLastPrompt < promptInterval) { + // Wait until interval and prompt + const disposable = this._register(disposableTimeout(() => { disposable.dispose(); this.promptMediumImportanceExeBasedTip(); }, promptInterval - timeSinceLastPrompt)); return; } - this.updateLastPromptedMediumExeTime(Date.now()); const [exeName, tips] = [...this.mediumImportanceTipsByExe.entries()][0]; this.promptExeRecommendations(tips) .then(result => { switch (result) { case RecommendationsNotificationResult.Accepted: + // Accepted: Update the last prompted time and caches. + this.updateLastPromptedMediumExeTime(Date.now()); this.mediumImportanceTipsByExe.delete(exeName); this.addToRecommendedExecutables(tips[0].exeName, tips); + + // Schedule the next recommendation for next internval + const disposable1 = this._register(disposableTimeout(() => { disposable1.dispose(); this.promptMediumImportanceExeBasedTip(); }, promptInterval)); break; - case RecommendationsNotificationResult.TooMany: - this.updateLastPromptedMediumExeTime(lastPromptedMediumExeTime); - break; + case RecommendationsNotificationResult.Ignored: - this.updateLastPromptedMediumExeTime(lastPromptedMediumExeTime); - this.mediumImportanceExecutableTips.delete(exeName); + // Ignored: Remove from the cache and prompt next recommendation + this.mediumImportanceTipsByExe.delete(exeName); + this.promptMediumImportanceExeBasedTip(); + break; + + case RecommendationsNotificationResult.TooMany: + // Too many notifications. Schedule the prompt after one hour + const disposable2 = this._register(disposableTimeout(() => { disposable2.dispose(); this.promptMediumImportanceExeBasedTip(); }, 60 * 60 * 1000 /* 1 hour */)); break; } - // Schedule next prompt when new window is opened - this._register(Event.once(this.nativeHostMainService.onWindowOpen)(() => this.promptMediumImportanceExeBasedTip())); }); } From 244934ed1016e873889d3392d652ef549afb64ba Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 29 Sep 2020 16:59:52 +0200 Subject: [PATCH 0041/1882] fix #107652 --- .../platform/native/electron-main/nativeHostMainService.ts | 2 +- src/vs/platform/windows/electron-main/windows.ts | 1 + src/vs/platform/windows/electron-main/windowsMainService.ts | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index ea874cb1795e3..09723f15ee9f5 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -74,7 +74,7 @@ export class NativeHostMainService implements INativeHostMainService { //#region Events - readonly onWindowOpen = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-created', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); + readonly onWindowOpen = Event.map(this.windowsMainService.onWindowOpened, window => window.id); readonly onWindowMaximize = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-maximize', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); readonly onWindowUnmaximize = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-unmaximize', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 7c4340170ca39..7b73ca5463c03 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -98,6 +98,7 @@ export interface IWindowsMainService { readonly _serviceBrand: undefined; + readonly onWindowOpened: Event; readonly onWindowReady: Event; readonly onWindowsCountChanged: Event; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 40ac58b8eca80..84c6b5e7cf872 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -163,6 +163,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private shuttingDown = false; + private readonly _onWindowOpened = this._register(new Emitter()); + readonly onWindowOpened = this._onWindowOpened.event; + private readonly _onWindowReady = this._register(new Emitter()); readonly onWindowReady = this._onWindowReady.event; @@ -1416,6 +1419,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Add to our list of windows WindowsMainService.WINDOWS.push(createdWindow); + // Indicate new window via event + this._onWindowOpened.fire(createdWindow); + // Indicate number change via event this._onWindowsCountChanged.fire({ oldCount: WindowsMainService.WINDOWS.length - 1, newCount: WindowsMainService.WINDOWS.length }); From 12c36762037937a25decc05d8113390408983630 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 29 Sep 2020 08:58:25 -0700 Subject: [PATCH 0042/1882] debug: update js-debug Fixes https://github.com/microsoft/vscode/issues/107642 --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index 004c172321583..669660099a34f 100644 --- a/product.json +++ b/product.json @@ -91,7 +91,7 @@ }, { "name": "ms-vscode.js-debug", - "version": "1.50.0", + "version": "1.50.1", "repo": "https://github.com/microsoft/vscode-js-debug", "metadata": { "id": "25629058-ddac-4e17-abba-74678e126c5d", @@ -106,7 +106,7 @@ }, { "name": "ms-vscode.vscode-js-profile-table", - "version": "0.0.6", + "version": "0.0.10", "repo": "https://github.com/microsoft/vscode-js-debug", "metadata": { "id": "7e52b41b-71ad-457b-ab7e-0620f1fc4feb", From 44cd620f57988099f8d5cb24d8a16c8d1cfb4064 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 29 Sep 2020 10:28:47 -0700 Subject: [PATCH 0043/1882] update pkg json --- remote/package.json | 1 + remote/yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/remote/package.json b/remote/package.json index bed90d1e4fdaa..b47a58cd71b6f 100644 --- a/remote/package.json +++ b/remote/package.json @@ -15,6 +15,7 @@ "node-pty": "0.10.0-beta17", "semver-umd": "^5.5.7", "spdlog": "^0.11.1", + "tas-client-umd": "^0.1.2", "vscode-nsfw": "1.2.8", "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", diff --git a/remote/yarn.lock b/remote/yarn.lock index ade6bb557a476..c64b50914215e 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -373,6 +373,11 @@ spdlog@^0.11.1: mkdirp "^0.5.1" nan "^2.14.0" +tas-client-umd@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.2.tgz#fe93ae085f65424292ac79feff4f1add3e50e624" + integrity sha512-rT9BdDCejckqOTQL2ShX67QtTiAUGbmPm5ZTC8giXobBvZC6JuvBVy5G32hoGZ3Q0dpTvMfgpf3iVFNN2F7wzg== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" From 75272ee5813011f6de69efea0086598df6cd4773 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 29 Sep 2020 10:53:12 -0700 Subject: [PATCH 0044/1882] move endgame notebooke --- .vscode/notebooks/endgame.github-issues | 182 ++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 .vscode/notebooks/endgame.github-issues diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues new file mode 100644 index 0000000000000..05049a404364b --- /dev/null +++ b/.vscode/notebooks/endgame.github-issues @@ -0,0 +1,182 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "# Endgame", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Macros", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github\n\n$MILESTONE=milestone:\"September 2020\"\n\n$MINE=assignee:@me", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Endgame Master", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Test Plan Items", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE label:testplan-item is:open", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Feature Requests Missing Labels", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE is:issue is:closed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Open Issues on the Milestone", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -label:testplan-item -label:iteration-plan -label:endgame-plan is:open", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Testing", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### My Assigned Test Plan Items", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE label:testplan-item is:open", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### My Created Test Plan Items", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE author:@me label:testplan-item", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Verification", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Issues to Verify", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Issues filed by me", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE author:@me sort:updated-asc is:closed is:issue label:bug -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Issues filed by others", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE -author:@me sort:updated-asc is:closed is:issue label:bug -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Verification Needed", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE is:issue is:closed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate ", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### My Issues Needing Verification", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE sort:updated-desc is:closed is:issue -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found\n", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### My Open Issues", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:open is:issue", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Documentation", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Needing Release Notes", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode $MILESTONE $MINE is:closed label:feature-request -label:on-release-notes", + "editable": true + } +] \ No newline at end of file From be89f7901c102115387694cf53d2c5344d46ea4f Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 29 Sep 2020 11:40:16 -0700 Subject: [PATCH 0045/1882] rename --- .vscode/notebooks/endgame.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 05049a404364b..3622427f78f97 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -20,7 +20,7 @@ { "kind": 1, "language": "markdown", - "value": "## Endgame Master", + "value": "## Endgame Champion", "editable": true }, { From 7d57a8f6f546b5e30027e7cfa87bd834eb5c7bbb Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 29 Sep 2020 22:30:13 +0200 Subject: [PATCH 0046/1882] - do not show more than one exe base recommendation - exe base recommendation should not hide current recommendation --- .../extensionRecommendationNotificationService.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index 54fd0db41c09f..b4ba09f78baef 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -67,8 +67,8 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec } private recommendedExtensions: string[] = []; + private recommendationSources: RecommendationSource[] = []; - private notificationsCount: number = 0; private hideVisibleNotificationPromise: CancelablePromise | undefined; private visibleNotification: VisibleRecommendationNotification | undefined; private pendingNotificaitons: PendingRecommendationNotification[] = []; @@ -100,8 +100,10 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec return RecommendationsNotificationResult.Ignored; } - // Ignore exe recommendation if the window has shown two recommendations already - if (source === RecommendationSource.EXE && this.notificationsCount >= 2) { + // Ignore exe recommendation if the window + // => has shown an exe based recommendation already + // => or has shown any two recommendations already + if (source === RecommendationSource.EXE && (this.recommendationSources.includes(RecommendationSource.EXE) || this.recommendationSources.length >= 2)) { return RecommendationsNotificationResult.TooMany; } @@ -261,15 +263,15 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec * If a new recommendation comes in * => If no recommendation is visible, show it immediately * => Otherwise, add to the pending queue - * => If it is higher or same priority, hide the current notification after showing it for 3s. + * => If it is not exe based and has higher or same priority as current, hide the current notification after showing it for 3s. * => Otherwise wait until the current notification is hidden. */ private async showNotification(recommendationNotification: RecommendationNotification): Promise { - this.notificationsCount++; + this.recommendationSources.push(recommendationNotification.priority); if (this.visibleNotification) { return new Promise((onDidShow, e) => { this.pendingNotificaitons.push({ ...recommendationNotification, onDidShow }); - if (recommendationNotification.priority <= this.visibleNotification!.priority) { + if (recommendationNotification.priority !== RecommendationSource.EXE && recommendationNotification.priority <= this.visibleNotification!.priority) { this.hideVisibleNotification(3000); } }); From 20b9010c39fcddde3d237ea4071792b7187cad82 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Sep 2020 16:20:51 -0700 Subject: [PATCH 0047/1882] Update README.md --- test/smoke/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/smoke/README.md b/test/smoke/README.md index 8f1d567c3e6a7..0b12079cd1b94 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -5,6 +5,9 @@ Make sure you are on **Node v12.x**. ### Run ```bash +# Build extensions in repo (if needed) +yarn && yarn compile + # Install Dependencies and Compile yarn --cwd test/smoke From 68c8f83b3865a60ea9c90e3d8245788f81ca5b7a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Sep 2020 15:43:25 -0700 Subject: [PATCH 0048/1882] Remove extra constructor --- .../contrib/webviewView/browser/webviewViewService.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts index 60d4a3a7a6fc3..68196b0f01f2f 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts @@ -44,10 +44,6 @@ export class WebviewViewService extends Disposable implements IWebviewViewServic private readonly _awaitingRevival = new Map void }>(); - constructor() { - super(); - } - register(viewType: string, resolver: IWebviewViewResolver): IDisposable { if (this._views.has(viewType)) { throw new Error(`View resolver already registered for ${viewType}`); From 07589030eb969dfab524278c81d4e074e10846b9 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 29 Sep 2020 17:26:08 -0700 Subject: [PATCH 0049/1882] get contributed notebook provider with the right view type. --- .../workbench/contrib/notebook/browser/notebookEditorWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index cc0916988393f..f555ab0c52f84 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -727,7 +727,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } private async _setKernels(textModel: NotebookTextModel, tokenSource: CancellationTokenSource) { - const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; + const provider = this.notebookService.getContributedNotebookProvider(textModel.viewType) || this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; const availableKernels2 = await this.notebookService.getContributedNotebookKernels2(textModel.viewType, textModel.uri, tokenSource.token); if (tokenSource.token.isCancellationRequested) { From ee536909df199846e539b51778697b32e37092f0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 29 Sep 2020 17:03:21 -0700 Subject: [PATCH 0050/1882] Enable logging for TS Server on web The web server does not have a log file --- .../src/tsServer/spawner.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index 7f4aaeefe13e7..95278f65d8764 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -10,6 +10,7 @@ import { ClientCapabilities, ClientCapability, ServerType } from '../typescriptS import API from '../utils/api'; import { SeparateSyntaxServerConfiguration, TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration'; import { Logger } from '../utils/logger'; +import { isWeb } from '../utils/platform'; import { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider'; import { PluginManager } from '../utils/plugins'; import { TelemetryReporter } from '../utils/telemetry'; @@ -203,11 +204,15 @@ export class TypeScriptServerSpawner { } if (TypeScriptServerSpawner.isLoggingEnabled(configuration)) { - const logDir = this._logDirectoryProvider.getNewLogDirectory(); - if (logDir) { - tsServerLogFile = path.join(logDir, `tsserver.log`); + if (isWeb()) { args.push('--logVerbosity', TsServerLogLevel.toString(configuration.tsServerLogLevel)); - args.push('--logFile', tsServerLogFile); + } else { + const logDir = this._logDirectoryProvider.getNewLogDirectory(); + if (logDir) { + tsServerLogFile = path.join(logDir, `tsserver.log`); + args.push('--logVerbosity', TsServerLogLevel.toString(configuration.tsServerLogLevel)); + args.push('--logFile', tsServerLogFile); + } } } From 6ef40a891bf5d7cd157138e4312f62da0f6946c1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 29 Sep 2020 17:59:05 -0700 Subject: [PATCH 0051/1882] Disable TS plugins on web --- .../src/tsServer/spawner.ts | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index 95278f65d8764..a3ad0abb1c735 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -216,21 +216,23 @@ export class TypeScriptServerSpawner { } } - const pluginPaths = this._pluginPathsProvider.getPluginPaths(); + if (!isWeb()) { + const pluginPaths = this._pluginPathsProvider.getPluginPaths(); - if (pluginManager.plugins.length) { - args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(',')); + if (pluginManager.plugins.length) { + args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(',')); - const isUsingBundledTypeScriptVersion = currentVersion.path === this._versionProvider.defaultVersion.path; - for (const plugin of pluginManager.plugins) { - if (isUsingBundledTypeScriptVersion || plugin.enableForWorkspaceTypeScriptVersions) { - pluginPaths.push(plugin.path); + const isUsingBundledTypeScriptVersion = currentVersion.path === this._versionProvider.defaultVersion.path; + for (const plugin of pluginManager.plugins) { + if (isUsingBundledTypeScriptVersion || plugin.enableForWorkspaceTypeScriptVersions) { + pluginPaths.push(plugin.path); + } } } - } - if (pluginPaths.length !== 0) { - args.push('--pluginProbeLocations', pluginPaths.join(',')); + if (pluginPaths.length !== 0) { + args.push('--pluginProbeLocations', pluginPaths.join(',')); + } } if (configuration.npmLocation) { From 706857dfa5267252b62abd22a0a30418f9cff812 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 29 Sep 2020 17:59:19 -0700 Subject: [PATCH 0052/1882] Pass executingFilePath to TS server --- .../src/tsServer/serverProcess.browser.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts index 01071f6e10e2a..bc06d18ee3566 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts @@ -19,7 +19,13 @@ export class WorkerServerProcess implements TsServerProcess { _configuration: TypeScriptServiceConfiguration, ) { const worker = new Worker(tsServerPath); - return new WorkerServerProcess(worker, args); + return new WorkerServerProcess(worker, [ + ...args, + + // Explicitly give TS Server its path so it can + // load local resources + '--executingFilePath', tsServerPath, + ]); } private _onDataHandlers = new Set<(data: Proto.Response) => void>(); From 6f4a9dfdc7878157002ec044f3fad3f36e7540b1 Mon Sep 17 00:00:00 2001 From: Chuck Lantz Date: Wed, 30 Sep 2020 02:04:01 +0000 Subject: [PATCH 0053/1882] Switch to a image for the dev container --- .devcontainer/Dockerfile | 122 ------------------------ .devcontainer/README.md | 44 ++++++--- .devcontainer/bin/init-dev-container.sh | 91 ------------------ .devcontainer/bin/set-resolution | 25 ----- .devcontainer/devcontainer.json | 36 ++----- .devcontainer/fluxbox/apps | 9 -- .devcontainer/fluxbox/init | 9 -- .devcontainer/fluxbox/menu | 16 ---- .nvmrc | 2 +- 9 files changed, 41 insertions(+), 313 deletions(-) delete mode 100644 .devcontainer/Dockerfile delete mode 100644 .devcontainer/bin/init-dev-container.sh delete mode 100644 .devcontainer/bin/set-resolution delete mode 100644 .devcontainer/fluxbox/apps delete mode 100644 .devcontainer/fluxbox/init delete mode 100644 .devcontainer/fluxbox/menu diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 504e4cebfe93d..0000000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,122 +0,0 @@ -#------------------------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. -#------------------------------------------------------------------------------------------------------------- - -FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-12 - -ARG TARGET_DISPLAY=":1" - -# VNC options -ARG MAX_VNC_RESOLUTION=1920x1080x16 -ARG TARGET_VNC_RESOLUTION=1920x1080 -ARG TARGET_VNC_DPI=72 -ARG TARGET_VNC_PORT=5901 -ARG VNC_PASSWORD="vscode" - -# noVNC (VNC web client) options -ARG INSTALL_NOVNC="true" -ARG NOVNC_VERSION=1.1.0 -ARG TARGET_NOVNC_PORT=6080 -ARG WEBSOCKETIFY_VERSION=0.9.0 - -# Firefox is useful for testing things like browser launch events, but optional -ARG INSTALL_FIREFOX="false" - -# Expected non-root username from base image -ARG USERNAME=node - -# Core environment variables for X11, VNC, and fluxbox -ENV DBUS_SESSION_BUS_ADDRESS="autolaunch:" \ - MAX_VNC_RESOLUTION="${MAX_VNC_RESOLUTION}" \ - VNC_RESOLUTION="${TARGET_VNC_RESOLUTION}" \ - VNC_DPI="${TARGET_VNC_DPI}" \ - VNC_PORT="${TARGET_VNC_PORT}" \ - NOVNC_PORT="${TARGET_NOVNC_PORT}" \ - DISPLAY="${TARGET_DISPLAY}" \ - LANG="en_US.UTF-8" \ - LANGUAGE="en_US.UTF-8" \ - VISUAL="nano" \ - EDITOR="nano" - -# Configure apt and install packages -RUN apt-get update \ - && export DEBIAN_FRONTEND=noninteractive \ - # - # Install the Cascadia Code fonts - https://github.com/microsoft/cascadia-code - && curl -sSL https://github.com/microsoft/cascadia-code/releases/download/v2004.30/CascadiaCode_2004.30.zip -o /tmp/cascadia-fonts.zip \ - && unzip /tmp/cascadia-fonts.zip -d /tmp/cascadia-fonts \ - && mkdir -p /usr/share/fonts/truetype/cascadia \ - && mv /tmp/cascadia-fonts/ttf/* /usr/share/fonts/truetype/cascadia/ \ - && rm -rf /tmp/cascadia-fonts.zip /tmp/cascadia-fonts \ - # - # Install X11, fluxbox and VS Code dependencies - && apt-get -y install --no-install-recommends \ - xvfb \ - x11vnc \ - fluxbox \ - dbus-x11 \ - x11-utils \ - x11-xserver-utils \ - xdg-utils \ - fbautostart \ - xterm \ - eterm \ - gnome-terminal \ - gnome-keyring \ - seahorse \ - nautilus \ - libx11-dev \ - libxkbfile-dev \ - libsecret-1-dev \ - libnotify4 \ - libnss3 \ - libxss1 \ - libasound2 \ - libgbm1 \ - xfonts-base \ - xfonts-terminus \ - fonts-noto \ - fonts-wqy-microhei \ - fonts-droid-fallback \ - vim-tiny \ - nano \ - # - # [Optional] Install noVNC - && if [ "${INSTALL_NOVNC}" = "true" ]; then \ - mkdir -p /usr/local/novnc \ - && curl -sSL https://github.com/novnc/noVNC/archive/v${NOVNC_VERSION}.zip -o /tmp/novnc-install.zip \ - && unzip /tmp/novnc-install.zip -d /usr/local/novnc \ - && cp /usr/local/novnc/noVNC-${NOVNC_VERSION}/vnc_lite.html /usr/local/novnc/noVNC-${NOVNC_VERSION}/index.html \ - && rm /tmp/novnc-install.zip \ - && curl -sSL https://github.com/novnc/websockify/archive/v${WEBSOCKETIFY_VERSION}.zip -o /tmp/websockify-install.zip \ - && unzip /tmp/websockify-install.zip -d /usr/local/novnc \ - && apt-get -y install --no-install-recommends python-numpy \ - && ln -s /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION} /usr/local/novnc/noVNC-${NOVNC_VERSION}/utils/websockify \ - && rm /tmp/websockify-install.zip; \ - fi \ - # - # [Optional] Install Firefox - && if [ "${INSTALL_FIREFOX}" = "true" ]; then \ - apt-get -y install --no-install-recommends firefox-esr; \ - fi \ - # - # Clean up - && apt-get autoremove -y \ - && apt-get clean -y \ - && rm -rf /var/lib/apt/lists/* - -COPY bin/init-dev-container.sh /usr/local/share/ -COPY bin/set-resolution /usr/local/bin/ -COPY fluxbox/* /root/.fluxbox/ -COPY fluxbox/* /home/${USERNAME}/.fluxbox/ - -# Update privs, owners of config files -RUN mkdir -p /var/run/dbus /root/.vnc /home/${USERNAME}/.vnc \ - && touch /root/.Xmodmap /home/${USERNAME}/.Xmodmap \ - && echo "${VNC_PASSWORD}" | tee /root/.vnc/passwd > /home/${USERNAME}/.vnc/passwd \ - && chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/.Xmodmap /home/${USERNAME}/.fluxbox /home/${USERNAME}/.vnc \ - && chmod +x /usr/local/share/init-dev-container.sh /usr/local/bin/set-resolution - -ENTRYPOINT ["/usr/local/share/init-dev-container.sh"] -CMD ["sleep", "infinity"] diff --git a/.devcontainer/README.md b/.devcontainer/README.md index a0f3b2f75f1ec..8de0ffeffac7a 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,8 +1,8 @@ # Code - OSS Development Container -This repository includes configuration for a development container for working with Code - OSS in an isolated local container or using [Visual Studio Codespaces](https://aka.ms/vso). +This repository includes configuration for a development container for working with Code - OSS in an isolated local container or using [GitHub Codespaces](https://github.com/features/codespaces). -> **Tip:** The default VNC password is `vscode`. The VNC server runs on port `5901` with a web client at `6080`. For better performance, we recommend using a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/). Applications like the macOS Screen Sharing app will not perform as well. [Chicken](https://sourceforge.net/projects/chicken/) is a good macOS alternative. +> **Tip:** The default VNC password is `vscode`. The VNC server runs on port `5901` with a web client at `6080`. For better performance, we recommend using a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/). Applications like the macOS Screen Sharing app will not perform as well. ## Quick start - local @@ -18,9 +18,9 @@ This repository includes configuration for a development container for working w > Note that the Remote - Containers extension requires the Visual Studio Code distribution of Code - OSS. See the [FAQ](https://aka.ms/vscode-remote/faq/license) for details. -4. Press Ctrl/Cmd + Shift + P and select **Remote-Containers: Clone Repository in Container Volume...**. +4. Press Ctrl/Cmd + Shift + P and select **Remote - Containers: Open Repository in Container...**. - > **Tip:** While you can use your local source tree instead, operations like `yarn install` can be slow on macOS or using the Hyper-V engine on Windows. We recommend the "clone repository in container" approach instead since it uses "named volume" rather than the local filesystem. + > **Tip:** While you can use your local source tree instead, operations like `yarn install` can be slow on macOS or using the Hyper-V engine on Windows. We recommend the "open repository" approach instead since it uses "named volume" rather than the local filesystem. 5. Type `https://github.com/microsoft/vscode` (or a branch or PR URL) in the input box and press Enter. @@ -30,25 +30,41 @@ Anything you start in VS Code or the integrated terminal will appear here. Next: **[Try it out!](#try-it)** -## Quick start - Codespaces +## Quick start - GitHub Codespaces ->Note that the Codespaces browser-based editor cannot currently access the desktop environment in this container (due to a [missing feature](https://github.com/MicrosoftDocs/vsonline/issues/117)). We recommend using Visual Studio Code from the desktop to connect instead in the near term. +> **IMPORTANT:** The current user beta for GitHub Codespaces uses a "Basic" sized codespace which is too small to run a full build of VS Code. You'll soon be able to use a "Standard" sized codespace (4-core, 8GB) that will be better suited for this purpose. -1. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the [Visual Studio Codespaces](https://aka.ms/vscs-ext-vscode) extension. +1. From the [microsoft/vscode GitHub repository](https://github.com/microsoft/vscode), click on the **Code** dropdown, select **Open with Codespaces**, and the **New codespace** - ![Image of VS Codespaces extension](https://microsoft.github.io/vscode-remote-release/images/codespaces-extn.png) + > Note that you will not see these options if you are not in the beta yet. - > Note that the Visual Studio Codespaces extension requires the Visual Studio Code distribution of Code - OSS. +2. After the codespace is up and running in your browser, press Ctrl/Cmd + Shift + P and select **View: Show Remote Explorer**. -2. Sign in by pressing Ctrl/Cmd + Shift + P and selecting **Codespaces: Sign In**. You may also need to use the **Codespaces: Create Plan** if you do not have a plan. See the [Codespaces docs](https://aka.ms/vso-docs/vscode) for details. +3. You should see port `6080` under **Forwarded Ports**. Select the line and click on the globe icon to open it in a browser tab. -3. Press Ctrl/Cmd + Shift + P and select **Codespaces: Create New Codespace**. + > If you do not see port `6080`, press Ctrl/Cmd + Shift + P, select **Forward a Port** and enter port `6080`. -4. Use default settings (which should include **Standard** 4 core, 8 GB RAM Codespace), select a plan, and then enter the repository URL `https://github.com/microsoft/vscode` (or a branch or PR URL) in the input box when prompted. +4. In the new tab, you should see noVNC. Click **Connect** and enter `vscode` as the password. -5. After the container is running, open a web browser and go to [http://localhost:6080](http://localhost:6080) or use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to `localhost:5901` and enter `vscode` as the password. +Anything you start in VS Code or the integrated terminal will appear here. + +Next: **[Try it out!](#try-it)** + +### Using VS Code with GitHub Codespaces + +You will likely see better performance when accessing the codespace you created from VS Code since you can use a[VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/). Here's how to do it. + +1. [Create a codespace](#quick-start---github-codespaces) if you have not already. -6. Anything you start in VS Code or the integrated terminal will appear here. +2. Set up [VS Code for use with GitHub Codespaces](https://docs.github.com/github/developing-online-with-codespaces/using-codespaces-in-visual-studio-code) + +3. After the VS Code is up and running, press Ctrl/Cmd + Shift + P, choose **Codespaces: Connect to Codespace**, and select the codespace you created. + +4. After you've connected to the codespace, use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to `localhost:5901` and enter `vscode` as the password. + +5. Anything you start in VS Code or the integrated terminal will appear here. + +Next: **[Try it out!](#try-it)** ## Try it! diff --git a/.devcontainer/bin/init-dev-container.sh b/.devcontainer/bin/init-dev-container.sh deleted file mode 100644 index 260cc27592243..0000000000000 --- a/.devcontainer/bin/init-dev-container.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash - -NONROOT_USER=node -LOG=/tmp/container-init.log - -# Execute the command it not already running -startInBackgroundIfNotRunning() -{ - log "Starting $1." - echo -e "\n** $(date) **" | sudoIf tee -a /tmp/$1.log > /dev/null - if ! pidof $1 > /dev/null; then - keepRunningInBackground "$@" - while ! pidof $1 > /dev/null; do - sleep 1 - done - log "$1 started." - else - echo "$1 is already running." | sudoIf tee -a /tmp/$1.log > /dev/null - log "$1 is already running." - fi -} - -# Keep command running in background -keepRunningInBackground() -{ - ($2 sh -c "while :; do echo [\$(date)] Process started.; $3; echo [\$(date)] Process exited!; sleep 5; done 2>&1" | sudoIf tee -a /tmp/$1.log > /dev/null & echo "$!" | sudoIf tee /tmp/$1.pid > /dev/null) -} - -# Use sudo to run as root when required -sudoIf() -{ - if [ "$(id -u)" -ne 0 ]; then - sudo "$@" - else - "$@" - fi -} - -# Use sudo to run as non-root user if not already running -sudoUserIf() -{ - if [ "$(id -u)" -eq 0 ]; then - sudo -u ${NONROOT_USER} "$@" - else - "$@" - fi -} - -# Log messages -log() -{ - echo -e "[$(date)] $@" | sudoIf tee -a $LOG > /dev/null -} - -log "** SCRIPT START **" - -# Start dbus. -log 'Running "/etc/init.d/dbus start".' -if [ -f "/var/run/dbus/pid" ] && ! pidof dbus-daemon > /dev/null; then - sudoIf rm -f /var/run/dbus/pid -fi -sudoIf /etc/init.d/dbus start 2>&1 | sudoIf tee -a /tmp/dbus-daemon-system.log > /dev/null -while ! pidof dbus-daemon > /dev/null; do - sleep 1 -done - -# Set up Xvfb. -startInBackgroundIfNotRunning "Xvfb" sudoIf "Xvfb ${DISPLAY:-:1} +extension RANDR -screen 0 ${MAX_VNC_RESOLUTION:-1920x1080x16}" - -# Start fluxbox as a light weight window manager. -startInBackgroundIfNotRunning "fluxbox" sudoUserIf "dbus-launch startfluxbox" - -# Start x11vnc -startInBackgroundIfNotRunning "x11vnc" sudoIf "x11vnc -display ${DISPLAY:-:1} -rfbport ${VNC_PORT:-5901} -localhost -no6 -xkb -shared -forever -passwdfile $HOME/.vnc/passwd" - -# Set resolution -/usr/local/bin/set-resolution ${VNC_RESOLUTION:-1280x720} ${VNC_DPI:-72} - - -# Spin up noVNC if installed and not runnning. -if [ -d "/usr/local/novnc" ] && [ "$(ps -ef | grep /usr/local/novnc/noVNC*/utils/launch.sh | grep -v grep)" = "" ]; then - keepRunningInBackground "noVNC" sudoIf "/usr/local/novnc/noVNC*/utils/launch.sh --listen ${NOVNC_PORT:-6080} --vnc localhost:${VNC_PORT:-5901}" - log "noVNC started." -else - log "noVNC is already running or not installed." -fi - -# Run whatever was passed in -log "Executing \"$@\"." -"$@" -log "** SCRIPT EXIT **" diff --git a/.devcontainer/bin/set-resolution b/.devcontainer/bin/set-resolution deleted file mode 100644 index 5b4ca79f51883..0000000000000 --- a/.devcontainer/bin/set-resolution +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -RESOLUTION=${1:-${VNC_RESOLUTION:-1920x1080}} -DPI=${2:-${VNC_DPI:-72}} -if [ -z "$1" ]; then - echo -e "**Current Settings **\n" - xrandr - echo -n -e "\nEnter new resolution (WIDTHxHEIGHT, blank for ${RESOLUTION}, Ctrl+C to abort).\n> " - read NEW_RES - if [ "${NEW_RES}" != "" ]; then - RESOLUTION=${NEW_RES} - fi - if [ -z "$2" ]; then - echo -n -e "\nEnter new DPI (blank for ${DPI}, Ctrl+C to abort).\n> " - read NEW_DPI - if [ "${NEW_DPI}" != "" ]; then - DPI=${NEW_DPI} - fi - fi -fi - -xrandr --fb ${RESOLUTION} --dpi ${DPI} > /dev/null 2>&1 - -echo -e "\n**New Settings **\n" -xrandr -echo diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 722bace6df74d..3c0cab4da28a6 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,42 +1,26 @@ { "name": "Code - OSS", - "build": { - "dockerfile": "Dockerfile", - "args": { - "MAX_VNC_RESOLUTION": "1920x1080x16", - "TARGET_VNC_RESOLUTION": "1280x768", - "TARGET_VNC_PORT": "5901", - "TARGET_NOVNC_PORT": "6080", - "VNC_PASSWORD": "vscode", - "INSTALL_FIREFOX": "true" - } - }, + + // Image contents: https://github.com/microsoft/vscode-dev-containers/blob/master/repository-containers/images/github.com/microsoft/vscode/.devcontainer/base.Dockerfile + "image": "mcr.microsoft.com/vscode/devcontainers/repos/microsoft/vscode:dev", + + "workspaceMount": "source=${localWorkspaceFolder},target=/home/node/workspace/vscode,type=bind,consistency=cached", + "workspaceFolder": "/home/node/workspace/vscode", "overrideCommand": false, - "runArgs": [ - "--init", - // seccomp=unconfined is required for Chrome sandboxing - "--security-opt", "seccomp=unconfined" - ], + "runArgs": [ "--init", "--security-opt", "seccomp=unconfined"], "settings": { - // zsh is also available "terminal.integrated.shell.linux": "/bin/bash", "resmon.show.battery": false, - "resmon.show.cpufreq": false, - "remote.extensionKind": { - "ms-vscode.js-debug-nightly": "workspace", - "msjsdiag.debugger-for-chrome": "workspace" - }, - "debug.chrome.useV3": true + "resmon.show.cpufreq": false }, - // noVNC, VNC ports - "forwardPorts": [6080, 5901], + // noVNC, VNC ports, debug + "forwardPorts": [6080, 5901, 9222], "extensions": [ "dbaeumer.vscode-eslint", "EditorConfig.EditorConfig", - "msjsdiag.debugger-for-chrome", "mutantdino.resourcemonitor", "GitHub.vscode-pull-request-github" ], diff --git a/.devcontainer/fluxbox/apps b/.devcontainer/fluxbox/apps deleted file mode 100644 index d43f05e9e2057..0000000000000 --- a/.devcontainer/fluxbox/apps +++ /dev/null @@ -1,9 +0,0 @@ -[app] (name=code-oss-dev) - [Position] (CENTER) {0 0} - [Maximized] {yes} - [Dimensions] {100% 100%} -[end] -[transient] (role=GtkFileChooserDialog) - [Position] (CENTER) {0 0} - [Dimensions] {70% 70%} -[end] diff --git a/.devcontainer/fluxbox/init b/.devcontainer/fluxbox/init deleted file mode 100644 index a6b8d73fa73d4..0000000000000 --- a/.devcontainer/fluxbox/init +++ /dev/null @@ -1,9 +0,0 @@ -session.menuFile: ~/.fluxbox/menu -session.keyFile: ~/.fluxbox/keys -session.styleFile: /usr/share/fluxbox/styles//Squared_for_Debian -session.configVersion: 13 -session.screen0.workspaces: 1 -session.screen0.workspacewarping: false -session.screen0.toolbar.widthPercent: 100 -session.screen0.strftimeFormat: %d %b, %a %02k:%M:%S -session.screen0.toolbar.tools: prevworkspace, workspacename, nextworkspace, clock, prevwindow, nextwindow, iconbar, systemtray diff --git a/.devcontainer/fluxbox/menu b/.devcontainer/fluxbox/menu deleted file mode 100644 index ff5955a2fe75a..0000000000000 --- a/.devcontainer/fluxbox/menu +++ /dev/null @@ -1,16 +0,0 @@ -[begin] ( Code - OSS Development Container ) - [exec] (File Manager) { nautilus ~ } <> - [exec] (Terminal) {/usr/bin/gnome-terminal --working-directory=~ } <> - [exec] (Start Code - OSS) { x-terminal-emulator -T "Code - OSS Build" -e bash /workspaces/vscode*/scripts/code.sh } <> - [submenu] (System >) {} - [exec] (Set Resolution) { x-terminal-emulator -T "Set Resolution" -e bash /usr/local/bin/set-resolution } <> - [exec] (Passwords and Keys) { seahorse } <> - [exec] (Top) { x-terminal-emulator -T "Top" -e /usr/bin/top } <> - [exec] (Editres) {editres} <> - [exec] (Xfontsel) {xfontsel} <> - [exec] (Xkill) {xkill} <> - [exec] (Xrefresh) {xrefresh} <> - [end] - [config] (Configuration >) - [workspaces] (Workspaces >) -[end] diff --git a/.nvmrc b/.nvmrc index f599e28b8ab0d..48082f72f087c 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -10 +12 From c057a062061910ae1b41624674615b9674e47f84 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Sep 2020 21:50:46 -0700 Subject: [PATCH 0054/1882] Don't set special setting link format in aria-label Fix #107664 --- src/vs/workbench/contrib/preferences/browser/settingsTree.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 8daf08e3cb7c8..0174be3416c7d 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1836,7 +1836,8 @@ class SettingsTreeAccessibilityProvider implements IListAccessibilityProvider Date: Tue, 29 Sep 2020 21:54:27 -0700 Subject: [PATCH 0055/1882] Limit scope when SettingTree focus context is applied Fix #107745 --- .../contrib/preferences/browser/settingsEditor2.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index fa9d3ddf2b714..656789c84b81d 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -686,8 +686,10 @@ export class SettingsEditor2 extends EditorPane { })); this._register(this.settingsTree.onDidFocus(() => { - this._currentFocusContext = SettingsFocusContext.SettingTree; - this.settingRowFocused.set(true); + if (document.activeElement?.classList.contains('monaco-list')) { + this._currentFocusContext = SettingsFocusContext.SettingTree; + this.settingRowFocused.set(true); + } })); this._register(this.settingsTree.onDidBlur(() => { From 0c50e1db17168a738235bd7e5c02b9f05ea5df59 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Sep 2020 08:03:36 +0200 Subject: [PATCH 0056/1882] @sbatten keep the build green please --- remote/package.json | 2 +- remote/yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/remote/package.json b/remote/package.json index b47a58cd71b6f..2a129cfbb06bc 100644 --- a/remote/package.json +++ b/remote/package.json @@ -15,7 +15,7 @@ "node-pty": "0.10.0-beta17", "semver-umd": "^5.5.7", "spdlog": "^0.11.1", - "tas-client-umd": "^0.1.2", + "tas-client-umd": "0.1.2", "vscode-nsfw": "1.2.8", "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", diff --git a/remote/yarn.lock b/remote/yarn.lock index c64b50914215e..7e617fee91839 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -373,7 +373,7 @@ spdlog@^0.11.1: mkdirp "^0.5.1" nan "^2.14.0" -tas-client-umd@^0.1.2: +tas-client-umd@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.2.tgz#fe93ae085f65424292ac79feff4f1add3e50e624" integrity sha512-rT9BdDCejckqOTQL2ShX67QtTiAUGbmPm5ZTC8giXobBvZC6JuvBVy5G32hoGZ3Q0dpTvMfgpf3iVFNN2F7wzg== From b26d4316f1385c4b2727ef98c0a7db0664a9337f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Sep 2020 08:54:21 +0200 Subject: [PATCH 0057/1882] editors - add a bit more clarifying docs --- src/vs/workbench/common/editor.ts | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 88da6d9ba1bae..50b9473002a17 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -394,15 +394,15 @@ export interface IEditorInput extends IDisposable { readonly onDidChangeLabel: Event; /** - * Returns the optional associated canonical resource of this input. + * Returns the optional associated resource of this input. * * This resource should be unique for all editors of the same - * kind and is often used to identify the editor input among + * kind and input and is often used to identify the editor input among * others. * - * Note: use `EditorResourceAccessor` to access the resource - * of editors with support for side-by-side editors and - * canonical vs original resources. + * **Note:** DO NOT use this property for anything but identity + * checks. DO NOT use this property to present as label to the user. + * Please refer to `EditorResourceAccessor` documentation in that case. */ readonly resource: URI | undefined; @@ -677,6 +677,10 @@ export interface IEditorInputWithPreferredResource { * "normalize" the URIs so that only one editor opens for different * URIs. But when displaying the editor label to the user, the * preferred URI should be used. + * + * Not all editors have a `preferredResouce`. The `EditorResourceAccessor` + * utility can be used to always get the right resource without having + * to do instanceof checks. */ readonly preferredResource: URI; } @@ -767,6 +771,10 @@ export class SideBySideEditorInput extends EditorInput { this._register(this.primary.onDidChangeLabel(() => this._onDidChangeLabel.fire())); } + /** + * Use `EditorResourceAccessor` utility method to access the resources + * of both sides of the diff editor. + */ get resource(): URI | undefined { return undefined; } @@ -1313,9 +1321,10 @@ class EditorResourceAccessorImpl { * be used whenever the URI is used to e.g. compare with other editors or when * caching certain data based on the URI. * - * For example: on Windows and macOS, the same file URI with different casing may + * For example: on Windows and macOS, the same file URI with different casing may * point to the same file. The editor may chose to "normalize" the URI into a canonical - * form so that only one editor opens for same file URIs with different casing. + * form so that only one editor opens for same file URIs with different casing. As + * such, the original URI and the canonical URI can be different. */ getOriginalUri(editor: IEditorInput | undefined | null): URI | undefined; getOriginalUri(editor: IEditorInput | undefined | null, options: IEditorResourceAccessorOptions & { supportSideBySide?: SideBySideEditor.PRIMARY | SideBySideEditor.SECONDARY }): URI | undefined; @@ -1356,7 +1365,8 @@ class EditorResourceAccessorImpl { * * For example: on Windows and macOS, the same file URI with different casing may * point to the same file. The editor may chose to "normalize" the URI into a canonical - * form so that only one editor opens for same file URIs with different casing. + * form so that only one editor opens for same file URIs with different casing. As + * such, the original URI and the canonical URI can be different. */ getCanonicalUri(editor: IEditorInput | undefined | null): URI | undefined; getCanonicalUri(editor: IEditorInput | undefined | null, options: IEditorResourceAccessorOptions & { supportSideBySide?: SideBySideEditor.PRIMARY | SideBySideEditor.SECONDARY }): URI | undefined; From 426dbaeafee98bd58987d48bb097584d66877c6b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Sep 2020 08:57:50 +0200 Subject: [PATCH 0058/1882] fix startup timings computation in timer service --- src/vs/workbench/services/timer/browser/timerService.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/timer/browser/timerService.ts b/src/vs/workbench/services/timer/browser/timerService.ts index a41052db6a4b3..65126d9699e27 100644 --- a/src/vs/workbench/services/timer/browser/timerService.ts +++ b/src/vs/workbench/services/timer/browser/timerService.ts @@ -8,7 +8,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IUpdateService } from 'vs/platform/update/common/update'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -320,7 +320,10 @@ export abstract class AbstractTimerService implements ITimerService { @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @ITelemetryService private readonly _telemetryService: ITelemetryService, ) { - this._startupMetrics = this._extensionService.whenInstalledExtensionsRegistered() + this._startupMetrics = Promise.all([ + this._extensionService.whenInstalledExtensionsRegistered(), + _lifecycleService.when(LifecyclePhase.Restored) + ]) .then(() => this._computeStartupMetrics()) .then(metrics => { this._reportStartupTimes(metrics); From 7cf15b2d3169fc66ba7037c23f731124b8eb47f7 Mon Sep 17 00:00:00 2001 From: Cameron Little Date: Wed, 30 Sep 2020 08:52:38 +0200 Subject: [PATCH 0059/1882] Fix crash in LSP servers when initializationOptions are not provided --- .../css-language-features/server/src/cssServer.ts | 2 +- .../html-language-features/server/src/htmlServer.ts | 10 +++++----- .../json-language-features/server/src/jsonServer.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/extensions/css-language-features/server/src/cssServer.ts b/extensions/css-language-features/server/src/cssServer.ts index c2666a46c50ed..0b30955bd3f41 100644 --- a/extensions/css-language-features/server/src/cssServer.ts +++ b/extensions/css-language-features/server/src/cssServer.ts @@ -67,7 +67,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } } - requestService = getRequestService(params.initializationOptions.handledSchemas || ['file'], connection, runtime); + requestService = getRequestService(params.initializationOptions?.handledSchemas || ['file'], connection, runtime); function getClientCapability(name: string, def: T) { const keys = name.split('.'); diff --git a/extensions/html-language-features/server/src/htmlServer.ts b/extensions/html-language-features/server/src/htmlServer.ts index ade37f2253366..1ddfef6ce3498 100644 --- a/extensions/html-language-features/server/src/htmlServer.ts +++ b/extensions/html-language-features/server/src/htmlServer.ts @@ -112,16 +112,16 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } } - requestService = getRequestService(params.initializationOptions.handledSchemas || ['file'], connection, runtime); + requestService = getRequestService(initializationOptions?.handledSchemas || ['file'], connection, runtime); const workspace = { get settings() { return globalSettings; }, get folders() { return workspaceFolders; } }; - languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace, params.capabilities, requestService); + languageModes = getLanguageModes(initializationOptions?.embeddedLanguages || { css: true, javascript: true }, workspace, params.capabilities, requestService); - const dataPaths: string[] = params.initializationOptions.dataPaths || []; + const dataPaths: string[] = initializationOptions?.dataPaths || []; fetchHTMLDataProviders(dataPaths, requestService).then(dataProviders => { languageModes.updateDataProviders(dataProviders); }); @@ -146,7 +146,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false); - dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions.provideFormatter !== 'boolean'); + dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof initializationOptions?.provideFormatter !== 'boolean'); scopedSettingsSupport = getClientCapability('workspace.configuration', false); workspaceFoldersSupport = getClientCapability('workspace.workspaceFolders', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); @@ -155,7 +155,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/'] } : undefined, hoverProvider: true, documentHighlightProvider: true, - documentRangeFormattingProvider: params.initializationOptions.provideFormatter === true, + documentRangeFormattingProvider: initializationOptions?.provideFormatter === true, documentLinkProvider: { resolveProvider: false }, documentSymbolProvider: true, definitionProvider: true, diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index 761e638a9f3d9..26191bc4cc3a2 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -137,7 +137,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } : undefined, hoverProvider: true, documentSymbolProvider: true, - documentRangeFormattingProvider: params.initializationOptions.provideFormatter === true, + documentRangeFormattingProvider: params.initializationOptions?.provideFormatter === true, colorProvider: {}, foldingRangeProvider: true, selectionRangeProvider: true, From f48da22bdf8a3bc4264838167ba27d581b8055f1 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 30 Sep 2020 09:16:06 +0200 Subject: [PATCH 0060/1882] Update README.md --- test/smoke/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/test/smoke/README.md b/test/smoke/README.md index 0b12079cd1b94..b213445924a3f 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -33,6 +33,7 @@ You must always run the smoketest version which matches the release you are test ```bash git checkout release/1.22 +yarn && yarn compile yarn --cwd test/smoke ``` From 46ba25e11d30a9d7477e6c014f7fbcee679d54bc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Sep 2020 09:19:29 +0200 Subject: [PATCH 0061/1882] search editor - go back to canonical URI for now --- src/vs/workbench/browser/parts/editor/titleControl.ts | 2 +- .../contrib/searchEditor/browser/searchEditor.contribution.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index fac34fe064d02..ef49ef6ed5d43 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -229,7 +229,7 @@ export abstract class TitleControl extends Themable { // Update contexts this.contextKeyService.bufferChangeEvents(() => { - this.resourceContext.set(this.group.activeEditor ? withUndefinedAsNull(EditorResourceAccessor.getOriginalUri(this.group.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY })) : null); + this.resourceContext.set(withUndefinedAsNull(EditorResourceAccessor.getOriginalUri(this.group.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }))); this.editorPinnedContext.set(this.group.activeEditor ? this.group.isPinned(this.group.activeEditor) : false); this.editorStickyContext.set(this.group.activeEditor ? this.group.isSticky(this.group.activeEditor) : false); }); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index ee6655bc50ff4..3d48ff0d51947 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -21,7 +21,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { ActiveEditorContext, Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry, EditorResourceAccessor } from 'vs/workbench/common/editor'; +import { ActiveEditorContext, Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { IViewsService } from 'vs/workbench/common/views'; import { getSearchView } from 'vs/workbench/contrib/search/browser/searchActions'; import { searchRefreshIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; @@ -75,7 +75,7 @@ class SearchEditorContribution implements IWorkbenchContribution { this.editorService.overrideOpenEditor({ open: (editor, options, group) => { - const resource = EditorResourceAccessor.getOriginalUri(editor); + const resource = editor.resource; if (!resource) { return undefined; } if (extname(resource) !== SEARCH_EDITOR_EXT) { From bce8db3171177467309da630940e7bca1f932bc5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Sep 2020 09:28:41 +0200 Subject: [PATCH 0062/1882] search - use original URI for getOutOfWorkspaceEditorResources --- src/vs/workbench/contrib/search/common/search.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/common/search.ts b/src/vs/workbench/contrib/search/common/search.ts index 0a757f9c3093f..c0e440eb3232c 100644 --- a/src/vs/workbench/contrib/search/common/search.ts +++ b/src/vs/workbench/contrib/search/common/search.ts @@ -96,7 +96,7 @@ export function getOutOfWorkspaceEditorResources(accessor: ServicesAccessor): UR const fileService = accessor.get(IFileService); const resources = editorService.editors - .map(editor => EditorResourceAccessor.getCanonicalUri(editor, { supportSideBySide: SideBySideEditor.PRIMARY })) + .map(editor => EditorResourceAccessor.getOriginalUri(editor, { supportSideBySide: SideBySideEditor.PRIMARY })) .filter(resource => !!resource && !contextService.isInsideWorkspace(resource) && fileService.canHandleResource(resource)); return resources as URI[]; From 7ae81709d205071d8105aedc4a9681b37f6dc49b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Sep 2020 10:08:54 +0200 Subject: [PATCH 0063/1882] editor overrides - go back to canonical resource for now --- src/vs/workbench/browser/contextkeys.ts | 4 ++-- src/vs/workbench/browser/parts/editor/editorActions.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index d63d93e67f644..6fa3ae394c591 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; -import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, EditorResourceAccessor } from 'vs/workbench/common/editor'; +import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext } 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'; @@ -215,7 +215,7 @@ export class WorkbenchContextKeysHandler extends Disposable { this.activeEditorContext.set(activeEditorPane.getId()); this.activeEditorIsReadonly.set(activeEditorPane.input.isReadonly()); - const activeEditorResource = EditorResourceAccessor.getOriginalUri(activeEditorPane.input); + const activeEditorResource = activeEditorPane.input.resource; const editors = activeEditorResource ? this.editorService.getEditorOverrides(activeEditorResource, undefined, activeGroup) : []; this.activeEditorAvailableEditorIds.set(editors.map(([_, entry]) => entry.id).join(',')); } else { diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 0474168ee6ba0..8b18d4e90bab8 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; -import { IEditorInput, IEditorIdentifier, IEditorCommandsContext, CloseDirection, SaveReason, EditorsOrder, SideBySideEditorInput, EditorResourceAccessor } from 'vs/workbench/common/editor'; +import { IEditorInput, IEditorIdentifier, IEditorCommandsContext, CloseDirection, SaveReason, EditorsOrder, SideBySideEditorInput } from 'vs/workbench/common/editor'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -1846,7 +1846,7 @@ export class ToggleEditorTypeAction extends Action { return; } - const activeEditorResource = EditorResourceAccessor.getOriginalUri(activeEditorPane.input); + const activeEditorResource = activeEditorPane.input.resource; if (!activeEditorResource) { return; } From 200296d6e6efba09a84b00003dbdf975ceb93739 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Sep 2020 10:12:24 +0200 Subject: [PATCH 0064/1882] editors - more fixes around editor resources in "open with" --- src/vs/workbench/services/editor/common/editorOpenWith.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/editor/common/editorOpenWith.ts b/src/vs/workbench/services/editor/common/editorOpenWith.ts index 328ee76a678c9..f0e0503c3544b 100644 --- a/src/vs/workbench/services/editor/common/editorOpenWith.ts +++ b/src/vs/workbench/services/editor/common/editorOpenWith.ts @@ -37,7 +37,7 @@ export async function openEditorWith( configurationService: IConfigurationService, quickInputService: IQuickInputService, ): Promise { - const resource = EditorResourceAccessor.getOriginalUri(input); + const resource = input.resource; if (!resource) { return; } @@ -60,7 +60,8 @@ export async function openEditorWith( } // Prompt - const resourceExt = extname(resource); + const originalResource = EditorResourceAccessor.getOriginalUri(input) || resource; + const resourceExt = extname(originalResource); const items: (IQuickPickItem & { handler: IOpenEditorOverrideHandler })[] = allEditorOverrides.map(([handler, entry]) => { return { @@ -81,7 +82,7 @@ export async function openEditorWith( if (items.length) { picker.selectedItems = [items[0]]; } - picker.placeholder = nls.localize('promptOpenWith.placeHolder', "Select editor for '{0}'", basename(resource)); + picker.placeholder = nls.localize('promptOpenWith.placeHolder', "Select editor for '{0}'", basename(originalResource)); const pickedItem = await new Promise<(IQuickPickItem & { handler: IOpenEditorOverrideHandler }) | undefined>(resolve => { picker.onDidAccept(() => { From 0f779f644ff5196575bfc8d89844cec972da8c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 30 Sep 2020 11:08:56 +0200 Subject: [PATCH 0065/1882] remove nvmrc --- .nvmrc | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index f599e28b8ab0d..0000000000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -10 From db674322687bbfc592402e1bebdcf91f39b30c1a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Sep 2020 11:33:48 +0200 Subject: [PATCH 0066/1882] editors - adopt uri identity in one more place --- src/vs/workbench/services/editor/browser/editorService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 85bff9d25b196..02bdd13b06d6b 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -250,7 +250,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Determine new resulting target resource let targetResource: URI; - if (isEqual(source, resource)) { + if (this.uriIdentityService.extUri.isEqual(source, resource)) { targetResource = target; // file got moved } else { const ignoreCase = !this.fileService.hasCapability(resource, FileSystemProviderCapabilities.PathCaseSensitive); From 59c991e334b098b8674a706fdc7fa822bfbbce47 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Sep 2020 11:52:35 +0200 Subject: [PATCH 0067/1882] fix #107675 --- .../browser/parts/editor/tabsTitleControl.ts | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index df1cd093bb9b6..33eefc1ee552b 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -442,7 +442,7 @@ export class TabsTitleControl extends TitleControl { } pinEditor(editor: IEditorInput): void { - this.withTab(editor, (editor, index, tabContainer, tabLabelWidget, tabLabel) => this.redrawLabel(editor, index, tabContainer, tabLabelWidget, tabLabel)); + this.withTab(editor, (editor, index, tabContainer, tabLabelWidget, tabLabel) => this.redrawTabLabel(editor, index, tabContainer, tabLabelWidget, tabLabel)); } stickEditor(editor: IEditorInput): void { @@ -458,15 +458,21 @@ export class TabsTitleControl extends TitleControl { // Update tab this.withTab(editor, (editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar) => this.redrawTab(editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar)); + // Sticky change has an impact on each tab's border because + // it potentially moves the border to the last pinned tab + this.forEachTab((editor, index, tabContainer, tabLabelWidget, tabLabel) => { + this.redrawTabBorders(index, tabContainer); + }); + // A change to the sticky state requires a layout to keep the active editor visible this.layout(this.dimension); } setActive(isGroupActive: boolean): void { - // Activity has an impact on each tab + // Activity has an impact on each tab's active indication this.forEachTab((editor, index, tabContainer, tabLabelWidget, tabLabel) => { - this.redrawEditorActiveAndDirty(isGroupActive, editor, tabContainer, tabLabelWidget); + this.redrawTabActiveAndDirty(isGroupActive, editor, tabContainer, tabLabelWidget); }); // Activity has an impact on the toolbar, so we need to update and layout @@ -493,7 +499,7 @@ export class TabsTitleControl extends TitleControl { // As such we need to redraw each label this.forEachTab((editor, index, tabContainer, tabLabelWidget, tabLabel) => { - this.redrawLabel(editor, index, tabContainer, tabLabelWidget, tabLabel); + this.redrawTabLabel(editor, index, tabContainer, tabLabelWidget, tabLabel); }); // A change to a label requires a layout to keep the active editor visible @@ -501,7 +507,7 @@ export class TabsTitleControl extends TitleControl { } updateEditorDirty(editor: IEditorInput): void { - this.withTab(editor, (editor, index, tabContainer, tabLabelWidget) => this.redrawEditorActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget)); + this.withTab(editor, (editor, index, tabContainer, tabLabelWidget) => this.redrawTabActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget)); } updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void { @@ -1020,11 +1026,10 @@ export class TabsTitleControl extends TitleControl { private redrawTab(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel, tabActionBar: ActionBar): void { const isTabSticky = this.group.isSticky(index); - const isTabLastSticky = isTabSticky && this.group.stickyCount === index + 1; const options = this.accessor.partOptions; // Label - this.redrawLabel(editor, index, tabContainer, tabLabelWidget, tabLabel); + this.redrawTabLabel(editor, index, tabContainer, tabLabelWidget, tabLabel); // Action const tabAction = isTabSticky ? this.unpinEditorAction : this.closeEditorAction; @@ -1035,11 +1040,6 @@ export class TabsTitleControl extends TitleControl { tabActionBar.push(tabAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(tabAction) }); } - // Borders / Outline - const borderRightColor = ((isTabLastSticky ? this.getColor(TAB_LAST_PINNED_BORDER) : undefined) || this.getColor(TAB_BORDER) || this.getColor(contrastBorder)); - tabContainer.style.borderRight = borderRightColor ? `1px solid ${borderRightColor}` : ''; - tabContainer.style.outlineColor = this.getColor(activeContrastBorder) || ''; - // Settings const tabActionsVisibility = isTabSticky && options.pinnedTabSizing === 'compact' ? 'off' /* treat sticky compact tabs as tabCloseButton: 'off' */ : options.tabCloseButton; ['off', 'left', 'right'].forEach(option => { @@ -1079,11 +1079,14 @@ export class TabsTitleControl extends TitleControl { tabContainer.style.left = 'auto'; } + // Borders / outline + this.redrawTabBorders(index, tabContainer); + // Active / dirty state - this.redrawEditorActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget); + this.redrawTabActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget); } - private redrawLabel(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel): void { + private redrawTabLabel(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel): void { const options = this.accessor.partOptions; // Unless tabs are sticky compact, show the full label and description @@ -1127,15 +1130,15 @@ export class TabsTitleControl extends TitleControl { } } - private redrawEditorActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { + private redrawTabActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { const isTabActive = this.group.isActive(editor); - const hasModifiedBorderTop = this.doRedrawEditorDirty(isGroupActive, isTabActive, editor, tabContainer); + const hasModifiedBorderTop = this.doRedrawTabDirty(isGroupActive, isTabActive, editor, tabContainer); - this.doRedrawEditorActive(isGroupActive, !hasModifiedBorderTop, editor, tabContainer, tabLabelWidget); + this.doRedrawTabActive(isGroupActive, !hasModifiedBorderTop, editor, tabContainer, tabLabelWidget); } - private doRedrawEditorActive(isGroupActive: boolean, allowBorderTop: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { + private doRedrawTabActive(isGroupActive: boolean, allowBorderTop: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { // Tab is active if (this.group.isActive(editor)) { @@ -1181,7 +1184,7 @@ export class TabsTitleControl extends TitleControl { } } - private doRedrawEditorDirty(isGroupActive: boolean, isTabActive: boolean, editor: IEditorInput, tabContainer: HTMLElement): boolean { + private doRedrawTabDirty(isGroupActive: boolean, isTabActive: boolean, editor: IEditorInput, tabContainer: HTMLElement): boolean { let hasModifiedBorderColor = false; // Tab: dirty (unless saving) @@ -1222,6 +1225,16 @@ export class TabsTitleControl extends TitleControl { return hasModifiedBorderColor; } + private redrawTabBorders(index: number, tabContainer: HTMLElement): void { + const isTabSticky = this.group.isSticky(index); + const isTabLastSticky = isTabSticky && this.group.stickyCount === index + 1; + + // Borders / Outline + const borderRightColor = ((isTabLastSticky ? this.getColor(TAB_LAST_PINNED_BORDER) : undefined) || this.getColor(TAB_BORDER) || this.getColor(contrastBorder)); + tabContainer.style.borderRight = borderRightColor ? `1px solid ${borderRightColor}` : ''; + tabContainer.style.outlineColor = this.getColor(activeContrastBorder) || ''; + } + getDimensions(): IEditorGroupTitleDimensions { let height = TabsTitleControl.TAB_HEIGHT; if (this.breadcrumbsControl && !this.breadcrumbsControl.isHidden()) { From 34c563a21fc77ed8cfa0db36219e577d7b66dd36 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Sep 2020 12:02:37 +0200 Subject: [PATCH 0068/1882] log paths to open for #99492 --- src/vs/platform/windows/electron-main/windowsMainService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 84c6b5e7cf872..9d98a0a817a51 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -386,6 +386,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic openConfig = this.validateOpenConfig(openConfig); const pathsToOpen = this.getPathsToOpen(openConfig); + this.logService.trace('windowsManager#open pathsToOpen', pathsToOpen); const foldersToAdd: IFolderPathToOpen[] = []; const foldersToOpen: IFolderPathToOpen[] = []; From fbeadf8c63f4a402d9c7cada41d79cab1b55e940 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 30 Sep 2020 12:17:32 +0200 Subject: [PATCH 0069/1882] Fallback to first remote task info when no folder is open Previous behavior was to fallback to local. It makes more sense to fallback to the the first remote details instead Related to https://github.com/microsoft/vscode-docker/issues/2350 --- .../contrib/tasks/browser/abstractTaskService.ts | 9 ++++++--- .../contrib/tasks/browser/terminalTaskSystem.ts | 2 +- src/vs/workbench/contrib/tasks/common/taskSystem.ts | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 7b7ba3dde691e..c211f46c15afd 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -1586,11 +1586,14 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.contextService, this.environmentService, AbstractTaskService.OutputChannelId, this.fileService, this.terminalInstanceService, this.pathService, this.viewDescriptorService, this.logService, - (workspaceFolder: IWorkspaceFolder) => { - if (!workspaceFolder) { + (workspaceFolder: IWorkspaceFolder | undefined) => { + if (workspaceFolder) { + return this._taskSystemInfos.get(workspaceFolder.uri.scheme); + } else if (this._taskSystemInfos.size > 0) { + return this._taskSystemInfos.values().next().value; + } else { return undefined; } - return this._taskSystemInfos.get(workspaceFolder.uri.scheme); } ); } diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index a73e2f7d2f37d..ffc8bb30a88a1 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -635,7 +635,7 @@ export class TerminalTaskSystem implements ITaskSystem { const folders = this.contextService.getWorkspace().folders; workspaceFolder = folders.length > 0 ? folders[0] : undefined; } - const systemInfo: TaskSystemInfo | undefined = this.currentTask.systemInfo = workspaceFolder ? this.taskSystemInfoResolver(workspaceFolder) : undefined; + const systemInfo: TaskSystemInfo | undefined = this.currentTask.systemInfo = this.taskSystemInfoResolver(workspaceFolder); let variables = new Set(); this.collectTaskVariables(variables, task); diff --git a/src/vs/workbench/contrib/tasks/common/taskSystem.ts b/src/vs/workbench/contrib/tasks/common/taskSystem.ts index 7f5b1758e18f9..75c7dea7a9239 100644 --- a/src/vs/workbench/contrib/tasks/common/taskSystem.ts +++ b/src/vs/workbench/contrib/tasks/common/taskSystem.ts @@ -125,7 +125,7 @@ export interface TaskSystemInfo { } export interface TaskSystemInfoResolver { - (workspaceFolder: IWorkspaceFolder): TaskSystemInfo | undefined; + (workspaceFolder: IWorkspaceFolder | undefined): TaskSystemInfo | undefined; } export interface ITaskSystem { From 568c007b7945ce80e3f3a73575d8a854aeb286a4 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 30 Sep 2020 12:51:19 +0200 Subject: [PATCH 0070/1882] Pass group to the undo/redo service even when there is a single text edit (#107739 , #107677) --- .../contrib/bulkEdit/browser/bulkTextEdits.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts index 70103d64a4302..533654d987b9a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts @@ -218,12 +218,12 @@ export class BulkTextEdits { } if (tasks.length === 1) { // This edit touches a single model => keep things simple - for (const task of tasks) { - task.model.pushStackElement(); - task.apply(); - task.model.pushStackElement(); - this._progress.report(undefined); - } + const task = tasks[0]; + const singleModelEditStackElement = new SingleModelEditStackElement(task.model, task.getBeforeCursorState()); + this._undoRedoService.pushElement(singleModelEditStackElement, this._undoRedoGroup); + task.apply(); + singleModelEditStackElement.close(); + this._progress.report(undefined); } else { // prepare multi model undo element const multiModelEditStackElement = new MultiModelEditStackElement( From e45ca3642c9a3a8c1e9c579e22e2fd092259886a Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 30 Sep 2020 13:43:56 +0200 Subject: [PATCH 0071/1882] fixes #107728 --- .../contrib/debug/browser/debugActionViewItems.ts | 4 ++-- .../contrib/debug/browser/debugConfigurationManager.ts | 8 ++++---- src/vs/workbench/contrib/debug/common/debug.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index 2772c78afacbb..bb1db4db2c3d4 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -34,7 +34,7 @@ export class StartDebugActionViewItem implements IActionViewItem { private options: { label: string, handler: (() => Promise) }[] = []; private toDispose: IDisposable[]; private selected = 0; - private providers: { label: string, provider: IDebugConfigurationProvider, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[] = []; + private providers: { label: string, provider: IDebugConfigurationProvider | undefined, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[] = []; constructor( private context: unknown, @@ -196,7 +196,7 @@ export class StartDebugActionViewItem implements IActionViewItem { } this.providers.forEach(p => { - if (p.provider.type === manager.selectedConfiguration.config?.type) { + if (p.provider && p.provider.type === manager.selectedConfiguration.config?.type) { this.selected = this.options.length; } diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index cc245009912d3..e0f705c163dc2 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -263,7 +263,7 @@ export class ConfigurationManager implements IConfigurationManager { return results.reduce((first, second) => first.concat(second), []); } - async getDynamicProviders(): Promise<{ label: string, provider: IDebugConfigurationProvider, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]> { + async getDynamicProviders(): Promise<{ label: string, provider: IDebugConfigurationProvider | undefined, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]> { const extensions = await this.extensionService.getExtensions(); const onDebugDynamicConfigurationsName = 'onDebugDynamicConfigurations'; const debugDynamicExtensionsTypes = extensions.reduce((acc, e) => { @@ -295,7 +295,7 @@ export class ConfigurationManager implements IConfigurationManager { await Promise.all(debugDynamicExtensionsTypes.map(type => this.activateDebuggers(onDebugDynamicConfigurationsName, type))); return debugDynamicExtensionsTypes.map(type => { - const provider = this.configProviders.find(p => p.type === type && p.triggerKind === DebugConfigurationProviderTriggerKind.Dynamic && p.provideDebugConfigurations)!; + const provider = this.configProviders.find(p => p.type === type && p.triggerKind === DebugConfigurationProviderTriggerKind.Dynamic && p.provideDebugConfigurations); return { label: this.getDebuggerLabel(type)!, provider, @@ -539,9 +539,9 @@ export class ConfigurationManager implements IConfigurationManager { // We could not find the previously used name. We should get all dynamic configurations from providers // And potentially auto select the previously used dynamic configuration #96293 const providers = await this.getDynamicProviders(); - const provider = providers.find(p => p.provider.type === type); + const provider = providers.find(p => p.provider && p.provider.type === type); let nameToSet = names.length ? names[0] : undefined; - if (provider && launch && launch.workspace) { + if (provider && launch && launch.workspace && provider.provider) { const token = new CancellationTokenSource(); const dynamicConfigs = await provider.provider.provideDebugConfigurations!(launch.workspace.uri, token.token); const dynamicConfig = dynamicConfigs.find(c => c.name === name); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index d73df3e92d897..b594496921b64 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -694,7 +694,7 @@ export interface IConfigurationManager { isDebuggerInterestedInLanguage(language: string): boolean; hasDebugConfigurationProvider(debugType: string): boolean; - getDynamicProviders(): Promise<{ label: string, provider: IDebugConfigurationProvider, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]>; + getDynamicProviders(): Promise<{ label: string, provider: IDebugConfigurationProvider | undefined, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]>; registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable; unregisterDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): void; From 7c9a6e92c7fb592ca9f42f000e121f721780c0c0 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 30 Sep 2020 13:46:44 +0200 Subject: [PATCH 0072/1882] test-resolver fix prepublish --- extensions/vscode-test-resolver/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode-test-resolver/package.json b/extensions/vscode-test-resolver/package.json index 90e3e0e87eedc..fd7a9c2f881a2 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -11,7 +11,7 @@ "extensionKind": [ "ui" ], "scripts": { "compile": "node ./node_modules/vscode/bin/compile -watch -p ./", - "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-test-resolver ./tsconfig.json" + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-test-resolver" }, "activationEvents": [ "onResolveRemoteAuthority:test", From 8908fba1a0ed2ac8a3ac6de998d0a2abc03290ef Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Sep 2020 14:16:38 +0200 Subject: [PATCH 0073/1882] disable ext host cached data when developing extensions, fixes https://github.com/microsoft/vscode/issues/105512 --- .../electron-browser/localProcessExtensionHost.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index a92220fc8d66e..5aaa1ffe50806 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -170,6 +170,12 @@ export class LocalProcessExtensionHost implements IExtensionHost { delete env['DYLD_LIBRARY_PATH']; } + if (this._isExtensionDevHost) { + // Unset `VSCODE_NODE_CACHED_DATA_DIR` when developing extensions because it might + // be that dependencies, that otherwise would be cached, get modified. + delete env['VSCODE_NODE_CACHED_DATA_DIR']; + } + const opts = { env, // We only detach the extension host on windows. Linux and Mac orphan by default From 7bda89c31b2ee78b5b72ac06ea3fca9ddd3f1429 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Sep 2020 14:42:40 +0200 Subject: [PATCH 0074/1882] more logging for startup --- src/vs/platform/windows/electron-main/windowsMainService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 9d98a0a817a51..22bbc942ccaee 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -448,6 +448,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Open based on config const usedWindows = this.doOpen(openConfig, workspacesToOpen, foldersToOpen, emptyToRestore, emptyToOpen, fileInputs, foldersToAdd); + this.logService.trace(`windowsManager#open used window count ${usedWindows.length} (workspacesToOpen: ${workspacesToOpen.length}, foldersToOpen: ${foldersToOpen.length}, emptyToRestore: ${emptyToRestore.length}, emptyToOpen: ${emptyToOpen})`); + // Make sure to pass focus to the most relevant of the windows if we open multiple if (usedWindows.length > 1) { const focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && openConfig.cli._.length && !openConfig.cli['file-uri'] && !openConfig.cli['folder-uri'] && !(openConfig.urisToOpen && openConfig.urisToOpen.length); @@ -740,6 +742,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, fileInputs?: IFileInputs): ICodeWindow { + this.logService.trace('windowsManager#doOpenFilesInExistingWindow'); + window.focus(); // make sure window has focus const params: { filesToOpenOrCreate?: IPath[], filesToDiff?: IPath[], filesToWait?: IPathsToWaitFor, termProgram?: string } = {}; From 27abbcc806f4ce06f54f5c0ed967b0d85e13e715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 30 Sep 2020 14:57:15 +0200 Subject: [PATCH 0075/1882] fixes #103073 --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index d89a50e6e06af..89ae5f47db20a 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { basename, dirname } from 'vs/base/common/resources'; import { IDisposable, Disposable, DisposableStore, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -import { append, $, Dimension } from 'vs/base/browser/dom'; +import { append, $, Dimension, asCSSUrl } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, ISCMService } from 'vs/workbench/contrib/scm/common/scm'; import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; @@ -421,7 +421,7 @@ class ResourceRenderer implements ICompressibleTreeRenderer Date: Wed, 30 Sep 2020 15:06:15 +0200 Subject: [PATCH 0076/1882] fixes #107377 --- src/vs/workbench/contrib/files/browser/views/explorerView.ts | 5 +++-- 1 file changed, 3 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 e75bc48e39fe4..874c92d571bff 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -342,8 +342,9 @@ export class ExplorerView extends ViewPane { const activeFile = this.getActiveFile(); if (activeFile) { const focus = this.tree.getFocus(); - if (focus.length === 1 && focus[0].resource.toString() === activeFile.toString()) { - // No action needed, active file is already focused + const selection = this.tree.getSelection(); + if (focus.length === 1 && focus[0].resource.toString() === activeFile.toString() && selection.length === 1 && selection[0].resource.toString() === activeFile.toString()) { + // No action needed, active file is already focused and selected return; } this.explorerService.select(activeFile, reveal); From c7761327668e66164a403e91899d113f9bc9bb24 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 30 Sep 2020 14:51:07 +0200 Subject: [PATCH 0077/1882] Use setting links in `Preferred .* Theme` descriptions. Fixes #107715 --- .../services/themes/common/themeConfiguration.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/themes/common/themeConfiguration.ts b/src/vs/workbench/services/themes/common/themeConfiguration.ts index 795b03edf96bb..71b4a464aaea0 100644 --- a/src/vs/workbench/services/themes/common/themeConfiguration.ts +++ b/src/vs/workbench/services/themes/common/themeConfiguration.ts @@ -40,7 +40,7 @@ const colorThemeSettingSchema: IConfigurationPropertySchema = { }; const preferredDarkThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', - description: nls.localize('preferredDarkColorTheme', 'Specifies the preferred color theme for dark OS appearance when \'{0}\' is enabled.', ThemeSettings.DETECT_COLOR_SCHEME), + markdownDescription: nls.localize('preferredDarkColorTheme', 'Specifies the preferred color theme for dark OS appearance when `#{0}#` is enabled.', ThemeSettings.DETECT_COLOR_SCHEME), default: DEFAULT_THEME_DARK_SETTING_VALUE, enum: colorThemeSettingEnum, enumDescriptions: colorThemeSettingEnumDescriptions, @@ -48,7 +48,7 @@ const preferredDarkThemeSettingSchema: IConfigurationPropertySchema = { }; const preferredLightThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', - description: nls.localize('preferredLightColorTheme', 'Specifies the preferred color theme for light OS appearance when \'{0}\' is enabled.', ThemeSettings.DETECT_COLOR_SCHEME), + markdownDescription: nls.localize('preferredLightColorTheme', 'Specifies the preferred color theme for light OS appearance when `#{0}#` is enabled.', ThemeSettings.DETECT_COLOR_SCHEME), default: DEFAULT_THEME_LIGHT_SETTING_VALUE, enum: colorThemeSettingEnum, enumDescriptions: colorThemeSettingEnumDescriptions, @@ -56,7 +56,7 @@ const preferredLightThemeSettingSchema: IConfigurationPropertySchema = { }; const preferredHCThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', - description: nls.localize('preferredHCColorTheme', 'Specifies the preferred color theme used in high contrast mode when \'{0}\' is enabled.', ThemeSettings.DETECT_HC), + markdownDescription: nls.localize('preferredHCColorTheme', 'Specifies the preferred color theme used in high contrast mode when `#{0}#` is enabled.', ThemeSettings.DETECT_HC), default: DEFAULT_THEME_HC_SETTING_VALUE, enum: colorThemeSettingEnum, enumDescriptions: colorThemeSettingEnumDescriptions, @@ -153,6 +153,7 @@ const tokenColorSchema: IJSONSchema = { semanticHighlighting: { description: nls.localize('editorColors.semanticHighlighting', 'Whether semantic highlighting should be enabled for this theme.'), deprecationMessage: nls.localize('editorColors.semanticHighlighting.deprecationMessage', 'Use `enabled` in `editor.semanticTokenColorCustomizations` setting instead.'), + markdownDeprecationMessage: nls.localize('editorColors.semanticHighlighting.deprecationMessageMarkdown', 'Use `enabled` in `#editor.semanticTokenColorCustomizations#` setting instead.'), type: 'boolean' } } @@ -189,6 +190,7 @@ const semanticTokenColorCustomizationSchema: IConfigurationPropertySchema = { const experimentalTokenStylingCustomizationSchema: IConfigurationPropertySchema = { deprecationMessage: nls.localize('editorColors.experimentalTokenStyling.deprecationMessage', 'Use `editor.semanticTokenColorCustomizations` instead.'), + markdownDeprecationMessage: nls.localize('editorColors.experimentalTokenStyling.deprecationMessageMarkdown', 'Use `#editor.semanticTokenColorCustomizations#` instead.'), default: {}, allOf: [{ $ref: tokenStylingSchemaId }], }; From 3fbe605f8bb7e8e0fa7af244474dc25a966680dd Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 30 Sep 2020 16:17:40 +0200 Subject: [PATCH 0078/1882] Deduplicate ports auto forwarded on all interfaces and localhost --- .../contrib/remote/browser/remote.ts | 5 ++- .../contrib/remote/browser/tunnelView.ts | 32 ++----------------- .../remote/common/remoteExplorerService.ts | 28 +++++++++++++++- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 3f5443776f577..92c3efc52c427 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -42,7 +42,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { SwitchRemoteViewItem, SwitchRemoteAction } from 'vs/workbench/contrib/remote/browser/explorerViewItems'; import { Action, IActionViewItem, IAction } from 'vs/base/common/actions'; import { isStringArray } from 'vs/base/common/types'; -import { IRemoteExplorerService, MakeAddress } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { IRemoteExplorerService, MakeAddress, mapHasTunnelLocalhostOrAllInterfaces } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { TunnelPanelDescriptor, TunnelViewModel, forwardedPortsViewEnabled, OpenPortInBrowserAction } from 'vs/workbench/contrib/remote/browser/tunnelView'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -872,6 +872,9 @@ class AutomaticPortForwarding extends Disposable implements IWorkbenchContributi this.isStarted = true; const urlFinder = this._register(new UrlFinder(this.terminalService)); this._register(urlFinder.onDidMatchLocalUrl(async (localUrl) => { + if (mapHasTunnelLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, localUrl.host, localUrl.port)) { + return; + } const forwarded = await this.remoteExplorerService.forward(localUrl); if (forwarded) { const address = MakeAddress(forwarded.tunnelRemoteHost, forwarded.tunnelRemotePort); diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 2fa312a6b0eea..880acc7e77c0a 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -26,7 +26,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { ActionRunner, IAction } from 'vs/base/common/actions'; import { IMenuService, MenuId, IMenu, MenuRegistry, MenuItemAction, ILocalizedString, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { createAndFillInContextMenuActions, createAndFillInActionBarActions, MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IRemoteExplorerService, TunnelModel, MakeAddress, TunnelType, ITunnelItem, Tunnel } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { IRemoteExplorerService, TunnelModel, MakeAddress, TunnelType, ITunnelItem, Tunnel, mapHasTunnelLocalhostOrAllInterfaces } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; @@ -155,37 +155,11 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { }); } - private mapHasTunnel(map: Map, host: string, port: number): boolean { - if (!isLocalhost(host)) { - return map.has(MakeAddress(host, port)); - } - - const stringAddress = MakeAddress('localhost', port); - if (map.has(stringAddress)) { - return true; - } - const numberAddress = MakeAddress('127.0.0.1', port); - if (map.has(numberAddress)) { - return true; - } - return false; - } - get candidates(): TunnelItem[] { const candidates: TunnelItem[] = []; this._candidates.forEach(value => { - if (!this.mapHasTunnel(this.model.forwarded, value.host, value.port) && - !this.mapHasTunnel(this.model.detected, value.host, value.port)) { - // The host:port hasn't been forwarded or detected. However, if the candidate is 0.0.0.0, - // also check that the port hasn't already been forwarded with localhost, and vice versa. - // For example: no need to show 0.0.0.0:3000 as a candidate if localhost:3000 is already forwarded. - const otherHost = value.host === '0.0.0.0' ? 'localhost' : (value.host === 'localhost' ? '0.0.0.0' : undefined); - if (otherHost) { - if (this.mapHasTunnel(this.model.forwarded, otherHost, value.port) || - this.mapHasTunnel(this.model.detected, otherHost, value.port)) { - return; - } - } + if (!mapHasTunnelLocalhostOrAllInterfaces(this.model.forwarded, value.host, value.port) && + !mapHasTunnelLocalhostOrAllInterfaces(this.model.detected, value.host, value.port)) { candidates.push(new TunnelItem(TunnelType.Candidate, value.host, value.port, undefined, undefined, false, undefined, value.detail)); } }); diff --git a/src/vs/workbench/services/remote/common/remoteExplorerService.ts b/src/vs/workbench/services/remote/common/remoteExplorerService.ts index 99a8e1489c20f..5aeea8ec43969 100644 --- a/src/vs/workbench/services/remote/common/remoteExplorerService.ts +++ b/src/vs/workbench/services/remote/common/remoteExplorerService.ts @@ -7,7 +7,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { isLocalhost, ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IEditableData } from 'vs/workbench/common/views'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -52,6 +52,32 @@ export function MakeAddress(host: string, port: number): string { return host + ':' + port; } +export function mapHasTunnel(map: Map, host: string, port: number): boolean { + if (!isLocalhost(host)) { + return map.has(MakeAddress(host, port)); + } + + const stringAddress = MakeAddress('localhost', port); + if (map.has(stringAddress)) { + return true; + } + const numberAddress = MakeAddress('127.0.0.1', port); + if (map.has(numberAddress)) { + return true; + } + return false; +} + +export function mapHasTunnelLocalhostOrAllInterfaces(map: Map, host: string, port: number): boolean { + if (!mapHasTunnel(map, host, port)) { + const otherHost = host === '0.0.0.0' ? 'localhost' : (host === 'localhost' ? '0.0.0.0' : undefined); + if (otherHost) { + return mapHasTunnel(map, otherHost, port); + } + } + return true; +} + export class TunnelModel extends Disposable { readonly forwarded: Map; readonly detected: Map; From 091c17a04af843cafafe1dab4d81cca26baadf62 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 30 Sep 2020 17:21:48 +0200 Subject: [PATCH 0079/1882] Add more debugging information to undo redo elements --- .../undoRedo/common/undoRedoService.ts | 4 +-- .../contrib/bulkEdit/browser/bulkFileEdits.ts | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts index 76ed91065c6e7..4affe9a551a02 100644 --- a/src/vs/platform/undoRedo/common/undoRedoService.ts +++ b/src/vs/platform/undoRedo/common/undoRedoService.ts @@ -53,7 +53,7 @@ class ResourceStackElement { } public toString(): string { - return `[id:${this.id}] [group:${this.groupId}] [${this.isValid ? 'VALID' : 'INVALID'}] ${this.actual}`; + return `[id:${this.id}] [group:${this.groupId}] [${this.isValid ? ' VALID' : 'INVALID'}] ${this.actual.constructor.name} - ${this.actual}`; } } @@ -176,7 +176,7 @@ class WorkspaceStackElement { } public toString(): string { - return `[id:${this.id}] [group:${this.groupId}] [${this.invalidatedResources ? 'INVALID' : 'VALID'}] ${this.actual}`; + return `[id:${this.id}] [group:${this.groupId}] [${this.invalidatedResources ? 'INVALID' : ' VALID'}] ${this.actual.constructor.name} - ${this.actual}`; } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts index a7d058fee424f..feccf435d0a13 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts @@ -15,6 +15,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILogService } from 'vs/platform/log/common/log'; import { VSBuffer } from 'vs/base/common/buffer'; import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; +import * as resources from 'vs/base/common/resources'; interface IFileOperation { uris: URI[]; @@ -24,6 +25,9 @@ interface IFileOperation { class Noop implements IFileOperation { readonly uris = []; async perform() { return this; } + toString(): string { + return '(noop)'; + } } class RenameOperation implements IFileOperation { @@ -48,6 +52,15 @@ class RenameOperation implements IFileOperation { await this._workingCopyFileService.move([{ source: this.oldUri, target: this.newUri }], { overwrite: this.options.overwrite }); return new RenameOperation(this.oldUri, this.newUri, this.options, this._workingCopyFileService, this._fileService); } + + toString(): string { + const oldBasename = resources.basename(this.oldUri); + const newBasename = resources.basename(this.newUri); + if (oldBasename !== newBasename) { + return `(rename ${oldBasename} to ${newBasename})`; + } + return `(rename ${this.oldUri} to ${this.newUri})`; + } } class CreateOperation implements IFileOperation { @@ -73,6 +86,10 @@ class CreateOperation implements IFileOperation { await this._workingCopyFileService.create(this.newUri, this.contents, { overwrite: this.options.overwrite }); return this._instaService.createInstance(DeleteOperation, this.newUri, this.options, true); } + + toString(): string { + return `(create ${resources.basename(this.newUri)} with ${this.contents?.byteLength || 0} bytes)`; + } } class DeleteOperation implements IFileOperation { @@ -114,6 +131,10 @@ class DeleteOperation implements IFileOperation { await this._workingCopyFileService.delete([this.oldUri], { useTrash, recursive: this.options.recursive }); return this._instaService.createInstance(CreateOperation, this.oldUri, this.options, contents); } + + toString(): string { + return `(delete ${resources.basename(this.oldUri)})`; + } } class FileUndoRedoElement implements IWorkspaceUndoRedoElement { @@ -144,6 +165,10 @@ class FileUndoRedoElement implements IWorkspaceUndoRedoElement { this.operations[i] = undo; } } + + public toString(): string { + return this.operations.map(op => String(op)).join(', '); + } } export class BulkFileEdits { From d87e3c37d16bbf8f191e7d69249a80bf6c6504af Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 30 Sep 2020 17:38:30 +0200 Subject: [PATCH 0080/1882] Save text buffers before renaming to avoid unexpected undo/redo stack elements (#107739) Co-authored-by: Johannes Rieken --- src/vs/platform/undoRedo/common/undoRedoService.ts | 7 ------- .../contrib/bulkEdit/browser/bulkFileEdits.ts | 14 +++++++++++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts index 4affe9a551a02..45bc17c16c0e2 100644 --- a/src/vs/platform/undoRedo/common/undoRedoService.ts +++ b/src/vs/platform/undoRedo/common/undoRedoService.ts @@ -277,13 +277,6 @@ class ResourceEditStack { } } this._future = []; - if (this._past.length > 0) { - const lastElement = this._past[this._past.length - 1]; - if (lastElement.type === UndoRedoElementType.Resource && !lastElement.isValid) { - // clear undo stack - this._past = []; - } - } this._past.push(element); this.versionId++; } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts index feccf435d0a13..1fd2fc56977f1 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts @@ -16,6 +16,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { VSBuffer } from 'vs/base/common/buffer'; import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; import * as resources from 'vs/base/common/resources'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; interface IFileOperation { uris: URI[]; @@ -38,6 +39,7 @@ class RenameOperation implements IFileOperation { readonly options: WorkspaceFileEditOptions, @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, @IFileService private readonly _fileService: IFileService, + @ITextFileService private readonly _textFileService: ITextFileService, ) { } get uris() { @@ -49,8 +51,18 @@ class RenameOperation implements IFileOperation { if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) { return new Noop(); // not overwriting, but ignoring, and the target file exists } + + // See https://github.com/microsoft/vscode/issues/107739 + // `IWorkingCopyFileService.move` ends up pushing to the undo/redo service + // if we attempt to move a dirty file. + try { + await this._textFileService.save(this.oldUri, { + skipSaveParticipants: true + }); + } catch (err) { } + await this._workingCopyFileService.move([{ source: this.oldUri, target: this.newUri }], { overwrite: this.options.overwrite }); - return new RenameOperation(this.oldUri, this.newUri, this.options, this._workingCopyFileService, this._fileService); + return new RenameOperation(this.oldUri, this.newUri, this.options, this._workingCopyFileService, this._fileService, this._textFileService); } toString(): string { From 860a9ad6526e1f77e0dc9cc486de085be30e2b62 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 30 Sep 2020 17:51:29 +0200 Subject: [PATCH 0081/1882] Make sure to return `Access-Control-Allow-Origin` header also in error case --- resources/web/code-web.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/resources/web/code-web.js b/resources/web/code-web.js index b3d1636bedd5f..c38826c084747 100644 --- a/resources/web/code-web.js +++ b/resources/web/code-web.js @@ -276,12 +276,13 @@ async function handleStatic(req, res, parsedUrl) { if (/^\/static\/extensions\//.test(parsedUrl.pathname)) { const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/static/extensions/'.length)); const filePath = getExtensionFilePath(relativePath, (await builtInExtensionsPromise).locations); + const responseHeaders = { + 'Access-Control-Allow-Origin': '*' + }; if (!filePath) { - return serveError(req, res, 400, `Bad request.`); + return serveError(req, res, 400, `Bad request.`, responseHeaders); } - return serveFile(req, res, filePath, { - 'Access-Control-Allow-Origin': '*' - }); + return serveFile(req, res, filePath, responseHeaders); } // Strip `/static/` from the path @@ -299,12 +300,13 @@ async function handleExtension(req, res, parsedUrl) { // Strip `/extension/` from the path const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/extension/'.length)); const filePath = getExtensionFilePath(relativePath, (await commandlineProvidedExtensionsPromise).locations); + const responseHeaders = { + 'Access-Control-Allow-Origin': '*' + }; if (!filePath) { - return serveError(req, res, 400, `Bad request.`); + return serveError(req, res, 400, `Bad request.`, responseHeaders); } - return serveFile(req, res, filePath, { - 'Access-Control-Allow-Origin': '*' - }); + return serveFile(req, res, filePath, responseHeaders); } /** @@ -517,8 +519,9 @@ function getExtensionFilePath(relativePath, locations) { * @param {import('http').ServerResponse} res * @param {string} errorMessage */ -function serveError(req, res, errorCode, errorMessage) { - res.writeHead(errorCode, { 'Content-Type': 'text/plain' }); +function serveError(req, res, errorCode, errorMessage, responseHeaders = Object.create(null)) { + responseHeaders['Content-Type'] = 'text/plain'; + res.writeHead(errorCode, responseHeaders); res.end(errorMessage); } @@ -583,7 +586,8 @@ async function serveFile(req, res, filePath, responseHeaders = Object.create(nul fs.createReadStream(filePath).pipe(res); } catch (error) { console.error(error.toString()); - res.writeHead(404, { 'Content-Type': 'text/plain' }); + responseHeaders['Content-Type'] = 'text/plain'; + res.writeHead(404, responseHeaders); return res.end('Not found'); } } From 6558e439b340e248061a7143573593aab1ed6bea Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 30 Sep 2020 10:08:58 -0700 Subject: [PATCH 0082/1882] fix #107647 --- .../notebook/browser/diff/notebookDiffActions.ts | 8 ++++---- .../contrib/notebook/browser/notebook.contribution.ts | 11 +++++++---- .../notebook/browser/notebookDiffEditorInput.ts | 8 ++++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts index 856b0aae9bf19..92d9f547cf32c 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts @@ -37,14 +37,14 @@ registerAction2(class extends Action2 { const activeEditor = editorService.activeEditorPane; if (activeEditor && activeEditor instanceof NotebookTextDiffEditor) { - const leftResource = (activeEditor.input as NotebookDiffEditorInput).originalResource; - const rightResource = (activeEditor.input as NotebookDiffEditorInput).resource; + const diffEditorInput = activeEditor.input as NotebookDiffEditorInput; + const leftResource = diffEditorInput.originalResource; + const rightResource = diffEditorInput.resource; const options = { preserveFocus: false }; - const label = localize('diffLeftRightLabel', "{0} ⟷ {1}", leftResource.toString(true), rightResource.toString(true)); - + const label = diffEditorInput.textDiffName; await editorService.openEditor({ leftResource, rightResource, label, options }, viewColumnToEditorGroup(editorGroupService, undefined)); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 593022037f4e0..920a3688e11fa 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -109,22 +109,25 @@ class NotebookDiffEditorFactory implements IEditorInputFactory { originalResource: input.originalResource, name: input.name, originalName: input.originalName, + textDiffName: input.textDiffName, viewType: input.viewType, }); } deserialize(instantiationService: IInstantiationService, raw: string) { - type Data = { resource: URI, originalResource: URI, name: string, originalName: string, viewType: string, group: number }; + type Data = { resource: URI, originalResource: URI, name: string, originalName: string, viewType: string, textDiffName: string | undefined, group: number }; const data = parse(raw); if (!data) { return undefined; } - const { resource, originalResource, name, originalName, viewType } = data; + const { resource, originalResource, name, originalName, textDiffName, viewType } = data; if (!data || !URI.isUri(resource) || !URI.isUri(originalResource) || typeof name !== 'string' || typeof originalName !== 'string' || typeof viewType !== 'string') { return undefined; } - const input = NotebookDiffEditorInput.create(instantiationService, resource, name, originalResource, originalName, viewType); + const input = NotebookDiffEditorInput.create(instantiationService, resource, name, originalResource, originalName, + textDiffName || nls.localize('diffLeftRightLabel', "{0} ⟷ {1}", originalResource.toString(true), resource.toString(true)), + viewType); return input; } @@ -407,7 +410,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri const info = associatedEditors[0]; - const notebookInput = NotebookDiffEditorInput.create(this.instantiationService, notebookUri, modifiedInput.getName(), originalNotebookUri, originalInput.getName(), info.id); + const notebookInput = NotebookDiffEditorInput.create(this.instantiationService, notebookUri, modifiedInput.getName(), originalNotebookUri, originalInput.getName(), diffEditorInput.getName(), info.id); const notebookOptions = new NotebookEditorOptions({ ...options, override: false }); return { override: this.editorService.openEditor(notebookInput, notebookOptions, group) }; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts index 526cc98242050..feee0ec45481d 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; import * as glob from 'vs/base/common/glob'; import { EditorInput, IEditorInput, GroupIdentifier, ISaveOptions, IMoveResult, IRevertOptions, EditorModel } from 'vs/workbench/common/editor'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -50,8 +49,8 @@ class NotebookDiffEditorModel extends EditorModel implements INotebookDiffEditor } export class NotebookDiffEditorInput extends EditorInput { - static create(instantiationService: IInstantiationService, resource: URI, name: string, originalResource: URI, originalName: string, viewType: string | undefined, options: NotebookEditorInputOptions = {}) { - return instantiationService.createInstance(NotebookDiffEditorInput, resource, name, originalResource, originalName, viewType, options); + static create(instantiationService: IInstantiationService, resource: URI, name: string, originalResource: URI, originalName: string, textDiffName: string, viewType: string | undefined, options: NotebookEditorInputOptions = {}) { + return instantiationService.createInstance(NotebookDiffEditorInput, resource, name, originalResource, originalName, textDiffName, viewType, options); } static readonly ID: string = 'workbench.input.diffNotebookInput'; @@ -65,6 +64,7 @@ export class NotebookDiffEditorInput extends EditorInput { public readonly name: string, public readonly originalResource: URI, public readonly originalName: string, + public readonly textDiffName: string, public readonly viewType: string | undefined, public readonly options: NotebookEditorInputOptions, @INotebookService private readonly _notebookService: INotebookService, @@ -82,7 +82,7 @@ export class NotebookDiffEditorInput extends EditorInput { } getName(): string { - return nls.localize('sideBySideLabels', "{0} ↔ {1}", this.originalName, this.name); + return this.textDiffName; } isDirty() { From 1463659e245c343bf15cbe3763b2f65a21121773 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 30 Sep 2020 10:16:19 -0700 Subject: [PATCH 0083/1882] re #107011. --- src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 8a2f4de0c2ba8..9ddb42f76a374 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -182,7 +182,6 @@ export class CommentsList extends WorkbenchAsyncDataTree { dataSource, { accessibilityProvider: options.accessibilityProvider, - keyboardSupport: true, identityProvider: { getId: (element: any) => { if (element instanceof CommentsModel) { From 9003ae2d5355cefc6348fac8efd391b00a46024d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 30 Sep 2020 10:40:24 -0700 Subject: [PATCH 0084/1882] Custom selectbox should add decorator label and description to aria label, and should not use aria-describedby Fix #107662 --- .../browser/ui/selectBox/selectBoxCustom.ts | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index b9b99af53425a..c2395b8d6f733 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -29,7 +29,6 @@ const SELECT_OPTION_ENTRY_TEMPLATE_ID = 'selectOption.entry.template'; interface ISelectListTemplateData { root: HTMLElement; text: HTMLElement; - itemDescription: HTMLElement; decoratorRight: HTMLElement; disposables: IDisposable[]; } @@ -44,8 +43,6 @@ class SelectListRenderer implements IListRenderer element.text, + getAriaLabel: element => { + let label = element.text; + if (element.decoratorRight) { + label += `. ${element.decoratorRight}`; + } + + if (element.description) { + label += `. ${element.description}`; + } + + return label; + }, getWidgetAriaLabel: () => localize({ key: 'selectBox', comment: ['Behave like native select dropdown element.'] }, "Select Box"), getRole: () => 'option', getWidgetRole: () => 'listbox' From 6a0111c6c3b9077a76b372efd65868c4f39247ea Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 30 Sep 2020 10:44:32 -0700 Subject: [PATCH 0085/1882] Screen reader should announce settings search result count Fix #107797 --- .../contrib/preferences/browser/settingsEditor2.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 656789c84b81d..ac4fadcee70c2 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; +import * as aria from 'vs/base/browser/ui/aria/aria'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button } from 'vs/base/browser/ui/button/button'; @@ -1319,12 +1320,16 @@ export class SettingsEditor2 extends EditorPane { if (this.tocTreeModel && this.tocTreeModel.settingsTreeRoot) { const count = this.tocTreeModel.settingsTreeRoot.count; + let resultString: string; switch (count) { - case 0: this.countElement.innerText = localize('noResults', "No Settings Found"); break; - case 1: this.countElement.innerText = localize('oneResult', "1 Setting Found"); break; - default: this.countElement.innerText = localize('moreThanOneResult', "{0} Settings Found", count); + case 0: resultString = localize('noResults', "No Settings Found"); break; + case 1: resultString = localize('oneResult', "1 Setting Found"); break; + default: resultString = localize('moreThanOneResult', "{0} Settings Found", count); } + this.countElement.innerText = resultString; + aria.status(resultString); + if (this.countElement.style.display !== 'block') { this.countElement.style.display = 'block'; this.layout(this.dimension); From 9d4ebd493db552a03f9c8e0b7a9c47f57c8847e6 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 30 Sep 2020 11:04:11 -0700 Subject: [PATCH 0086/1882] add my test plan item in endgame notebook. --- .vscode/notebooks/endgame.github-issues | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 3622427f78f97..6f34facace95d 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -35,6 +35,16 @@ "value": "$REPOS $MILESTONE label:testplan-item is:open", "editable": true }, + { + "kind": 1, + "language": "markdown", + "value": "#### My test plan items" + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE label:testplan-item is:open $MINE " + }, { "kind": 1, "language": "markdown", From 9382e6375fb1cf026730e0d37056cb3b4ec4e38c Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Wed, 30 Sep 2020 14:20:58 -0400 Subject: [PATCH 0087/1882] Fixes #107802 - renames resourceFolder to resourceDirname Also fixes issue with reset not clearing some of the keys --- src/vs/workbench/common/resources.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index be7617ad1a57f..94e7e7a4bac15 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -24,7 +24,7 @@ export class ResourceContextKey extends Disposable implements IContextKey { static readonly Scheme = new RawContextKey('resourceScheme', undefined); static readonly Filename = new RawContextKey('resourceFilename', undefined); - static readonly Folder = new RawContextKey('resourceFolder', undefined); + static readonly Dirname = new RawContextKey('resourceDirname', undefined); static readonly Path = new RawContextKey('resourcePath', undefined); static readonly LangId = new RawContextKey('resourceLangId', undefined); static readonly Resource = new RawContextKey('resource', undefined); @@ -35,7 +35,7 @@ export class ResourceContextKey extends Disposable implements IContextKey { private readonly _resourceKey: IContextKey; private readonly _schemeKey: IContextKey; private readonly _filenameKey: IContextKey; - private readonly _folderKey: IContextKey; + private readonly _dirnameKey: IContextKey; private readonly _pathKey: IContextKey; private readonly _langIdKey: IContextKey; private readonly _extensionKey: IContextKey; @@ -51,7 +51,7 @@ export class ResourceContextKey extends Disposable implements IContextKey { this._schemeKey = ResourceContextKey.Scheme.bindTo(this._contextKeyService); this._filenameKey = ResourceContextKey.Filename.bindTo(this._contextKeyService); - this._folderKey = ResourceContextKey.Folder.bindTo(this._contextKeyService); + this._dirnameKey = ResourceContextKey.Dirname.bindTo(this._contextKeyService); this._pathKey = ResourceContextKey.Path.bindTo(this._contextKeyService); this._langIdKey = ResourceContextKey.LangId.bindTo(this._contextKeyService); this._resourceKey = ResourceContextKey.Resource.bindTo(this._contextKeyService); @@ -76,7 +76,7 @@ export class ResourceContextKey extends Disposable implements IContextKey { this._resourceKey.set(value); this._schemeKey.set(value ? value.scheme : null); this._filenameKey.set(value ? basename(value) : null); - this._folderKey.set(value ? dirname(value).fsPath : null); + this._dirnameKey.set(value ? dirname(value).fsPath : null); this._pathKey.set(value ? value.fsPath : null); this._langIdKey.set(value ? this._modeService.getModeIdByFilepathOrFirstLine(value) : null); this._extensionKey.set(value ? extname(value) : null); @@ -88,9 +88,11 @@ export class ResourceContextKey extends Disposable implements IContextKey { reset(): void { this._contextKeyService.bufferChangeEvents(() => { - this._schemeKey.reset(); - this._langIdKey.reset(); this._resourceKey.reset(); + this._schemeKey.reset(); + this._filenameKey.reset(); + this._dirnameKey.reset(); + this._pathKey.reset(); this._langIdKey.reset(); this._extensionKey.reset(); this._hasResource.reset(); From 39da5d05465987032cd78d8f94b5caf016dc96b0 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 30 Sep 2020 20:52:10 +0200 Subject: [PATCH 0088/1882] fixes #107580 --- .../browser/actions/navigationActions.ts | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts index f5568f1f08fa9..a53bd1e9a887e 100644 --- a/src/vs/workbench/browser/actions/navigationActions.ts +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -19,6 +19,8 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { isAncestor } from 'vs/base/browser/dom'; abstract class BaseNavigationAction extends Action { @@ -215,8 +217,8 @@ function findVisibleNeighbour(layoutService: IWorkbenchLayoutService, part: Part return findVisibleNeighbour(layoutService, neighbour, next); } -function focusNextOrPreviousPart(layoutService: IWorkbenchLayoutService, next: boolean): void { - const currentlyFocusedPart = layoutService.hasFocus(Parts.EDITOR_PART) ? Parts.EDITOR_PART : layoutService.hasFocus(Parts.ACTIVITYBAR_PART) ? Parts.ACTIVITYBAR_PART : +function focusNextOrPreviousPart(layoutService: IWorkbenchLayoutService, editorService: IEditorService, next: boolean): void { + const currentlyFocusedPart = isActiveElementInNotebookEditor(editorService) ? Parts.EDITOR_PART : layoutService.hasFocus(Parts.EDITOR_PART) ? Parts.EDITOR_PART : layoutService.hasFocus(Parts.ACTIVITYBAR_PART) ? Parts.ACTIVITYBAR_PART : layoutService.hasFocus(Parts.STATUSBAR_PART) ? Parts.STATUSBAR_PART : layoutService.hasFocus(Parts.SIDEBAR_PART) ? Parts.SIDEBAR_PART : layoutService.hasFocus(Parts.PANEL_PART) ? Parts.PANEL_PART : undefined; let partToFocus = Parts.EDITOR_PART; if (currentlyFocusedPart) { @@ -226,6 +228,17 @@ function focusNextOrPreviousPart(layoutService: IWorkbenchLayoutService, next: b layoutService.focusPart(partToFocus); } +function isActiveElementInNotebookEditor(editorService: IEditorService): boolean { + const activeEditorPane = editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined; + if (activeEditorPane?.isNotebookEditor) { + const control = editorService.activeEditorPane?.getControl() as { getDomNode(): HTMLElement; getOverflowContainerDomNode(): HTMLElement; }; + const activeElement = document.activeElement; + return isAncestor(activeElement, control.getDomNode()) || isAncestor(activeElement, control.getOverflowContainerDomNode()); + } + + return false; +} + export class FocusNextPart extends Action { static readonly ID = 'workbench.action.focusNextPart'; static readonly LABEL = nls.localize('focusNextPart', "Focus Next Part"); @@ -233,13 +246,14 @@ export class FocusNextPart extends Action { constructor( id: string, label: string, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService + @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, + @IEditorService private readonly editorService: IEditorService ) { super(id, label); } async run(): Promise { - focusNextOrPreviousPart(this.layoutService, true); + focusNextOrPreviousPart(this.layoutService, this.editorService, true); } } @@ -250,13 +264,14 @@ export class FocusPreviousPart extends Action { constructor( id: string, label: string, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService + @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, + @IEditorService private readonly editorService: IEditorService ) { super(id, label); } async run(): Promise { - focusNextOrPreviousPart(this.layoutService, false); + focusNextOrPreviousPart(this.layoutService, this.editorService, false); } } From 357d4e7a0ea11545b95cc46e45ed6471194f37c6 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 30 Sep 2020 11:58:01 -0700 Subject: [PATCH 0089/1882] revert unnecessary endgame notebook change --- .vscode/notebooks/endgame.github-issues | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 6f34facace95d..3622427f78f97 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -35,16 +35,6 @@ "value": "$REPOS $MILESTONE label:testplan-item is:open", "editable": true }, - { - "kind": 1, - "language": "markdown", - "value": "#### My test plan items" - }, - { - "kind": 2, - "language": "github-issues", - "value": "$REPOS $MILESTONE label:testplan-item is:open $MINE " - }, { "kind": 1, "language": "markdown", From 296147c88e8d6bf3959b9747e36841c1af9aff55 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 30 Sep 2020 20:58:18 +0200 Subject: [PATCH 0090/1882] Fix integration tests --- .../src/singlefolder-tests/workspace.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 90f1205cee957..32b6ee6851a7f 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -926,7 +926,10 @@ suite('vscode API - workspace', () => { assert.ok(await vscode.workspace.applyEdit(we)); const document = await vscode.workspace.openTextDocument(newUri); - assert.equal(document.isDirty, true); + // See https://github.com/microsoft/vscode/issues/107739 + // RenameOperation currently saves the file before applying the rename + // so that is why the document is not dirty here + assert.equal(document.isDirty, false); await document.save(); assert.equal(document.isDirty, false); From e94761aaa07eb24a16107e76b70205c96d76b68a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 30 Sep 2020 14:34:54 -0700 Subject: [PATCH 0091/1882] Down arrow from setting tabs should focus settings list Fix #107747 --- .../contrib/preferences/browser/settingsEditor2.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index ac4fadcee70c2..790ccd8ac7c42 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -476,6 +476,12 @@ export class SettingsEditor2 extends EditorPane { this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, targetWidgetContainer, { enableRemoteSettings: true })); this.settingsTargetsWidget.settingsTarget = ConfigurationTarget.USER_LOCAL; this.settingsTargetsWidget.onDidTargetChange(target => this.onDidSettingsTargetChange(target)); + this._register(DOM.addDisposableListener(targetWidgetContainer, DOM.EventType.KEY_DOWN, e => { + const event = new StandardKeyboardEvent(e); + if (event.keyCode === KeyCode.DownArrow) { + this.focusSettings(); + } + })); if (this.userDataSyncWorkbenchService.enabled && this.userDataAutoSyncService.canToggleEnablement()) { const syncControls = this._register(this.instantiationService.createInstance(SyncControls, headerControlsContainer)); From 675deb4ebb320676bfd9ebf1d65df4ec331fa105 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 30 Sep 2020 15:06:59 -0700 Subject: [PATCH 0092/1882] ActionBar should not intercept escape unless needed Fix #107665 --- src/vs/base/browser/ui/actionbar/actionbar.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 0aaeeec0e4202..07d3893ff832f 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -64,8 +64,9 @@ export class ActionBar extends Disposable implements IActionRunner { private _onDidBlur = this._register(new Emitter()); readonly onDidBlur = this._onDidBlur.event; - private _onDidCancel = this._register(new Emitter()); + private _onDidCancel = this._register(new Emitter({ onFirstListenerAdd: () => this.cancelHasListener = true })); readonly onDidCancel = this._onDidCancel.event; + private cancelHasListener = false; private _onDidRun = this._register(new Emitter()); readonly onDidRun = this._onDidRun.event; @@ -138,7 +139,7 @@ export class ActionBar extends Disposable implements IActionRunner { eventHandled = this.focusPrevious(); } else if (nextKeys && (event.equals(nextKeys[0]) || event.equals(nextKeys[1]))) { eventHandled = this.focusNext(); - } else if (event.equals(KeyCode.Escape)) { + } else if (event.equals(KeyCode.Escape) && this.cancelHasListener) { this._onDidCancel.fire(); } else if (this.isTriggerKeyEvent(event)) { // Staying out of the else branch even if not triggered From 9ba56acfe2d919e1d246ead30b8825d514c7d29d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 30 Sep 2020 22:23:29 +0200 Subject: [PATCH 0093/1882] accept recommendation if recommended extensions are installed while it is open --- src/vs/base/common/async.ts | 15 + ...ensionRecommendationNotificationService.ts | 287 +++++++++--------- 2 files changed, 163 insertions(+), 139 deletions(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index ea4ccc21a5d0f..410aad779bdf7 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -56,6 +56,21 @@ export function raceCancellation(promise: Promise, token: CancellationToke return Promise.race([promise, new Promise(resolve => token.onCancellationRequested(() => resolve(defaultValue)))]); } +/** + * Returns as soon as one of the promises is resolved and cancels remaining promises + */ +export async function raceCancellablePromises(cancellablePromises: CancelablePromise[]): Promise { + let resolvedPromiseIndex = -1; + const promises = cancellablePromises.map((promise, index) => promise.then(result => { resolvedPromiseIndex = index; return result; })); + const result = await Promise.race(promises); + cancellablePromises.forEach((cancellablePromise, index) => { + if (index !== resolvedPromiseIndex) { + cancellablePromise.cancel(); + } + }); + return result; +} + export function raceTimeout(promise: Promise, timeout: number, onTimeout?: () => void): Promise { let promiseResolve: ((value: T | undefined) => void) | undefined = undefined; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index b4ba09f78baef..794fd29db2cbe 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -5,9 +5,11 @@ import { IAction } from 'vs/base/common/actions'; import { distinct } from 'vs/base/common/arrays'; -import { CancelablePromise, timeout } from 'vs/base/common/async'; +import { CancelablePromise, createCancelablePromise, raceCancellablePromises, raceCancellation, timeout } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -45,15 +47,21 @@ const ignoreImportantExtensionRecommendationStorageKey = 'extensionsAssistant/im const donotShowWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; const choiceNever = localize('neverShowAgain', "Don't Show Again"); -type RecommendationNotification = { +type RecommendationsNotification = { severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions, priority: RecommendationSource, }; -type VisibleRecommendationNotification = RecommendationNotification & { notificationHandle: INotificationHandle, from: number }; -type PendingRecommendationNotification = RecommendationNotification & { onDidShow: (notificationHandle: INotificationHandle) => void }; +type RecommendationsNotificationActions = { + onDidInstallRecommendedExtensions(extensions: IExtension[]): void; + onDidShowRecommendedExtensions(extensions: IExtension[]): void; + onDidCancelRecommendedExtensions(extensions: IExtension[]): void; + onDidNeverShowRecommendedExtensionsAgain(extensions: IExtension[]): void; +}; +type VisibleRecommendationsNotification = RecommendationsNotification & { notificationHandle: INotificationHandle, from: number }; +type PendingRecommendationsNotification = RecommendationsNotification & { token: CancellationToken, onDidShow: (notificationHandle: INotificationHandle) => void }; export class ExtensionRecommendationNotificationService implements IExtensionRecommendationNotificationService { @@ -70,8 +78,8 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec private recommendationSources: RecommendationSource[] = []; private hideVisibleNotificationPromise: CancelablePromise | undefined; - private visibleNotification: VisibleRecommendationNotification | undefined; - private pendingNotificaitons: PendingRecommendationNotification[] = []; + private visibleNotification: VisibleRecommendationsNotification | undefined; + private pendingNotificaitons: PendingRecommendationsNotification[] = []; constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @@ -96,6 +104,63 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec } async promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string, source: RecommendationSource): Promise { + const ignoredRecommendations = [...this.extensionIgnoredRecommendationsService.ignoredRecommendations, ...this.ignoredRecommendations]; + extensionIds = extensionIds.filter(id => !ignoredRecommendations.includes(id)); + if (!extensionIds.length) { + return RecommendationsNotificationResult.Ignored; + } + + return this.promptRecommendationsNotification(extensionIds, message, searchValue, source, { + onDidInstallRecommendedExtensions: (extensions: IExtension[]) => extensions.forEach(extension => this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId: extension.identifier.id })), + onDidShowRecommendedExtensions: (extensions: IExtension[]) => extensions.forEach(extension => this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId: extension.identifier.id })), + onDidCancelRecommendedExtensions: (extensions: IExtension[]) => extensions.forEach(extension => this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId: extension.identifier.id })), + onDidNeverShowRecommendedExtensionsAgain: (extensions: IExtension[]) => { + for (const extension of extensions) { + this.addToImportantRecommendationsIgnore(extension.identifier.id); + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: extension.identifier.id }); + } + this.notificationService.prompt( + Severity.Info, + localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), + [{ + label: localize('ignoreAll', "Yes, Ignore All"), + run: () => this.setIgnoreRecommendationsConfig(true) + }, { + label: localize('no', "No"), + run: () => this.setIgnoreRecommendationsConfig(false) + }] + ); + }, + }); + } + + async promptWorkspaceRecommendations(recommendations: string[]): Promise { + if (this.storageService.getBoolean(donotShowWorkspaceRecommendationsStorageKey, StorageScope.WORKSPACE, false)) { + return; + } + + let installed = await this.extensionManagementService.getInstalled(); + installed = installed.filter(l => this.extensionEnablementService.getEnablementState(l) !== EnablementState.DisabledByExtensionKind); // Filter extensions disabled by kind + recommendations = recommendations.filter(extensionId => installed.every(local => !areSameExtensions({ id: extensionId }, local.identifier))); + if (!recommendations.length) { + return; + } + + const result = await this.promptRecommendationsNotification(recommendations, localize('workspaceRecommended', "Do you want to install the recommended extensions for this repository?"), '@recommended ', RecommendationSource.WORKSPACE, { + onDidInstallRecommendedExtensions: () => this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }), + onDidShowRecommendedExtensions: () => this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }), + onDidCancelRecommendedExtensions: () => this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }), + onDidNeverShowRecommendedExtensionsAgain: () => this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }), + }); + + if (result === RecommendationsNotificationResult.Accepted) { + this.storageService.store(donotShowWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); + } + + } + + private async promptRecommendationsNotification(extensionIds: string[], message: string, searchValue: string, source: RecommendationSource, recommendationsNotificationActions: RecommendationsNotificationActions): Promise { + if (this.hasToIgnoreRecommendationNotifications()) { return RecommendationsNotificationResult.Ignored; } @@ -107,12 +172,6 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec return RecommendationsNotificationResult.TooMany; } - const ignoredRecommendations = [...this.extensionIgnoredRecommendationsService.ignoredRecommendations, ...this.ignoredRecommendations]; - extensionIds = extensionIds.filter(id => !ignoredRecommendations.includes(id)); - if (!extensionIds.length) { - return RecommendationsNotificationResult.Ignored; - } - // Ignore exe recommendation if recommendations are already shown if (source === RecommendationSource.EXE && extensionIds.every(id => this.recommendedExtensions.includes(id))) { return RecommendationsNotificationResult.Ignored; @@ -129,132 +188,84 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec this.recommendedExtensions = distinct([...this.recommendedExtensions, ...extensionIds]); - return new Promise(async (c, e) => { + return raceCancellablePromises([ + this.showRecommendationsNotification(extensions, message, searchValue, source, recommendationsNotificationActions), + this.waitUntilRecommendationsAreInstalled(extensions) + ]); + + } + + private showRecommendationsNotification(extensions: IExtension[], message: string, searchValue: string, source: RecommendationSource, + { onDidInstallRecommendedExtensions, onDidShowRecommendedExtensions, onDidCancelRecommendedExtensions, onDidNeverShowRecommendedExtensionsAgain }: RecommendationsNotificationActions): CancelablePromise { + return createCancelablePromise(async token => { let result = RecommendationsNotificationResult.Accepted; - const handle = await this.showNotification({ - severity: Severity.Info, - message, - priority: source, - choices: [{ - label: localize('install', "Install"), - run: async () => { - this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); - await Promise.all(extensions.map(async extension => { - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId: extension.identifier.id }); - this.extensionsWorkbenchService.open(extension, { pinned: true }); - await this.extensionManagementService.installFromGallery(extension.gallery!); - })); - } - }, { - label: localize('show recommendations', "Show Recommendations"), - run: async () => { - for (const extension of extensions) { - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId: extension.identifier.id }); - this.extensionsWorkbenchService.open(extension, { pinned: true }); + try { + const handle = await this.doShowRecommendationsNotification({ + severity: Severity.Info, + message, + priority: source, + choices: [{ + label: localize('install', "Install"), + run: async () => { + this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); + onDidInstallRecommendedExtensions(extensions); + await Promise.all(extensions.map(async extension => { + this.extensionsWorkbenchService.open(extension, { pinned: true }); + await this.extensionManagementService.installFromGallery(extension.gallery!); + })); } - this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); - } - }, { - label: choiceNever, - isSecondary: true, - run: () => { - for (const extension of extensions) { - this.addToImportantRecommendationsIgnore(extension.identifier.id); - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: extension.identifier.id }); + }, { + label: localize('show recommendations', "Show Recommendations"), + run: async () => { + onDidShowRecommendedExtensions(extensions); + for (const extension of extensions) { + this.extensionsWorkbenchService.open(extension, { pinned: true }); + } + this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); } - this.notificationService.prompt( - Severity.Info, - localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), - [{ - label: localize('ignoreAll', "Yes, Ignore All"), - run: () => this.setIgnoreRecommendationsConfig(true) - }, { - label: localize('no', "No"), - run: () => this.setIgnoreRecommendationsConfig(false) - }] - ); - } - }], - options: { - sticky: true, - onCancel: () => { - result = RecommendationsNotificationResult.Cancelled; - for (const extension of extensions) { - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId: extension.identifier.id }); + }, { + label: choiceNever, + isSecondary: true, + run: () => { + onDidNeverShowRecommendedExtensionsAgain(extensions); + } + }], + options: { + sticky: true, + onCancel: () => { + result = RecommendationsNotificationResult.Cancelled; + onDidCancelRecommendedExtensions(extensions); } } - } - } - ); - const disposable = handle.onDidClose(() => { - disposable.dispose(); - c(result); - }); - }); - } - - async promptWorkspaceRecommendations(recommendations: string[]): Promise { - if (this.hasToIgnoreWorkspaceRecommendationNotifications()) { - return; - } - - let installed = await this.extensionManagementService.getInstalled(); - installed = installed.filter(l => this.extensionEnablementService.getEnablementState(l) !== EnablementState.DisabledByExtensionKind); // Filter extensions disabled by kind - recommendations = recommendations.filter(extensionId => installed.every(local => !areSameExtensions({ id: extensionId }, local.identifier))); - - if (!recommendations.length) { - return; - } + }, token); - const extensions = await this.getInstallableExtensions(recommendations); - if (!extensions.length) { - return; - } + await raceCancellation(Event.toPromise(handle.onDidClose), token); + handle.close(); - const searchValue = '@recommended '; - let cancelled = false; - const handle = await this.showNotification({ - severity: Severity.Info, - message: localize('workspaceRecommended', "Do you want to install the recommended extensions for this repository?"), - choices: [{ - label: localize('install', "Install"), - run: async () => { - this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }); - await Promise.all(extensions.map(async extension => { - this.extensionsWorkbenchService.open(extension, { pinned: true }); - await this.extensionManagementService.installFromGallery(extension.gallery!); - })); - } - }, { - label: localize('showRecommendations', "Show Recommendations"), - run: async () => { - this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }); - this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); - } - }, { - label: localize('neverShowAgain', "Don't Show Again"), - isSecondary: true, - run: () => { - this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); + } catch (error) { + if (!isPromiseCanceledError(error)) { + throw error; } - }], - options: { - sticky: true, - onCancel: () => { - cancelled = true; - this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }); - } - }, - priority: RecommendationSource.WORKSPACE - }); - - const disposable = handle.onDidClose(() => { - disposable.dispose(); - if (!cancelled) { - this.storageService.store(donotShowWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); } + + return result; }); + } + private waitUntilRecommendationsAreInstalled(extensions: IExtension[]): CancelablePromise { + const installedExtensions: string[] = []; + const disposables = new DisposableStore(); + return createCancelablePromise(async token => { + disposables.add(token.onCancellationRequested(e => disposables.dispose())); + return new Promise((c, e) => { + disposables.add(this.extensionManagementService.onInstallExtension(e => { + installedExtensions.push(e.identifier.id.toLowerCase()); + if (extensions.every(e => installedExtensions.includes(e.identifier.id.toLowerCase()))) { + c(RecommendationsNotificationResult.Accepted); + } + })); + }); + }); } /** @@ -266,18 +277,20 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec * => If it is not exe based and has higher or same priority as current, hide the current notification after showing it for 3s. * => Otherwise wait until the current notification is hidden. */ - private async showNotification(recommendationNotification: RecommendationNotification): Promise { - this.recommendationSources.push(recommendationNotification.priority); + private async doShowRecommendationsNotification(recommendationsNotification: RecommendationsNotification, token: CancellationToken): Promise { + this.recommendationSources.push(recommendationsNotification.priority); if (this.visibleNotification) { - return new Promise((onDidShow, e) => { - this.pendingNotificaitons.push({ ...recommendationNotification, onDidShow }); - if (recommendationNotification.priority !== RecommendationSource.EXE && recommendationNotification.priority <= this.visibleNotification!.priority) { + return new Promise((c, e) => { + const index = this.pendingNotificaitons.length; + const disposable = token.onCancellationRequested(() => this.pendingNotificaitons.splice(index, 1)); + this.pendingNotificaitons.push({ ...recommendationsNotification, onDidShow: (notificationHandle) => { disposable.dispose(); c(notificationHandle); }, token }); + if (recommendationsNotification.priority !== RecommendationSource.EXE && recommendationsNotification.priority <= this.visibleNotification!.priority) { this.hideVisibleNotification(3000); } }); } else { - const notificationHandle = this.notificationService.prompt(recommendationNotification.severity, recommendationNotification.message, recommendationNotification.choices, recommendationNotification.options); - this.visibleNotification = { ...recommendationNotification, notificationHandle, from: Date.now() }; + const notificationHandle = this.notificationService.prompt(recommendationsNotification.severity, recommendationsNotification.message, recommendationsNotification.choices, recommendationsNotification.options); + this.visibleNotification = { ...recommendationsNotification, notificationHandle, from: Date.now() }; const disposable = Event.once(Event.filter(notificationHandle.onDidChangeVisibility, e => !e))(() => { disposable.dispose(); this.showNextNotification(); @@ -295,7 +308,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec .then(() => { this.unsetVisibileNotification(); if (nextNotificaiton) { - this.showNotification(nextNotificaiton).then(nextNotificaiton.onDidShow); + this.doShowRecommendationsNotification(nextNotificaiton, nextNotificaiton.token).then(nextNotificaiton.onDidShow); } }); } @@ -332,10 +345,6 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec this.visibleNotification = undefined; } - private hasToIgnoreWorkspaceRecommendationNotifications(): boolean { - return this.hasToIgnoreRecommendationNotifications() || this.storageService.getBoolean(donotShowWorkspaceRecommendationsStorageKey, StorageScope.WORKSPACE, false); - } - private async getInstallableExtensions(extensionIds: string[]): Promise { const extensions: IExtension[] = []; if (extensionIds.length) { From 9041772b4c625e35216717488879c7a97522f628 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 1 Oct 2020 00:11:56 +0200 Subject: [PATCH 0094/1882] attach hidden notification with silent one --- ...ensionRecommendationNotificationService.ts | 152 +++++++++++------- 1 file changed, 98 insertions(+), 54 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index 794fd29db2cbe..71962e89b81d1 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -8,15 +8,15 @@ import { distinct } from 'vs/base/common/arrays'; import { CancelablePromise, createCancelablePromise, raceCancellablePromises, raceCancellation, timeout } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { isPromiseCanceledError } from 'vs/base/common/errors'; -import { Event } from 'vs/base/common/event'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; +import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; -import { INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, Severity } from 'vs/platform/notification/common/notification'; +import { INotificationHandle, INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; @@ -47,21 +47,72 @@ const ignoreImportantExtensionRecommendationStorageKey = 'extensionsAssistant/im const donotShowWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; const choiceNever = localize('neverShowAgain', "Don't Show Again"); -type RecommendationsNotification = { - severity: Severity, - message: string, - choices: IPromptChoice[], - options?: IPromptOptions, - priority: RecommendationSource, -}; type RecommendationsNotificationActions = { onDidInstallRecommendedExtensions(extensions: IExtension[]): void; onDidShowRecommendedExtensions(extensions: IExtension[]): void; onDidCancelRecommendedExtensions(extensions: IExtension[]): void; onDidNeverShowRecommendedExtensionsAgain(extensions: IExtension[]): void; }; -type VisibleRecommendationsNotification = RecommendationsNotification & { notificationHandle: INotificationHandle, from: number }; -type PendingRecommendationsNotification = RecommendationsNotification & { token: CancellationToken, onDidShow: (notificationHandle: INotificationHandle) => void }; + +class RecommendationsNotification { + + private _onDidClose = new Emitter(); + readonly onDidClose = this._onDidClose.event; + + private _onDidChangeVisibility = new Emitter(); + readonly onDidChangeVisibility = this._onDidChangeVisibility.event; + + private notificationHandle: INotificationHandle | undefined; + private cancelled: boolean = false; + + constructor( + private readonly severity: Severity, + private readonly message: string, + private readonly choices: IPromptChoice[], + private readonly notificationService: INotificationService + ) { } + + show(): void { + if (!this.notificationHandle) { + this.updateNotificationHandle(this.notificationService.prompt(this.severity, this.message, this.choices, { sticky: true, onCancel: () => this.cancelled = true })); + } + } + + hide(): void { + if (this.notificationHandle) { + this.onDidCloseDisposable.clear(); + this.notificationHandle.close(); + this.cancelled = false; + this.updateNotificationHandle(this.notificationService.prompt(this.severity, this.message, this.choices, { silent: true, sticky: false, onCancel: () => this.cancelled = true })); + } + } + + isCancelled(): boolean { + return this.cancelled; + } + + private onDidCloseDisposable = new MutableDisposable(); + private onDidChangeVisibilityDisposable = new MutableDisposable(); + private updateNotificationHandle(notificationHandle: INotificationHandle) { + this.onDidCloseDisposable.clear(); + this.onDidChangeVisibilityDisposable.clear(); + this.notificationHandle = notificationHandle; + + this.onDidCloseDisposable.value = this.notificationHandle.onDidClose(() => { + this.onDidCloseDisposable.dispose(); + this.onDidChangeVisibilityDisposable.dispose(); + + this._onDidClose.fire(); + + this._onDidClose.dispose(); + this._onDidChangeVisibility.dispose(); + }); + this.onDidChangeVisibilityDisposable.value = this.notificationHandle.onDidChangeVisibility((e) => this._onDidChangeVisibility.fire(e)); + } +} + +type PendingRecommendationsNotification = { recommendationsNotification: RecommendationsNotification, source: RecommendationSource, token: CancellationToken }; +type VisibleRecommendationsNotification = { recommendationsNotification: RecommendationsNotification, source: RecommendationSource, from: number }; export class ExtensionRecommendationNotificationService implements IExtensionRecommendationNotificationService { @@ -198,13 +249,11 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec private showRecommendationsNotification(extensions: IExtension[], message: string, searchValue: string, source: RecommendationSource, { onDidInstallRecommendedExtensions, onDidShowRecommendedExtensions, onDidCancelRecommendedExtensions, onDidNeverShowRecommendedExtensionsAgain }: RecommendationsNotificationActions): CancelablePromise { return createCancelablePromise(async token => { - let result = RecommendationsNotificationResult.Accepted; + let accepted = false; try { - const handle = await this.doShowRecommendationsNotification({ - severity: Severity.Info, - message, - priority: source, - choices: [{ + accepted = await this.doShowRecommendationsNotification( + Severity.Info, message, + [{ label: localize('install', "Install"), run: async () => { this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); @@ -230,17 +279,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec onDidNeverShowRecommendedExtensionsAgain(extensions); } }], - options: { - sticky: true, - onCancel: () => { - result = RecommendationsNotificationResult.Cancelled; - onDidCancelRecommendedExtensions(extensions); - } - } - }, token); - - await raceCancellation(Event.toPromise(handle.onDidClose), token); - handle.close(); + source, token); } catch (error) { if (!isPromiseCanceledError(error)) { @@ -248,7 +287,13 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec } } - return result; + if (accepted) { + return RecommendationsNotificationResult.Accepted; + } else { + onDidCancelRecommendedExtensions(extensions); + return RecommendationsNotificationResult.Cancelled; + } + }); } @@ -277,25 +322,27 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec * => If it is not exe based and has higher or same priority as current, hide the current notification after showing it for 3s. * => Otherwise wait until the current notification is hidden. */ - private async doShowRecommendationsNotification(recommendationsNotification: RecommendationsNotification, token: CancellationToken): Promise { - this.recommendationSources.push(recommendationsNotification.priority); - if (this.visibleNotification) { - return new Promise((c, e) => { + private async doShowRecommendationsNotification(severity: Severity, message: string, choices: IPromptChoice[], source: RecommendationSource, token: CancellationToken): Promise { + const disposables = new DisposableStore(); + try { + this.recommendationSources.push(source); + const recommendationsNotification = new RecommendationsNotification(severity, message, choices, this.notificationService); + if (this.visibleNotification) { const index = this.pendingNotificaitons.length; - const disposable = token.onCancellationRequested(() => this.pendingNotificaitons.splice(index, 1)); - this.pendingNotificaitons.push({ ...recommendationsNotification, onDidShow: (notificationHandle) => { disposable.dispose(); c(notificationHandle); }, token }); - if (recommendationsNotification.priority !== RecommendationSource.EXE && recommendationsNotification.priority <= this.visibleNotification!.priority) { + token.onCancellationRequested(() => this.pendingNotificaitons.splice(index, 1), disposables); + this.pendingNotificaitons.push({ recommendationsNotification, source, token }); + if (source !== RecommendationSource.EXE && source <= this.visibleNotification!.source) { this.hideVisibleNotification(3000); } - }); - } else { - const notificationHandle = this.notificationService.prompt(recommendationsNotification.severity, recommendationsNotification.message, recommendationsNotification.choices, recommendationsNotification.options); - this.visibleNotification = { ...recommendationsNotification, notificationHandle, from: Date.now() }; - const disposable = Event.once(Event.filter(notificationHandle.onDidChangeVisibility, e => !e))(() => { - disposable.dispose(); - this.showNextNotification(); - }); - return notificationHandle; + } else { + this.visibleNotification = { recommendationsNotification, source, from: Date.now() }; + recommendationsNotification.show(); + Event.once(Event.filter(recommendationsNotification.onDidChangeVisibility, e => !e))(() => this.showNextNotification()); + } + await raceCancellation(Event.toPromise(recommendationsNotification.onDidClose), token); + return !recommendationsNotification.isCancelled(); + } finally { + disposables.dispose(); } } @@ -308,7 +355,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec .then(() => { this.unsetVisibileNotification(); if (nextNotificaiton) { - this.doShowRecommendationsNotification(nextNotificaiton, nextNotificaiton.token).then(nextNotificaiton.onDidShow); + nextNotificaiton.recommendationsNotification.show(); } }); } @@ -320,7 +367,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec let index = this.pendingNotificaitons.length - 1; if (this.pendingNotificaitons.length) { for (let i = 0; i < this.pendingNotificaitons.length; i++) { - if (this.pendingNotificaitons[i].priority <= this.pendingNotificaitons[index].priority) { + if (this.pendingNotificaitons[i].source <= this.pendingNotificaitons[index].source) { index = i; } } @@ -330,12 +377,9 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec private hideVisibleNotification(timeInMillis: number): void { if (this.visibleNotification && !this.hideVisibleNotificationPromise) { - const visibleRecommendationNotification = this.visibleNotification; - this.hideVisibleNotificationPromise = timeout(Math.max(timeInMillis - (Date.now() - visibleRecommendationNotification.from), 0)); - this.hideVisibleNotificationPromise.then(() => { - visibleRecommendationNotification.notificationHandle.close(); - this.notificationService.prompt(visibleRecommendationNotification.severity, visibleRecommendationNotification.message, visibleRecommendationNotification.choices, { ...visibleRecommendationNotification.options, silent: true, sticky: false }); - }); + const visibleNotification = this.visibleNotification; + this.hideVisibleNotificationPromise = timeout(Math.max(timeInMillis - (Date.now() - visibleNotification.from), 0)); + this.hideVisibleNotificationPromise.then(() => visibleNotification!.recommendationsNotification.hide()); } } From 4b84029768d76b8f07ddc790e76f5e21c9f05438 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 1 Oct 2020 00:43:34 +0200 Subject: [PATCH 0095/1882] fix tests --- .../extensionRecommendationsService.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index df68a63663ab2..94fc4af4425a7 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -17,7 +17,7 @@ import { import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -188,6 +188,7 @@ suite('ExtensionRecommendationsService Test', () => { uninstallEvent: Emitter, didUninstallEvent: Emitter; let prompted: boolean; + let promptedEmitter = new Emitter(); let onModelAddedEvent: Emitter; let experimentService: TestExperimentService; @@ -265,6 +266,7 @@ suite('ExtensionRecommendationsService Test', () => { class TestNotificationService2 extends TestNotificationService { public prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions) { prompted = true; + promptedEmitter.fire(); return super.prompt(severity, message, choices, options); } } @@ -356,15 +358,14 @@ suite('ExtensionRecommendationsService Test', () => { test('ExtensionRecommendationsService: Prompt for valid workspace recommendations', async () => { await setUpFolderWorkspace('myFolder', mockTestData.recommendedExtensions); testObject = instantiationService.createInstance(ExtensionRecommendationsService); - await testObject.activationPromise; + await Event.toPromise(promptedEmitter.event); const recommendations = Object.keys(testObject.getAllRecommendationsWithReason()); assert.equal(recommendations.length, mockTestData.validRecommendedExtensions.length); mockTestData.validRecommendedExtensions.forEach(x => { assert.equal(recommendations.indexOf(x.toLowerCase()) > -1, true); }); - assert.ok(prompted); }); test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if they are already installed', () => { From 46ae3f8280c821c643c4b04a8a048809f08f401c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 1 Oct 2020 01:05:55 +0200 Subject: [PATCH 0096/1882] Fix #107290 --- .../extensions/browser/extensionsActions.ts | 32 ++++++++++++++----- .../common/extensionEnablementService.ts | 6 ++-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 17b2b8a5093f2..d9c4aa1def433 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -2908,12 +2908,16 @@ export class DisableAllAction extends Action { this._register(this.extensionsWorkbenchService.onChange(() => this.update())); } + private getExtensionsToDisable(): IExtension[] { + return this.extensionsWorkbenchService.local.filter(e => e.type === ExtensionType.User && !!e.local && this.extensionEnablementService.isEnabled(e.local) && this.extensionEnablementService.canChangeEnablement(e.local)); + } + private update(): void { - this.enabled = this.extensionsWorkbenchService.local.some(e => e.type === ExtensionType.User && !!e.local && this.extensionEnablementService.isEnabled(e.local) && this.extensionEnablementService.canChangeEnablement(e.local)); + this.enabled = this.getExtensionsToDisable().length > 0; } run(): Promise { - return this.extensionsWorkbenchService.setEnablement(this.extensionsWorkbenchService.local.filter(e => e.type === ExtensionType.User), EnablementState.DisabledGlobally); + return this.extensionsWorkbenchService.setEnablement(this.getExtensionsToDisable(), EnablementState.DisabledGlobally); } } @@ -2934,12 +2938,16 @@ export class DisableAllWorkspaceAction extends Action { this._register(this.extensionsWorkbenchService.onChange(() => this.update(), this)); } + private getExtensionsToDisable(): IExtension[] { + return this.extensionsWorkbenchService.local.filter(e => e.type === ExtensionType.User && !!e.local && this.extensionEnablementService.isEnabled(e.local) && this.extensionEnablementService.canChangeEnablement(e.local)); + } + private update(): void { - this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => e.type === ExtensionType.User && !!e.local && this.extensionEnablementService.isEnabled(e.local) && this.extensionEnablementService.canChangeEnablement(e.local)); + this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.getExtensionsToDisable().length > 0; } run(): Promise { - return this.extensionsWorkbenchService.setEnablement(this.extensionsWorkbenchService.local.filter(e => e.type === ExtensionType.User), EnablementState.DisabledWorkspace); + return this.extensionsWorkbenchService.setEnablement(this.getExtensionsToDisable(), EnablementState.DisabledWorkspace); } } @@ -2958,12 +2966,16 @@ export class EnableAllAction extends Action { this._register(this.extensionsWorkbenchService.onChange(() => this.update())); } + private getExtensionsToEnable(): IExtension[] { + return this.extensionsWorkbenchService.local.filter(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local)); + } + private update(): void { - this.enabled = this.extensionsWorkbenchService.local.some(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local)); + this.enabled = this.getExtensionsToEnable().length > 0; } run(): Promise { - return this.extensionsWorkbenchService.setEnablement(this.extensionsWorkbenchService.local, EnablementState.EnabledGlobally); + return this.extensionsWorkbenchService.setEnablement(this.getExtensionsToEnable(), EnablementState.EnabledGlobally); } } @@ -2984,12 +2996,16 @@ export class EnableAllWorkspaceAction extends Action { this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this.update(), this)); } + private getExtensionsToEnable(): IExtension[] { + return this.extensionsWorkbenchService.local.filter(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local)); + } + private update(): void { - this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local)); + this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.getExtensionsToEnable().length > 0; } run(): Promise { - return this.extensionsWorkbenchService.setEnablement(this.extensionsWorkbenchService.local, EnablementState.EnabledWorkspace); + return this.extensionsWorkbenchService.setEnablement(this.getExtensionsToEnable(), EnablementState.EnabledWorkspace); } } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts index 32f3dc52c1ff6..6e7e8c5c9ed18 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts @@ -84,12 +84,12 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench private throwErrorIfCannotChangeEnablement(extension: IExtension): void { if (isLanguagePackExtension(extension.manifest)) { - throw new Error(localize('cannot disable language pack extension', "Cannot disable {0} extension because it contributes language packs.", extension.manifest.displayName || extension.identifier.id)); + throw new Error(localize('cannot disable language pack extension', "Cannot change enablement of {0} extension because it contributes language packs.", extension.manifest.displayName || extension.identifier.id)); } if (this.userDataAutoSyncService.isEnabled() && this.userDataSyncAccountService.account && isAuthenticaionProviderExtension(extension.manifest) && extension.manifest.contributes!.authentication!.some(a => a.id === this.userDataSyncAccountService.account!.authenticationProviderId)) { - throw new Error(localize('cannot disable auth extension', "Cannot disable {0} extension because Settings Sync depends on it.", extension.manifest.displayName || extension.identifier.id)); + throw new Error(localize('cannot disable auth extension', "Cannot change enablement {0} extension because Settings Sync depends on it.", extension.manifest.displayName || extension.identifier.id)); } } @@ -110,7 +110,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench throw new Error(localize('noWorkspace', "No workspace.")); } if (isAuthenticaionProviderExtension(extension.manifest)) { - throw new Error(localize('cannot disable auth extension in workspace', "Cannot disable {0} extension in workspace because it contributes authentication providers", extension.manifest.displayName || extension.identifier.id)); + throw new Error(localize('cannot disable auth extension in workspace', "Cannot change enablement of {0} extension in workspace because it contributes authentication providers", extension.manifest.displayName || extension.identifier.id)); } } From 264d2d28fb819410452913dfbf103a857506bb63 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 30 Sep 2020 16:16:38 -0700 Subject: [PATCH 0097/1882] fix #107847. --- src/vs/editor/browser/controller/coreCommands.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index 63e9488203a1a..840d070f69f4e 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import { isFirefox } from 'vs/base/browser/browser'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as types from 'vs/base/common/types'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -1640,6 +1641,11 @@ export namespace CoreNavigationCommands { super(SelectAllCommand); } public runDOMCommand(): void { + if (isFirefox) { + (document.activeElement).focus(); + (document.activeElement).select(); + } + document.execCommand('selectAll'); } public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { From 7f3b2e575c4a9f52639b0fa33c0d7afdfa2a8673 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 1 Oct 2020 01:23:53 +0200 Subject: [PATCH 0098/1882] #104626 Use extUri for resource functions --- .../common/abstractSynchronizer.ts | 42 ++++--- .../userDataSync/common/extensionsSync.ts | 19 ++- .../userDataSync/common/globalStateSync.ts | 21 ++-- .../userDataSync/common/keybindingsSync.ts | 17 ++- .../userDataSync/common/settingsSync.ts | 17 ++- .../userDataSync/common/snippetsSync.ts | 117 +++++++++--------- 6 files changed, 116 insertions(+), 117 deletions(-) diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index 16c5f2f999aed..b8be4d3be943d 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { IFileService, IFileContent, FileChangesEvent, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files'; +import { IFileService, IFileContent, FileChangesEvent, FileOperationResult, FileOperationError, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { VSBuffer } from 'vs/base/common/buffer'; import { URI } from 'vs/base/common/uri'; import { @@ -13,7 +13,7 @@ import { IUserDataManifest, ISyncData, IRemoteUserData, PREVIEW_DIR_NAME, IResourcePreview as IBaseResourcePreview, Change, MergeState, IUserDataInitializer } from 'vs/platform/userDataSync/common/userDataSync'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { joinPath, dirname, isEqual, basename } from 'vs/base/common/resources'; +import { IExtUri, extUri, extUriIgnorePathCase } from 'vs/base/common/resources'; import { CancelablePromise, RunOnceScheduler, createCancelablePromise } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -53,8 +53,8 @@ function isSyncData(thing: any): thing is ISyncData { return false; } -function getLastSyncResourceUri(syncResource: SyncResource, environmentService: IEnvironmentService): URI { - return joinPath(environmentService.userDataSyncHome, syncResource, `lastSync${syncResource}.json`); +function getLastSyncResourceUri(syncResource: SyncResource, environmentService: IEnvironmentService, extUri: IExtUri): URI { + return extUri.joinPath(environmentService.userDataSyncHome, syncResource, `lastSync${syncResource}.json`); } export interface IResourcePreview { @@ -100,6 +100,7 @@ export abstract class AbstractSynchroniser extends Disposable { protected readonly syncFolder: URI; protected readonly syncPreviewFolder: URI; + protected readonly extUri: IExtUri; private readonly currentMachineIdPromise: Promise; private _status: SyncStatus = SyncStatus.Idle; @@ -135,9 +136,10 @@ export abstract class AbstractSynchroniser extends Disposable { ) { super(); this.syncResourceLogLabel = uppercaseFirstLetter(this.resource); - this.syncFolder = joinPath(environmentService.userDataSyncHome, resource); - this.syncPreviewFolder = joinPath(this.syncFolder, PREVIEW_DIR_NAME); - this.lastSyncResource = getLastSyncResourceUri(resource, environmentService); + this.extUri = this.fileService.hasCapability(environmentService.userDataSyncHome, FileSystemProviderCapabilities.PathCaseSensitive) ? extUri : extUriIgnorePathCase; + this.syncFolder = this.extUri.joinPath(environmentService.userDataSyncHome, resource); + this.syncPreviewFolder = this.extUri.joinPath(this.syncFolder, PREVIEW_DIR_NAME); + this.lastSyncResource = getLastSyncResourceUri(resource, environmentService, this.extUri); this.currentMachineIdPromise = getServiceMachineId(environmentService, fileService, storageService); } @@ -423,7 +425,7 @@ export abstract class AbstractSynchroniser extends Disposable { let preview = await this.syncPreviewPromise; const index = preview.resourcePreviews.findIndex(({ localResource, remoteResource, previewResource }) => - isEqual(localResource, resource) || isEqual(remoteResource, resource) || isEqual(previewResource, resource)); + this.extUri.isEqual(localResource, resource) || this.extUri.isEqual(remoteResource, resource) || this.extUri.isEqual(previewResource, resource)); if (index === -1) { return; } @@ -483,7 +485,7 @@ export abstract class AbstractSynchroniser extends Disposable { private updateConflicts(resourcePreviews: IEditableResourcePreview[]): void { const conflicts = resourcePreviews.filter(({ mergeState }) => mergeState === MergeState.Conflict); - if (!equals(this._conflicts, conflicts, (a, b) => isEqual(a.previewResource, b.previewResource))) { + if (!equals(this._conflicts, conflicts, (a, b) => this.extUri.isEqual(a.previewResource, b.previewResource))) { this._conflicts = conflicts; this._onDidChangeConflicts.fire(conflicts); } @@ -513,8 +515,8 @@ export abstract class AbstractSynchroniser extends Disposable { } async getMachineId({ uri }: ISyncResourceHandle): Promise { - const ref = basename(uri); - if (isEqual(uri, this.toRemoteBackupResource(ref))) { + const ref = this.extUri.basename(uri); + if (this.extUri.isEqual(uri, this.toRemoteBackupResource(ref))) { const { content } = await this.getUserData(ref); if (content) { const syncData = this.parseSyncData(content); @@ -525,12 +527,12 @@ export abstract class AbstractSynchroniser extends Disposable { } async resolveContent(uri: URI): Promise { - const ref = basename(uri); - if (isEqual(uri, this.toRemoteBackupResource(ref))) { + const ref = this.extUri.basename(uri); + if (this.extUri.isEqual(uri, this.toRemoteBackupResource(ref))) { const { content } = await this.getUserData(ref); return content; } - if (isEqual(uri, this.toLocalBackupResource(ref))) { + if (this.extUri.isEqual(uri, this.toLocalBackupResource(ref))) { return this.userDataSyncBackupStoreService.resolveContent(this.resource, ref); } return null; @@ -540,13 +542,13 @@ export abstract class AbstractSynchroniser extends Disposable { const syncPreview = this.syncPreviewPromise ? await this.syncPreviewPromise : null; if (syncPreview) { for (const resourcePreview of syncPreview.resourcePreviews) { - if (isEqual(resourcePreview.acceptedResource, uri)) { + if (this.extUri.isEqual(resourcePreview.acceptedResource, uri)) { return resourcePreview.acceptResult ? resourcePreview.acceptResult.content : null; } - if (isEqual(resourcePreview.remoteResource, uri)) { + if (this.extUri.isEqual(resourcePreview.remoteResource, uri)) { return resourcePreview.remoteContent; } - if (isEqual(resourcePreview.localResource, uri)) { + if (this.extUri.isEqual(resourcePreview.localResource, uri)) { return resourcePreview.localContent; } } @@ -727,7 +729,7 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser { @IConfigurationService configurationService: IConfigurationService, ) { super(resource, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService); - this._register(this.fileService.watch(dirname(file))); + this._register(this.fileService.watch(this.extUri.dirname(file))); this._register(this.fileService.onDidFilesChange(e => this.onFileChanges(e))); } @@ -804,6 +806,7 @@ export abstract class AbstractJsonFileSynchroniser extends AbstractFileSynchroni export abstract class AbstractInitializer implements IUserDataInitializer { + protected readonly extUri: IExtUri; private readonly lastSyncResource: URI; constructor( @@ -812,7 +815,8 @@ export abstract class AbstractInitializer implements IUserDataInitializer { @IUserDataSyncLogService protected readonly logService: IUserDataSyncLogService, @IFileService protected readonly fileService: IFileService, ) { - this.lastSyncResource = getLastSyncResourceUri(this.resource, environmentService); + this.extUri = this.fileService.hasCapability(environmentService.userDataSyncHome, FileSystemProviderCapabilities.PathCaseSensitive) ? extUri : extUriIgnorePathCase; + this.lastSyncResource = getLastSyncResourceUri(this.resource, environmentService, extUri); } async initialize({ ref, content }: IUserData): Promise { diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 0fe85a4a9e873..c799731973114 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -18,7 +18,6 @@ import { merge, getIgnoredExtensions } from 'vs/platform/userDataSync/common/ext import { AbstractInitializer, AbstractSynchroniser, IAcceptResult, IMergeResult, IResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { URI } from 'vs/base/common/uri'; -import { joinPath, dirname, basename, isEqual } from 'vs/base/common/resources'; import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; import { compare } from 'vs/base/common/strings'; @@ -82,7 +81,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse protected readonly version: number = 4; protected isEnabled(): boolean { return super.isEnabled() && this.extensionGalleryService.isEnabled(); } - private readonly previewResource: URI = joinPath(this.syncPreviewFolder, 'extensions.json'); + private readonly previewResource: URI = this.extUri.joinPath(this.syncPreviewFolder, 'extensions.json'); private readonly localResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }); private readonly remoteResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }); private readonly acceptedResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }); @@ -183,17 +182,17 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse protected async getAcceptResult(resourcePreview: IExtensionResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise { /* Accept local resource */ - if (isEqual(resource, this.localResource)) { + if (this.extUri.isEqual(resource, this.localResource)) { return this.acceptLocal(resourcePreview); } /* Accept remote resource */ - if (isEqual(resource, this.remoteResource)) { + if (this.extUri.isEqual(resource, this.remoteResource)) { return this.acceptRemote(resourcePreview); } /* Accept preview resource */ - if (isEqual(resource, this.previewResource)) { + if (this.extUri.isEqual(resource, this.previewResource)) { return resourcePreview.previewResult; } @@ -272,18 +271,18 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse } async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource: URI }[]> { - return [{ resource: joinPath(uri, 'extensions.json'), comparableResource: ExtensionsSynchroniser.EXTENSIONS_DATA_URI }]; + return [{ resource: this.extUri.joinPath(uri, 'extensions.json'), comparableResource: ExtensionsSynchroniser.EXTENSIONS_DATA_URI }]; } async resolveContent(uri: URI): Promise { - if (isEqual(uri, ExtensionsSynchroniser.EXTENSIONS_DATA_URI)) { + if (this.extUri.isEqual(uri, ExtensionsSynchroniser.EXTENSIONS_DATA_URI)) { const installedExtensions = await this.extensionManagementService.getInstalled(); const ignoredExtensions = getIgnoredExtensions(installedExtensions, this.configurationService); const localExtensions = this.getLocalExtensions(installedExtensions).filter(e => !ignoredExtensions.some(id => areSameExtensions({ id }, e.identifier))); return this.format(localExtensions); } - if (isEqual(this.remoteResource, uri) || isEqual(this.localResource, uri) || isEqual(this.acceptedResource, uri)) { + if (this.extUri.isEqual(this.remoteResource, uri) || this.extUri.isEqual(this.localResource, uri) || this.extUri.isEqual(this.acceptedResource, uri)) { return this.resolvePreviewContent(uri); } @@ -292,11 +291,11 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse return content; } - content = await super.resolveContent(dirname(uri)); + content = await super.resolveContent(this.extUri.dirname(uri)); if (content) { const syncData = this.parseSyncData(content); if (syncData) { - switch (basename(uri)) { + switch (this.extUri.basename(uri)) { case 'extensions.json': return this.format(this.parseExtensions(syncData)); } diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index ab8209b396b23..4c4e6da32d585 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -10,7 +10,6 @@ import { import { VSBuffer } from 'vs/base/common/buffer'; import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { dirname, joinPath, basename, isEqual } from 'vs/base/common/resources'; import { IFileService } from 'vs/platform/files/common/files'; import { IStringDictionary } from 'vs/base/common/collections'; import { edit } from 'vs/platform/userDataSync/common/content'; @@ -49,7 +48,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs private static readonly GLOBAL_STATE_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'globalState', path: `/globalState.json` }); protected readonly version: number = 1; - private readonly previewResource: URI = joinPath(this.syncPreviewFolder, 'globalState.json'); + private readonly previewResource: URI = this.extUri.joinPath(this.syncPreviewFolder, 'globalState.json'); private readonly localResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }); private readonly remoteResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }); private readonly acceptedResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }); @@ -67,7 +66,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { super(SyncResource.GlobalState, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService); - this._register(this.fileService.watch(dirname(this.environmentService.argvResource))); + this._register(this.fileService.watch(this.extUri.dirname(this.environmentService.argvResource))); this._register( Event.any( /* Locale change */ @@ -123,17 +122,17 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs protected async getAcceptResult(resourcePreview: IGlobalStateResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise { /* Accept local resource */ - if (isEqual(resource, this.localResource)) { + if (this.extUri.isEqual(resource, this.localResource)) { return this.acceptLocal(resourcePreview); } /* Accept remote resource */ - if (isEqual(resource, this.remoteResource)) { + if (this.extUri.isEqual(resource, this.remoteResource)) { return this.acceptRemote(resourcePreview); } /* Accept preview resource */ - if (isEqual(resource, this.previewResource)) { + if (this.extUri.isEqual(resource, this.previewResource)) { return resourcePreview.previewResult; } @@ -205,16 +204,16 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs } async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource: URI }[]> { - return [{ resource: joinPath(uri, 'globalState.json'), comparableResource: GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI }]; + return [{ resource: this.extUri.joinPath(uri, 'globalState.json'), comparableResource: GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI }]; } async resolveContent(uri: URI): Promise { - if (isEqual(uri, GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI)) { + if (this.extUri.isEqual(uri, GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI)) { const localGlobalState = await this.getLocalGlobalState(); return this.format(localGlobalState); } - if (isEqual(this.remoteResource, uri) || isEqual(this.localResource, uri) || isEqual(this.acceptedResource, uri)) { + if (this.extUri.isEqual(this.remoteResource, uri) || this.extUri.isEqual(this.localResource, uri) || this.extUri.isEqual(this.acceptedResource, uri)) { return this.resolvePreviewContent(uri); } @@ -223,11 +222,11 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs return content; } - content = await super.resolveContent(dirname(uri)); + content = await super.resolveContent(this.extUri.dirname(uri)); if (content) { const syncData = this.parseSyncData(content); if (syncData) { - switch (basename(uri)) { + switch (this.extUri.basename(uri)) { case 'globalState.json': return this.format(JSON.parse(syncData.content)); } diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 3d9816563da70..80f54b97a02e4 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -21,7 +21,6 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { AbstractInitializer, AbstractJsonFileSynchroniser, IAcceptResult, IFileResourcePreview, IMergeResult } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { URI } from 'vs/base/common/uri'; -import { joinPath, isEqual, dirname, basename } from 'vs/base/common/resources'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -55,7 +54,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem /* Version 2: Change settings from `sync.${setting}` to `settingsSync.{setting}` */ protected readonly version: number = 2; - private readonly previewResource: URI = joinPath(this.syncPreviewFolder, 'keybindings.json'); + private readonly previewResource: URI = this.extUri.joinPath(this.syncPreviewFolder, 'keybindings.json'); private readonly localResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }); private readonly remoteResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }); private readonly acceptedResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }); @@ -149,7 +148,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem protected async getAcceptResult(resourcePreview: IKeybindingsResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise { /* Accept local resource */ - if (isEqual(resource, this.localResource)) { + if (this.extUri.isEqual(resource, this.localResource)) { return { content: resourcePreview.fileContent ? resourcePreview.fileContent.value.toString() : null, localChange: Change.None, @@ -158,7 +157,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem } /* Accept remote resource */ - if (isEqual(resource, this.remoteResource)) { + if (this.extUri.isEqual(resource, this.remoteResource)) { return { content: resourcePreview.remoteContent, localChange: Change.Modified, @@ -167,7 +166,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem } /* Accept preview resource */ - if (isEqual(resource, this.previewResource)) { + if (this.extUri.isEqual(resource, this.previewResource)) { if (content === undefined) { return { content: resourcePreview.previewResult.content, @@ -258,22 +257,22 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource: URI }[]> { const comparableResource = (await this.fileService.exists(this.file)) ? this.file : this.localResource; - return [{ resource: joinPath(uri, 'keybindings.json'), comparableResource }]; + return [{ resource: this.extUri.joinPath(uri, 'keybindings.json'), comparableResource }]; } async resolveContent(uri: URI): Promise { - if (isEqual(this.remoteResource, uri) || isEqual(this.localResource, uri) || isEqual(this.acceptedResource, uri)) { + if (this.extUri.isEqual(this.remoteResource, uri) || this.extUri.isEqual(this.localResource, uri) || this.extUri.isEqual(this.acceptedResource, uri)) { return this.resolvePreviewContent(uri); } let content = await super.resolveContent(uri); if (content) { return content; } - content = await super.resolveContent(dirname(uri)); + content = await super.resolveContent(this.extUri.dirname(uri)); if (content) { const syncData = this.parseSyncData(content); if (syncData) { - switch (basename(uri)) { + switch (this.extUri.basename(uri)) { case 'keybindings.json': return this.getKeybindingsContentFromSyncContent(syncData.content); } diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index f1734e5e8389c..6b583f9c809f8 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -21,7 +21,6 @@ import { AbstractInitializer, AbstractJsonFileSynchroniser, IAcceptResult, IFile import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { URI } from 'vs/base/common/uri'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { joinPath, isEqual, dirname, basename } from 'vs/base/common/resources'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { Edit } from 'vs/base/common/jsonFormatter'; import { setProperty, applyEdits } from 'vs/base/common/jsonEdit'; @@ -49,7 +48,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement /* Version 2: Change settings from `sync.${setting}` to `settingsSync.{setting}` */ protected readonly version: number = 2; - readonly previewResource: URI = joinPath(this.syncPreviewFolder, 'settings.json'); + readonly previewResource: URI = this.extUri.joinPath(this.syncPreviewFolder, 'settings.json'); readonly localResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }); readonly remoteResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }); readonly acceptedResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }); @@ -141,7 +140,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement const ignoredSettings = await this.getIgnoredSettings(); /* Accept local resource */ - if (isEqual(resource, this.localResource)) { + if (this.extUri.isEqual(resource, this.localResource)) { return { /* Remove ignored settings */ content: resourcePreview.fileContent ? updateIgnoredSettings(resourcePreview.fileContent.value.toString(), '{}', ignoredSettings, formattingOptions) : null, @@ -151,7 +150,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement } /* Accept remote resource */ - if (isEqual(resource, this.remoteResource)) { + if (this.extUri.isEqual(resource, this.remoteResource)) { return { /* Update ignored settings from local file content */ content: resourcePreview.remoteContent !== null ? updateIgnoredSettings(resourcePreview.remoteContent, resourcePreview.fileContent ? resourcePreview.fileContent.value.toString() : '{}', ignoredSettings, formattingOptions) : null, @@ -161,7 +160,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement } /* Accept preview resource */ - if (isEqual(resource, this.previewResource)) { + if (this.extUri.isEqual(resource, this.previewResource)) { if (content === undefined) { return { content: resourcePreview.previewResult.content, @@ -244,24 +243,24 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource: URI }[]> { const comparableResource = (await this.fileService.exists(this.file)) ? this.file : this.localResource; - return [{ resource: joinPath(uri, 'settings.json'), comparableResource }]; + return [{ resource: this.extUri.joinPath(uri, 'settings.json'), comparableResource }]; } async resolveContent(uri: URI): Promise { - if (isEqual(this.remoteResource, uri) || isEqual(this.localResource, uri) || isEqual(this.acceptedResource, uri)) { + if (this.extUri.isEqual(this.remoteResource, uri) || this.extUri.isEqual(this.localResource, uri) || this.extUri.isEqual(this.acceptedResource, uri)) { return this.resolvePreviewContent(uri); } let content = await super.resolveContent(uri); if (content) { return content; } - content = await super.resolveContent(dirname(uri)); + content = await super.resolveContent(this.extUri.dirname(uri)); if (content) { const syncData = this.parseSyncData(content); if (syncData) { const settingsSyncContent = this.parseSettingsSyncContent(syncData.content); if (settingsSyncContent) { - switch (basename(uri)) { + switch (this.extUri.basename(uri)) { case 'settings.json': return settingsSyncContent.settings; } diff --git a/src/vs/platform/userDataSync/common/snippetsSync.ts b/src/vs/platform/userDataSync/common/snippetsSync.ts index c5b1029bd14ab..70f144acb8d9e 100644 --- a/src/vs/platform/userDataSync/common/snippetsSync.ts +++ b/src/vs/platform/userDataSync/common/snippetsSync.ts @@ -14,7 +14,6 @@ import { AbstractInitializer, AbstractSynchroniser, IAcceptResult, IFileResource import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStringDictionary } from 'vs/base/common/collections'; import { URI } from 'vs/base/common/uri'; -import { joinPath, extname, relativePath, isEqualOrParent, basename, dirname } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; import { merge, IMergeResult as ISnippetsMergeResult, areSame } from 'vs/platform/userDataSync/common/snippetsMerge'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -53,7 +52,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD } private onFileChanges(e: FileChangesEvent): void { - if (!e.changes.some(change => isEqualOrParent(change.resource, this.snippetsFolder))) { + if (!e.changes.some(change => this.extUri.isEqualOrParent(change.resource, this.snippetsFolder))) { return; } this.triggerLocalChange(); @@ -82,7 +81,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD protected async getAcceptResult(resourcePreview: ISnippetsResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise { /* Accept local resource */ - if (isEqualOrParent(resource, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }))) { + if (this.extUri.isEqualOrParent(resource, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }))) { return { content: resourcePreview.fileContent ? resourcePreview.fileContent.value.toString() : null, localChange: Change.None, @@ -93,7 +92,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD } /* Accept remote resource */ - if (isEqualOrParent(resource, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }))) { + if (this.extUri.isEqualOrParent(resource, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }))) { return { content: resourcePreview.remoteContent, localChange: resourcePreview.remoteContent !== null @@ -104,7 +103,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD } /* Accept preview resource */ - if (isEqualOrParent(resource, this.syncPreviewFolder)) { + if (this.extUri.isEqualOrParent(resource, this.syncPreviewFolder)) { if (content === undefined) { return { content: resourcePreview.previewResult.content, @@ -172,15 +171,15 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD }; resourcePreviews.set(key, { fileContent: null, - localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + localResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), localContent: null, - remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), remoteContent: remoteSnippets[key], - previewResource: joinPath(this.syncPreviewFolder, key), + previewResource: this.extUri.joinPath(this.syncPreviewFolder, key), previewResult, localChange: previewResult.localChange, remoteChange: previewResult.remoteChange, - acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) }); } @@ -193,16 +192,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD remoteChange: Change.None, }; resourcePreviews.set(key, { - localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + localResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), fileContent: localFileContent[key], localContent: localFileContent[key].value.toString(), - remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), remoteContent: remoteSnippets[key], - previewResource: joinPath(this.syncPreviewFolder, key), + previewResource: this.extUri.joinPath(this.syncPreviewFolder, key), previewResult, localChange: previewResult.localChange, remoteChange: previewResult.remoteChange, - acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) }); } @@ -215,16 +214,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD remoteChange: Change.None, }; resourcePreviews.set(key, { - localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + localResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), fileContent: localFileContent[key], localContent: localFileContent[key].value.toString(), - remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), remoteContent: null, - previewResource: joinPath(this.syncPreviewFolder, key), + previewResource: this.extUri.joinPath(this.syncPreviewFolder, key), previewResult, localChange: previewResult.localChange, remoteChange: previewResult.remoteChange, - acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) }); } @@ -237,16 +236,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD remoteChange: Change.Added, }; resourcePreviews.set(key, { - localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + localResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), fileContent: localFileContent[key], localContent: localFileContent[key].value.toString(), - remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), remoteContent: null, - previewResource: joinPath(this.syncPreviewFolder, key), + previewResource: this.extUri.joinPath(this.syncPreviewFolder, key), previewResult, localChange: previewResult.localChange, remoteChange: previewResult.remoteChange, - acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) }); } @@ -259,16 +258,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD remoteChange: Change.Modified, }; resourcePreviews.set(key, { - localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + localResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), fileContent: localFileContent[key], localContent: localFileContent[key].value.toString(), - remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), remoteContent: remoteSnippets[key], - previewResource: joinPath(this.syncPreviewFolder, key), + previewResource: this.extUri.joinPath(this.syncPreviewFolder, key), previewResult, localChange: previewResult.localChange, remoteChange: previewResult.remoteChange, - acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) }); } @@ -281,16 +280,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD remoteChange: Change.Deleted, }; resourcePreviews.set(key, { - localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + localResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), fileContent: null, localContent: null, - remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), remoteContent: remoteSnippets[key], - previewResource: joinPath(this.syncPreviewFolder, key), + previewResource: this.extUri.joinPath(this.syncPreviewFolder, key), previewResult, localChange: previewResult.localChange, remoteChange: previewResult.remoteChange, - acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) }); } @@ -303,16 +302,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD remoteChange: remoteSnippets[key] ? Change.Modified : Change.Added }; resourcePreviews.set(key, { - localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + localResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), fileContent: localFileContent[key] || null, localContent: localFileContent[key] ? localFileContent[key].value.toString() : null, - remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), remoteContent: remoteSnippets[key] || null, - previewResource: joinPath(this.syncPreviewFolder, key), + previewResource: this.extUri.joinPath(this.syncPreviewFolder, key), previewResult, localChange: previewResult.localChange, remoteChange: previewResult.remoteChange, - acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) }); } @@ -326,16 +325,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD remoteChange: Change.None }; resourcePreviews.set(key, { - localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + localResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), fileContent: localFileContent[key] || null, localContent: localFileContent[key] ? localFileContent[key].value.toString() : null, - remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), remoteContent: remoteSnippets[key] || null, - previewResource: joinPath(this.syncPreviewFolder, key), + previewResource: this.extUri.joinPath(this.syncPreviewFolder, key), previewResult, localChange: previewResult.localChange, remoteChange: previewResult.remoteChange, - acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) }); } } @@ -351,10 +350,10 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD const snippets = this.parseSnippets(syncData); const result = []; for (const snippet of Object.keys(snippets)) { - const resource = joinPath(uri, snippet); - const comparableResource = joinPath(this.snippetsFolder, snippet); + const resource = this.extUri.joinPath(uri, snippet); + const comparableResource = this.extUri.joinPath(this.snippetsFolder, snippet); const exists = await this.fileService.exists(comparableResource); - result.push({ resource, comparableResource: exists ? comparableResource : joinPath(this.syncPreviewFolder, snippet).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }) }); + result.push({ resource, comparableResource: exists ? comparableResource : this.extUri.joinPath(this.syncPreviewFolder, snippet).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }) }); } return result; } @@ -363,9 +362,9 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD } async resolveContent(uri: URI): Promise { - if (isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' })) - || isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' })) - || isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }))) { + if (this.extUri.isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' })) + || this.extUri.isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' })) + || this.extUri.isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }))) { return this.resolvePreviewContent(uri); } @@ -374,12 +373,12 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD return content; } - content = await super.resolveContent(dirname(uri)); + content = await super.resolveContent(this.extUri.dirname(uri)); if (content) { const syncData = this.parseSyncData(content); if (syncData) { const snippets = this.parseSnippets(syncData); - return snippets[basename(uri)] || null; + return snippets[this.extUri.basename(uri)] || null; } } @@ -402,7 +401,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD const local: IStringDictionary = {}; for (const resourcePreview of resourcePreviews) { if (resourcePreview.fileContent) { - local[basename(resourcePreview.localResource!)] = resourcePreview.fileContent; + local[this.extUri.basename(resourcePreview.localResource!)] = resourcePreview.fileContent; } } await this.backupLocal(JSON.stringify(this.toSnippetsContents(local))); @@ -411,28 +410,28 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD private async updateLocalSnippets(resourcePreviews: ISnippetsAcceptedResourcePreview[], force: boolean): Promise { for (const { fileContent, acceptResult, localResource, remoteResource, localChange } of resourcePreviews) { if (localChange !== Change.None) { - const key = remoteResource ? basename(remoteResource) : basename(localResource!); - const resource = joinPath(this.snippetsFolder, key); + const key = remoteResource ? this.extUri.basename(remoteResource) : this.extUri.basename(localResource!); + const resource = this.extUri.joinPath(this.snippetsFolder, key); // Removed if (localChange === Change.Deleted) { - this.logService.trace(`${this.syncResourceLogLabel}: Deleting snippet...`, basename(resource)); + this.logService.trace(`${this.syncResourceLogLabel}: Deleting snippet...`, this.extUri.basename(resource)); await this.fileService.del(resource); - this.logService.info(`${this.syncResourceLogLabel}: Deleted snippet`, basename(resource)); + this.logService.info(`${this.syncResourceLogLabel}: Deleted snippet`, this.extUri.basename(resource)); } // Added else if (localChange === Change.Added) { - this.logService.trace(`${this.syncResourceLogLabel}: Creating snippet...`, basename(resource)); + this.logService.trace(`${this.syncResourceLogLabel}: Creating snippet...`, this.extUri.basename(resource)); await this.fileService.createFile(resource, VSBuffer.fromString(acceptResult.content!), { overwrite: force }); - this.logService.info(`${this.syncResourceLogLabel}: Created snippet`, basename(resource)); + this.logService.info(`${this.syncResourceLogLabel}: Created snippet`, this.extUri.basename(resource)); } // Updated else { - this.logService.trace(`${this.syncResourceLogLabel}: Updating snippet...`, basename(resource)); + this.logService.trace(`${this.syncResourceLogLabel}: Updating snippet...`, this.extUri.basename(resource)); await this.fileService.writeFile(resource, VSBuffer.fromString(acceptResult.content!), force ? undefined : fileContent!); - this.logService.info(`${this.syncResourceLogLabel}: Updated snippet`, basename(resource)); + this.logService.info(`${this.syncResourceLogLabel}: Updated snippet`, this.extUri.basename(resource)); } } } @@ -444,7 +443,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD for (const { acceptResult, localResource, remoteResource, remoteChange } of resourcePreviews) { if (remoteChange !== Change.None) { - const key = localResource ? basename(localResource) : basename(remoteResource!); + const key = localResource ? this.extUri.basename(localResource) : this.extUri.basename(remoteResource!); if (remoteChange === Change.Deleted) { delete newSnippets[key]; } else { @@ -489,9 +488,9 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD } for (const entry of stat.children || []) { const resource = entry.resource; - const extension = extname(resource); + const extension = this.extUri.extname(resource); if (extension === '.json' || extension === '.code-snippets') { - const key = relativePath(this.snippetsFolder, resource)!; + const key = this.extUri.relativePath(this.snippetsFolder, resource)!; const content = await this.fileService.readFile(resource); snippets[key] = content; } @@ -526,9 +525,9 @@ export class SnippetsInitializer extends AbstractInitializer { for (const key of Object.keys(remoteSnippets)) { const content = remoteSnippets[key]; if (content) { - const resource = joinPath(this.environmentService.snippetsHome, key); + const resource = this.extUri.joinPath(this.environmentService.snippetsHome, key); await this.fileService.createFile(resource, VSBuffer.fromString(content)); - this.logService.info('Created snippet', basename(resource)); + this.logService.info('Created snippet', this.extUri.basename(resource)); } } From e097cf1cf9e834dcd674523e83c176e5e5f429f2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 1 Oct 2020 01:30:24 +0200 Subject: [PATCH 0099/1882] #105622 Probable fix --- src/vs/platform/userDataSync/common/globalStateSync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 4c4e6da32d585..c1f4daa7bf52b 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -237,7 +237,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs } private format(globalState: IGlobalState): string { - const storageKeys = Object.keys(globalState.storage).sort(); + const storageKeys = globalState.storage ? Object.keys(globalState.storage).sort() : []; const storage: IStringDictionary = {}; storageKeys.forEach(key => storage[key] = globalState.storage[key]); globalState.storage = storage; From c17be0166314d1f83fd10eff380bfe36eca02e56 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 30 Sep 2020 16:33:06 -0700 Subject: [PATCH 0100/1882] debug: recover gracefully from a stuck auto attach pipe --- extensions/debug-auto-launch/src/extension.ts | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/extensions/debug-auto-launch/src/extension.ts b/extensions/debug-auto-launch/src/extension.ts index bbf8c8433f1c2..5ae907a9dbe83 100644 --- a/extensions/debug-auto-launch/src/extension.ts +++ b/extensions/debug-auto-launch/src/extension.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { promises as fs } from 'fs'; import { createServer, Server } from 'net'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; @@ -203,7 +204,27 @@ async function createAttachServer(context: vscode.ExtensionContext) { return undefined; } - server = new Promise((resolve, reject) => { + server = createServerInner(ipcAddress).catch(err => { + console.error(err); + return undefined; + }); + + return await server; +} + +const createServerInner = async (ipcAddress: string) => { + try { + return await createServerInstance(ipcAddress); + } catch (e) { + // On unix/linux, the file can 'leak' if the process exits unexpectedly. + // If we see this, try to delete the file and then listen again. + await fs.unlink(ipcAddress).catch(() => undefined); + return await createServerInstance(ipcAddress); + } +}; + +const createServerInstance = (ipcAddress: string) => + new Promise((resolve, reject) => { const s = createServer(socket => { let data: Buffer[] = []; socket.on('data', async chunk => { @@ -229,14 +250,8 @@ async function createAttachServer(context: vscode.ExtensionContext) { }) .on('error', reject) .listen(ipcAddress, () => resolve(s)); - }).catch(err => { - console.error(err); - return undefined; }); - return await server; -} - /** * Destroys the auto-attach server, if it's running. */ From 0fc9b2574605cc8356e7bee1d5850564807a2cd6 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 30 Sep 2020 16:40:30 -0700 Subject: [PATCH 0101/1882] fix #107763. --- .../notebook/browser/contrib/fold/folding.ts | 3 +++ .../browser/viewModel/notebookViewModel.ts | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts index 9be776cc1081f..0e4f30a001d95 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts @@ -223,6 +223,9 @@ registerAction2(class extends Action2 { } else { controller.setFoldingStateDown(index, CellFoldingState.Collapsed, levels); } + + const viewIndex = editor.viewModel!.getNearestVisibleCellIndexUpwards(index); + editor.selectElement(editor.viewModel!.viewCells[viewIndex]); } } }); diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 7a3f1fdcccdee..792aa02eabf2a 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -460,6 +460,30 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return this._viewCells.indexOf(cell as CellViewModel); } + /** + * If this._viewCells[index] is visible then return index + */ + getNearestVisibleCellIndexUpwards(index: number) { + for (let i = this._hiddenRanges.length - 1; i >= 0; i--) { + const cellRange = this._hiddenRanges[i]; + const foldStart = cellRange.start - 1; + const foldEnd = cellRange.end; + + if (foldStart > index) { + continue; + } + + if (foldStart <= index && foldEnd >= index) { + return index; + } + + // foldStart <= index, foldEnd < index + break; + } + + return index; + } + getNextVisibleCellIndex(index: number) { for (let i = 0; i < this._hiddenRanges.length; i++) { const cellRange = this._hiddenRanges[i]; From 228973889bc0e352f7cd11cc14755893f8d81864 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 30 Sep 2020 18:16:28 -0700 Subject: [PATCH 0102/1882] Revert "Allow updating the title of a composite even if it is not active (#106892)" This reverts commit 2ac15236ea0940c34a3367ea4d63de09596ffc32. --- src/vs/workbench/browser/parts/compositePart.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 556c347822dcb..84de90962516f 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -284,14 +284,13 @@ export abstract class CompositePart extends Part { protected onTitleAreaUpdate(compositeId: string): void { - // Title - const compositeItem = this.instantiatedCompositeItems.get(compositeId); - if (compositeItem) { - this.updateTitle(compositeItem.composite.getId(), compositeItem.composite.getTitle()); - } - - // Actions + // Active Composite if (this.activeComposite && this.activeComposite.getId() === compositeId) { + + // Title + this.updateTitle(this.activeComposite.getId(), this.activeComposite.getTitle()); + + // Actions const actionsBinding = this.collectCompositeActions(this.activeComposite); this.mapActionsBindingToComposite.set(this.activeComposite.getId(), actionsBinding); actionsBinding(); From 19cdb0e25fd4f26ebed049aecac2294ed3a391f4 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Thu, 1 Oct 2020 01:17:02 -0400 Subject: [PATCH 0103/1882] Bumps version of github-browser --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 669660099a34f..4a347b143a6f8 100644 --- a/product.json +++ b/product.json @@ -123,7 +123,7 @@ "webBuiltInExtensions": [ { "name": "ms-vscode.github-browser", - "version": "0.0.12", + "version": "0.0.13", "repo": "https://github.com/microsoft/vscode-github-browser", "metadata": { "id": "c1bcff4b-4ecb-466e-b8f6-b02788b5fb5a", From fd4d8ddc1994c16583b12e12b38ba48283c2102e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 1 Oct 2020 08:07:38 +0200 Subject: [PATCH 0104/1882] fix #106847 --- .../contrib/output/common/outputLinkComputer.ts | 2 +- .../test/browser/outputLinkProvider.test.ts | 17 +++++++++++++++++ .../common/textFileEditorModelManager.ts | 1 - 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/output/common/outputLinkComputer.ts b/src/vs/workbench/contrib/output/common/outputLinkComputer.ts index f9e6036bc3d57..e532dbc50c2f4 100644 --- a/src/vs/workbench/contrib/output/common/outputLinkComputer.ts +++ b/src/vs/workbench/contrib/output/common/outputLinkComputer.ts @@ -155,7 +155,7 @@ export class OutputLinkComputer { const fullMatch = strings.rtrim(match[0], '.'); // remove trailing "." that likely indicate end of sentence const index = line.indexOf(fullMatch, offset); - offset += index + fullMatch.length; + offset = index + fullMatch.length; const linkRange = { startColumn: index + 1, diff --git a/src/vs/workbench/contrib/output/test/browser/outputLinkProvider.test.ts b/src/vs/workbench/contrib/output/test/browser/outputLinkProvider.test.ts index a53b8c389e737..49f91eb199242 100644 --- a/src/vs/workbench/contrib/output/test/browser/outputLinkProvider.test.ts +++ b/src/vs/workbench/contrib/output/test/browser/outputLinkProvider.test.ts @@ -280,4 +280,21 @@ suite('OutputLinkProvider', () => { assert.equal(result[0].range.startColumn, 6); assert.equal(result[0].range.endColumn, 86); }); + + test('OutputLinkProvider - #106847', function () { + const rootFolder = isWindows ? URI.file('C:\\Users\\bpasero\\Desktop\\test-ts') : + URI.file('C:/Users/bpasero/Desktop'); + + let patterns = OutputLinkComputer.createPatterns(rootFolder); + + let contextService = new TestContextService(); + + let line = toOSPath('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa C:\\Users\\bpasero\\Desktop\\test-ts\\prj.conf C:\\Users\\bpasero\\Desktop\\test-ts\\prj.conf C:\\Users\\bpasero\\Desktop\\test-ts\\prj.conf'); + let result = OutputLinkComputer.detectLinks(line, 1, patterns, contextService); + assert.equal(result.length, 3); + + for (const res of result) { + assert.ok(res.range.startColumn > 0 && res.range.endColumn > 0); + } + }); }); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index ce6e47faf2f51..b1614cc6d9932 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -185,7 +185,6 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE snapshot: sourceModel.isDirty() ? sourceModel.createSnapshot() : undefined }); } - } } From 3254ba153cf812ee172863d7cb1c19965533abaf Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 1 Oct 2020 09:27:44 +0200 Subject: [PATCH 0105/1882] Revert "more logging for https://github.com/microsoft/vscode/issues/100524" This reverts commit 933e3fa613c85e62a0690e46526eb9a06aa5ee29. --- .../decorations/browser/decorationsService.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index 7ab5ba02ff5a4..301187f332b67 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -208,8 +208,7 @@ class DecorationProviderWrapper { constructor( readonly provider: IDecorationsProvider, private readonly _uriEmitter: Emitter, - private readonly _flushEmitter: Emitter, - @ILogService private readonly _logService: ILogService, + private readonly _flushEmitter: Emitter ) { this._dispoable = this.provider.onDidChange(uris => { if (!uris) { @@ -239,17 +238,16 @@ class DecorationProviderWrapper { } getOrRetrieve(uri: URI, includeChildren: boolean, callback: (data: IDecorationData, isChild: boolean) => void): void { + let item = this.data.get(uri); if (item === undefined) { // unknown -> trigger request - this._logService.trace('[Decorations] getOrRetrieve -> FETCH', this.provider.label, uri); item = this._fetchData(uri); } if (item && !(item instanceof DecorationDataRequest)) { // found something (which isn't pending anymore) - this._logService.trace('[Decorations] getOrRetrieve -> RESULT', this.provider.label, uri); callback(item, false); } @@ -259,7 +257,6 @@ class DecorationProviderWrapper { if (iter) { for (let item = iter.next(); !item.done; item = iter.next()) { if (item.value && !(item.value instanceof DecorationDataRequest)) { - this._logService.trace('[Decorations] getOrRetrieve -> RESULT (children)', this.provider.label, uri); callback(item.value, true); } } @@ -272,7 +269,6 @@ class DecorationProviderWrapper { // check for pending request and cancel it const pendingRequest = this.data.get(uri); if (pendingRequest instanceof DecorationDataRequest) { - this._logService.trace('[Decorations] fetchData -> CANCEL previous', this.provider.label, uri); pendingRequest.source.cancel(); this.data.delete(uri); } @@ -301,7 +297,6 @@ class DecorationProviderWrapper { } private _keepItem(uri: URI, data: IDecorationData | undefined): IDecorationData | null { - this._logService.trace('[Decorations] keepItem -> CANCEL previous', this.provider.label, uri, data); const deco = data ? data : null; const old = this.data.set(uri, deco); if (deco || old) { @@ -348,8 +343,7 @@ export class DecorationsService implements IDecorationsService { const wrapper = new DecorationProviderWrapper( provider, this._onDidChangeDecorationsDelayed, - this._onDidChangeDecorations, - this._logService + this._onDidChangeDecorations ); const remove = this._data.push(wrapper); From 41cbd15c3236d78e2bd17d798a4fc663e49375e2 Mon Sep 17 00:00:00 2001 From: msftwindowslinux <68022544+msftwindowslinux@users.noreply.github.com> Date: Thu, 1 Oct 2020 03:17:49 -0500 Subject: [PATCH 0106/1882] force .exe, .dll as binary to fix CRLF/LF problems (#107862) Co-authored-by: Your Name --- build/.gitattributes | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/.gitattributes b/build/.gitattributes index fcadb2cf97913..d2690a8f26ffc 100644 --- a/build/.gitattributes +++ b/build/.gitattributes @@ -1 +1,3 @@ * text eol=lf +*.exe binary +*.dll binary From 7c20f5057a12fd5e87399015d2869b09001548b9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 1 Oct 2020 10:21:51 +0200 Subject: [PATCH 0107/1882] update ref viewlet --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 4a347b143a6f8..87b8182cb90a3 100644 --- a/product.json +++ b/product.json @@ -61,7 +61,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.67", + "version": "0.0.68", "repo": "https://github.com/microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", From 35d4bbf59214f01250d76343b827af7ba5731677 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 1 Oct 2020 10:43:13 +0200 Subject: [PATCH 0108/1882] fix https://github.com/microsoft/vscode/issues/106215 --- .../workbench/contrib/notebook/browser/notebookEditorWidget.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index f555ab0c52f84..4639ac14ca4e3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -703,6 +703,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor lineNumber: selection.startLineNumber, column: selection.startColumn }); + await this.revealLineInCenterIfOutsideViewportAsync(cell, selection.startLineNumber); } if (!cellOptions.options?.preserveFocus) { editor.focus(); From 781e0e3192012b56f585cf5858686d7825a5d6c2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 1 Oct 2020 11:36:45 +0200 Subject: [PATCH 0109/1882] windows - remove not needed and confusing disable-restore-windows CLI arg --- src/vs/platform/environment/common/argv.ts | 1 - src/vs/platform/environment/node/argv.ts | 1 - src/vs/platform/windows/electron-main/windowsMainService.ts | 2 +- test/automation/src/code.ts | 1 - test/smoke/src/areas/multiroot/multiroot.test.ts | 2 +- 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index db19acb10c785..2ac99a2120ec4 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -56,7 +56,6 @@ export interface NativeParsedArgs { 'enable-proposed-api'?: string[]; // undefined or array of 1 or more 'open-url'?: boolean; 'skip-release-notes'?: boolean; - 'disable-restore-windows'?: boolean; 'disable-telemetry'?: boolean; 'export-default-configuration'?: string; 'install-source'?: string; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index fba8ce9fa295d..662547909e57b 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -90,7 +90,6 @@ export const OPTIONS: OptionDescriptions> = { 'driver': { type: 'string' }, 'logExtensionHostCommunication': { type: 'boolean' }, 'skip-release-notes': { type: 'boolean' }, - 'disable-restore-windows': { type: 'boolean' }, 'disable-telemetry': { type: 'boolean' }, 'disable-updates': { type: 'boolean' }, 'disable-crash-reporter': { type: 'boolean' }, diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 22bbc942ccaee..cce5a30a96397 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -433,7 +433,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // These are windows to restore because of hot-exit or from previous session (only performed once on startup!) // let workspacesToRestore: IWorkspacePathToOpen[] = []; - if (openConfig.initialStartup && !openConfig.cli.extensionDevelopmentPath && !openConfig.cli['disable-restore-windows']) { + if (openConfig.initialStartup && !openConfig.cli.extensionDevelopmentPath) { // Untitled workspaces are always restored workspacesToRestore = this.workspacesMainService.getUntitledWorkspacesSync(); diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index ba7249f6b6739..6307cc3263b4d 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -141,7 +141,6 @@ export async function spawn(options: SpawnOptions): Promise { '--disable-crash-reporter', `--extensions-dir=${options.extensionsPath}`, `--user-data-dir=${options.userDataDir}`, - `--disable-restore-windows`, '--driver', handle ]; diff --git a/test/smoke/src/areas/multiroot/multiroot.test.ts b/test/smoke/src/areas/multiroot/multiroot.test.ts index 604d0f5a2df1d..3c11cd5c553d0 100644 --- a/test/smoke/src/areas/multiroot/multiroot.test.ts +++ b/test/smoke/src/areas/multiroot/multiroot.test.ts @@ -40,7 +40,7 @@ export function setup() { // restart with preventing additional windows from restoring // to ensure the window after restart is the multi-root workspace - await app.restart({ workspaceOrFolder: workspaceFilePath, extraArgs: ['--disable-restore-windows'] }); + await app.restart({ workspaceOrFolder: workspaceFilePath }); }); it('shows results from all folders', async function () { From f29f23f34f7adfb54426d6086d2dddef11f19cd2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 1 Oct 2020 11:40:53 +0200 Subject: [PATCH 0110/1882] fix #99492 --- .../platform/windows/electron-main/windowsMainService.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index cce5a30a96397..6813c6aa70f3e 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -391,7 +391,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic const foldersToAdd: IFolderPathToOpen[] = []; const foldersToOpen: IFolderPathToOpen[] = []; const workspacesToOpen: IWorkspacePathToOpen[] = []; + const workspacesToRestore: IWorkspacePathToOpen[] = []; const emptyToRestore: IEmptyWindowBackupInfo[] = []; // empty windows with backupPath + let emptyToOpen: number = 0; let fileInputs: IFileInputs | undefined; // collect all file inputs for (const path of pathsToOpen) { @@ -432,11 +434,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // // These are windows to restore because of hot-exit or from previous session (only performed once on startup!) // - let workspacesToRestore: IWorkspacePathToOpen[] = []; - if (openConfig.initialStartup && !openConfig.cli.extensionDevelopmentPath) { + if (openConfig.initialStartup) { // Untitled workspaces are always restored - workspacesToRestore = this.workspacesMainService.getUntitledWorkspacesSync(); + workspacesToRestore.push(...this.workspacesMainService.getUntitledWorkspacesSync()); workspacesToOpen.push(...workspacesToRestore); // Empty windows with backups are always restored From a7caa978408b4a6198a03185ff8941d1b5f3df2b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 1 Oct 2020 12:02:09 +0200 Subject: [PATCH 0111/1882] OSColorScheme: Separate dark and hc --- src/vs/code/electron-main/window.ts | 6 ++++-- src/vs/platform/native/common/native.ts | 5 ++--- .../electron-main/nativeHostMainService.ts | 19 ++++++------------ src/vs/platform/windows/common/windows.ts | 8 ++++++-- .../environment/browser/environmentService.ts | 3 +-- .../browser/browserHostColorSchemeService.ts | 20 +++++++++++-------- .../themes/browser/workbenchThemeService.ts | 8 ++------ .../themes/common/hostColorSchemeService.ts | 4 ++-- .../nativeHostColorSchemeService.ts | 11 +++++----- .../electron-browser/workbenchTestServices.ts | 3 +-- 10 files changed, 41 insertions(+), 46 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 892a1ef024a40..08934128694c2 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -35,7 +35,6 @@ import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifec import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; import { IFileService } from 'vs/platform/files/common/files'; import { FileAccess, Schemas } from 'vs/base/common/network'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; export interface IWindowCreationOptions { state: IWindowState; @@ -784,7 +783,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { windowConfiguration.fullscreen = this.isFullScreen; // Set Accessibility Config - windowConfiguration.colorScheme = (nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors) ? ColorScheme.HIGH_CONTRAST : nativeTheme.shouldUseDarkColors ? ColorScheme.DARK : ColorScheme.LIGHT; + windowConfiguration.colorScheme = { + dark: nativeTheme.shouldUseDarkColors, + highContrast: nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors + }; windowConfiguration.autoDetectHighContrast = windowConfig?.autoDetectHighContrast ?? true; windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled; diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index e25232fe80625..9f9507e191f2b 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -5,10 +5,9 @@ import { Event } from 'vs/base/common/event'; import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue, MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; -import { IOpenedWindow, IWindowOpenable, IOpenEmptyWindowOptions, IOpenWindowOptions } from 'vs/platform/windows/common/windows'; +import { IOpenedWindow, IWindowOpenable, IOpenEmptyWindowOptions, IOpenWindowOptions, IColorScheme } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; import { URI } from 'vs/base/common/uri'; export interface ICPUProperties { @@ -48,7 +47,7 @@ export interface ICommonNativeHostService { readonly onOSResume: Event; - readonly onColorSchemeChange: Event; + readonly onColorSchemeChange: Event; // Window getWindows(): Promise; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 09723f15ee9f5..92421779ea5a0 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -8,7 +8,7 @@ import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-m import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, Menu, BrowserWindow, app, clipboard, powerMonitor, nativeTheme } from 'electron'; import { OpenContext } from 'vs/platform/windows/node/window'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { IOpenedWindow, IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; +import { IOpenedWindow, IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions, IColorScheme } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { isMacintosh, isWindows, isRootUser, isLinux } from 'vs/base/common/platform'; import { ICommonNativeHostService, IOSProperties, IOSStatistics } from 'vs/platform/native/common/native'; @@ -22,7 +22,6 @@ import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; import { arch, totalmem, release, platform, type, loadavg, freemem, cpus } from 'os'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; import { virtualMachineHint } from 'vs/base/node/id'; import { ILogService } from 'vs/platform/log/common/log'; import { dirname, join } from 'vs/base/common/path'; @@ -52,16 +51,10 @@ export class NativeHostMainService implements INativeHostMainService { // Color Scheme changes nativeTheme.on('updated', () => { - let colorScheme: ColorScheme; - if (nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors) { - colorScheme = ColorScheme.HIGH_CONTRAST; - } else if (nativeTheme.shouldUseDarkColors) { - colorScheme = ColorScheme.DARK; - } else { - colorScheme = ColorScheme.LIGHT; - } - - this._onColorSchemeChange.fire(colorScheme); + this._onColorSchemeChange.fire({ + highContrast: nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors, + dark: nativeTheme.shouldUseDarkColors + }); }); } @@ -87,7 +80,7 @@ export class NativeHostMainService implements INativeHostMainService { readonly onOSResume = Event.fromNodeEventEmitter(powerMonitor, 'resume'); - private readonly _onColorSchemeChange = new Emitter(); + private readonly _onColorSchemeChange = new Emitter(); readonly onColorSchemeChange = this._onColorSchemeChange.event; //#endregion diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index edad2e03f792e..95c57f1f7f131 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -11,7 +11,6 @@ import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platf import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { LogLevel } from 'vs/platform/log/common/log'; import { ExportData } from 'vs/base/common/performance'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; export const WindowMinimumSize = { WIDTH: 400, @@ -213,12 +212,17 @@ export interface INativeRunKeybindingInWindowRequest { userSettingsLabel: string; } +export interface IColorScheme { + dark: boolean; + highContrast: boolean; +} + export interface IWindowConfiguration { sessionId: string; remoteAuthority?: string; - colorScheme: ColorScheme; + colorScheme: IColorScheme; autoDetectHighContrast?: boolean; filesToOpenOrCreate?: IPath[]; diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index ecff5e060e855..1b211fca53828 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -15,7 +15,6 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { memoize } from 'vs/base/common/decorators'; import { onUnexpectedError } from 'vs/base/common/errors'; import { parseLineAndColumnAware } from 'vs/base/common/extpath'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; class BrowserWorkbenchConfiguration implements IWindowConfiguration { @@ -72,7 +71,7 @@ class BrowserWorkbenchConfiguration implements IWindowConfiguration { } get colorScheme() { - return ColorScheme.LIGHT; + return { dark: false, highContrast: false }; } } diff --git a/src/vs/workbench/services/themes/browser/browserHostColorSchemeService.ts b/src/vs/workbench/services/themes/browser/browserHostColorSchemeService.ts index 086043eb04100..f3ce7567fa4ec 100644 --- a/src/vs/workbench/services/themes/browser/browserHostColorSchemeService.ts +++ b/src/vs/workbench/services/themes/browser/browserHostColorSchemeService.ts @@ -8,7 +8,6 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IHostColorSchemeService } from 'vs/workbench/services/themes/common/hostColorSchemeService'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; export class BrowserHostColorSchemeService extends Disposable implements IHostColorSchemeService { @@ -38,15 +37,20 @@ export class BrowserHostColorSchemeService extends Disposable implements IHostCo return this._onDidSchemeChangeEvent.event; } - get colorScheme(): ColorScheme { - if (window.matchMedia(`(forced-colors: active)`).matches) { - return ColorScheme.HIGH_CONTRAST; - } else if (window.matchMedia(`(prefers-color-scheme: light)`).matches) { - return ColorScheme.LIGHT; + get dark(): boolean { + if (window.matchMedia(`(prefers-color-scheme: light)`).matches) { + return false; } else if (window.matchMedia(`(prefers-color-scheme: dark)`).matches) { - return ColorScheme.DARK; + return true; + } + return this.environmentService.configuration.colorScheme.dark; + } + + get highContrast(): boolean { + if (window.matchMedia(`(forced-colors: active)`).matches) { + return true; } - return this.environmentService.configuration.colorScheme; + return this.environmentService.configuration.colorScheme.highContrast; } } diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 1e763a8786a55..4cc6db3160caf 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -363,15 +363,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private getPreferredColorScheme(): ColorScheme | undefined { - const detectHCThemeSetting = this.configurationService.getValue(ThemeSettings.DETECT_HC); - if (this.hostColorService.colorScheme === ColorScheme.HIGH_CONTRAST && detectHCThemeSetting) { + if (this.configurationService.getValue(ThemeSettings.DETECT_HC) && this.hostColorService.highContrast) { return ColorScheme.HIGH_CONTRAST; } if (this.configurationService.getValue(ThemeSettings.DETECT_COLOR_SCHEME)) { - const osScheme = this.hostColorService.colorScheme; - if (osScheme !== ColorScheme.HIGH_CONTRAST) { - return osScheme; - } + return this.hostColorService.dark ? ColorScheme.DARK : ColorScheme.LIGHT; } return undefined; } diff --git a/src/vs/workbench/services/themes/common/hostColorSchemeService.ts b/src/vs/workbench/services/themes/common/hostColorSchemeService.ts index a1fac0124e746..65428bf420b05 100644 --- a/src/vs/workbench/services/themes/common/hostColorSchemeService.ts +++ b/src/vs/workbench/services/themes/common/hostColorSchemeService.ts @@ -5,7 +5,6 @@ import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; export const IHostColorSchemeService = createDecorator('hostColorSchemeService'); @@ -13,7 +12,8 @@ export interface IHostColorSchemeService { readonly _serviceBrand: undefined; - readonly colorScheme: ColorScheme; + readonly dark: boolean; + readonly highContrast: boolean; readonly onDidChangeColorScheme: Event; } diff --git a/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts b/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts index f495c43e6175d..51c20c73bfbc3 100644 --- a/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts +++ b/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts @@ -8,7 +8,6 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { Disposable } from 'vs/base/common/lifecycle'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; import { IHostColorSchemeService } from 'vs/workbench/services/themes/common/hostColorSchemeService'; export class NativeHostColorSchemeService extends Disposable implements IHostColorSchemeService { @@ -27,9 +26,9 @@ export class NativeHostColorSchemeService extends Disposable implements IHostCol private registerListeners(): void { // Color Scheme - this._register(this.nativeHostService.onColorSchemeChange(scheme => { - this._colorScheme = scheme; - + this._register(this.nativeHostService.onColorSchemeChange(({ highContrast, dark }) => { + this.dark = dark; + this.highContrast = highContrast; this._onDidChangeColorScheme.fire(); })); } @@ -37,8 +36,8 @@ export class NativeHostColorSchemeService extends Disposable implements IHostCol private readonly _onDidChangeColorScheme = this._register(new Emitter()); readonly onDidChangeColorScheme = this._onDidChangeColorScheme.event; - private _colorScheme: ColorScheme = this.environmentService.configuration.colorScheme; - get colorScheme() { return this._colorScheme; } + public dark: boolean = this.environmentService.configuration.colorScheme.dark; + public highContrast: boolean = this.environmentService.configuration.colorScheme.highContrast; } diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 0370252d1f471..d3fa18f6d169f 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -40,7 +40,6 @@ import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/ur import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IOSProperties, IOSStatistics } from 'vs/platform/native/common/native'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; import { homedir } from 'os'; export const TestWorkbenchConfiguration: INativeWorkbenchConfiguration = { @@ -54,7 +53,7 @@ export const TestWorkbenchConfiguration: INativeWorkbenchConfiguration = { userEnv: {}, execPath: process.execPath, perfEntries: [], - colorScheme: ColorScheme.DARK, + colorScheme: { dark: true, highContrast: false }, ...parseArgs(process.argv, OPTIONS) }; From 4f77d34a08f9a37bfcc86e64d6b0dbdd7def5dca Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 1 Oct 2020 12:21:23 +0200 Subject: [PATCH 0112/1882] editors - let the diff editor compute a good name --- src/vs/workbench/api/browser/mainThreadEditors.ts | 5 ----- .../services/editor/browser/editorService.ts | 11 +++-------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index adfd675a463f9..738493d397d18 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; import { disposed } from 'vs/base/common/errors'; import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { equals as objectEquals } from 'vs/base/common/objects'; @@ -348,10 +347,6 @@ CommandsRegistry.registerCommand('_workbench.diff', async function (accessor: Se }; } - if (!label) { - label = localize('diffLeftRightLabel', "{0} ⟷ {1}", leftResource.toString(true), rightResource.toString(true)); - } - await editorService.openEditor({ leftResource, rightResource, label, description, options }, viewColumnToEditorGroup(editorGroupService, position)); }); diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 02bdd13b06d6b..b0b177e0aca05 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -818,7 +818,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { const rightInput = this.createEditorInput({ resource: resourceDiffInput.rightResource, forceFile: resourceDiffInput.forceFile }); return new DiffEditorInput( - resourceDiffInput.label || this.toSideBySideLabel(leftInput, rightInput, '↔'), + resourceDiffInput.label || this.toSideBySideLabel(leftInput, rightInput), resourceDiffInput.description, leftInput, rightInput @@ -968,18 +968,13 @@ export class EditorService extends Disposable implements EditorServiceImpl { return input; } - private toSideBySideLabel(leftInput: EditorInput, rightInput: EditorInput, divider: string): string | undefined { - - // Without any resource, do not try to compute a label - if (!leftInput.resource || !rightInput.resource) { - return undefined; - } + private toSideBySideLabel(leftInput: EditorInput, rightInput: EditorInput): string | undefined { // If both editors are file inputs, we produce an optimized label // by adding the relative path of both inputs to the label. This // makes it easier to understand a file-based comparison. if (this.fileEditorInputFactory.isFileEditorInput(leftInput) && this.fileEditorInputFactory.isFileEditorInput(rightInput)) { - return `${this.labelService.getUriLabel(leftInput.preferredResource, { relative: true })} ${divider} ${this.labelService.getUriLabel(rightInput.preferredResource, { relative: true })}`; + return `${this.labelService.getUriLabel(leftInput.preferredResource, { relative: true })} ↔ ${this.labelService.getUriLabel(rightInput.preferredResource, { relative: true })}`; } // Signal back that the label should be computed from within the editor From 9d9afaacfb0362b15c3828a19f056cfd428d6fc5 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 1 Oct 2020 12:30:25 +0200 Subject: [PATCH 0113/1882] fixes #107520 --- src/vs/workbench/contrib/debug/browser/baseDebugView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index e449ec3836d4e..f9bf6a70990dd 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -159,6 +159,7 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer Date: Thu, 1 Oct 2020 13:21:25 +0200 Subject: [PATCH 0114/1882] #107695 --- src/vs/workbench/contrib/debug/browser/debugHover.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 2bad9d0895b5a..92abeb7dc6550 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -285,8 +285,6 @@ export class DebugHoverWidget implements IContentWidget { this.complexValueTitle.textContent = expression.value; this.complexValueTitle.title = expression.value; this.layoutTreeAndContainer(true); - this.editor.layoutContentWidget(this); - this.scrollbar.scanDomNode(); this.tree.scrollTop = 0; this.tree.scrollLeft = 0; this.complexValueContainer.hidden = false; @@ -302,6 +300,8 @@ export class DebugHoverWidget implements IContentWidget { const treeHeight = Math.min(this.editor.getLayoutInfo().height * 0.7, this.tree.contentHeight + scrollBarHeight); this.treeContainer.style.height = `${treeHeight}px`; this.tree.layout(treeHeight, initialLayout ? 400 : undefined); + this.editor.layoutContentWidget(this); + this.scrollbar.scanDomNode(); } hide(): void { From 14788f2b45ed37727fe1a9add514e446ac8823c4 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 1 Oct 2020 13:30:06 +0200 Subject: [PATCH 0115/1882] Handle errors on sockets in tunnel service Part of microsoft/vscode-remote-release#3561 --- src/vs/platform/remote/node/tunnelService.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/remote/node/tunnelService.ts b/src/vs/platform/remote/node/tunnelService.ts index c8d84bdeff4b8..0a27aabb5e526 100644 --- a/src/vs/platform/remote/node/tunnelService.ts +++ b/src/vs/platform/remote/node/tunnelService.ts @@ -107,10 +107,17 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel { this._socketsDispose.delete(localSocket.localAddress); remoteSocket.end(); }); - localSocket.on('close', () => remoteSocket.end()); + localSocket.on('error', () => { + this._socketsDispose.delete(localSocket.localAddress); + remoteSocket.destroy(); + }); + remoteSocket.on('end', () => localSocket.end()); remoteSocket.on('close', () => localSocket.end()); + remoteSocket.on('error', () => { + localSocket.destroy(); + }); localSocket.pipe(remoteSocket); remoteSocket.pipe(localSocket); From 3565fba49a338cfa1050bfa71c5bd1d9e13c7385 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 1 Oct 2020 13:44:49 +0200 Subject: [PATCH 0116/1882] fixes #96293 --- .../contrib/debug/browser/debugActionViewItems.ts | 4 ++-- .../contrib/debug/browser/debugConfigurationManager.ts | 9 ++++++--- src/vs/workbench/contrib/debug/common/debug.ts | 4 +++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index bb1db4db2c3d4..ec89f5b46193f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -196,7 +196,7 @@ export class StartDebugActionViewItem implements IActionViewItem { } this.providers.forEach(p => { - if (p.provider && p.provider.type === manager.selectedConfiguration.config?.type) { + if (p.provider && p.provider.type === manager.selectedConfiguration.type) { this.selected = this.options.length; } @@ -204,7 +204,7 @@ export class StartDebugActionViewItem implements IActionViewItem { label: `${p.label}...`, handler: async () => { const picked = await p.pick(); if (picked) { - await manager.selectConfiguration(picked.launch, picked.config.name, picked.config); + await manager.selectConfiguration(picked.launch, picked.config.name, picked.config, p.provider?.type); return true; } return false; diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index e0f705c163dc2..fd8ae385b05a1 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -58,6 +58,7 @@ export class ConfigurationManager implements IConfigurationManager { private selectedName: string | undefined; private selectedLaunch: ILaunch | undefined; private selectedConfig: IConfig | undefined; + private selectedType: string | undefined; private toDispose: IDisposable[]; private readonly _onDidSelectConfigurationName = new Emitter(); private configProviders: IDebugConfigurationProvider[]; @@ -493,11 +494,12 @@ export class ConfigurationManager implements IConfigurationManager { return this.launches.find(l => l.workspace && l.workspace.uri.toString() === workspaceUri.toString()); } - get selectedConfiguration(): { launch: ILaunch | undefined, name: string | undefined, config: IConfig | undefined } { + get selectedConfiguration(): { launch: ILaunch | undefined, name: string | undefined, config: IConfig | undefined, type: string | undefined } { return { launch: this.selectedLaunch, name: this.selectedName, - config: this.selectedConfig + config: this.selectedConfig, + type: this.selectedType }; } @@ -555,7 +557,8 @@ export class ConfigurationManager implements IConfigurationManager { } this.selectedConfig = config; - this.storageService.store(DEBUG_SELECTED_TYPE, this.selectedConfig?.type, StorageScope.WORKSPACE); + this.selectedType = type || this.selectedConfig?.type; + this.storageService.store(DEBUG_SELECTED_TYPE, this.selectedType, StorageScope.WORKSPACE); const configForType = this.selectedConfig || (this.selectedLaunch && this.selectedName ? this.selectedLaunch.getConfiguration(this.selectedName) : undefined); if (configForType) { this.debugConfigurationTypeContext.set(configForType.type); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index b594496921b64..4035cc5ba464a 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -671,9 +671,11 @@ export interface IConfigurationManager { launch: ILaunch | undefined; config: IConfig | undefined; name: string | undefined; + // Type is used when matching dynamic configurations to their corresponding provider + type: string | undefined; }; - selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig): Promise; + selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, type?: string): Promise; getLaunches(): ReadonlyArray; From 8ba0dba49d6412fe20e155236f11e4d5cb363821 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 1 Oct 2020 13:56:04 +0200 Subject: [PATCH 0117/1882] Update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6ee3e5a1988a4..ffe37d502896d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.50.0", - "distro": "8b294911aabcfe39d612f32d1d2aa3cd6e9732b3", + "distro": "565621b84e46bd334ef1b1f54538ad82b700df37", "author": { "name": "Microsoft Corporation" }, @@ -193,4 +193,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} \ No newline at end of file +} From a6c0ce8b393898e48e7991991f579223389e4ed8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 1 Oct 2020 12:18:14 +0000 Subject: [PATCH 0118/1882] :lipstick: --- .../services/environment/browser/environmentService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 1b211fca53828..8d5e0fb796661 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -8,7 +8,7 @@ import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; -import { IPath, IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { IColorScheme, IPath, IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchConstructionOptions as IWorkbenchOptions } from 'vs/workbench/workbench.web.api'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -70,7 +70,7 @@ class BrowserWorkbenchConfiguration implements IWindowConfiguration { return undefined; } - get colorScheme() { + get colorScheme(): IColorScheme { return { dark: false, highContrast: false }; } } From 9c53416c7aa968617d97728031979925feca1b85 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 1 Oct 2020 14:39:15 +0200 Subject: [PATCH 0119/1882] fix #107883 --- .../workbench/common/editor/textResourceEditorInput.ts | 2 +- .../workbench/services/editor/browser/editorService.ts | 2 +- .../services/textfile/browser/textFileService.ts | 6 +++--- src/vs/workbench/services/textfile/common/textfiles.ts | 10 +++++++++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/common/editor/textResourceEditorInput.ts b/src/vs/workbench/common/editor/textResourceEditorInput.ts index 31231c1c6964a..11c643d08d291 100644 --- a/src/vs/workbench/common/editor/textResourceEditorInput.ts +++ b/src/vs/workbench/common/editor/textResourceEditorInput.ts @@ -209,7 +209,7 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput implem // Save / Save As let target: URI | undefined; if (saveAs) { - target = await this.textFileService.saveAs(this.resource, undefined, options); + target = await this.textFileService.saveAs(this.resource, undefined, { ...options, suggestedTarget: this.preferredResource }); } else { target = await this.textFileService.save(this.resource, options); } diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index b0b177e0aca05..c61fcfbe6a8b5 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -489,7 +489,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#endregion - //#region preventOpenEditor() + //#region editor overrides private readonly openEditorHandlers: IOpenEditorOverrideHandler[] = []; diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 0c9b41e53e9dc..a413b65b630a5 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; -import { ITextFileService, ITextFileStreamContent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, ITextFileSaveOptions, ITextFileEditorModelManager, IResourceEncoding, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ITextFileStreamContent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, ITextFileSaveOptions, ITextFileEditorModelManager, IResourceEncoding, stringToSnapshot, ITextFileSaveAsOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { IRevertOptions, IEncodingSupport } from 'vs/workbench/common/editor'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IFileService, FileOperationError, FileOperationResult, IFileStatWithMetadata, ICreateFileOptions, IFileStreamContent } from 'vs/platform/files/common/files'; @@ -219,11 +219,11 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return undefined; } - async saveAs(source: URI, target?: URI, options?: ITextFileSaveOptions): Promise { + async saveAs(source: URI, target?: URI, options?: ITextFileSaveAsOptions): Promise { // Get to target resource if (!target) { - target = await this.fileDialogService.pickFileToSave(await this.suggestSavePath(source), options?.availableFileSystems); + target = await this.fileDialogService.pickFileToSave(await this.suggestSavePath(options?.suggestedTarget ?? source), options?.availableFileSystems); } if (!target) { diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 1f9e42af4ddc1..782d1a91ceb20 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -65,7 +65,7 @@ export interface ITextFileService extends IDisposable { * @param options optional save options * @return Path of the saved resource or undefined if canceled. */ - saveAs(resource: URI, targetResource?: URI, options?: ITextFileSaveOptions): Promise; + saveAs(resource: URI, targetResource?: URI, options?: ITextFileSaveAsOptions): Promise; /** * Reverts the provided resource. @@ -386,6 +386,14 @@ export interface ITextFileSaveOptions extends ISaveOptions { ignoreErrorHandler?: boolean; } +export interface ITextFileSaveAsOptions extends ITextFileSaveOptions { + + /** + * Optional URI to use as suggested file path to save as. + */ + suggestedTarget?: URI; +} + export interface ITextFileLoadOptions { /** From 60f19d593986ef3eacee55daf75798ff297e2ef8 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 1 Oct 2020 14:55:16 +0200 Subject: [PATCH 0120/1882] Fix code hover span margin Fixes #107849 --- src/vs/base/browser/ui/hover/hover.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/hover/hover.css b/src/vs/base/browser/ui/hover/hover.css index f961071b82073..a5390be414d9b 100644 --- a/src/vs/base/browser/ui/hover/hover.css +++ b/src/vs/base/browser/ui/hover/hover.css @@ -133,7 +133,7 @@ } /** Spans in markdown hovers need a margin-bottom to avoid looking cramped: https://github.com/microsoft/vscode/issues/101496 **/ -.monaco-hover .markdown-hover .hover-contents span { +.monaco-hover .markdown-hover .hover-contents:not(.code-hover-contents) span { margin-bottom: 4px; display: inline-block; } From 0579e831389c977e3f51981b8b59f524f510df89 Mon Sep 17 00:00:00 2001 From: HiDeo Date: Thu, 1 Oct 2020 15:00:07 +0200 Subject: [PATCH 0121/1882] Fix various typos --- src/vs/vscode.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 8c190ce7ef27e..f20e79170fe1b 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -6933,7 +6933,7 @@ declare module 'vscode' { /** * Fired when the webview content posts a message. * - * Webview content can post strings or json serilizable objects back to a VS Code extension. They cannot + * Webview content can post strings or json serializable objects back to a VS Code extension. They cannot * post `Blob`, `File`, `ImageData` and other DOM specific objects since the extension that receives the * message does not run in a browser environment. */ @@ -6945,7 +6945,7 @@ declare module 'vscode' { * Messages are only delivered if the webview is live (either visible or in the * background with `retainContextWhenHidden`). * - * @param message Body of the message. This must be a string or other json serilizable object. + * @param message Body of the message. This must be a string or other json serializable object. */ postMessage(message: any): Thenable; From da13bb75621520267be23109b0341bf5d909585f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 1 Oct 2020 15:38:11 +0200 Subject: [PATCH 0122/1882] todo-tag --- src/vs/base/common/map.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 0b8c7460f7962..76a9129d26b32 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -150,7 +150,7 @@ export class UriIterator implements IKeyIterator { this._states.push(UriIteratorState.Authority); } if (this._value.path) { - //todo@jrieken the case-sensitive logic is copied form `resources.ts#hasToIgnoreCase` + //todo@jrieken #107886 the case-sensitive logic is copied form `resources.ts#hasToIgnoreCase` // which cannot be used because it depends on this const caseSensitive = key.scheme === Schemas.file && isLinux; this._pathIterator = new PathIterator(false, caseSensitive); From 87d7c6dd51aef587c9ac0d543a86609438223fce Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 1 Oct 2020 15:44:58 +0200 Subject: [PATCH 0123/1882] Listener leak when scrolling PULL REQUESTS view Fixes #107801 --- src/vs/platform/contextkey/browser/contextKeyService.ts | 9 +++++++-- src/vs/platform/contextkey/common/contextkey.ts | 2 +- src/vs/workbench/contrib/views/browser/treeView.ts | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index 3006ed42c917f..87593130eaf2f 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -328,7 +328,7 @@ export abstract class AbstractContextKeyService implements IContextKeyService { public abstract getContextValuesContainer(contextId: number): Context; public abstract createChildContext(parentContextId?: number): number; public abstract disposeContext(contextId: number): void; - public abstract updateParent(parentContextKeyService: IContextKeyService): void; + public abstract updateParent(parentContextKeyService?: IContextKeyService): void; } export class ContextKeyService extends AbstractContextKeyService implements IContextKeyService { @@ -423,6 +423,7 @@ class ScopedContextKeyService extends AbstractContextKeyService { public dispose(): void { this._isDisposed = true; this._parent.disposeContext(this._myContextId); + this._parentChangeListener?.dispose(); if (this._domNode) { this._domNode.removeAttribute(KEYBINDING_CONTEXT_ATTR); this._domNode = undefined; @@ -454,7 +455,11 @@ class ScopedContextKeyService extends AbstractContextKeyService { this._parent.disposeContext(contextId); } - public updateParent(parentContextKeyService: AbstractContextKeyService): void { + public updateParent(parentContextKeyService?: AbstractContextKeyService): void { + if (!parentContextKeyService) { + this._parentChangeListener?.dispose(); + return; + } const thisContainer = this._parent.getContextValuesContainer(this._myContextId); const oldAllValues = thisContainer.collectAllValues(); this._parent = parentContextKeyService; diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index dad27b1742b62..7c53660832335 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -1139,7 +1139,7 @@ export interface IContextKeyService { createScoped(target?: IContextKeyServiceTarget): IContextKeyService; getContext(target: IContextKeyServiceTarget | null): IContext; - updateParent(parentContextKeyService: IContextKeyService): void; + updateParent(parentContextKeyService?: IContextKeyService): void; } export const SET_CONTEXT_COMMAND_ID = 'setContext'; diff --git a/src/vs/workbench/contrib/views/browser/treeView.ts b/src/vs/workbench/contrib/views/browser/treeView.ts index c078819f700c7..613e62662eaea 100644 --- a/src/vs/workbench/contrib/views/browser/treeView.ts +++ b/src/vs/workbench/contrib/views/browser/treeView.ts @@ -1027,7 +1027,9 @@ class TreeMenus extends Disposable implements IDisposable { createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g)); menu.dispose(); - + // When called without a parameter, updateParent will dispose the parent change listener. + // We cannot call dispose on the contextKeyService because it will break submenus. + contextKeyService.updateParent(); return result; } } From bddfa3a73cc4cc6d3986f763a91d8d605b3d3afd Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 1 Oct 2020 16:21:45 +0200 Subject: [PATCH 0124/1882] Don't use simple file dialog for user data Fixes #107882 --- .../services/dialogs/electron-sandbox/fileDialogService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts b/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts index 79b5ea23dc253..a6287ee77bdf9 100644 --- a/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts @@ -58,7 +58,10 @@ export class FileDialogService extends AbstractFileDialogService implements IFil private shouldUseSimplified(schema: string): { useSimplified: boolean, isSetting: boolean } { const setting = (this.configurationService.getValue('files.simpleDialog.enable') === true); const newWindowSetting = (this.configurationService.getValue('window.openFilesInNewWindow') === 'on'); - return { useSimplified: (schema !== Schemas.file) || setting, isSetting: newWindowSetting }; + return { + useSimplified: ((schema !== Schemas.file) && (schema !== Schemas.userData)) || setting, + isSetting: newWindowSetting + }; } async pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { From ade814447c31d10643eb61ed81ad5f84148cc84e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 1 Oct 2020 14:29:52 +0000 Subject: [PATCH 0125/1882] Fix #107889 --- .../browser/extensionRecommendationNotificationService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index 71962e89b81d1..71a028d4d0e66 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -327,6 +327,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec try { this.recommendationSources.push(source); const recommendationsNotification = new RecommendationsNotification(severity, message, choices, this.notificationService); + Event.once(Event.filter(recommendationsNotification.onDidChangeVisibility, e => !e))(() => this.showNextNotification()); if (this.visibleNotification) { const index = this.pendingNotificaitons.length; token.onCancellationRequested(() => this.pendingNotificaitons.splice(index, 1), disposables); @@ -337,7 +338,6 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec } else { this.visibleNotification = { recommendationsNotification, source, from: Date.now() }; recommendationsNotification.show(); - Event.once(Event.filter(recommendationsNotification.onDidChangeVisibility, e => !e))(() => this.showNextNotification()); } await raceCancellation(Event.toPromise(recommendationsNotification.onDidClose), token); return !recommendationsNotification.isCancelled(); @@ -355,6 +355,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec .then(() => { this.unsetVisibileNotification(); if (nextNotificaiton) { + this.visibleNotification = { recommendationsNotification: nextNotificaiton.recommendationsNotification, source: nextNotificaiton.source, from: Date.now() }; nextNotificaiton.recommendationsNotification.show(); } }); From b8e87aae6dca78fe0f8dbffd9d6a34921d9913f4 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 1 Oct 2020 17:35:54 +0200 Subject: [PATCH 0126/1882] fixes #107860 --- .../contrib/debug/browser/debugActionViewItems.ts | 9 ++++++--- src/vs/workbench/contrib/debug/browser/debugToolBar.ts | 2 +- src/vs/workbench/contrib/debug/browser/debugViewlet.ts | 2 +- src/vs/workbench/contrib/debug/browser/repl.ts | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index ec89f5b46193f..97d2519b2970e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -234,6 +234,7 @@ export class StartDebugActionViewItem implements IActionViewItem { export class FocusSessionActionViewItem extends SelectActionViewItem { constructor( action: IAction, + session: IDebugSession | undefined, @IDebugService protected readonly debugService: IDebugService, @IThemeService themeService: IThemeService, @IContextViewService contextViewService: IContextViewService, @@ -262,15 +263,17 @@ export class FocusSessionActionViewItem extends SelectActionViewItem { }); this._register(this.debugService.onDidEndSession(() => this.update())); - this.update(); + this.update(session); } protected getActionContext(_: string, index: number): any { return this.getSessions()[index]; } - private update() { - const session = this.getSelectedSession(); + private update(session?: IDebugSession) { + if (!session) { + session = this.getSelectedSession(); + } const sessions = this.getSessions(); const names = sessions.map(s => { const label = s.getLabel(); diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 5dcee2af03364..93635f7ff1ae0 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -75,7 +75,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { orientation: ActionsOrientation.HORIZONTAL, actionViewItemProvider: (action: IAction) => { if (action.id === FocusSessionAction.ID) { - return this.instantiationService.createInstance(FocusSessionActionViewItem, action); + return this.instantiationService.createInstance(FocusSessionActionViewItem, action, undefined); } else if (action instanceof MenuItemAction) { return this.instantiationService.createInstance(MenuEntryActionViewItem, action); } else if (action instanceof SubmenuItemAction) { diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index 1d805139611c0..f13479c4ec5b2 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -171,7 +171,7 @@ export class DebugViewPaneContainer extends ViewPaneContainer { return this.startDebugActionViewItem; } if (action.id === FocusSessionAction.ID) { - return new FocusSessionActionViewItem(action, this.debugService, this.themeService, this.contextViewService, this.configurationService); + return new FocusSessionActionViewItem(action, undefined, this.debugService, this.themeService, this.contextViewService, this.configurationService); } if (action instanceof MenuItemAction) { return this.instantiationService.createInstance(MenuEntryActionViewItem, action); diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 7fe44b35aaf80..b25915d28d5bc 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -459,7 +459,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { getActionViewItem(action: IAction): IActionViewItem | undefined { if (action.id === SelectReplAction.ID) { - return this.instantiationService.createInstance(SelectReplActionViewItem, this.selectReplAction); + return this.instantiationService.createInstance(SelectReplActionViewItem, this.selectReplAction, this.tree.getInput()); } else if (action.id === FILTER_ACTION_ID) { this.filterActionViewItem = this.instantiationService.createInstance(ReplFilterActionViewItem, action, localize('workbench.debug.filter.placeholder', "Filter (e.g. text, !exclude)"), this.filterState); return this.filterActionViewItem; From 57bc9aef0e23eef835f01553df58dd12946edf98 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 1 Oct 2020 09:43:45 -0700 Subject: [PATCH 0127/1882] Bump actions --- .github/workflows/author-verified.yml | 2 +- .github/workflows/commands.yml | 2 +- .github/workflows/deep-classifier-monitor.yml | 2 +- .github/workflows/deep-classifier-runner.yml | 2 +- .github/workflows/deep-classifier-scraper.yml | 2 +- .github/workflows/english-please.yml | 2 +- .github/workflows/feature-request.yml | 2 +- .github/workflows/latest-release-monitor.yml | 2 +- .github/workflows/locker.yml | 2 +- .github/workflows/needs-more-info-closer.yml | 2 +- .github/workflows/on-label.yml | 2 +- .github/workflows/on-open.yml | 2 +- .github/workflows/release-pipeline-labeler.yml | 2 +- .github/workflows/test-plan-item-validator.yml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/author-verified.yml b/.github/workflows/author-verified.yml index 03d389c5e647c..7b837aff02c88 100644 --- a/.github/workflows/author-verified.yml +++ b/.github/workflows/author-verified.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v35 + ref: v36 path: ./actions - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index f61d114b1579f..519f693178680 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -13,7 +13,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v35 + ref: v36 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Commands diff --git a/.github/workflows/deep-classifier-monitor.yml b/.github/workflows/deep-classifier-monitor.yml index 9d4c54dc6be5b..7b93c32e45b22 100644 --- a/.github/workflows/deep-classifier-monitor.yml +++ b/.github/workflows/deep-classifier-monitor.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v35 + ref: v36 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index 82ae49039fb56..e11b72cdda2b4 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v35 + ref: v36 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-scraper.yml b/.github/workflows/deep-classifier-scraper.yml index 9fe52cd3477f8..340b24d9cd1c4 100644 --- a/.github/workflows/deep-classifier-scraper.yml +++ b/.github/workflows/deep-classifier-scraper.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v35 + ref: v36 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/english-please.yml b/.github/workflows/english-please.yml index 6cf4d65455cc6..24449970f5e76 100644 --- a/.github/workflows/english-please.yml +++ b/.github/workflows/english-please.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v35 + ref: v36 path: ./actions - name: Install Actions if: contains(github.event.issue.labels.*.name, '*english-please') diff --git a/.github/workflows/feature-request.yml b/.github/workflows/feature-request.yml index 6cdf0bd6fc3f7..dbeb6d835a07b 100644 --- a/.github/workflows/feature-request.yml +++ b/.github/workflows/feature-request.yml @@ -18,7 +18,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v35 + ref: v36 - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') run: npm install --production --prefix ./actions diff --git a/.github/workflows/latest-release-monitor.yml b/.github/workflows/latest-release-monitor.yml index 55855054383d1..bcde48936cb3f 100644 --- a/.github/workflows/latest-release-monitor.yml +++ b/.github/workflows/latest-release-monitor.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v35 + ref: v36 - name: Install Actions run: npm install --production --prefix ./actions - name: Install Storage Module diff --git a/.github/workflows/locker.yml b/.github/workflows/locker.yml index 2d21b96b8336c..b014eeb0faacb 100644 --- a/.github/workflows/locker.yml +++ b/.github/workflows/locker.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v35 + ref: v36 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Locker diff --git a/.github/workflows/needs-more-info-closer.yml b/.github/workflows/needs-more-info-closer.yml index dae985ca35ffb..4ae14c392cd4c 100644 --- a/.github/workflows/needs-more-info-closer.yml +++ b/.github/workflows/needs-more-info-closer.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v35 + ref: v36 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Needs More Info Closer diff --git a/.github/workflows/on-label.yml b/.github/workflows/on-label.yml index 60a9b09af014f..ecf4899c3d6ad 100644 --- a/.github/workflows/on-label.yml +++ b/.github/workflows/on-label.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v35 + ref: v36 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index e985909b446b9..2b02cfe7ad710 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v35 + ref: v36 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/release-pipeline-labeler.yml b/.github/workflows/release-pipeline-labeler.yml index a52b895e51c5a..0e9c508e757ec 100644 --- a/.github/workflows/release-pipeline-labeler.yml +++ b/.github/workflows/release-pipeline-labeler.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v35 + ref: v36 path: ./actions - name: Checkout Repo if: github.event_name != 'issues' diff --git a/.github/workflows/test-plan-item-validator.yml b/.github/workflows/test-plan-item-validator.yml index 7a173b0e2f321..3e697189ae11d 100644 --- a/.github/workflows/test-plan-item-validator.yml +++ b/.github/workflows/test-plan-item-validator.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v35 + ref: v36 - name: Install Actions if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') run: npm install --production --prefix ./actions From 49a19e81a722c96bc11d3e6c37ae1dd8ad416061 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 1 Oct 2020 09:56:13 -0700 Subject: [PATCH 0128/1882] fix #106215. --- .../contrib/notebook/browser/view/notebookCellList.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index c2cce19e6e568..19498a1055dc2 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -836,8 +836,8 @@ export class NotebookCellList extends WorkbenchList implements ID this.view.setScrollTop(positionOffset - this.view.renderHeight / 2); // after rendering, it might be pushed down due to markdown cell dynamic height - const elementTop = this.view.elementTop(viewIndex); - this.view.setScrollTop(elementTop - this.view.renderHeight / 2); + const newPositionOffset = this.view.elementTop(viewIndex) + element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); + this.view.setScrollTop(newPositionOffset - this.view.renderHeight / 2); // reveal editor if (!element.editorAttached) { From d23279ddc5d6382029d767b73c2fbf6a356d5495 Mon Sep 17 00:00:00 2001 From: kieferrm Date: Thu, 1 Oct 2020 17:19:10 +0000 Subject: [PATCH 0129/1882] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ffe37d502896d..06831b259324b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.50.0", - "distro": "565621b84e46bd334ef1b1f54538ad82b700df37", + "distro": "66a02247b2916cb7cf8a3b70677e1d798c6527f2", "author": { "name": "Microsoft Corporation" }, From 477eaf554d285b10d00dc0859ae9417ddd0cc67a Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 1 Oct 2020 10:25:57 -0700 Subject: [PATCH 0130/1882] bot: add js debug log commands --- .github/commands.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/commands.json b/.github/commands.json index 12bff691c0680..9ac28360c0b43 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -133,6 +133,13 @@ "action": "updateLabels", "addLabel": "~needs more info" }, + { + "type": "comment", + "name": "jsDebugLogs", + "action": "updateLabels", + "addLabel": "needs more info", + "comment": "Please collect trace logs using the following instructions:\n\n> If you're able to, add `\"trace\": true` to your `launch.json` and reproduce the issue. The location of the log file on your disk will be written to the Debug Console. Share that with us.\n\n> ⚠️ This log file will not contain source code, but will contain file paths. You can drop it into https://microsoft.github.io/vscode-pwa-analyzer/index.html to see what it contains. If you'd rather not share the log publicly, you can email it to connor@xbox.com" + }, { "type": "comment", "name": "closedWith", From 6e55a9b9682d717a00340e747021dc86f480838a Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 1 Oct 2020 10:27:18 -0700 Subject: [PATCH 0131/1882] bot: tweak js debug log comment format --- .github/commands.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/commands.json b/.github/commands.json index 9ac28360c0b43..7a1220f7d4307 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -138,7 +138,7 @@ "name": "jsDebugLogs", "action": "updateLabels", "addLabel": "needs more info", - "comment": "Please collect trace logs using the following instructions:\n\n> If you're able to, add `\"trace\": true` to your `launch.json` and reproduce the issue. The location of the log file on your disk will be written to the Debug Console. Share that with us.\n\n> ⚠️ This log file will not contain source code, but will contain file paths. You can drop it into https://microsoft.github.io/vscode-pwa-analyzer/index.html to see what it contains. If you'd rather not share the log publicly, you can email it to connor@xbox.com" + "comment": "Please collect trace logs using the following instructions:\n\n> If you're able to, add `\"trace\": true` to your `launch.json` and reproduce the issue. The location of the log file on your disk will be written to the Debug Console. Share that with us.\n>\n> ⚠️ This log file will not contain source code, but will contain file paths. You can drop it into https://microsoft.github.io/vscode-pwa-analyzer/index.html to see what it contains. If you'd rather not share the log publicly, you can email it to connor@xbox.com" }, { "type": "comment", From 604d445cc3e9c539090a4a7edf72932da3b4a0df Mon Sep 17 00:00:00 2001 From: Dhaiyra Date: Thu, 1 Oct 2020 23:03:04 +0530 Subject: [PATCH 0132/1882] added preserve case and excluse setting in FindInFile interface --- src/vs/workbench/contrib/search/browser/searchActions.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 574189c1a095e..de9d9a0bdef8c 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -154,12 +154,14 @@ export abstract class FindOrReplaceInFilesAction extends Action { export interface IFindInFilesArgs { query?: string; replace?: string; + preserveCase?: boolean; triggerSearch?: boolean; filesToInclude?: string; filesToExclude?: string; isRegex?: boolean; isCaseSensitive?: boolean; matchWholeWord?: boolean; + excludeSettingAndIgnoreFiles?: boolean; } export const FindInFilesCommand: ICommandHandler = (accessor, args: IFindInFilesArgs = {}) => { From e443e3753121390a5a856649e1859d55977db948 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 1 Oct 2020 19:34:53 +0200 Subject: [PATCH 0133/1882] fix #107893 --- src/vs/base/browser/dom.ts | 1 - src/vs/base/common/network.ts | 2 ++ src/vs/workbench/browser/parts/editor/editorGroupView.ts | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index e4606bf9deca7..65b6eb9f72ec2 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1229,7 +1229,6 @@ export function asCSSUrl(uri: URI): string { return `url('${FileAccess.asBrowserUri(uri).toString(true).replace(/'/g, '%27')}')`; } - export function triggerDownload(dataOrUri: Uint8Array | URI, name: string): void { // If the data is provided as Buffer, we create a diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 02cc27db26df0..f475b10e5e81d 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -135,6 +135,8 @@ class FileAccessImpl { /** * Returns a URI to use in contexts where the browser is responsible * for loading (e.g. fetch()) or when used within the DOM. + * + * **Note:** use `dom.ts#asCSSUrl` whenever the URL is to be used in CSS context. */ asBrowserUri(uri: URI): URI; asBrowserUri(moduleId: string, moduleIdToUrl: { toUrl(moduleId: string): string }): URI; diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 4f7b6066e0101..012c84479cfc5 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -8,7 +8,7 @@ import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGro import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, ActiveEditorDirtyContext, IEditorPane, EditorGroupEditorsCountContext, SaveReason, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane, ActiveEditorStickyContext, ActiveEditorPinnedContext, Deprecated_EditorPinnedContext, Deprecated_EditorDirtyContext, EditorResourceAccessor } from 'vs/workbench/common/editor'; import { Event, Emitter, Relay } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor } from 'vs/base/browser/dom'; +import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor, asCSSUrl } from 'vs/base/browser/dom'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; @@ -1780,7 +1780,7 @@ registerThemingParticipant((theme, collector, environment) => { const letterpress = `./media/letterpress${theme.type === 'dark' ? '-dark' : theme.type === 'hc' ? '-hc' : ''}.svg`; collector.addRule(` .monaco-workbench .part.editor > .content .editor-group-container.empty .editor-group-letterpress { - background-image: url('${FileAccess.asBrowserUri(letterpress, require).fsPath}') + background-image: ${asCSSUrl(FileAccess.asBrowserUri(letterpress, require))} } `); From 96eb2e8c4b158bc6c047c20b0e98f38026d3d35b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 1 Oct 2020 10:50:54 -0700 Subject: [PATCH 0134/1882] Remove custom dropdown dev test flag checked in by mistake Get #107909 back to where it was in 1.49 --- src/vs/base/browser/ui/selectBox/selectBoxCustom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index c2395b8d6f733..62438e1b3917e 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -97,7 +97,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi private selectionDetailsPane!: HTMLElement; private _skipLayout: boolean = false; - private _sticky: boolean = true; // for dev purposes only + private _sticky: boolean = false; // for dev purposes only constructor(options: ISelectOptionItem[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) { From 45b377077c4b33cab1c395ebfdaf566e52d92f17 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 1 Oct 2020 20:05:45 +0200 Subject: [PATCH 0135/1882] Auto detect color scheme sets one theme for all open windows despite different preferred schemes set in workspaces. Fixes #107855 --- .../themes/browser/workbenchThemeService.ts | 3 ++- .../themes/common/themeConfiguration.ts | 22 +++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 4cc6db3160caf..36b7fd663c306 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -378,7 +378,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (themeSettingId) { const theme = await this.colorThemeRegistry.findThemeBySettingsId(themeSettingId, undefined); if (theme) { - return this.setColorTheme(theme.id, ConfigurationTarget.USER); + const configurationTarget = this.settings.findAutoConfigurationTarget(settingId); + return this.setColorTheme(theme.id, configurationTarget); } } return null; diff --git a/src/vs/workbench/services/themes/common/themeConfiguration.ts b/src/vs/workbench/services/themes/common/themeConfiguration.ts index 71b4a464aaea0..62c3b7abc60d7 100644 --- a/src/vs/workbench/services/themes/common/themeConfiguration.ts +++ b/src/vs/workbench/services/themes/common/themeConfiguration.ts @@ -298,6 +298,18 @@ export class ThemeConfiguration { return theme; } + public findAutoConfigurationTarget(key: string) { + let settings = this.configurationService.inspect(key); + if (!types.isUndefined(settings.workspaceFolderValue)) { + return ConfigurationTarget.WORKSPACE_FOLDER; + } else if (!types.isUndefined(settings.workspaceValue)) { + return ConfigurationTarget.WORKSPACE; + } else if (!types.isUndefined(settings.userRemote)) { + return ConfigurationTarget.USER_REMOTE; + } + return ConfigurationTarget.USER; + } + private async writeConfiguration(key: string, value: any, settingsTarget: ConfigurationTarget | 'auto' | undefined): Promise { if (settingsTarget === undefined) { return; @@ -305,15 +317,7 @@ export class ThemeConfiguration { let settings = this.configurationService.inspect(key); if (settingsTarget === 'auto') { - if (!types.isUndefined(settings.workspaceFolderValue)) { - settingsTarget = ConfigurationTarget.WORKSPACE_FOLDER; - } else if (!types.isUndefined(settings.workspaceValue)) { - settingsTarget = ConfigurationTarget.WORKSPACE; - } else if (!types.isUndefined(settings.userRemote)) { - settingsTarget = ConfigurationTarget.USER_REMOTE; - } else { - settingsTarget = ConfigurationTarget.USER; - } + settingsTarget = this.findAutoConfigurationTarget(key); } if (settingsTarget === ConfigurationTarget.USER) { From c98463a24c9e3de30bca40092c6957b2efe022a2 Mon Sep 17 00:00:00 2001 From: olegoid Date: Wed, 30 Sep 2020 16:15:57 -0400 Subject: [PATCH 0136/1882] Add support for opaque AAD access tokens --- extensions/microsoft-authentication/src/AADHelper.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 0f3ff17b9f890..ecfc8456266d2 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -68,6 +68,7 @@ export interface ITokenResponse { refresh_token: string; scope: string; token_type: string; + id_token: string; } function parseQuery(uri: vscode.Uri) { @@ -449,7 +450,15 @@ export class AzureActiveDirectoryService { } private getTokenFromResponse(json: ITokenResponse, scope: string, existingId?: string): IToken { - const claims = this.getTokenClaims(json.access_token); + let claims = undefined; + + try { + claims = this.getTokenClaims(json.access_token); + } catch { + Logger.info('Failed to fetch token claims from access_token. Attempting to parse id_token instead'); + claims = this.getTokenClaims(json.id_token); + } + return { expiresIn: json.expires_in, expiresAt: json.expires_in ? Date.now() + json.expires_in * 1000 : undefined, From f19ccd13c2d0a181da935e9c673f44c6a8e9180b Mon Sep 17 00:00:00 2001 From: olegoid Date: Thu, 1 Oct 2020 13:34:26 -0400 Subject: [PATCH 0137/1882] Made id_token optional in ITokenResponse According to: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#successful-response-1 --- extensions/microsoft-authentication/src/AADHelper.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index ecfc8456266d2..f5dfc791f044d 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -68,7 +68,7 @@ export interface ITokenResponse { refresh_token: string; scope: string; token_type: string; - id_token: string; + id_token?: string; } function parseQuery(uri: vscode.Uri) { @@ -454,9 +454,13 @@ export class AzureActiveDirectoryService { try { claims = this.getTokenClaims(json.access_token); - } catch { - Logger.info('Failed to fetch token claims from access_token. Attempting to parse id_token instead'); - claims = this.getTokenClaims(json.id_token); + } catch (e) { + if (json.id_token) { + Logger.info('Failed to fetch token claims from access_token. Attempting to parse id_token instead'); + claims = this.getTokenClaims(json.id_token); + } else { + throw e; + } } return { From 1b960248d60ae4c1b6af855f675b6749d24e4f31 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 1 Oct 2020 21:08:26 +0200 Subject: [PATCH 0138/1882] document ISchemaAssociation.schema --- extensions/json-language-features/server/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extensions/json-language-features/server/README.md b/extensions/json-language-features/server/README.md index 3ff9899e29896..2e8745c7d8aca 100644 --- a/extensions/json-language-features/server/README.md +++ b/extensions/json-language-features/server/README.md @@ -167,7 +167,12 @@ interface ISchemaAssociation { * A match succeeds when there is at least one pattern matching and last matching pattern does not start with '!'. */ fileMatch: string[]; - + + /* + * The schema for the given URI. + * If no schema is provided, the schema will be fetched with the schema request service (if available). + */ + schema?: JSONSchema; } ``` From bdddde3da2fbf5a05cdc8d0fe3b1b76f4f8ed9c4 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Thu, 1 Oct 2020 15:35:54 -0400 Subject: [PATCH 0139/1882] Fixes #107751 - shrinks description before title --- src/vs/workbench/browser/parts/views/media/paneviewlet.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/browser/parts/views/media/paneviewlet.css b/src/vs/workbench/browser/parts/views/media/paneviewlet.css index fea8488c9c1bc..f724b9379102a 100644 --- a/src/vs/workbench/browser/parts/views/media/paneviewlet.css +++ b/src/vs/workbench/browser/parts/views/media/paneviewlet.css @@ -34,6 +34,7 @@ text-overflow: ellipsis; overflow: hidden; font-size: 11px; + min-width: 3ch; -webkit-margin-before: 0; -webkit-margin-after: 0; } @@ -47,6 +48,7 @@ text-overflow: ellipsis; text-transform: none; white-space: nowrap; + flex-shrink: 100000; } .monaco-pane-view .pane > .pane-header .description .codicon { From 5052a7bc2ec35879b4ef5c835de78c9620bc1d9e Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 1 Oct 2020 16:06:14 -0700 Subject: [PATCH 0140/1882] debug: update js-debug --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index 87b8182cb90a3..ecfb44dd74e09 100644 --- a/product.json +++ b/product.json @@ -91,7 +91,7 @@ }, { "name": "ms-vscode.js-debug", - "version": "1.50.1", + "version": "1.50.2", "repo": "https://github.com/microsoft/vscode-js-debug", "metadata": { "id": "25629058-ddac-4e17-abba-74678e126c5d", @@ -106,7 +106,7 @@ }, { "name": "ms-vscode.vscode-js-profile-table", - "version": "0.0.10", + "version": "0.0.11", "repo": "https://github.com/microsoft/vscode-js-debug", "metadata": { "id": "7e52b41b-71ad-457b-ab7e-0620f1fc4feb", From 0004cabf8763ce2e5132ecfb9b02faa61f4ddfa3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 2 Oct 2020 08:34:18 +0200 Subject: [PATCH 0141/1882] fix #107850 --- .../services/history/browser/history.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index c1557b3ee5b77..eff59cd35337e 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -615,8 +615,13 @@ export class HistoryService extends Disposable implements IHistoryService { private static readonly MAX_RECENTLY_CLOSED_EDITORS = 20; private recentlyClosedEditors: IRecentlyClosedEditor[] = []; + private ignoreEditorCloseEvent = false; private onEditorClosed(event: IEditorCloseEvent): void { + if (this.ignoreEditorCloseEvent) { + return; // blocked + } + const { editor, replaced } = event; if (replaced) { return; // ignore if editor was replaced @@ -695,7 +700,17 @@ export class HistoryService extends Disposable implements IHistoryService { const restoredEditor = this.editorInputFactory.getEditorInputFactory(lastClosedEditor.serialized.typeId)?.deserialize(this.instantiationService, lastClosedEditor.serialized.value); let editorPane: IEditorPane | undefined = undefined; if (restoredEditor && !this.editorGroupService.activeGroup.isOpened(restoredEditor)) { - editorPane = await this.editorService.openEditor(restoredEditor, options); + // Fix for https://github.com/microsoft/vscode/issues/107850 + // If opening an editor fails, it is possible that we get + // another editor-close event as a result. But we really do + // want to ignore that in our list of recently closed editors + // to prevent endless loops. + this.ignoreEditorCloseEvent = true; + try { + editorPane = await this.editorService.openEditor(restoredEditor, options); + } finally { + this.ignoreEditorCloseEvent = false; + } } // If no editor was opened, try with the next one @@ -705,6 +720,8 @@ export class HistoryService extends Disposable implements IHistoryService { // but make sure to remove this one from the list to prevent // endless loops. remove(this.recentlyClosedEditors, lastClosedEditor); + + // Try with next one this.reopenLastClosedEditor(); } } From e8ceafb07a6a29eeae4d4cb4a56c010c40625cc2 Mon Sep 17 00:00:00 2001 From: Justin Steven Date: Fri, 2 Oct 2020 17:21:51 +1000 Subject: [PATCH 0142/1882] Fix a bypass for CVE-2020-16881 Fixes #107951 Uses child_process.execFile() rather than child_process.exec() to more effectively resolve the command injection vulnerability. --- extensions/npm/src/features/packageJSONContribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/npm/src/features/packageJSONContribution.ts b/extensions/npm/src/features/packageJSONContribution.ts index f154a8752391a..7103ce1fa1a81 100644 --- a/extensions/npm/src/features/packageJSONContribution.ts +++ b/extensions/npm/src/features/packageJSONContribution.ts @@ -282,8 +282,8 @@ export class PackageJSONContribution implements IJSONContribution { private npmView(pack: string): Promise { return new Promise((resolve, _reject) => { - const command = 'npm view --json ' + pack + ' description dist-tags.latest homepage version'; - cp.exec(command, (error, stdout) => { + const args = ['view', '--json', pack, 'description', 'dist-tags.latest', 'homepage', 'version']; + cp.execFile('npm', args, (error, stdout) => { if (!error) { try { const content = JSON.parse(stdout); From cc2a8dc1774e5f09bd8ff066c6d0d64bf0403494 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 1 Oct 2020 21:11:11 +0200 Subject: [PATCH 0143/1882] JSON Language Server 1.3.0 --- extensions/json-language-features/server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index ef46663531f81..c0a1147bdf0d5 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -1,7 +1,7 @@ { "name": "vscode-json-languageserver", "description": "JSON language server", - "version": "1.2.3", + "version": "1.3.0", "author": "Microsoft Corporation", "license": "MIT", "engines": { From 55efd45c06b651af0b190042b749e815aefbff8c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 2 Oct 2020 07:53:53 +0000 Subject: [PATCH 0144/1882] Fix #106963 --- src/vs/workbench/contrib/extensions/browser/extensionsList.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index ebed4cec04e8d..e6609df5c2853 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -228,8 +228,10 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = const backgroundColor = theme.getColor(listActiveSelectionBackground) || WORKBENCH_BACKGROUND(theme); const authorForeground = listActiveSelectionForegroundColor.transparent(.9).makeOpaque(backgroundColor); collector.addRule(`.extensions-list .monaco-list:focus .monaco-list-row:not(.disabled).focused.selected .author { color: ${authorForeground}; }`); + collector.addRule(`.extensions-list .monaco-list:focus .monaco-list-row:not(.disabled).selected .author { color: ${authorForeground}; }`); const disabledExtensionForeground = listActiveSelectionForegroundColor.transparent(.5).makeOpaque(backgroundColor); collector.addRule(`.extensions-list .monaco-list:focus .monaco-list-row.disabled.focused.selected { color: ${disabledExtensionForeground}; }`); + collector.addRule(`.extensions-list .monaco-list:focus .monaco-list-row.disabled.selected { color: ${disabledExtensionForeground}; }`); } const listInactiveSelectionForegroundColor = theme.getColor(listInactiveSelectionForeground); From 02cfa0b9a8f44403386b74de713ffea63011954f Mon Sep 17 00:00:00 2001 From: Hung-Wei Hung Date: Fri, 2 Oct 2020 16:33:43 +0800 Subject: [PATCH 0145/1882] Create empty commit with changed files #107753 --- extensions/git/src/commands.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 6d0ed96cb120b..ebad5d70cdaa5 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1386,8 +1386,14 @@ export class CommandCenter { } } + if (!opts) { + opts = { all: noStagedChanges }; + } else if (!opts.all && noStagedChanges && !opts.empty) { + opts = { ...opts, all: true }; + } + // no changes, and the user has not configured to commit all in this case - if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit) { + if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit && !opts.empty) { const suggestSmartCommit = config.get('suggestSmartCommit') === true; if (!suggestSmartCommit) { @@ -1411,13 +1417,7 @@ export class CommandCenter { } } - if (!opts) { - opts = { all: noStagedChanges }; - } else if (!opts.all && noStagedChanges) { - opts = { ...opts, all: true }; - } - - // enable signing of commits if configurated + // enable signing of commits if configured opts.signCommit = enableCommitSigning; if (config.get('alwaysSignOff')) { @@ -2007,7 +2007,7 @@ export class CommandCenter { forcePushMode = config.get('useForcePushWithLease') === true ? ForcePushMode.ForceWithLease : ForcePushMode.Force; if (config.get('confirmForcePush')) { - const message = localize('confirm force push', "You are about to force push your changes, this can be destructive and could inadvertedly overwrite changes made by others.\n\nAre you sure to continue?"); + const message = localize('confirm force push', "You are about to force push your changes, this can be destructive and could inadvertently overwrite changes made by others.\n\nAre you sure to continue?"); const yes = localize('ok', "OK"); const neverAgain = localize('never ask again', "OK, Don't Ask Again"); const pick = await window.showWarningMessage(message, { modal: true }, yes, neverAgain); From 0ecb64a2c8945dd1193967019f0734af539ca9c3 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 2 Oct 2020 11:18:44 +0200 Subject: [PATCH 0146/1882] bot: debug issues to connor --- .github/classifier.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/classifier.json b/.github/classifier.json index 4e9b695a0c18b..96701987a9ccf 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -20,8 +20,8 @@ "context-keys": {"assign": []}, "css-less-scss": {"assign": ["aeschli"]}, "custom-editors": {"assign": ["mjbvz"]}, - "debug": {"assign": ["isidorn"]}, - "debug-console": {"assign": ["isidorn"]}, + "debug": {"assign": ["connor4312 "]}, + "debug-console": {"assign": ["connor4312 "]}, "dialogs": {"assign": ["sbatten"]}, "diff-editor": {"assign": []}, "dropdown": {"assign": []}, From 247e5dc148c0447937c232cf39558bd01f53f33b Mon Sep 17 00:00:00 2001 From: Rares Folea <30867783+raresraf@users.noreply.github.com> Date: Fri, 2 Oct 2020 17:42:47 +0300 Subject: [PATCH 0147/1882] Fix typo extensions/search-result/README.md (#107961) --- extensions/search-result/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/search-result/README.md b/extensions/search-result/README.md index c3e1b53525c99..fe886e4bde170 100644 --- a/extensions/search-result/README.md +++ b/extensions/search-result/README.md @@ -2,4 +2,4 @@ **Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. -This extension provides Syntax Highlighting, Symbol Infomation, Result Highlighting, and Go to Definition capabilities for the Search Results Editor. +This extension provides Syntax Highlighting, Symbol Information, Result Highlighting, and Go to Definition capabilities for the Search Results Editor. From 7701642b2618cdd284721de80d9c40eefa56fc3c Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 2 Oct 2020 09:42:54 -0700 Subject: [PATCH 0148/1882] update distro. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 06831b259324b..770b44b0c1ff5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.50.0", - "distro": "66a02247b2916cb7cf8a3b70677e1d798c6527f2", + "distro": "9c21f6fe853c7843005a92bd4f6dea9b8eba03a2", "author": { "name": "Microsoft Corporation" }, From 916de64d89b265b84d3dff6baf3d6a6c04884b8c Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 2 Oct 2020 14:16:53 -0700 Subject: [PATCH 0149/1882] fix: debug buttons in a nested package.json Fixes #108000 --- extensions/npm/src/npmView.ts | 2 +- extensions/npm/src/scriptHover.ts | 5 +++-- extensions/npm/src/tasks.ts | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index c1145d0806bce..c1b5f6fdddd4f 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -137,7 +137,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { } private async debugScript(script: NpmScript) { - startDebugging(script.task.name, script.getFolder()); + startDebugging(script.task.definition.script, path.dirname(script.package.resourceUri!.fsPath), script.getFolder()); } private findScript(document: TextDocument, script?: NpmScript): number { diff --git a/extensions/npm/src/scriptHover.ts b/extensions/npm/src/scriptHover.ts index f8a5482bef85a..b1edc275267eb 100644 --- a/extensions/npm/src/scriptHover.ts +++ b/extensions/npm/src/scriptHover.ts @@ -11,6 +11,7 @@ import { createTask, startDebugging, findAllScriptRanges } from './tasks'; import * as nls from 'vscode-nls'; +import { dirname } from 'path'; const localize = nls.loadMessageBundle(); @@ -107,12 +108,12 @@ export class NpmScriptHoverProvider implements HoverProvider { } } - public debugScriptFromHover(args: any) { + public debugScriptFromHover(args: { script: string; documentUri: Uri }) { let script = args.script; let documentUri = args.documentUri; let folder = workspace.getWorkspaceFolder(documentUri); if (folder) { - startDebugging(script, folder); + startDebugging(script, dirname(documentUri.fsPath), folder); } } } diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index f7def2f8876fb..7c97a0d1015ec 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -357,11 +357,12 @@ export function runScript(script: string, document: TextDocument) { } } -export function startDebugging(scriptName: string, folder: WorkspaceFolder) { +export function startDebugging(scriptName: string, cwd: string, folder: WorkspaceFolder) { const config: DebugConfiguration = { type: 'pwa-node', request: 'launch', name: `Debug ${scriptName}`, + cwd, runtimeExecutable: getPackageManager(folder), runtimeArgs: [ 'run', From dfa980ebdad245a60b217de7468bc8ced953c906 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 2 Oct 2020 14:59:01 -0700 Subject: [PATCH 0150/1882] Fix NotebookDocumentBackup comment --- src/vs/vscode.proposed.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 43b7b86306a94..a1e531adc0c8b 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1673,7 +1673,7 @@ declare module 'vscode' { /** * Unique identifier for the backup. * - * This id is passed back to your extension in `openCustomDocument` when opening a notebook editor from a backup. + * This id is passed back to your extension in `openNotebook` when opening a notebook editor from a backup. */ readonly id: string; From 8284242ed12dc43ee490a29b192a388c8193e3b7 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 2 Oct 2020 15:22:24 -0700 Subject: [PATCH 0151/1882] fix #107438. --- .../browser/view/renderers/backLayerWebView.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 8cc06b086003b..2a8b91faadf4f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -23,7 +23,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { dirname, joinPath } from 'vs/base/common/resources'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { preloadsScriptStr } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads'; -import { Schemas } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IFileService } from 'vs/platform/files/common/files'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -356,9 +356,6 @@ export class BackLayerWebView extends Disposable { } async createWebview(): Promise { - const pathsPath = getPathFromAmdModule(require, 'vs/loader.js'); - const loader = asWebviewUri(this.environmentService, this.id, URI.file(pathsPath)); - let coreDependencies = ''; let resolveFunc: () => void; @@ -369,6 +366,9 @@ export class BackLayerWebView extends Disposable { const baseUrl = asWebviewUri(this.environmentService, this.id, dirname(this.documentUri)); if (!isWeb) { + const loaderUri = FileAccess.asFileUri('vs/loader.js', require); + const loader = asWebviewUri(this.environmentService, this.id, loaderUri); + coreDependencies = ` - -`; - const iframeContent = `data:text/html;charset=utf-8,${encodeURIComponent(html)}`; - iframe.setAttribute('src', iframeContent); - const timeout = setTimeout(() => { - this._onDidExit.fire([ExtensionHostExitCode.StartTimeout10s, 'The Web Worker Extension Host did not start in 10s']); - }, 10000); + iframe.setAttribute('src', `${webWorkerExtensionHostIframeSrc}?vscodeWebWorkerExtHostId=${vscodeWebWorkerExtHostId}`); const barrier = new Barrier(); let port!: MessagePort; + let barrierError: Error | null = null; + let barrierHasError = false; + let startTimeout: any = null; + + const rejectBarrier = (exitCode: number, error: Error) => { + barrierError = error; + barrierHasError = true; + onUnexpectedError(barrierError); + clearTimeout(startTimeout); + this._onDidExit.fire([ExtensionHostExitCode.UnexpectedError, barrierError.message]); + barrier.open(); + }; + + const resolveBarrier = (messagePort: MessagePort) => { + port = messagePort; + clearTimeout(startTimeout); + barrier.open(); + }; + + startTimeout = setTimeout(() => { + rejectBarrier(ExtensionHostExitCode.StartTimeout10s, new Error('The Web Worker Extension Host did not start in 10s')); + }, 10000); this._register(dom.addDisposableListener(window, 'message', (event) => { if (event.source !== iframe.contentWindow) { @@ -141,21 +168,15 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost err.message = message; err.name = name; err.stack = stack; - onUnexpectedError(err); - clearTimeout(timeout); - this._onDidExit.fire([ExtensionHostExitCode.UnexpectedError, err.message]); - return; + return rejectBarrier(ExtensionHostExitCode.UnexpectedError, err); } const { data } = event.data; if (barrier.isOpen() || !(data instanceof MessagePort)) { console.warn('UNEXPECTED message', event); - clearTimeout(timeout); - this._onDidExit.fire([ExtensionHostExitCode.UnexpectedError, 'UNEXPECTED message']); - return; + const err = new Error('UNEXPECTED message'); + return rejectBarrier(ExtensionHostExitCode.UnexpectedError, err); } - port = data; - clearTimeout(timeout); - barrier.open(); + resolveBarrier(data); })); document.body.appendChild(iframe); @@ -165,6 +186,10 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost // with the worker extension host await barrier.wait(); + if (barrierHasError) { + throw barrierError; + } + port.onmessage = (event) => { const { data } = event; if (!(data instanceof ArrayBuffer)) { diff --git a/src/vs/workbench/services/extensions/common/webWorkerIframe.ts b/src/vs/workbench/services/extensions/common/webWorkerIframe.ts deleted file mode 100644 index 7b354788d4689..0000000000000 --- a/src/vs/workbench/services/extensions/common/webWorkerIframe.ts +++ /dev/null @@ -1,47 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export const WEB_WORKER_IFRAME = { - sha: 'sha256-r24mDVsMuFEo8ChaY9ppVJKbY3CUM4I12Aw/yscWZbg=', - js: ` -(function() { - const workerSrc = document.getElementById('vscode-worker-src').getAttribute('data-value'); - const worker = new Worker(workerSrc, { name: 'WorkerExtensionHost' }); - const vscodeWebWorkerExtHostId = document.getElementById('vscode-web-worker-ext-host-id').getAttribute('data-value'); - - worker.onmessage = (event) => { - const { data } = event; - if (!(data instanceof MessagePort)) { - console.warn('Unknown data received', event); - window.parent.postMessage({ - vscodeWebWorkerExtHostId, - error: { - name: 'Error', - message: 'Unknown data received', - stack: [] - } - }, '*'); - return; - } - window.parent.postMessage({ - vscodeWebWorkerExtHostId, - data: data - }, '*', [data]); - }; - - worker.onerror = (event) => { - console.error(event.message, event.error); - window.parent.postMessage({ - vscodeWebWorkerExtHostId, - error: { - name: event.error ? event.error.name : '', - message: event.error ? event.error.message : '', - stack: event.error ? event.error.stack : [] - } - }, '*'); - }; -})(); -` -}; diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts index 6a54c82b1af10..dd554377b19c7 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts @@ -45,9 +45,9 @@ self.addEventListener = () => console.trace(`'addEventListener' has been blocked (self)['webkitResolveLocalFileSystemSyncURL'] = undefined; (self)['webkitResolveLocalFileSystemURL'] = undefined; -if (location.protocol === 'data:') { +if ((self).Worker) { // make sure new Worker(...) always uses data: - const _Worker = Worker; + const _Worker = (self).Worker; Worker = function (stringUrl: string | URL, options?: WorkerOptions) { const js = `importScripts('${stringUrl}');`; options = options || {}; diff --git a/src/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html b/src/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html new file mode 100644 index 0000000000000..a99d3df29dfa9 --- /dev/null +++ b/src/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html @@ -0,0 +1,50 @@ + + + + + + + + + diff --git a/src/vs/workbench/services/extensions/worker/httpsWebWorkerExtensionHostIframe.html b/src/vs/workbench/services/extensions/worker/httpsWebWorkerExtensionHostIframe.html new file mode 100644 index 0000000000000..27b6114a13b2a --- /dev/null +++ b/src/vs/workbench/services/extensions/worker/httpsWebWorkerExtensionHostIframe.html @@ -0,0 +1,50 @@ + + + + + + + + + diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 16accfae70b00..68906ff6fa77a 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -277,6 +277,11 @@ interface IWorkbenchConstructionOptions { */ readonly webviewEndpoint?: string; + /** + * An URL pointing to the web worker extension host