From b24cb8b47d4ed3703e5018f6c45f5feee70d2095 Mon Sep 17 00:00:00 2001 From: kingwl Date: Mon, 11 May 2020 19:01:45 +0800 Subject: [PATCH 001/103] 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 ea99da5140a..b0b11e653a7 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 39965be4f75..eba9ea7eefc 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 d6eeeec88ba..48984566cdd 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 1b5c8d48371..8323ef3a459 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 9e85d0fca0c..7466cfd8112 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 99a5b2271d6..02e1b4890af 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 002/103] 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 48984566cdd..c370133d8d7 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 02e1b4890af..99a5b2271d6 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 003/103] 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 d6eeeec88ba..f1e217422ff 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 1b5c8d48371..62ca189e51a 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 fc797d2430903cd49c06e97ed92be84bccb4eca8 Mon Sep 17 00:00:00 2001 From: Asif Hasan Date: Wed, 3 Jun 2020 18:00:11 -0500 Subject: [PATCH 004/103] 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 44a8858c63d..e93d0212a0c 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 005/103] 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 66d91da4d7a..b95e7009d10 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 e93d0212a0c..577eff4fd7c 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 006/103] Add better support for checkout type config --- extensions/git/package.json | 15 ++----------- extensions/git/package.nls.json | 6 +----- extensions/git/src/commands.ts | 37 ++++++++++++++++++++++++--------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index d33bafbad5f..2d056f6a393 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 be817e22df1..b1506ed9568 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 e60dc39e6a6..9c2e59cf515 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 checkoutType = config.get('checkoutType') || 'local,remote,tags'; + const checkoutTypes = checkoutType.trim().split(',').map(type => type.trim()); - 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)); + const results: CheckoutItem[] = []; + const invalids = new Set(); + const seens = new Set(); + checkoutTypes.forEach(type => { + if (seens.has(type)) { + return; + } + seens.add(type); - return [...heads, ...tags, ...remoteHeads]; + 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 007/103] 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 9c2e59cf515..a2b741ad4d3 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 008/103] 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 a2b741ad4d3..f21aa23f9ae 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 009/103] 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 748868778dd..ba3e194ad03 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 0e8ea1c649e..af825fe317f 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 edebe7e54c1..3662ae60747 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 010/103] 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 748868778dd..767a70a5a68 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 0e8ea1c649e..0780ac89b8d 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 f618a8669c2..a862568e22d 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 d974c7bf1ca..9019a8e4772 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 edebe7e54c1..124b34caf7a 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 011/103] 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 48e20a5a033..57bb729e852 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 8b21b331facc4c79b001bf7744f98b2017b6fbd2 Mon Sep 17 00:00:00 2001 From: ae1020 Date: Wed, 19 Aug 2020 20:22:39 -0400 Subject: [PATCH 012/103] 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 5ab03d9cd4c..d191083ce49 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 013/103] 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 23bfdb2c7bf..5baba5c997f 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 6e7f132e99f..9686d459bca 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 00bb9830d54..ba3c9b5f6f6 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 afb227be73b..491986dcc80 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 57bb729e852..0929cd2b297 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 296913a3fd8..505dcc7f506 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 d191083ce49..ec2d384ede4 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 21193bac130..595dd8670bc 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 30004656390..73f90d11a35 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 d371020b873..ed9ca3637d6 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 22ca0c1e8a751fe0f74495282f1610b981a519dd Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 9 Sep 2020 14:09:04 -0700 Subject: [PATCH 014/103] Add providerName option to git.api.getRemoteSources --- extensions/git/src/remoteSource.ts | 198 +++++++++++++++-------------- 1 file changed, 105 insertions(+), 93 deletions(-) diff --git a/extensions/git/src/remoteSource.ts b/extensions/git/src/remoteSource.ts index b736f606e67..2f3f493e75d 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(); - }); + const result = await new Promise(c => { + quickpick.onDidAccept(() => c(quickpick.selectedItems[0])); + quickpick.onDidHide(() => c(undefined)); + quickpick.show(); + }); - quickpick.hide(); - return result; + quickpick.hide(); + return result; } class RemoteSourceProviderQuickPick { - private quickpick: QuickPick; + private quickpick: QuickPick; - constructor(private provider: RemoteSourceProvider) { - this.quickpick = window.createQuickPick(); - this.quickpick.ignoreFocusOut = true; + 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"); - } - } + 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(); - } + @debounce(300) + private onDidChangeValue(): void { + this.query(); + } - @throttle - private async query(): Promise { - this.quickpick.busy = true; + @throttle + private async query(): Promise { + this.quickpick.busy = true; - try { - const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || []; + 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; - } - } + 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; - } + 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 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 })); + 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."); + 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; - } - }; + 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(); + quickpick.onDidChangeValue(updatePicks); + updatePicks(); - const result = await getQuickPickResult(quickpick); + 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 (result) { + if (result.url) { + return result.url; + } else if (result.provider) { + return await pickProviderSource(result.provider); + } + } + } - 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; + 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 015/103] Formatting --- extensions/git/src/remoteSource.ts | 200 ++++++++++++++--------------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/extensions/git/src/remoteSource.ts b/extensions/git/src/remoteSource.ts index 2f3f493e75d..a1acb2b9f98 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(); - }); + const result = await new Promise(c => { + quickpick.onDidAccept(() => c(quickpick.selectedItems[0])); + quickpick.onDidHide(() => c(undefined)); + quickpick.show(); + }); - quickpick.hide(); - return result; + quickpick.hide(); + return result; } class RemoteSourceProviderQuickPick { - private quickpick: QuickPick; + private quickpick: QuickPick; - constructor(private provider: RemoteSourceProvider) { - this.quickpick = window.createQuickPick(); - this.quickpick.ignoreFocusOut = true; + 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"); - } - } + 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(); - } + @debounce(300) + private onDidChangeValue(): void { + this.query(); + } - @throttle - private async query(): Promise { - this.quickpick.busy = true; + @throttle + private async query(): Promise { + this.quickpick.busy = true; - try { - const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || []; + 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; - } - } + 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; - } + 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 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 })); + 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."); + 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; - } - }; + 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(); + quickpick.onDidChangeValue(updatePicks); + updatePicks(); - const result = await getQuickPickResult(quickpick); + const result = await getQuickPickResult(quickpick); - if (result) { - if (result.url) { - return result.url; - } else if (result.provider) { - return await pickProviderSource(result.provider); - } - } - } + if (result) { + if (result.url) { + return result.url; + } else if (result.provider) { + return await pickProviderSource(result.provider); + } + } + } - return undefined; + return undefined; } async function pickProviderSource(provider: RemoteSourceProvider): Promise { - const quickpick = new RemoteSourceProviderQuickPick(provider); - const remote = await quickpick.pick(); + 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.") }); - } - } + 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; + 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 016/103] 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 a1acb2b9f98..1ecdc71c25e 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 2f232ccbca086943c38ae8f5970fe712308e32fc Mon Sep 17 00:00:00 2001 From: Emanuel Tesar Date: Wed, 14 Oct 2020 21:30:33 +0200 Subject: [PATCH 017/103] Enable tsec language service plugin --- src/tsconfig.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tsconfig.json b/src/tsconfig.json index 5e81e53802a..85e7f18d516 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -13,6 +13,12 @@ "sinon", "winreg", "trusted-types" + ], + "plugins": [ + { + // the path is relative to TS server, "../../" points to the root dir + "name": "../../node_modules/tsec/lib/tsec_lib/language_service_plugin.js" + } ] }, "include": [ From 8a7189599e36d9df0ececb05fc2bc9e109a957b3 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 5 Nov 2020 10:35:26 -0800 Subject: [PATCH 018/103] Add color token for error bg --- src/vs/editor/browser/widget/codeEditorWidget.ts | 7 +++++-- src/vs/platform/theme/common/colorRegistry.ts | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 734fcf4d94b..9d70939e7b5 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -37,7 +37,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import * as modes from 'vs/editor/common/modes'; import { editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity } from 'vs/editor/common/view/editorColorRegistry'; -import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground, editorErrorBackground } from 'vs/platform/theme/common/colorRegistry'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; @@ -1985,7 +1985,10 @@ registerThemingParticipant((theme, collector) => { if (errorForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorErrorDecoration} { background: url("data:image/svg+xml,${getSquigglySVGData(errorForeground)}") repeat-x bottom left; }`); } - + const errorBackground = theme.getColor(editorErrorBackground); + if (errorBackground) { + collector.addRule(`.monaco-editor .${ClassName.EditorErrorDecoration}::before { display: block; content: ''; width: 100%; height: 100%; background: ${errorBackground}; }`); + } const warningBorderColor = theme.getColor(editorWarningBorder); if (warningBorderColor) { collector.addRule(`.monaco-editor .${ClassName.EditorWarningDecoration} { border-bottom: 4px double ${warningBorderColor}; }`); diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 322c486f095..67c3936497d 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -243,6 +243,7 @@ export const scrollbarSliderActiveBackground = registerColor('scrollbarSlider.ac export const progressBarBackground = registerColor('progressBar.background', { dark: Color.fromHex('#0E70C0'), light: Color.fromHex('#0E70C0'), hc: contrastBorder }, nls.localize('progressBarBackground', "Background color of the progress bar that can show for long running operations.")); +export const editorErrorBackground = registerColor('editorError.background', { dark: null, light: null, hc: null }, nls.localize('editorError.background', 'Background color of error text in the editor.')); export const editorErrorForeground = registerColor('editorError.foreground', { dark: '#F48771', light: '#E51400', hc: null }, nls.localize('editorError.foreground', 'Foreground color of error squigglies in the editor.')); export const editorErrorBorder = registerColor('editorError.border', { dark: null, light: null, hc: Color.fromHex('#E47777').transparent(0.8) }, nls.localize('errorBorder', 'Border color of error boxes in the editor.')); From a2963771d18282e3e7ed66416a9611a9b97e8c30 Mon Sep 17 00:00:00 2001 From: Zuckjet <1083941774@qq.com> Date: Sun, 8 Nov 2020 07:01:44 +0800 Subject: [PATCH 019/103] hover info should not be broken within word (#106885) Fix #105630 --- src/vs/base/browser/ui/hover/hover.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/base/browser/ui/hover/hover.css b/src/vs/base/browser/ui/hover/hover.css index a5390be414d..c2e11fa89f1 100644 --- a/src/vs/base/browser/ui/hover/hover.css +++ b/src/vs/base/browser/ui/hover/hover.css @@ -87,7 +87,6 @@ .monaco-hover .monaco-tokenized-source { white-space: pre-wrap; - word-break: break-all; } .monaco-hover .hover-row.status-bar { From 7095ef144bb8bc43d19e5e3bfdb0237c820a74f5 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 8 Nov 2020 12:19:53 +0100 Subject: [PATCH 020/103] Revert "Revert "Fix #99971"" This reverts commit 5a73a68e1f629cdacd8f5d5481e70f252341e9df. --- .../extensions/browser/extensionEditor.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index f9bef7343d0..6a8a397c2f6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -903,6 +903,7 @@ export class ExtensionEditor extends EditorPane { this.renderLocalizations(content, manifest, layout), this.renderCustomEditors(content, manifest, layout), this.renderAuthentication(content, manifest, layout), + this.renderActivationEvents(content, manifest, layout), ]; scrollableContent.scanDomNode(); @@ -1417,6 +1418,21 @@ export class ExtensionEditor extends EditorPane { return true; } + private renderActivationEvents(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { + const activationEvents = manifest.activationEvents || []; + if (!activationEvents.length) { + return false; + } + + const details = $('details', { open: true, ontoggle: onDetailsToggle }, + $('summary', { tabindex: '0' }, localize('activation events', "Activation Events ({0})", activationEvents.length)), + $('ul', undefined, ...activationEvents.map(activationEvent => $('li', undefined, activationEvent))) + ); + + append(container, details); + return true; + } + private resolveKeybinding(rawKeyBinding: IKeyBinding): ResolvedKeybinding | null { let key: string | undefined; From 9ad6e7edf9f955ca1b388f9dd444b844cad7f3ab Mon Sep 17 00:00:00 2001 From: Valter Pires Date: Sun, 8 Nov 2020 15:30:16 +0400 Subject: [PATCH 021/103] Correct themeLabel in package.nls.json for "Tomorrow Night Blue" theme Fix themeLabel value in package.nls.json for "Tomorrow Night Blue" theme Incorrect value "Quiet Light" commited in 183b2a28924d46ad2c0012c7992e5812f7e7224c --- extensions/theme-tomorrow-night-blue/package.nls.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/theme-tomorrow-night-blue/package.nls.json b/extensions/theme-tomorrow-night-blue/package.nls.json index a37a31b282d..77b44577c81 100644 --- a/extensions/theme-tomorrow-night-blue/package.nls.json +++ b/extensions/theme-tomorrow-night-blue/package.nls.json @@ -1,5 +1,5 @@ { "displayName": "Tomorrow Night Blue Theme", "description": "Tomorrow night blue theme for Visual Studio Code", - "themeLabel": "Quiet Light" + "themeLabel": "Tomorrow Night Blue" } From 148b85862a780aa6011166af612011dd743fb938 Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 8 Nov 2020 13:44:24 -0800 Subject: [PATCH 022/103] fix #89250. --- src/vs/editor/contrib/find/findController.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index a6d26405df6..db45b86d8fc 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -83,6 +83,10 @@ export class CommonFindController extends Disposable implements IEditorContribut private readonly _clipboardService: IClipboardService; protected readonly _contextKeyService: IContextKeyService; + get editor() { + return this._editor; + } + public static get(editor: ICodeEditor): CommonFindController { return editor.getContribution(CommonFindController.ID); } @@ -583,7 +587,13 @@ export class NextMatchFindAction extends MatchFindAction { } protected _run(controller: CommonFindController): boolean { - return controller.moveToNextMatch(); + const result = controller.moveToNextMatch(); + if (result) { + controller.editor.pushUndoStop(); + return true; + } + + return false; } } @@ -604,7 +614,13 @@ export class NextMatchFindAction2 extends MatchFindAction { } protected _run(controller: CommonFindController): boolean { - return controller.moveToNextMatch(); + const result = controller.moveToNextMatch(); + if (result) { + controller.editor.pushUndoStop(); + return true; + } + + return false; } } From f9e19c9ad168afde6c7c63d0757ebeab1f2b606d Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 8 Nov 2020 21:11:25 -0800 Subject: [PATCH 023/103] only hide outputs when output is transient. --- .../workbench/contrib/notebook/browser/diff/cellComponents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index a53d3536553..43a3d299a5d 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -674,7 +674,7 @@ abstract class AbstractCellRenderer extends Disposable { const mode = this.modeService.create('json'); const originaloutputSource = this._getFormatedOutputJSON( - this.notebookEditor.textModel!.transientOptions + this.notebookEditor.textModel!.transientOptions.transientOutputs ? [] : this.cell.type === 'insert' ? this.cell.modified!.outputs || [] From 59a1893d6d66171437f3608d2e938efa616f421e Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 9 Nov 2020 08:39:31 +0100 Subject: [PATCH 024/103] Log number of loaded certificates (#91794) --- src/vs/workbench/services/extensions/node/proxyResolver.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 87fee781fb1..3050bc6e2d6 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -468,9 +468,12 @@ let _caCertificates: ReturnType | Promise; async function getCaCertificates(extHostLogService: ILogService) { if (!_caCertificates) { _caCertificates = readCaCertificates() - .then(res => res && res.certs.length ? res : undefined) + .then(res => { + extHostLogService.debug('ProxyResolver#getCaCertificates count', res && res.certs.length); + return res && res.certs.length ? res : undefined; + }) .catch(err => { - extHostLogService.error('ProxyResolver#getCertificates', toErrorMessage(err)); + extHostLogService.error('ProxyResolver#getCaCertificates error', toErrorMessage(err)); return undefined; }); } From 3154dd4f69c16055bf260fabcfba2ef162da70b2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 9 Nov 2020 08:33:14 +0100 Subject: [PATCH 025/103] editors - add workbench.editor.enablePreview to most commonly used settings --- src/vs/workbench/contrib/preferences/browser/settingsLayout.ts | 2 +- .../services/preferences/browser/preferencesService.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts index b2eb1cb2909..bc258efa43e 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts @@ -17,7 +17,7 @@ export interface ITOCEntry { export const commonlyUsedData: ITOCEntry = { id: 'commonlyUsed', label: localize('commonlyUsed', "Commonly Used"), - settings: ['files.autoSave', 'editor.fontSize', 'editor.fontFamily', 'editor.tabSize', 'editor.renderWhitespace', 'editor.cursorStyle', 'editor.multiCursorModifier', 'editor.insertSpaces', 'editor.wordWrap', 'files.exclude', 'files.associations'] + settings: ['files.autoSave', 'editor.fontSize', 'editor.fontFamily', 'editor.tabSize', 'editor.renderWhitespace', 'editor.cursorStyle', 'editor.multiCursorModifier', 'editor.insertSpaces', 'editor.wordWrap', 'files.exclude', 'files.associations', 'workbench.editor.enablePreview'] }; export const tocData: ITOCEntry = { diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 24d95740cb3..82f8a198074 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -578,7 +578,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic 'editor.insertSpaces', 'editor.wordWrap', 'files.exclude', - 'files.associations' + 'files.associations', + 'workbench.editor.enablePreview' ]; } From eb8c718e813d31c84f9a1ebb4f8c10c8aa94fda8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 9 Nov 2020 09:02:54 +0100 Subject: [PATCH 026/103] debt - fix compile errors in bootstrap files --- src/bootstrap-amd.js | 2 +- src/bootstrap-fork.js | 4 +- src/bootstrap-window.js | 7 +-- src/bootstrap.js | 6 +-- src/main.js | 20 +++++---- src/paths.js | 45 ++++++++++++------- src/vs/base/node/paths.ts | 9 +--- .../parts/sandbox/electron-browser/preload.js | 2 + src/vs/code/electron-main/app.ts | 8 ++-- .../environment/node/environmentService.ts | 2 +- 10 files changed, 60 insertions(+), 45 deletions(-) diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index 752e4ab63ed..22422e26b57 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -29,7 +29,7 @@ if (process.env['ELECTRON_RUN_AS_NODE'] || process.versions['electron']) { } // Pseudo NLS support -if (nlsConfig.pseudo) { +if (nlsConfig?.pseudo) { loader(['vs/nls'], function (nlsPlugin) { nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); }); diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js index 97a4d9934cd..711578cecd8 100644 --- a/src/bootstrap-fork.js +++ b/src/bootstrap-fork.js @@ -81,7 +81,7 @@ function pipeLoggingToParent() { // to start the stacktrace where the console message was being written if (process.env.VSCODE_LOG_STACK === 'true') { const stack = new Error().stack; - argsArray.push({ __$stack: stack.split('\n').slice(3).join('\n') }); + argsArray.push({ __$stack: stack?.split('\n').slice(3).join('\n') }); } try { @@ -114,7 +114,7 @@ function pipeLoggingToParent() { */ function safeSend(arg) { try { - process.send(arg); + process.send?.(arg); } catch (error) { // Can happen if the parent channel is closed meanwhile } diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index 3fc0f419b97..7dcd0badbe1 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -41,7 +41,7 @@ * extensionDevelopmentPath?: string[], * extensionTestsPath?: string, * userEnv?: { [key: string]: string | undefined }, - * appRoot?: string, + * appRoot: string, * nodeCachedDataDir?: string * }} */ const configuration = JSON.parse(args['config'] || '{}') || {}; @@ -61,7 +61,7 @@ const enableDeveloperTools = (safeProcess.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath; let developerToolsUnbind; if (enableDeveloperTools || (options && options.forceEnableDeveloperKeybindings)) { - developerToolsUnbind = registerDeveloperKeybindings(options && options.disallowReloadKeybinding); + developerToolsUnbind = registerDeveloperKeybindings(options?.disallowReloadKeybinding); } // Correctly inherit the parent's environment (TODO@sandbox non-sandboxed only) @@ -182,7 +182,7 @@ } /** - * @param {boolean} disallowReloadKeybinding + * @param {boolean | undefined} disallowReloadKeybinding * @returns {() => void} */ function registerDeveloperKeybindings(disallowReloadKeybinding) { @@ -203,6 +203,7 @@ const TOGGLE_DEV_TOOLS_KB_ALT = '123'; // F12 const RELOAD_KB = (safeProcess.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R + /** @type {((e: any) => void) | undefined} */ let listener = function (e) { const key = extractKey(e); if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) { diff --git a/src/bootstrap.js b/src/bootstrap.js index 0cb6466aeaf..4ec4c258967 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -42,7 +42,7 @@ //#region Add support for using node_modules.asar /** - * @param {string} appRoot + * @param {string | undefined} appRoot */ function enableASARSupport(appRoot) { if (!path || !Module) { @@ -124,7 +124,7 @@ //#region NLS helpers /** - * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean }} + * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean } | undefined} */ function setupNLS() { if (!path || !fs) { @@ -181,7 +181,7 @@ /** * @param {{ portable: string; applicationName: string; }} product - * @returns {{ portableDataPath: string; isPortable: boolean; }} + * @returns {{ portableDataPath: string; isPortable: boolean; } | undefined} */ function configurePortable(product) { if (!path || !fs) { diff --git a/src/main.js b/src/main.js index 2a9756ab2fa..16b7ca5f1e4 100644 --- a/src/main.js +++ b/src/main.js @@ -107,7 +107,7 @@ crashReporter.start({ // to ensure that no 'logs' folder is created on disk at a // location outside of the portable directory // (https://github.com/microsoft/vscode/issues/56651) -if (portable.isPortable) { +if (portable?.isPortable) { app.setAppLogsPath(path.join(userDataPath, 'logs')); } @@ -145,7 +145,7 @@ const nodeCachedDataDir = getNodeCachedDir(); * Support user defined locale: load it early before app('ready') * to have more things running in parallel. * - * @type {Promise} nlsConfig | undefined + * @type {Promise | undefined} */ let nlsConfigurationPromise = undefined; @@ -359,7 +359,7 @@ function getArgvConfigPath() { /** * @param {NativeParsedArgs} cliArgs - * @returns {string} + * @returns {string | null} */ function getJSFlags(cliArgs) { const jsFlags = []; @@ -387,7 +387,7 @@ function getUserDataPath(cliArgs) { return path.join(portable.portableDataPath, 'user-data'); } - return path.resolve(cliArgs['user-data-dir'] || paths.getDefaultUserDataPath(process.platform)); + return path.resolve(cliArgs['user-data-dir'] || paths.getDefaultUserDataPath()); } /** @@ -468,12 +468,14 @@ function getNodeCachedDir() { } async ensureExists() { - try { - await mkdirp(this.value); + if (typeof this.value === 'string') { + try { + await mkdirp(this.value); - return this.value; - } catch (error) { - // ignore + return this.value; + } catch (error) { + // ignore + } } } diff --git a/src/paths.js b/src/paths.js index a88927d200b..2042123d726 100644 --- a/src/paths.js +++ b/src/paths.js @@ -11,25 +11,38 @@ const path = require('path'); const os = require('os'); /** - * @param {string} platform * @returns {string} */ -function getAppDataPath(platform) { - switch (platform) { - case 'win32': return process.env['VSCODE_APPDATA'] || process.env['APPDATA'] || path.join(process.env['USERPROFILE'], 'AppData', 'Roaming'); - case 'darwin': return process.env['VSCODE_APPDATA'] || path.join(os.homedir(), 'Library', 'Application Support'); - case 'linux': return process.env['VSCODE_APPDATA'] || process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config'); - default: throw new Error('Platform not supported'); +function getDefaultUserDataPath() { + + // Support global VSCODE_APPDATA environment variable + let appDataPath = process.env['VSCODE_APPDATA']; + + // Otherwise check per platform + if (!appDataPath) { + switch (process.platform) { + case 'win32': + appDataPath = process.env['APPDATA']; + if (!appDataPath) { + const userProfile = process.env['USERPROFILE']; + if (typeof userProfile !== 'string') { + throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable'); + } + appDataPath = path.join(userProfile, 'AppData', 'Roaming'); + } + break; + case 'darwin': + appDataPath = path.join(os.homedir(), 'Library', 'Application Support'); + break; + case 'linux': + appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config'); + break; + default: + throw new Error('Platform not supported'); + } } + + return path.join(appDataPath, pkg.name); } -/** - * @param {string} platform - * @returns {string} - */ -function getDefaultUserDataPath(platform) { - return path.join(getAppDataPath(platform), pkg.name); -} - -exports.getAppDataPath = getAppDataPath; exports.getDefaultUserDataPath = getDefaultUserDataPath; diff --git a/src/vs/base/node/paths.ts b/src/vs/base/node/paths.ts index 977eaf8806f..eaf03e6e400 100644 --- a/src/vs/base/node/paths.ts +++ b/src/vs/base/node/paths.ts @@ -5,12 +5,7 @@ import { FileAccess } from 'vs/base/common/network'; -interface IPaths { - getAppDataPath(platform: string): string; - getDefaultUserDataPath(platform: string): string; -} - const pathsPath = FileAccess.asFileUri('paths', require).fsPath; -const paths = require.__$__nodeRequire(pathsPath); -export const getAppDataPath = paths.getAppDataPath; +const paths = require.__$__nodeRequire<{ getDefaultUserDataPath(): string }>(pathsPath); + export const getDefaultUserDataPath = paths.getDefaultUserDataPath; diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js index d0cb6d4286b..d6a7d0446a2 100644 --- a/src/vs/base/parts/sandbox/electron-browser/preload.js +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -223,6 +223,8 @@ * shell specific environment from the OS shell to ensure we are seeing * all development related environment variables. We do this from the * main process because it may involve spawning a shell. + * + * @returns {Promise} */ function resolveEnv() { return new Promise(function (resolve) { diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 6e753f45e78..233fcfa3a34 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -591,12 +591,13 @@ export class CodeApplication extends Disposable { return undefined; } })).filter(pendingUriToHandle => { - // if URI should be blocked, filter it out + + // If URI should be blocked, filter it out if (this.shouldBlockURI(pendingUriToHandle)) { return false; } - // filter out any protocol link that wants to open as window so that + // Filter out any protocol link that wants to open as window so that // we open the right set of windows on startup and not restore the // previous workspace too. const windowOpenable = this.getWindowOpenableFromProtocolLink(pendingUriToHandle); @@ -614,7 +615,8 @@ export class CodeApplication extends Disposable { const environmentService = this.environmentService; urlService.registerHandler({ async handleURL(uri: URI): Promise { - // if URI should be blocked, behave as if it's handled + + // If URI should be blocked, behave as if it's handled if (app.shouldBlockURI(uri)) { return true; } diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 1e67add4612..76e81a13ce7 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -245,5 +245,5 @@ export function parsePathArg(arg: string | undefined, process: NodeJS.Process): } export function parseUserDataDir(args: NativeParsedArgs, process: NodeJS.Process): string { - return parsePathArg(args['user-data-dir'], process) || path.resolve(paths.getDefaultUserDataPath(process.platform)); + return parsePathArg(args['user-data-dir'], process) || path.resolve(paths.getDefaultUserDataPath()); } From 7bfd7fb68583d57e2b6b9c0ba4922672dbe8a2b6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 9 Nov 2020 09:09:23 +0100 Subject: [PATCH 027/103] Keyboard shortcuts for switching focus between left & right sides of diff view (fix #95068) --- .../browser/parts/editor/editorCommands.ts | 73 ++++++++++++++++++- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index de74d85fd76..8eb81049a38 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -47,6 +47,9 @@ export const UNPIN_EDITOR_COMMAND_ID = 'workbench.action.unpinEditor'; export const TOGGLE_DIFF_SIDE_BY_SIDE = 'toggle.diff.renderSideBySide'; export const GOTO_NEXT_CHANGE = 'workbench.action.compareEditor.nextChange'; export const GOTO_PREVIOUS_CHANGE = 'workbench.action.compareEditor.previousChange'; +export const DIFF_FOCUS_PRIMARY_SIDE = 'workbench.action.compareEditor.focusPrimarySide'; +export const DIFF_FOCUS_SECONDARY_SIDE = 'workbench.action.compareEditor.focusSecondarySide'; +export const DIFF_FOCUS_OTHER_SIDE = 'workbench.action.compareEditor.focusOtherSide'; export const TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE = 'toggle.diff.ignoreTrimWhitespace'; export const SPLIT_EDITOR_UP = 'workbench.action.splitEditorUp'; @@ -268,18 +271,56 @@ function registerDiffEditorCommands(): void { handler: accessor => navigateInDiffEditor(accessor, false) }); - function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void { + function getActiveTextDiffEditor(accessor: ServicesAccessor): TextDiffEditor | undefined { const editorService = accessor.get(IEditorService); - const candidates = [editorService.activeEditorPane, ...editorService.visibleEditorPanes].filter(editor => editor instanceof TextDiffEditor); - if (candidates.length > 0) { - const navigator = (candidates[0]).getDiffNavigator(); + for (const editor of [editorService.activeEditorPane, ...editorService.visibleEditorPanes]) { + if (editor instanceof TextDiffEditor) { + return editor; + } + } + + return undefined; + } + + function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void { + const activeTextDiffEditor = getActiveTextDiffEditor(accessor); + + if (activeTextDiffEditor) { + const navigator = activeTextDiffEditor.getDiffNavigator(); if (navigator) { next ? navigator.next() : navigator.previous(); } } } + enum FocusTextDiffEditorMode { + Original, + Modified, + Toggle + } + + function focusInDiffEditor(accessor: ServicesAccessor, mode: FocusTextDiffEditorMode): void { + const activeTextDiffEditor = getActiveTextDiffEditor(accessor); + + if (activeTextDiffEditor) { + switch (mode) { + case FocusTextDiffEditorMode.Original: + activeTextDiffEditor.getControl()?.getOriginalEditor().focus(); + break; + case FocusTextDiffEditorMode.Modified: + activeTextDiffEditor.getControl()?.getModifiedEditor().focus(); + break; + case FocusTextDiffEditorMode.Toggle: + if (activeTextDiffEditor.getControl()?.getModifiedEditor().hasWidgetFocus()) { + return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original); + } else { + return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified); + } + } + } + } + function toggleDiffSideBySide(accessor: ServicesAccessor): void { const configurationService = accessor.get(IConfigurationService); @@ -302,6 +343,30 @@ function registerDiffEditorCommands(): void { handler: accessor => toggleDiffSideBySide(accessor) }); + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_FOCUS_PRIMARY_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_FOCUS_SECONDARY_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_FOCUS_OTHER_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Toggle) + }); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: TOGGLE_DIFF_SIDE_BY_SIDE, From 3517c02303b6f54de940fe32f523e3fc865b8042 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 9 Nov 2020 09:15:24 +0100 Subject: [PATCH 028/103] files - stop checking for BOM before writing This would always result in a file read which can be slow in remote connections. --- .../partsSplash.contribution.ts | 2 +- .../textfile/browser/textFileService.ts | 24 ++----------------- .../textfile/common/textFileEditorModel.ts | 3 +-- .../services/textfile/common/textfiles.ts | 10 -------- .../test/common/textFileService.io.test.ts | 14 +++++------ 5 files changed, 11 insertions(+), 42 deletions(-) diff --git a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts index dc2cdaeaf94..76c12fa6a45 100644 --- a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts +++ b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts @@ -102,7 +102,7 @@ class PartsSplash { layoutInfo, baseTheme }), - { encoding: 'utf8', overwriteEncoding: true } + { encoding: 'utf8' } ); if (baseTheme !== this._lastBaseTheme || colorInfo.editorBackground !== this._lastBackground) { diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 2ef157dadcc..4ecca900e14 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -34,7 +34,7 @@ import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/commo import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; -import { UTF8, UTF8_with_bom, UTF16be, UTF16le, encodingExists, UTF8_BOM, detectEncodingByBOMFromBuffer, toEncodeReadable, toDecodeStream, IDecodeStreamResult } from 'vs/workbench/services/textfile/common/encoding'; +import { UTF8, UTF8_with_bom, UTF16be, UTF16le, encodingExists, toEncodeReadable, toDecodeStream, IDecodeStreamResult } from 'vs/workbench/services/textfile/common/encoding'; import { consumeStream } from 'vs/base/common/stream'; import { IModeService } from 'vs/editor/common/services/modeService'; import { ILogService } from 'vs/platform/log/common/log'; @@ -532,7 +532,6 @@ export class EncodingOracle extends Disposable implements IResourceEncodings { @ITextResourceConfigurationService private textResourceConfigurationService: ITextResourceConfigurationService, @IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService, @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IFileService private fileService: IFileService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService ) { super(); @@ -569,26 +568,7 @@ export class EncodingOracle extends Disposable implements IResourceEncodings { async getWriteEncoding(resource: URI, options?: IWriteTextFileOptions): Promise<{ encoding: string, addBOM: boolean }> { const { encoding, hasBOM } = await this.getPreferredWriteEncoding(resource, options ? options.encoding : undefined); - // Some encodings come with a BOM automatically - if (hasBOM) { - return { encoding, addBOM: true }; - } - - // Ensure that we preserve an existing BOM if found for UTF8 - // unless we are instructed to overwrite the encoding - const overwriteEncoding = options?.overwriteEncoding; - if (!overwriteEncoding && encoding === UTF8) { - try { - const buffer = (await this.fileService.readFile(resource, { length: UTF8_BOM.length })).value; - if (detectEncodingByBOMFromBuffer(buffer, buffer.byteLength) === UTF8_with_bom) { - return { encoding, addBOM: true }; - } - } catch (error) { - // ignore - file might not exist - } - } - - return { encoding, addBOM: false }; + return { encoding, addBOM: hasBOM }; } async getPreferredWriteEncoding(resource: URI, preferredEncoding?: string): Promise { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index ffcc91f57fa..40827e31f2d 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -810,7 +810,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil try { const stat = await this.textFileService.write(lastResolvedFileStat.resource, textFileEditorModel.createSnapshot(), { overwriteReadonly: options.overwriteReadonly, - overwriteEncoding: options.overwriteEncoding, mtime: lastResolvedFileStat.mtime, encoding: this.getEncoding(), etag: (options.ignoreModifiedSince || !this.filesConfigurationService.preventSaveConflicts(lastResolvedFileStat.resource, textFileEditorModel.getMode())) ? ETAG_DISABLED : lastResolvedFileStat.etag, @@ -978,7 +977,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } if (!this.inConflictMode) { - this.save({ overwriteEncoding: true }); + this.save(); } } diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 692976da0af..73618d52304 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -124,11 +124,6 @@ export interface IWriteTextFileOptions extends IWriteFileOptions { */ encoding?: string; - /** - * If set to true, will enforce the selected encoding and not perform any detection using BOMs. - */ - overwriteEncoding?: boolean; - /** * Whether to overwrite a file even if it is readonly. */ @@ -370,11 +365,6 @@ export interface ITextFileSaveOptions extends ISaveOptions { */ overwriteReadonly?: boolean; - /** - * Overwrite the encoding of the file on disk as configured. - */ - overwriteEncoding?: boolean; - /** * Save the file with elevated privileges. * diff --git a/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts index 9aeeb40b349..71f8242e67b 100644 --- a/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts @@ -310,13 +310,13 @@ export default function createSuite(params: Params) { detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF8_with_bom); - // ensure BOM preserved - await service.write(resource, content, { encoding: UTF8 }); + // ensure BOM preserved if enforced + await service.write(resource, content, { encoding: UTF8_with_bom }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF8_with_bom); // allow to remove BOM - await service.write(resource, content, { encoding: UTF8, overwriteEncoding: true }); + await service.write(resource, content, { encoding: UTF8 }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, null); @@ -338,13 +338,13 @@ export default function createSuite(params: Params) { detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF8_with_bom); - // ensure BOM preserved - await service.write(resource, model.createSnapshot(), { encoding: UTF8 }); + // ensure BOM preserved if enforced + await service.write(resource, model.createSnapshot(), { encoding: UTF8_with_bom }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF8_with_bom); // allow to remove BOM - await service.write(resource, model.createSnapshot(), { encoding: UTF8, overwriteEncoding: true }); + await service.write(resource, model.createSnapshot(), { encoding: UTF8 }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, null); @@ -360,7 +360,7 @@ export default function createSuite(params: Params) { let detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF8_with_bom); - await service.write(resource, 'Hello World'); + await service.write(resource, 'Hello World', { encoding: detectedEncoding! }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF8_with_bom); }); From abcc6218725bc14c68c9780691ded463e48af4c4 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 9 Nov 2020 09:18:30 +0100 Subject: [PATCH 029/103] Rename option to `scrollByPage` --- .../browser/ui/scrollbar/abstractScrollbar.ts | 20 +++++++++---------- .../ui/scrollbar/horizontalScrollbar.ts | 2 +- .../browser/ui/scrollbar/scrollableElement.ts | 2 +- .../ui/scrollbar/scrollableElementOptions.ts | 4 ++-- .../browser/ui/scrollbar/scrollbarState.ts | 2 +- .../browser/ui/scrollbar/verticalScrollbar.ts | 2 +- .../ui/scrollbar/scrollbarState.test.ts | 4 ++-- .../editorScrollbar/editorScrollbar.ts | 2 +- src/vs/editor/common/config/editorOptions.ts | 8 ++++---- .../viewLayout/editorLayoutProvider.test.ts | 2 +- src/vs/monaco.d.ts | 4 ++-- 11 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts index 5baba5c997f..133ec935046 100644 --- a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts @@ -38,14 +38,14 @@ export interface AbstractScrollbarOptions { visibility: ScrollbarVisibility; extraScrollbarClassName: string; scrollable: Scrollable; - gutterClickMovesByPage: boolean; + scrollByPage: boolean; } export abstract class AbstractScrollbar extends Widget { protected _host: ScrollbarHost; protected _scrollable: Scrollable; - protected _gutterClickMovesByPage: boolean; + protected _scrollByPage: boolean; private _lazyRender: boolean; protected _scrollbarState: ScrollbarState; private _visibilityController: ScrollbarVisibilityController; @@ -61,7 +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._scrollByPage = opts.scrollByPage; 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()); @@ -214,14 +214,12 @@ export abstract class AbstractScrollbar extends Widget { offsetY = e.posy - domNodePosition.top; } - 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); + const offset = this._mouseDownRelativePosition(offsetX, offsetY); + this._setDesiredScrollPositionNow( + this._scrollByPage + ? this._scrollbarState.getDesiredScrollPositionFromOffsetPaged(offset) + : this._scrollbarState.getDesiredScrollPositionFromOffset(offset) + ); if (e.leftButton) { e.preventDefault(); diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index 9686d459bca..636751f72c6 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -34,7 +34,7 @@ export class HorizontalScrollbar extends AbstractScrollbar { visibility: options.horizontal, extraScrollbarClassName: 'horizontal', scrollable: scrollable, - gutterClickMovesByPage: options.gutterClickMovesByPage + scrollByPage: options.scrollByPage }); if (options.horizontalHasArrows) { diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index bcde9bce98e..115864f3dfb 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -619,7 +619,7 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme verticalHasArrows: (typeof opts.verticalHasArrows !== 'undefined' ? opts.verticalHasArrows : false), verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0), - gutterClickMovesByPage: (typeof opts.gutterClickMovesByPage !== 'undefined' ? opts.gutterClickMovesByPage : false) + scrollByPage: (typeof opts.scrollByPage !== 'undefined' ? opts.scrollByPage : 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 491986dcc80..556d2083912 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts @@ -118,7 +118,7 @@ export interface ScrollableElementCreationOptions { * Scroll gutter clicks move by page vs. jump to position. * Defaults to false. */ - gutterClickMovesByPage?: boolean; + scrollByPage?: boolean; } export interface ScrollableElementChangeOptions { @@ -151,5 +151,5 @@ export interface ScrollableElementResolvedOptions { verticalScrollbarSize: number; verticalSliderSize: number; verticalHasArrows: boolean; - gutterClickMovesByPage: boolean; + scrollByPage: boolean; } diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts index 0929cd2b297..d0d98b9a810 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts @@ -192,7 +192,7 @@ 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`. */ - public getDesiredScrollPositionFromOffsetAbsolute(offset: number): number { + public getDesiredScrollPositionFromOffset(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 505dcc7f506..4f8e8f30a19 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -34,7 +34,7 @@ export class VerticalScrollbar extends AbstractScrollbar { visibility: options.vertical, extraScrollbarClassName: 'vertical', scrollable: scrollable, - gutterClickMovesByPage: options.gutterClickMovesByPage + scrollByPage: options.scrollByPage }); 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 ec2d384ede4..4d3c0944a0c 100644 --- a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts +++ b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts @@ -18,7 +18,7 @@ suite('ScrollbarState', () => { assert.equal(actual.getSliderSize(), 20); assert.equal(actual.getSliderPosition(), 249); - assert.equal(actual.getDesiredScrollPositionFromOffsetAbsolute(259), 32849); + assert.equal(actual.getDesiredScrollPositionFromOffset(259), 32849); // 259 is greater than 230 so page down, 32787 + 339 = 33126 assert.equal(actual.getDesiredScrollPositionFromOffsetPaged(259), 33126); @@ -44,7 +44,7 @@ suite('ScrollbarState', () => { assert.equal(actual.getSliderSize(), 20); assert.equal(actual.getSliderPosition(), 230); - assert.equal(actual.getDesiredScrollPositionFromOffsetAbsolute(240 + 12), 32811); + assert.equal(actual.getDesiredScrollPositionFromOffset(240 + 12), 32811); // 240 + 12 = 252; greater than 230 so page down, 32787 + 339 = 33126 assert.equal(actual.getDesiredScrollPositionFromOffsetPaged(240 + 12), 33126); diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts index 595dd8670bc..5a6df759b50 100644 --- a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts +++ b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts @@ -56,7 +56,7 @@ export class EditorScrollbar extends ViewPart { mouseWheelScrollSensitivity: mouseWheelScrollSensitivity, fastScrollSensitivity: fastScrollSensitivity, scrollPredominantAxis: scrollPredominantAxis, - gutterClickMovesByPage: scrollbar.gutterClickMovesByPage, + scrollByPage: scrollbar.scrollByPage, }; 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 d18a74f656c..636eceaf91a 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2924,7 +2924,7 @@ export interface IEditorScrollbarOptions { * Scroll gutter clicks move by page vs jump to position. * Defaults to false. */ - gutterClickMovesByPage?: boolean; + scrollByPage?: boolean; } export interface InternalEditorScrollbarOptions { @@ -2940,7 +2940,7 @@ export interface InternalEditorScrollbarOptions { readonly horizontalSliderSize: number; readonly verticalScrollbarSize: number; readonly verticalSliderSize: number; - readonly gutterClickMovesByPage: boolean; + readonly scrollByPage: boolean; } function _scrollbarVisibilityFromString(visibility: string | undefined, defaultValue: ScrollbarVisibility): ScrollbarVisibility { @@ -2972,7 +2972,7 @@ class EditorScrollbar extends BaseEditorOption { horizontalSliderSize: EditorOptions.scrollbar.defaultValue.horizontalSliderSize, verticalScrollbarSize: input.verticalScrollbarWidth, verticalSliderSize: EditorOptions.scrollbar.defaultValue.verticalSliderSize, - gutterClickMovesByPage: EditorOptions.scrollbar.defaultValue.gutterClickMovesByPage, + scrollByPage: EditorOptions.scrollbar.defaultValue.scrollByPage, }; options._write(EditorOption.scrollbar, scrollbarOptions); const lineNumbersOptions: InternalEditorRenderLineNumbersOptions = { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 86dc6867ae1..29db9c7ef1e 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3664,7 +3664,7 @@ declare namespace monaco.editor { * Scroll gutter clicks move by page vs jump to position. * Defaults to false. */ - gutterClickMovesByPage?: boolean; + scrollByPage?: boolean; } export interface InternalEditorScrollbarOptions { @@ -3680,7 +3680,7 @@ declare namespace monaco.editor { readonly horizontalSliderSize: number; readonly verticalScrollbarSize: number; readonly verticalSliderSize: number; - readonly gutterClickMovesByPage: boolean; + readonly scrollByPage: boolean; } /** From 7ca4a47ba5e4b6893d53a0d068d5688fb75ff36b Mon Sep 17 00:00:00 2001 From: Erich Gamma Date: Mon, 9 Nov 2020 10:04:31 +0100 Subject: [PATCH 030/103] add vscode-test --- .vscode/notebooks/grooming-delta.github-issues | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.vscode/notebooks/grooming-delta.github-issues b/.vscode/notebooks/grooming-delta.github-issues index a91e6e0156f..97f86b4f386 100644 --- a/.vscode/notebooks/grooming-delta.github-issues +++ b/.vscode/notebooks/grooming-delta.github-issues @@ -158,7 +158,22 @@ { "kind": 1, "language": "markdown", - "value": "# vscode-chrome-debug", + "value": "# vscode-test" + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-test is:issue closed:>$since" + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-test is:issue created:>$since" + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-chrome-debug (deprecated)", "editable": true }, { From 875c2ced66174e98d5a732e9b51110181dd52b33 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 9 Nov 2020 10:29:20 +0100 Subject: [PATCH 031/103] Finalize FoldingRangeProvider.onDidChangeFoldingRanges. Fixes #108929 --- src/vs/vscode.d.ts | 6 ++++++ src/vs/vscode.proposed.d.ts | 11 ----------- .../workbench/api/common/extHostLanguageFeatures.ts | 6 ++---- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index eb7426c56be..c5534502c95 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -4325,6 +4325,12 @@ declare module 'vscode' { * [Folding](https://code.visualstudio.com/docs/editor/codebasics#_folding) in the editor. */ export interface FoldingRangeProvider { + + /** + * An optional event to signal that the folding ranges from this provider have changed. + */ + onDidChangeFoldingRanges?: Event; + /** * Returns a list of folding ranges or null and undefined if the provider * does not want to participate or was cancelled. diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index e289a214eaa..f0596e4e745 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2134,15 +2134,4 @@ declare module 'vscode' { notebook: NotebookDocument | undefined; } //#endregion - - //#region https://github.com/microsoft/vscode/issues/108929 FoldingRangeProvider.onDidChangeFoldingRanges @aeschli - export interface FoldingRangeProvider2 extends FoldingRangeProvider { - - /** - * An optional event to signal that the folding ranges from this provider have changed. - */ - onDidChangeFoldingRanges?: Event; - - } - //#endregion } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index b26a4ef0405..e4fab08b3fb 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -32,7 +32,6 @@ import { IdGenerator } from 'vs/base/common/idGenerator'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { Cache } from './cache'; import { StopWatch } from 'vs/base/common/stopwatch'; -import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; // --- adapter @@ -1814,7 +1813,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token), undefined); } - registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider2): vscode.Disposable { + registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { const handle = this._nextHandle(); const eventHandle = typeof provider.onDidChangeFoldingRanges === 'function' ? this._nextHandle() : undefined; @@ -1823,8 +1822,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF let result = this._createDisposable(handle); if (eventHandle !== undefined) { - checkProposedApiEnabled(extension); - const subscription = provider.onDidChangeFoldingRanges!(_ => this._proxy.$emitFoldingRangeEvent(eventHandle)); + const subscription = provider.onDidChangeFoldingRanges!(() => this._proxy.$emitFoldingRangeEvent(eventHandle)); result = Disposable.from(result, subscription); } From 5a534883c79ac373ffd8598317b13fc20ab5ddc3 Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Mon, 9 Nov 2020 04:55:58 -0500 Subject: [PATCH 032/103] Fixes #110212 --- .../browser/links/terminalValidatedLocalLinkProvider.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts index 5c0e8243fd4..fb420ef52ae 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts @@ -49,6 +49,8 @@ export const unixLineAndColumnMatchIndex = 11; // Each line and column clause have 6 groups (ie no. of expressions in round brackets) export const lineAndColumnClauseGroupCount = 6; +const MAX_LENGTH = 2000; + export class TerminalValidatedLocalLinkProvider extends TerminalBaseLinkProvider { constructor( private readonly _xterm: Terminal, @@ -85,6 +87,9 @@ export class TerminalValidatedLocalLinkProvider extends TerminalBaseLinkProvider } const text = getXtermLineContent(this._xterm.buffer.active, startLine, endLine, this._xterm.cols); + if (text.length > MAX_LENGTH) { + return []; + } // clone regex to do a global search on text const rex = new RegExp(this._localLinkRegex, 'g'); From aede2434b07a6f84ca42b94323927f415967f8cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 9 Nov 2020 11:07:58 +0100 Subject: [PATCH 033/103] fixes microsoft/vscode-remote-release#3180 --- .../contrib/cli/node/cli.contribution.ts | 192 +++++++++--------- 1 file changed, 91 insertions(+), 101 deletions(-) diff --git a/src/vs/workbench/contrib/cli/node/cli.contribution.ts b/src/vs/workbench/contrib/cli/node/cli.contribution.ts index 2000693fdf3..30972a41158 100644 --- a/src/vs/workbench/contrib/cli/node/cli.contribution.ts +++ b/src/vs/workbench/contrib/cli/node/cli.contribution.ts @@ -8,12 +8,8 @@ import * as path from 'vs/base/common/path'; import * as cp from 'child_process'; import * as pfs from 'vs/base/node/pfs'; import * as extpath from 'vs/base/node/extpath'; -import * as platform from 'vs/base/common/platform'; import { promisify } from 'util'; -import { Action } from 'vs/base/common/actions'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import product from 'vs/platform/product/common/product'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; @@ -21,6 +17,9 @@ import Severity from 'vs/base/common/severity'; import { ILogService } from 'vs/platform/log/common/log'; import { FileAccess } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IsMacNativeContext } from 'vs/platform/contextkey/common/contextkeys'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; function ignore(code: string, value: T): (err: any) => Promise { return err => err.code === code ? Promise.resolve(value) : Promise.reject(err); @@ -39,45 +38,65 @@ function isAvailable(): Promise { return Promise.resolve(pfs.exists(getSource())); } -class InstallAction extends Action { +const category = nls.localize('shellCommand', "Shell Command"); - static readonly ID = 'workbench.action.installCommandLine'; - static readonly LABEL = nls.localize('install', "Install '{0}' command in PATH", product.applicationName); +class InstallAction extends Action2 { - constructor( - id: string, - label: string, - @INotificationService private readonly notificationService: INotificationService, - @IDialogService private readonly dialogService: IDialogService, - @ILogService private readonly logService: ILogService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.installCommandLine', + title: { + value: nls.localize('install', "Install '{0}' command in PATH", product.applicationName), + original: `Shell Command: Install \'${product.applicationName}\' command in PATH` + }, + category, + f1: true, + precondition: ContextKeyExpr.and(IsMacNativeContext, ContextKeyExpr.equals('remoteName', '')) + }); } - private get target(): string { - return `/usr/local/bin/${this.productService.applicationName}`; - } + run(accessor: ServicesAccessor): Promise { + const productService = accessor.get(IProductService); + const notificationService = accessor.get(INotificationService); + const logService = accessor.get(ILogService); + const dialogService = accessor.get(IDialogService); + const target = `/usr/local/bin/${productService.applicationName}`; - run(): Promise { return isAvailable().then(isAvailable => { if (!isAvailable) { const message = nls.localize('not available', "This command is not available"); - this.notificationService.info(message); + notificationService.info(message); return undefined; } - return this.isInstalled() + return this.isInstalled(target) .then(isInstalled => { if (!isAvailable || isInstalled) { return Promise.resolve(null); } else { - return pfs.unlink(this.target) + return pfs.unlink(target) .then(undefined, ignore('ENOENT', null)) - .then(() => pfs.symlink(getSource(), this.target)) + .then(() => pfs.symlink(getSource(), target)) .then(undefined, err => { if (err.code === 'EACCES' || err.code === 'ENOENT') { - return this.createBinFolderAndSymlinkAsAdmin(); + return new Promise((resolve, reject) => { + const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; + + dialogService.show(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), buttons, { cancelId: 1 }).then(result => { + switch (result.choice) { + case 0 /* OK */: + const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'' + getSource() + '\' \'' + target + '\'\\" with administrator privileges"'; + + promisify(cp.exec)(command, {}) + .then(undefined, _ => Promise.reject(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'.")))) + .then(() => resolve(), reject); + break; + case 1 /* Cancel */: + reject(new Error(nls.localize('aborted', "Aborted"))); + break; + } + }); + }); } return Promise.reject(err); @@ -85,113 +104,84 @@ class InstallAction extends Action { } }) .then(() => { - this.logService.trace('cli#install', this.target); - this.notificationService.info(nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", this.productService.applicationName)); + logService.trace('cli#install', target); + notificationService.info(nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", productService.applicationName)); }); }); } - private isInstalled(): Promise { - return pfs.lstat(this.target) + private isInstalled(target: string): Promise { + return pfs.lstat(target) .then(stat => stat.isSymbolicLink()) - .then(() => extpath.realpath(this.target)) + .then(() => extpath.realpath(target)) .then(link => link === getSource()) .then(undefined, ignore('ENOENT', false)); } - - private createBinFolderAndSymlinkAsAdmin(): Promise { - return new Promise((resolve, reject) => { - const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; - - this.dialogService.show(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), buttons, { cancelId: 1 }).then(result => { - switch (result.choice) { - case 0 /* OK */: - const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'' + getSource() + '\' \'' + this.target + '\'\\" with administrator privileges"'; - - promisify(cp.exec)(command, {}) - .then(undefined, _ => Promise.reject(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'.")))) - .then(() => resolve(), reject); - break; - case 1 /* Cancel */: - reject(new Error(nls.localize('aborted', "Aborted"))); - break; - } - }); - }); - } } -class UninstallAction extends Action { +class UninstallAction extends Action2 { - static readonly ID = 'workbench.action.uninstallCommandLine'; - static readonly LABEL = nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName); - - constructor( - id: string, - label: string, - @INotificationService private readonly notificationService: INotificationService, - @ILogService private readonly logService: ILogService, - @IDialogService private readonly dialogService: IDialogService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.uninstallCommandLine', + title: { + value: nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName), + original: `Shell Command: Uninstall \'${product.applicationName}\' command from PATH` + }, + category, + f1: true, + precondition: ContextKeyExpr.and(IsMacNativeContext, ContextKeyExpr.equals('remoteName', '')) + }); } - private get target(): string { - return `/usr/local/bin/${this.productService.applicationName}`; - } + run(accessor: ServicesAccessor): Promise { + const productService = accessor.get(IProductService); + const notificationService = accessor.get(INotificationService); + const logService = accessor.get(ILogService); + const dialogService = accessor.get(IDialogService); + const target = `/usr/local/bin/${productService.applicationName}`; - run(): Promise { return isAvailable().then(isAvailable => { if (!isAvailable) { const message = nls.localize('not available', "This command is not available"); - this.notificationService.info(message); + notificationService.info(message); return undefined; } const uninstall = () => { - return pfs.unlink(this.target) + return pfs.unlink(target) .then(undefined, ignore('ENOENT', null)); }; return uninstall().then(undefined, err => { if (err.code === 'EACCES') { - return this.deleteSymlinkAsAdmin(); + return new Promise(async (resolve, reject) => { + const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; + + const { choice } = await dialogService.show(Severity.Info, nls.localize('warnEscalationUninstall', "Code will now prompt with 'osascript' for Administrator privileges to uninstall the shell command."), buttons, { cancelId: 1 }); + switch (choice) { + case 0 /* OK */: + const command = 'osascript -e "do shell script \\"rm \'' + target + '\'\\" with administrator privileges"'; + + promisify(cp.exec)(command, {}) + .then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", target)))) + .then(() => resolve(), reject); + break; + case 1 /* Cancel */: + reject(new Error(nls.localize('aborted', "Aborted"))); + break; + } + }); } return Promise.reject(err); }).then(() => { - this.logService.trace('cli#uninstall', this.target); - this.notificationService.info(nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", this.productService.applicationName)); + logService.trace('cli#uninstall', target); + notificationService.info(nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", productService.applicationName)); }); }); } - - private deleteSymlinkAsAdmin(): Promise { - return new Promise(async (resolve, reject) => { - const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; - - const { choice } = await this.dialogService.show(Severity.Info, nls.localize('warnEscalationUninstall', "Code will now prompt with 'osascript' for Administrator privileges to uninstall the shell command."), buttons, { cancelId: 1 }); - switch (choice) { - case 0 /* OK */: - const command = 'osascript -e "do shell script \\"rm \'' + this.target + '\'\\" with administrator privileges"'; - - promisify(cp.exec)(command, {}) - .then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", this.target)))) - .then(() => resolve(), reject); - break; - case 1 /* Cancel */: - reject(new Error(nls.localize('aborted', "Aborted"))); - break; - } - }); - } } -if (platform.isMacintosh) { - const category = nls.localize('shellCommand', "Shell Command"); - - const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); - workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(InstallAction), `Shell Command: Install \'${product.applicationName}\' command in PATH`, category); - workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(UninstallAction), `Shell Command: Uninstall \'${product.applicationName}\' command from PATH`, category); -} +registerAction2(InstallAction); +registerAction2(UninstallAction); From 85958bcecd86acc9023e0d40439e632b65e4a808 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Nov 2020 11:23:22 +0100 Subject: [PATCH 034/103] fix https://github.com/microsoft/vscode/issues/97451 --- src/vs/editor/contrib/gotoSymbol/goToCommands.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index 232aab8d0f8..83503350528 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -162,6 +162,9 @@ abstract class SymbolNavigationAction extends EditorAction { if (!range) { range = reference.range; } + if (!range) { + return undefined; + } const targetEditor = await editorService.openCodeEditor({ resource: reference.uri, From 35ff2c0df8d1705af11310b004824348b8c6b32b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 9 Nov 2020 11:24:12 +0100 Subject: [PATCH 035/103] fixes #110012 --- src/vs/platform/list/browser/listService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index c71a163ff96..94b55b1829d 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -833,7 +833,7 @@ function workbenchTreeDataPreamble(treeExpandOnFolderClick) + expandOnlyOnTwistieClick: options.expandOnlyOnTwistieClick ?? !configurationService.getValue(treeExpandOnFolderClick) } as TOptions }; } @@ -935,7 +935,7 @@ class WorkbenchTreeInternals { if (e.affectsConfiguration(openModeSettingKey)) { newOptions = { ...newOptions, expandOnlyOnDoubleClick: configurationService.getValue(openModeSettingKey) === 'doubleClick' }; } - if (e.affectsConfiguration(treeExpandOnFolderClick)) { + if (e.affectsConfiguration(treeExpandOnFolderClick) && options.expandOnlyOnTwistieClick === undefined) { newOptions = { ...newOptions, expandOnlyOnTwistieClick: !configurationService.getValue(treeExpandOnFolderClick) }; } if (Object.keys(newOptions).length > 0) { From 776541c380657f8e1bf9f4acc459a33d84f755bd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 9 Nov 2020 11:30:13 +0100 Subject: [PATCH 036/103] Setting to Disable Split Editor on Drag and Drop (fix #71016) --- .../workbench/browser/parts/editor/editor.ts | 3 +- .../browser/parts/editor/editorDropTarget.ts | 44 +++++++++++++------ .../browser/workbench.contribution.ts | 5 +++ src/vs/workbench/common/editor.ts | 1 + 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 8c5f62153b8..c05ce6d79b2 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -38,7 +38,8 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { openSideBySideDirection: 'right', closeEmptyGroups: true, labelFormat: 'default', - splitSizing: 'distribute' + splitSizing: 'distribute', + splitOnDragAndDrop: true }; export function impactsEditorPartOptions(event: IConfigurationChangeEvent): boolean { diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 7c63cf6e530..50f571563cd 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -12,7 +12,7 @@ import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { IEditorIdentifier, EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; -import { GroupDirection, MergeGroupMode, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { GroupDirection, IEditorGroupsService, MergeGroupMode, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -55,7 +55,8 @@ class DropOverlay extends Themable { @IInstantiationService private instantiationService: IInstantiationService, @IFileDialogService private readonly fileDialogService: IFileDialogService, @IEditorService private readonly editorService: IEditorService, - @INotificationService private readonly notificationService: INotificationService + @INotificationService private readonly notificationService: INotificationService, + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService ) { super(themeService); @@ -144,8 +145,14 @@ class DropOverlay extends Themable { } } - // Position overlay - this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup); + // Position overlay and conditionally enable or disable + // editor group splitting support based on setting and + // keymodifiers used. + let splitOnDragAndDrop = !!this.editorGroupService.partOptions.splitOnDragAndDrop; + if (this.isToggleSplitOperation(e)) { + splitOnDragAndDrop = !splitOnDragAndDrop; + } + this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup, splitOnDragAndDrop); // Make sure to stop any running cleanup scheduler to remove the overlay if (this.cleanupOverlayScheduler.isScheduled()) { @@ -363,24 +370,33 @@ class DropOverlay extends Themable { return (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh); } - private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean): void { + private isToggleSplitOperation(e: DragEvent): boolean { + return (e.altKey && !isMacintosh) || (e.shiftKey && isMacintosh); + } + + private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean, enableSplitting: boolean): void { const preferSplitVertically = this.accessor.partOptions.openSideBySideDirection === 'right'; const editorControlWidth = this.groupView.element.clientWidth; const editorControlHeight = this.groupView.element.clientHeight - this.getOverlayOffsetHeight(); let edgeWidthThresholdFactor: number; - if (isDraggingGroup) { - edgeWidthThresholdFactor = preferSplitVertically ? 0.3 : 0.1; // give larger threshold when dragging group depending on preferred split direction - } else { - edgeWidthThresholdFactor = 0.1; // 10% threshold to split if dragging editors - } - let edgeHeightThresholdFactor: number; - if (isDraggingGroup) { - edgeHeightThresholdFactor = preferSplitVertically ? 0.1 : 0.3; // give larger threshold when dragging group depending on preferred split direction + if (enableSplitting) { + if (isDraggingGroup) { + edgeWidthThresholdFactor = preferSplitVertically ? 0.3 : 0.1; // give larger threshold when dragging group depending on preferred split direction + } else { + edgeWidthThresholdFactor = 0.1; // 10% threshold to split if dragging editors + } + + if (isDraggingGroup) { + edgeHeightThresholdFactor = preferSplitVertically ? 0.1 : 0.3; // give larger threshold when dragging group depending on preferred split direction + } else { + edgeHeightThresholdFactor = 0.1; // 10% threshold to split if dragging editors + } } else { - edgeHeightThresholdFactor = 0.1; // 10% threshold to split if dragging editors + edgeWidthThresholdFactor = 0; + edgeHeightThresholdFactor = 0; } const edgeWidthThreshold = editorControlWidth * edgeWidthThresholdFactor; diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index e6c8f1dbfd8..fdc99470bb5 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -108,6 +108,11 @@ import { isStandalone } from 'vs/base/browser/browser'; ], 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'splitSizing' }, "Controls the sizing of editor groups when splitting them.") }, + 'workbench.editor.splitOnDragAndDrop': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('splitOnDragAndDrop', "Controls if editor groups can be split from drag and drop operations by dropping an editor or file on the edges of the editor area.") + }, 'workbench.editor.focusRecentEditorAfterClose': { 'type': 'boolean', 'description': nls.localize('focusRecentEditorAfterClose', "Controls whether tabs are closed in most recently used order or from left to right."), diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 30a375204cd..11b96aea9a5 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1266,6 +1266,7 @@ interface IEditorPartConfiguration { labelFormat?: 'default' | 'short' | 'medium' | 'long'; restoreViewState?: boolean; splitSizing?: 'split' | 'distribute'; + splitOnDragAndDrop?: boolean; limit?: { enabled?: boolean; value?: number; From 7efc22cc85d8163863c8b97f45289c7f86b6a653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 9 Nov 2020 11:37:03 +0100 Subject: [PATCH 037/103] git.publish: do not await notification resolution related to #109977 --- extensions/github/src/publish.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/github/src/publish.ts b/extensions/github/src/publish.ts index f099255924e..b1719635c96 100644 --- a/extensions/github/src/publish.ts +++ b/extensions/github/src/publish.ts @@ -202,9 +202,9 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository) } const openInGitHub = 'Open In GitHub'; - const action = await vscode.window.showInformationMessage(`Successfully published the '${owner}/${repo}' repository on GitHub.`, openInGitHub); - - if (action === openInGitHub) { - vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(githubRepository.html_url)); - } + vscode.window.showInformationMessage(`Successfully published the '${owner}/${repo}' repository on GitHub.`, openInGitHub).then(action => { + if (action === openInGitHub) { + vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(githubRepository.html_url)); + } + }); } From 4439de9a3b306eef9eda4d12e16133b99fe5bbf1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Nov 2020 11:38:09 +0100 Subject: [PATCH 038/103] tweak setting name `editor.suggest.showInlineDetails`, #109690 --- src/vs/editor/common/config/editorOptions.ts | 12 ++++++------ .../editor/contrib/suggest/suggestWidgetRenderer.ts | 2 +- src/vs/monaco.d.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 636eceaf91a..c509ff3da7c 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -3041,7 +3041,7 @@ export interface ISuggestOptions { /** * Show details inline with the label. Defaults to true. */ - showStatusDetailsInline?: boolean; + showInlineDetails?: boolean; /** * Show method-suggestions. */ @@ -3165,7 +3165,7 @@ class EditorSuggest extends BaseEditorOption Date: Mon, 9 Nov 2020 11:41:18 +0100 Subject: [PATCH 039/103] window - some renames :lipstick: --- .../electron-main/windowsMainService.ts | 157 +++++++++--------- 1 file changed, 83 insertions(+), 74 deletions(-) diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index b87cc3744ea..6d622945d68 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -72,7 +72,7 @@ interface IOpenBrowserWindowOptions { initialStartup?: boolean; - fileInputs?: IFileInputs; + filesToOpen?: IFilesToOpen; forceNewWindow?: boolean; forceNewTabbedWindow?: boolean; @@ -87,7 +87,7 @@ interface IPathParseOptions { remoteAuthority?: string; } -interface IFileInputs { +interface IFilesToOpen { filesToOpenOrCreate: IPath[]; filesToDiff: IPath[]; filesToWait?: IPathsToWaitFor; @@ -385,17 +385,17 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic this.logService.trace('windowsManager#open'); openConfig = this.validateOpenConfig(openConfig); - const pathsToOpen = this.getPathsToOpen(openConfig); - this.logService.trace('windowsManager#open pathsToOpen', pathsToOpen); - const foldersToAdd: IFolderPathToOpen[] = []; const foldersToOpen: IFolderPathToOpen[] = []; const workspacesToOpen: IWorkspacePathToOpen[] = []; const workspacesToRestore: IWorkspacePathToOpen[] = []; - const emptyToRestore: IEmptyWindowBackupInfo[] = []; // empty windows with backupPath + const emptyToRestore: IEmptyWindowBackupInfo[] = []; + let filesToOpen: IFilesToOpen | undefined; + let emptyToOpen = 0; - let emptyToOpen: number = 0; - let fileInputs: IFileInputs | undefined; // collect all file inputs + // Identify things to open from open config + const pathsToOpen = this.getPathsToOpen(openConfig); + this.logService.trace('windowsManager#open pathsToOpen', pathsToOpen); for (const path of pathsToOpen) { if (isFolderPathToOpen(path)) { if (openConfig.addMode) { @@ -408,10 +408,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } else if (isWorkspacePathToOpen(path)) { workspacesToOpen.push(path); } else if (path.fileUri) { - if (!fileInputs) { - fileInputs = { filesToOpenOrCreate: [], filesToDiff: [], remoteAuthority: path.remoteAuthority }; + if (!filesToOpen) { + filesToOpen = { filesToOpenOrCreate: [], filesToDiff: [], remoteAuthority: path.remoteAuthority }; } - fileInputs.filesToOpenOrCreate.push(path); + filesToOpen.filesToOpenOrCreate.push(path); } else if (path.backupPath) { emptyToRestore.push({ backupFolder: basename(path.backupPath), remoteAuthority: path.remoteAuthority }); } else { @@ -421,14 +421,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // When run with --diff, take the files to open as files to diff // if there are exactly two files provided. - if (fileInputs && openConfig.diffMode && fileInputs.filesToOpenOrCreate.length === 2) { - fileInputs.filesToDiff = fileInputs.filesToOpenOrCreate; - fileInputs.filesToOpenOrCreate = []; + if (openConfig.diffMode && filesToOpen?.filesToOpenOrCreate.length === 2) { + filesToOpen.filesToDiff = filesToOpen.filesToOpenOrCreate; + filesToOpen.filesToOpenOrCreate = []; } // When run with --wait, make sure we keep the paths to wait for - if (fileInputs && openConfig.waitMarkerFileURI) { - fileInputs.filesToWait = { paths: [...fileInputs.filesToDiff, ...fileInputs.filesToOpenOrCreate], waitMarkerFileUri: openConfig.waitMarkerFileURI }; + if (filesToOpen && openConfig.waitMarkerFileURI) { + filesToOpen.filesToWait = { paths: [...filesToOpen.filesToDiff, ...filesToOpen.filesToOpenOrCreate], waitMarkerFileUri: openConfig.waitMarkerFileURI }; } // @@ -447,7 +447,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Open based on config - const usedWindows = this.doOpen(openConfig, workspacesToOpen, foldersToOpen, emptyToRestore, emptyToOpen, fileInputs, foldersToAdd); + const usedWindows = this.doOpen(openConfig, workspacesToOpen, foldersToOpen, emptyToRestore, emptyToOpen, filesToOpen, foldersToAdd); this.logService.trace(`windowsManager#open used window count ${usedWindows.length} (workspacesToOpen: ${workspacesToOpen.length}, foldersToOpen: ${foldersToOpen.length}, emptyToRestore: ${emptyToRestore.length}, emptyToOpen: ${emptyToOpen})`); @@ -492,7 +492,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Remember in recent document list (unless this opens for extension development) // Also do not add paths when files are opened for diffing, only if opened individually - const isDiff = fileInputs && fileInputs.filesToDiff.length > 0; + const isDiff = filesToOpen && filesToOpen.filesToDiff.length > 0; if (!usedWindows.some(window => window.isExtensionDevelopmentHost) && !isDiff && !openConfig.noRecentEntry) { const recents: IRecent[] = []; for (let pathToOpen of pathsToOpen) { @@ -535,7 +535,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic foldersToOpen: IFolderPathToOpen[], emptyToRestore: IEmptyWindowBackupInfo[], emptyToOpen: number, - fileInputs: IFileInputs | undefined, + filesToOpen: IFilesToOpen | undefined, foldersToAdd: IFolderPathToOpen[] ) { const usedWindows: ICodeWindow[] = []; @@ -554,13 +554,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Handle files to open/diff or to create when we dont open a folder and we do not restore any folder/untitled from hot-exit const potentialWindowsCount = foldersToOpen.length + workspacesToOpen.length + emptyToRestore.length; - if (potentialWindowsCount === 0 && fileInputs) { + if (potentialWindowsCount === 0 && filesToOpen) { // Find suitable window or folder path to open files in - const fileToCheck = fileInputs.filesToOpenOrCreate[0] || fileInputs.filesToDiff[0]; + const fileToCheck = filesToOpen.filesToOpenOrCreate[0] || filesToOpen.filesToDiff[0]; // only look at the windows with correct authority - const windows = WindowsMainService.WINDOWS.filter(window => fileInputs && window.remoteAuthority === fileInputs.remoteAuthority); + const windows = WindowsMainService.WINDOWS.filter(window => filesToOpen && window.remoteAuthority === filesToOpen.remoteAuthority); const bestWindowOrFolder = findBestWindowOrFolderForFile({ windows, @@ -587,10 +587,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic else { // Do open files - usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, bestWindowOrFolder, fileInputs)); + usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, bestWindowOrFolder, filesToOpen)); // Reset these because we handled them - fileInputs = undefined; + filesToOpen = undefined; } } @@ -600,14 +600,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic userEnv: openConfig.userEnv, cli: openConfig.cli, initialStartup: openConfig.initialStartup, - fileInputs, + filesToOpen, forceNewWindow: true, - remoteAuthority: fileInputs.remoteAuthority, + remoteAuthority: filesToOpen.remoteAuthority, forceNewTabbedWindow: openConfig.forceNewTabbedWindow })); // Reset these because we handled them - fileInputs = undefined; + filesToOpen = undefined; } } @@ -619,14 +619,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic const windowsOnWorkspace = arrays.coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, workspaceToOpen.workspace))); if (windowsOnWorkspace.length > 0) { const windowOnWorkspace = windowsOnWorkspace[0]; - const fileInputsForWindow = (fileInputs?.remoteAuthority === windowOnWorkspace.remoteAuthority) ? fileInputs : undefined; + const filesToOpenInWindow = (filesToOpen?.remoteAuthority === windowOnWorkspace.remoteAuthority) ? filesToOpen : undefined; // Do open files - usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, fileInputsForWindow)); + usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, filesToOpenInWindow)); // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + if (filesToOpenInWindow) { + filesToOpen = undefined; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -639,14 +639,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } const remoteAuthority = workspaceToOpen.remoteAuthority; - const fileInputsForWindow = (fileInputs?.remoteAuthority === remoteAuthority) ? fileInputs : undefined; + const filesToOpenInWindow = (filesToOpen?.remoteAuthority === remoteAuthority) ? filesToOpen : undefined; // Do open folder - usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, workspaceToOpen, openFolderInNewWindow, fileInputsForWindow)); + usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, workspaceToOpen, openFolderInNewWindow, filesToOpenInWindow)); // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + if (filesToOpenInWindow) { + filesToOpen = undefined; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -661,14 +661,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic const windowsOnFolderPath = arrays.coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, folderToOpen.folderUri))); if (windowsOnFolderPath.length > 0) { const windowOnFolderPath = windowsOnFolderPath[0]; - const fileInputsForWindow = fileInputs?.remoteAuthority === windowOnFolderPath.remoteAuthority ? fileInputs : undefined; + const filesToOpenInWindow = filesToOpen?.remoteAuthority === windowOnFolderPath.remoteAuthority ? filesToOpen : undefined; // Do open files - usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, fileInputsForWindow)); + usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, filesToOpenInWindow)); // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + if (filesToOpenInWindow) { + filesToOpen = undefined; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -682,14 +682,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } const remoteAuthority = folderToOpen.remoteAuthority; - const fileInputsForWindow = (fileInputs?.remoteAuthority === remoteAuthority) ? fileInputs : undefined; + const filesToOpenInWindow = (filesToOpen?.remoteAuthority === remoteAuthority) ? filesToOpen : undefined; // Do open folder - usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, folderToOpen, openFolderInNewWindow, fileInputsForWindow)); + usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, folderToOpen, openFolderInNewWindow, filesToOpenInWindow)); // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + if (filesToOpenInWindow) { + filesToOpen = undefined; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -701,13 +701,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic if (allEmptyToRestore.length > 0) { allEmptyToRestore.forEach(emptyWindowBackupInfo => { const remoteAuthority = emptyWindowBackupInfo.remoteAuthority; - const fileInputsForWindow = (fileInputs?.remoteAuthority === remoteAuthority) ? fileInputs : undefined; + const filesToOpenInWindow = (filesToOpen?.remoteAuthority === remoteAuthority) ? filesToOpen : undefined; usedWindows.push(this.openInBrowserWindow({ userEnv: openConfig.userEnv, cli: openConfig.cli, initialStartup: openConfig.initialStartup, - fileInputs: fileInputsForWindow, + filesToOpen: filesToOpenInWindow, remoteAuthority, forceNewWindow: true, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, @@ -715,8 +715,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic })); // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + if (filesToOpenInWindow) { + filesToOpen = undefined; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -724,18 +724,18 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Handle empty to open (only if no other window opened) - if (usedWindows.length === 0 || fileInputs) { - if (fileInputs && !emptyToOpen) { + if (usedWindows.length === 0 || filesToOpen) { + if (filesToOpen && !emptyToOpen) { emptyToOpen++; } - const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined); + const remoteAuthority = filesToOpen ? filesToOpen.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined); for (let i = 0; i < emptyToOpen; i++) { - usedWindows.push(this.doOpenEmpty(openConfig, openFolderInNewWindow, remoteAuthority, fileInputs)); + usedWindows.push(this.doOpenEmpty(openConfig, openFolderInNewWindow, remoteAuthority, filesToOpen)); // Reset these because we handled them - fileInputs = undefined; + filesToOpen = undefined; openFolderInNewWindow = true; // any other window to open must open in new window then } } @@ -743,16 +743,16 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic return arrays.distinct(usedWindows); } - private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, fileInputs?: IFileInputs): ICodeWindow { + private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, filesToOpen?: IFilesToOpen): ICodeWindow { this.logService.trace('windowsManager#doOpenFilesInExistingWindow'); window.focus(); // make sure window has focus const params: { filesToOpenOrCreate?: IPath[], filesToDiff?: IPath[], filesToWait?: IPathsToWaitFor, termProgram?: string } = {}; - if (fileInputs) { - params.filesToOpenOrCreate = fileInputs.filesToOpenOrCreate; - params.filesToDiff = fileInputs.filesToDiff; - params.filesToWait = fileInputs.filesToWait; + if (filesToOpen) { + params.filesToOpenOrCreate = filesToOpen.filesToOpenOrCreate; + params.filesToDiff = filesToOpen.filesToDiff; + params.filesToWait = filesToOpen.filesToWait; } if (configuration.userEnv) { @@ -773,7 +773,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic return window; } - private doOpenEmpty(openConfig: IOpenConfiguration, forceNewWindow: boolean, remoteAuthority: string | undefined, fileInputs: IFileInputs | undefined, windowToUse?: ICodeWindow): ICodeWindow { + private doOpenEmpty(openConfig: IOpenConfiguration, forceNewWindow: boolean, remoteAuthority: string | undefined, filesToOpen: IFilesToOpen | undefined, windowToUse?: ICodeWindow): ICodeWindow { if (!forceNewWindow && !windowToUse && typeof openConfig.contextWindowId === 'number') { windowToUse = this.getWindowById(openConfig.contextWindowId); // fix for https://github.com/microsoft/vscode/issues/97172 } @@ -785,12 +785,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic remoteAuthority, forceNewWindow, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, - fileInputs, + filesToOpen, windowToUse }); } - private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, forceNewWindow: boolean, fileInputs: IFileInputs | undefined, windowToUse?: ICodeWindow): ICodeWindow { + private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, forceNewWindow: boolean, filesToOpen: IFilesToOpen | undefined, windowToUse?: ICodeWindow): ICodeWindow { if (!forceNewWindow && !windowToUse && typeof openConfig.contextWindowId === 'number') { windowToUse = this.getWindowById(openConfig.contextWindowId); // fix for https://github.com/microsoft/vscode/issues/49587 } @@ -801,7 +801,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic initialStartup: openConfig.initialStartup, workspace: folderOrWorkspace.workspace, folderUri: folderOrWorkspace.folderUri, - fileInputs, + filesToOpen, remoteAuthority: folderOrWorkspace.remoteAuthority, forceNewWindow, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, @@ -837,7 +837,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Convert multiple folders into workspace (if opened via API or CLI) // This will ensure to open these folders in one window instead of multiple - // If we are in addMode, we should not do this because in that case all + // If we are in `addMode`, we should not do this because in that case all // folders should be added to the existing window. if (!openConfig.addMode && isCommandLineOrAPICall) { const foldersToOpen = windowsToOpen.filter(path => !!path.folderUri); @@ -914,7 +914,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } } - // file uris const fileUris = cli['file-uri']; if (fileUris) { @@ -947,9 +946,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } private doGetWindowsFromLastSession(): IPathToOpen[] { - const restoreWindows = this.getRestoreWindowsSetting(); + const restoreWindowsSetting = this.getRestoreWindowsSetting(); - switch (restoreWindows) { + switch (restoreWindowsSetting) { // none: we always open an empty window case 'none': @@ -961,8 +960,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic case 'one': case 'all': case 'folders': + + // Collect previously opened windows const openedWindows: IWindowState[] = []; - if (restoreWindows !== 'one') { + if (restoreWindowsSetting !== 'one') { openedWindows.push(...this.windowsState.openedWindows); } if (this.windowsState.lastActiveWindow) { @@ -971,17 +972,25 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic const windowsToOpen: IPathToOpen[] = []; for (const openedWindow of openedWindows) { - if (openedWindow.workspace) { // Workspaces + + // Workspaces + if (openedWindow.workspace) { const pathToOpen = this.parseUri({ workspaceUri: openedWindow.workspace.configPath }, { remoteAuthority: openedWindow.remoteAuthority }); if (pathToOpen?.workspace) { windowsToOpen.push(pathToOpen); } - } else if (openedWindow.folderUri) { // Folders + } + + // Folders + else if (openedWindow.folderUri) { const pathToOpen = this.parseUri({ folderUri: openedWindow.folderUri }, { remoteAuthority: openedWindow.remoteAuthority }); if (pathToOpen?.folderUri) { windowsToOpen.push(pathToOpen); } - } else if (restoreWindows !== 'folders' && openedWindow.backupPath) { // Empty window, potentially editors open to be restored + } + + // Empty window, potentially editors open to be restored + else if (restoreWindowsSetting !== 'folders' && openedWindow.backupPath) { windowsToOpen.push({ backupPath: openedWindow.backupPath, remoteAuthority: openedWindow.remoteAuthority }); } } @@ -1360,11 +1369,11 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic configuration.folderUri = options.folderUri; configuration.remoteAuthority = options.remoteAuthority; - const fileInputs = options.fileInputs; - if (fileInputs) { - configuration.filesToOpenOrCreate = fileInputs.filesToOpenOrCreate; - configuration.filesToDiff = fileInputs.filesToDiff; - configuration.filesToWait = fileInputs.filesToWait; + const filesToOpen = options.filesToOpen; + if (filesToOpen) { + configuration.filesToOpenOrCreate = filesToOpen.filesToOpenOrCreate; + configuration.filesToDiff = filesToOpen.filesToDiff; + configuration.filesToWait = filesToOpen.filesToWait; } // if we know the backup folder upfront (for empty windows to restore), we can set it From 8cebd10782512c359ad177740376d45e97ea2163 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Nov 2020 12:20:14 +0100 Subject: [PATCH 040/103] set line height for suggest details, fixes https://github.com/microsoft/vscode/issues/110172 --- src/vs/editor/contrib/suggest/suggestWidgetDetails.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts b/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts index 72500145f14..642c9fddb0e 100644 --- a/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts +++ b/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts @@ -91,6 +91,7 @@ export class SuggestDetailsWidget { const lineHeightPx = `${lineHeight}px`; this.domNode.style.fontSize = fontSizePx; + this.domNode.style.lineHeight = lineHeightPx; this.domNode.style.fontWeight = fontWeight; this.domNode.style.fontFeatureSettings = fontInfo.fontFeatureSettings; this._type.style.fontFamily = fontFamily; From 5290baabac386b819404b989b4ef1953a05eda10 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Nov 2020 12:32:08 +0100 Subject: [PATCH 041/103] improve message when files is too large for formatting, fixes https://github.com/microsoft/vscode/issues/105986 --- src/vs/workbench/contrib/format/browser/formatActionsNone.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts index 9355b4e6dc1..709ef8fdb0f 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts @@ -55,6 +55,8 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction { return commandService.executeCommand('editor.action.formatDocument.multiple'); } else if (formatterCount === 1) { return commandService.executeCommand('editor.action.formatDocument'); + } else if (model.isTooLargeForSyncing()) { + notificationService.prompt(Severity.Info, nls.localize('too.large', "This file cannot be formatted because it is too large"), []); } else { const langName = model.getLanguageIdentifier().language; const message = nls.localize('no.provider', "There is no formatter for '{0}' files installed.", langName); From 07f0bade3702808ff504d4beaf6b0d813dc38c75 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Nov 2020 12:43:10 +0100 Subject: [PATCH 042/103] remove duplicated context key expressions, fixes https://github.com/microsoft/vscode/issues/97381 --- src/vs/editor/contrib/format/formatActions.ts | 7 +++---- .../workbench/contrib/format/browser/formatActionsNone.ts | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index bedb20cdd97..cdfdac89bbb 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -215,13 +215,12 @@ class FormatDocumentAction extends EditorAction { alias: 'Format Document', precondition: ContextKeyExpr.and(EditorContextKeys.notInCompositeEditor, EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider), kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentFormattingProvider), + kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }, weight: KeybindingWeight.EditorContrib }, contextMenuOpts: { - when: EditorContextKeys.hasDocumentFormattingProvider, group: '1_modification', order: 1.3 } @@ -249,12 +248,12 @@ class FormatSelectionAction extends EditorAction { alias: 'Format Selection', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentSelectionFormattingProvider), kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentSelectionFormattingProvider), + kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F), weight: KeybindingWeight.EditorContrib }, contextMenuOpts: { - when: ContextKeyExpr.and(EditorContextKeys.hasDocumentSelectionFormattingProvider, EditorContextKeys.hasNonEmptySelection), + when: EditorContextKeys.hasNonEmptySelection, group: '1_modification', order: 1.31 } diff --git a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts index 709ef8fdb0f..1efff633b1b 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts @@ -32,7 +32,7 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction { alias: 'Format Document', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider.toNegated()), kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentFormattingProvider.toNegated()), + kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }, weight: KeybindingWeight.EditorContrib, From 2720a8022e5f2f22a50ba036eeafd3cc9e7b6a3f Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 9 Nov 2020 13:34:25 +0100 Subject: [PATCH 043/103] Allow user tasks to run when no folder is open --- .../tasks/browser/abstractTaskService.ts | 118 +++++++----------- 1 file changed, 46 insertions(+), 72 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 9cbba05bec8..895c5d54164 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -40,7 +40,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; -import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IOutputService, IOutputChannel } from 'vs/workbench/contrib/output/common/output'; @@ -1702,7 +1702,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return; } - if (!contributed) { + if (contributed.length === 0) { result.add(key, ...folderTasks.set.tasks); } else { let configurations = folderTasks.configurations; @@ -1867,31 +1867,35 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected abstract updateWorkspaceTasks(runSource: TaskRunSource | void): void; protected computeWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise> { - if (this.workspaceFolders.length === 0) { - return Promise.resolve(new Map()); - } else { - let promises: Promise[] = []; - for (let folder of this.workspaceFolders) { - promises.push(this.computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined)); + let promises: Promise[] = []; + for (let folder of this.workspaceFolders) { + promises.push(this.computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined)); + } + return Promise.all(promises).then(async (values) => { + let result = new Map(); + for (let value of values) { + if (value) { + result.set(value.workspaceFolder.uri.toString(), value); + } } - return Promise.all(promises).then(async (values) => { - let result = new Map(); - for (let value of values) { - if (value) { - result.set(value.workspaceFolder.uri.toString(), value); - } - } - const userTasks = await this.computeUserTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined); - if (userTasks) { - result.set(USER_TASKS_GROUP_KEY, userTasks); - } - const workspaceFileTasks = await this.computeWorkspaceFileTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined); + let folder = this.workspaceFolders.length > 0 ? this.workspaceFolders[0] : undefined; + if (!folder) { + const userhome = await this.pathService.userHome(); + folder = new WorkspaceFolder({ uri: userhome, name: resources.basename(userhome), index: 0 }); + } + const userTasks = await this.computeUserTasks(folder, runSource).then((value) => value, () => undefined); + if (userTasks) { + result.set(USER_TASKS_GROUP_KEY, userTasks); + } + + if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { + const workspaceFileTasks = await this.computeWorkspaceFileTasks(folder, runSource).then((value) => value, () => undefined); if (workspaceFileTasks && this._workspace && this._workspace.configuration) { result.set(this._workspace.configuration.toString(), workspaceFileTasks); } - return result; - }); - } + } + return result; + }); } private get jsonTasksSupported(): boolean { @@ -2167,17 +2171,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } private canRunCommand(): boolean { - if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { - this.notificationService.prompt( - Severity.Info, - nls.localize('TaskService.noWorkspace', "Tasks are only available on a workspace folder."), - [{ - label: nls.localize('TaskService.learnMore', "Learn More"), - run: () => this.openerService.open(URI.parse('https://code.visualstudio.com/docs/editor/tasks')) - }] - ); - return false; - } return true; } @@ -2871,8 +2864,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } if (this.isTaskEntry(selection)) { this.configureTask(selection.task); - } else { + } else if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { this.openTaskFile(selection.folder.toResource('.vscode/tasks.json'), TaskSourceKind.Workspace); + } else { + const resource = this.getResourceForKind(TaskSourceKind.User); + if (resource) { + this.openTaskFile(resource, TaskSourceKind.User); + } } } @@ -2914,46 +2912,22 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return taskPromise.then((taskMap) => { let entries: QuickPickInput[] = []; let needsCreateOrOpen: boolean = true; - if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { - let tasks = taskMap.all(); - if (tasks.length > 0) { - tasks = tasks.sort((a, b) => a._label.localeCompare(b._label)); - for (let task of tasks) { - entries.push({ label: task._label, task, description: this.getTaskDescription(task), detail: this.showDetail() ? task.configurationProperties.detail : undefined }); - if (!ContributedTask.is(task)) { - needsCreateOrOpen = false; - } + let tasks = taskMap.all(); + if (tasks.length > 0) { + tasks = tasks.sort((a, b) => a._label.localeCompare(b._label)); + for (let task of tasks) { + entries.push({ label: task._label, task, description: this.getTaskDescription(task), detail: this.showDetail() ? task.configurationProperties.detail : undefined }); + if (!ContributedTask.is(task)) { + needsCreateOrOpen = false; } } - if (needsCreateOrOpen) { - let label = stats[0] !== undefined ? openLabel : createLabel; - if (entries.length) { - entries.push({ type: 'separator' }); - } - entries.push({ label, folder: this.contextService.getWorkspace().folders[0] }); - } - } else { - let folders = this.contextService.getWorkspace().folders; - let index = 0; - for (let folder of folders) { - let tasks = taskMap.get(folder); - if (tasks.length > 0) { - tasks = tasks.slice().sort((a, b) => a._label.localeCompare(b._label)); - for (let i = 0; i < tasks.length; i++) { - let entry: TaskQuickPickEntryType = { label: tasks[i]._label, task: tasks[i], description: this.getTaskDescription(tasks[i]) }; - if (i === 0) { - entries.push({ type: 'separator', label: folder.name }); - } - entries.push(entry); - } - } else { - let label = stats[index] !== undefined ? openLabel : createLabel; - let entry: TaskQuickPickEntryType = { label, folder: folder }; - entries.push({ type: 'separator', label: folder.name }); - entries.push(entry); - } - index++; + } + if (needsCreateOrOpen) { + let label = stats[0] !== undefined ? openLabel : createLabel; + if (entries.length) { + entries.push({ type: 'separator' }); } + entries.push({ label, folder: this.contextService.getWorkspace().folders[0] }); } if ((entries.length === 1) && !needsCreateOrOpen) { tokenSource.cancel(); From 551db7ec94f02a4bdc8999092cf8bef642b3992d Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 9 Nov 2020 13:50:47 +0100 Subject: [PATCH 044/103] Add new fileDirnameBasename variable Fixes #78316 --- .../configurationResolver/common/variableResolver.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 4d632ea03b4..2236fa47163 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -308,6 +308,12 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe const basename = paths.basename(getFilePath()); return (basename.slice(0, basename.length - paths.extname(basename).length)); + case 'fileDirnameBasename': + if (this._ignoreEditorVariables) { + return match; + } + return paths.basename(paths.dirname(getFilePath())); + case 'execPath': const ep = this._context.getExecPath(); if (ep) { From dd97a9d335adc8a08e92c2728e6dc353979f3dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 9 Nov 2020 14:23:24 +0100 Subject: [PATCH 045/103] :lipstick: --- extensions/git/package.json | 23 +++++++++++-- extensions/git/package.nls.json | 5 ++- extensions/git/src/commands.ts | 61 +++++++++++++++++++-------------- 3 files changed, 60 insertions(+), 29 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 56055194944..cddee92cf55 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1662,10 +1662,27 @@ "scope": "resource" }, "git.checkoutType": { - "type": "string", - + "type": "array", + "items": { + "type": "string", + "enum": [ + "local", + "tags", + "remote" + ], + "enumDescriptions": [ + "%config.checkoutType.local%", + "%config.checkoutType.tags%", + "%config.checkoutType.remote%" + ] + }, + "uniqueItems": true, "markdownDescription": "%config.checkoutType%", - "default": "local,remote,tags" + "default": [ + "local", + "remote", + "tags" + ] }, "git.ignoreLegacyWarning": { "type": "boolean", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index bc09de34c05..1f37952eeaa 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -101,7 +101,10 @@ "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 (local, remote or tags, split with ',') are listed when running `Checkout to...`.", + "config.checkoutType": "Controls what type of git refs are listed when running `Checkout to...`.", + "config.checkoutType.local": "Local branches", + "config.checkoutType.tags": "Tags", + "config.checkoutType.remote": "Remote branches", "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 1be44336f2d..90d5d6401c0 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -70,11 +70,6 @@ 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); } @@ -212,37 +207,53 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{ function createCheckoutItems(repository: Repository): CheckoutItem[] { const config = workspace.getConfiguration('git'); - const checkoutTypeString = config.get('checkoutType'); + const checkoutTypeConfig = config.get('checkoutType'); + let checkoutTypes: string[]; - const checkoutTypeOptions = ['local', 'remote', 'tags']; - const checkoutTypes = checkoutTypeString?.trim().split(',').map(type => type.trim()).filter(type => checkoutTypeOptions.includes(type)); + if (checkoutTypeConfig === 'all' || !checkoutTypeConfig || checkoutTypeConfig.length === 0) { + checkoutTypes = ['local', 'remote', 'tags']; + } else if (typeof checkoutTypeConfig === 'string') { + checkoutTypes = [checkoutTypeConfig]; + } else { + checkoutTypes = checkoutTypeConfig; + } - const results: CheckoutItem[] = []; - const seens = new Set(); - (checkoutTypes && checkoutTypes.length ? checkoutTypes : checkoutTypeOptions).forEach(type => { - if (seens.has(type)) { - return; + const processors = checkoutTypes.map(getCheckoutProcessor) + .filter(p => !!p) as CheckoutProcessor[]; + + for (const ref of repository.refs) { + for (const processor of processors) { + processor.onRef(ref); } - seens.add(type); + } - const { refType, checkoutItemCtor } = getRefTypeAndCheckoutItem(type); - results.push(...repository.refs.filter(ref => ref.type === refType).map(ref => new checkoutItemCtor(ref))); - }); - - return results; + return processors.reduce((r, p) => r.concat(...p.items), []); } -function getRefTypeAndCheckoutItem(type: string): RefTypeAndCheckoutItem { +class CheckoutProcessor { + + private refs: Ref[] = []; + get items(): CheckoutItem[] { return this.refs.map(r => new this.ctor(r)); } + constructor(private type: RefType, private ctor: { new(ref: Ref): CheckoutItem }) { } + + onRef(ref: Ref): void { + if (ref.type === this.type) { + this.refs.push(ref); + } + } +} + +function getCheckoutProcessor(type: string): CheckoutProcessor | undefined { switch (type) { case 'local': - return { refType: RefType.Head, checkoutItemCtor: CheckoutItem }; + return new CheckoutProcessor(RefType.Head, CheckoutItem); case 'remote': - return { refType: RefType.RemoteHead, checkoutItemCtor: CheckoutRemoteHeadItem }; + return new CheckoutProcessor(RefType.RemoteHead, CheckoutRemoteHeadItem); case 'tags': - return { refType: RefType.Tag, checkoutItemCtor: CheckoutTagItem }; - default: - throw new Error(`Unexpected type: ${type}`); + return new CheckoutProcessor(RefType.Tag, CheckoutTagItem); } + + return undefined; } function sanitizeRemoteName(name: string) { From 86d848d8e788f109621469de5872f8bb7c12a788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 9 Nov 2020 14:45:36 +0100 Subject: [PATCH 046/103] :lipstick: --- extensions/git/package.json | 29 +++++++++++++++++++++-------- extensions/git/package.nls.json | 3 ++- extensions/git/src/commands.ts | 27 ++++++++++++++------------- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 10097afd5ac..e0659e26395 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -163,12 +163,6 @@ "category": "Git", "icon": "$(discard)" }, - { - "command": "git.rename", - "title": "%command.rename%", - "category": "Git", - "icon": "$(discard)" - }, { "command": "git.cleanAll", "title": "%command.cleanAll%", @@ -187,6 +181,12 @@ "category": "Git", "icon": "$(discard)" }, + { + "command": "git.rename", + "title": "%command.rename%", + "category": "Git", + "icon": "$(discard)" + }, { "command": "git.commit", "title": "%command.commit%", @@ -616,6 +616,10 @@ "command": "git.cleanAllUntracked", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" }, + { + "command": "git.rename", + "when": "false" + }, { "command": "git.commit", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" @@ -1332,11 +1336,16 @@ ], "explorer/context": [ { - "command": "git.rename", - "group": "7_modification", + "submenu": "git.explorer", + "group": "7_git", "when": "config.git.enabled && !git.missing && !explorerResourceIsRoot" } ], + "git.explorer": [ + { + "command": "git.rename" + } + ], "git.commit": [ { "command": "git.commit", @@ -1555,6 +1564,10 @@ ] }, "submenus": [ + { + "id": "git.explorer", + "label": "%submenu.explorer%" + }, { "id": "git.commit", "label": "%submenu.commit%" diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 26a69c3439c..7b9d6cc1166 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -23,7 +23,7 @@ "command.unstage": "Unstage Changes", "command.unstageAll": "Unstage All Changes", "command.unstageSelectedRanges": "Unstage Selected Ranges", - "command.rename": "Rename (Git)", + "command.rename": "Rename", "command.clean": "Discard Changes", "command.cleanAll": "Discard All Changes", "command.cleanAllTracked": "Discard All Tracked Changes", @@ -176,6 +176,7 @@ "config.timeline.date": "Controls which date to use for items in the Timeline view", "config.timeline.date.committed": "Use the committed date", "config.timeline.date.authored": "Use the authored date", + "submenu.explorer": "Git", "submenu.commit": "Commit", "submenu.commit.amend": "Amend", "submenu.commit.signoff": "Sign Off", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 1447ed556a6..7ef60677563 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -921,22 +921,23 @@ 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()) { + if (!fromUri) { return; } - const fullToPath = path.join(rootPath || '', toPath); - this.outputChannel.appendLine(`git.rename from ${fromPath} to ${fullToPath}`); - await repository.move(fromPath, fullToPath); + const from = path.relative(repository.root, fromUri.path); + let to = await window.showInputBox({ + value: from, + valueSelection: [from.length - path.basename(from).length, from.length] + }); + + to = to?.trim(); + + if (!to) { + return; + } + + await repository.move(from, to); } @command('git.stage') From ec084a25bd324087ccccb5c359829e409d6a5df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 9 Nov 2020 15:19:15 +0100 Subject: [PATCH 047/103] :lipstick: --- extensions/git/src/commands.ts | 111 +++++++++++++-------------------- 1 file changed, 42 insertions(+), 69 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 5fae63ee600..ea5891403df 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, Selection, SourceControl, TextDocumentContentProvider } from 'vscode'; +import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider } from 'vscode'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider } from './api/git'; @@ -1786,25 +1786,20 @@ export class CommandCenter { @command('git.checkout', { repository: true }) async checkout(repository: Repository, treeish?: string): Promise { - if (typeof treeish === 'string') { - await repository.checkout(treeish); - return true; - } - - return this._checkout(repository); + return this._checkout(repository, { treeish }); } @command('git.checkoutDetached', { repository: true }) async checkoutDetached(repository: Repository, treeish?: string): Promise { - if (typeof treeish === 'string') { - await repository.checkout(treeish, { detached: true }); + return this._checkout(repository, { detached: true, treeish }); + } + + private async _checkout(repository: Repository, opts?: { detached?: boolean, treeish?: string }): Promise { + if (typeof opts?.treeish === 'string') { + await repository.checkout(opts?.treeish, opts); return true; } - return this._checkout(repository, { detached: true }); - } - - private async _checkout(repository: Repository, opts?: { detached?: boolean }): Promise { const createBranch = new CreateBranchItem(); const createBranchFrom = new CreateBranchFromItem(); const checkoutDetached = new CheckoutDetachedItem(); @@ -1838,7 +1833,28 @@ export class CommandCenter { } else if (choice === checkoutDetached) { return this._checkout(repository, { detached: true }); } else { - await (choice as CheckoutItem).run(repository, opts); + const item = choice as CheckoutItem; + + try { + await item.run(repository, opts); + } catch (err) { + if (err.gitErrorCode !== GitErrorCodes.DirtyWorkTree) { + throw err; + } + + const force = localize('force', "Force Checkout"); + const stash = localize('stashcheckout', "Stash & Checkout"); + const choice = await window.showWarningMessage(localize('local changes', "Your local changes would be overwritten by checkout."), { modal: true }, force, stash); + + if (choice === force) { + await this.cleanAll(repository); + await item.run(repository, opts); + } else if (choice === stash) { + await this.stash(repository); + await item.run(repository, opts); + await this.stashPopLatest(repository); + } + } } return true; @@ -2732,7 +2748,18 @@ export class CommandCenter { if (!options.repository) { result = Promise.resolve(method.apply(this, args)); } else { - const repositoryPromise = this.guessRepository(args[0]); + // 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(); + } + result = repositoryPromise.then(repository => { if (!repository) { return Promise.resolve(); @@ -2759,8 +2786,6 @@ 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()); @@ -2792,11 +2817,6 @@ export class CommandCenter { 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."); @@ -2859,59 +2879,12 @@ 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); From 23579d815faa6712bf7c628bad25d4a4eb4a2dbc Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 9 Nov 2020 15:27:50 +0100 Subject: [PATCH 048/103] [file icon theme] icon match the last word of folder name not the whole name. Fixes #110183 --- src/vs/editor/common/services/getIconClasses.ts | 4 ++-- src/vs/workbench/services/themes/browser/fileIconThemeData.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/services/getIconClasses.ts index b20a0b7c58b..60aca88ad68 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/services/getIconClasses.ts @@ -93,6 +93,6 @@ export function detectModeId(modelService: IModelService, modeService: IModeServ return modeService.getModeIdByFilepathOrFirstLine(resource); } -export function cssEscape(val: string): string { - return val.replace(/\s/g, '\\$&'); // make sure to not introduce CSS classes from files that contain whitespace +export function cssEscape(str: string): string { + return str.replace(/[\11\12\14\15\40]/g, '/'); // HTML class names can not contain certain whitespace characters, use / instead, which doesn't exist in file names. } diff --git a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts index c23d2a8a8f7..b6af6ccb3f3 100644 --- a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts @@ -377,5 +377,6 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i return result; } function escapeCSS(str: string) { + str = str.replace(/[\11\12\14\15\40]/g, '/'); // HTML class names can not contain certain whitespace characters, use / instead, which doesn't exist in file names. return window.CSS.escape(str); } From 294406d7a137427d29046b81f71c5d9fb835ec35 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 9 Nov 2020 15:38:35 +0100 Subject: [PATCH 049/103] Fix #110121 --- build/lib/i18n.resources.json | 4 + .../extensionRecommendationsService.ts | 3 +- .../browser/extensions.contribution.ts | 223 +++++++++- .../extensions/browser/extensionsActions.ts | 414 ++---------------- .../extensions/browser/extensionsViews.ts | 7 +- .../browser/workspaceRecommendations.ts | 39 +- .../extensionRecommendationsService.test.ts | 6 +- .../common/extensionRecommendations.ts | 5 - .../common/workspaceExtensionsConfig.ts | 233 ++++++++-- 9 files changed, 487 insertions(+), 447 deletions(-) diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 47f0155d8a5..6921e6c6525 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -365,6 +365,10 @@ { "name": "vs/workbench/services/authentication", "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/extensionRecommendations", + "project": "vscode-workbench" } ] } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 7df6cf1b99e..fa782cae308 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -100,7 +100,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte }) ]); - this._register(this.extensionRecommendationsManagementService.onDidChangeIgnoredRecommendations(() => this._onDidChangeRecommendations.fire())); + this._register(Event.any(this.workspaceRecommendations.onDidChangeRecommendations, this.configBasedRecommendations.onDidChangeRecommendations, this.extensionRecommendationsManagementService.onDidChangeIgnoredRecommendations)(() => this._onDidChangeRecommendations.fire())); this._register(this.extensionRecommendationsManagementService.onDidChangeGlobalIgnoredRecommendation(({ extensionId, isRecommended }) => { if (!isRecommended) { const reason = this.getAllRecommendationsWithReason()[extensionId]; @@ -111,7 +111,6 @@ export class ExtensionRecommendationsService extends Disposable implements IExte })); await this.promptWorkspaceRecommendations(); - this._register(Event.any(this.workspaceRecommendations.onDidChangeRecommendations, this.configBasedRecommendations.onDidChangeRecommendations)(() => this.promptWorkspaceRecommendations())); } private isEnabled(): boolean { diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index f4b33985ab1..82f662d9d4c 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -10,7 +10,7 @@ import { MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/acti import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionsLabel, ExtensionsLocalizedLabel, ExtensionsChannelId, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; @@ -18,7 +18,7 @@ import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction, ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction, - EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction, ClearExtensionsSearchResultsAction + EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction, ClearExtensionsSearchResultsAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor'; @@ -26,7 +26,7 @@ import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContrib import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import * as jsonContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeymapExtensions } from 'vs/workbench/contrib/extensions/common/extensionsUtils'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -62,6 +62,8 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { IAction } from 'vs/base/common/actions'; +import { IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; +import { Schemas } from 'vs/base/common/network'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); @@ -929,6 +931,220 @@ class ExtensionsContributions implements IWorkbenchContribution { } } }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.ignoreRecommendation', + title: { value: localize('workbench.extensions.action.ignoreRecommendation', "Ignore Recommendation"), original: `Ignore Recommendation` }, + menu: { + id: MenuId.ExtensionContext, + group: '3_recommendations', + when: ContextKeyExpr.has('isExtensionRecommended'), + order: 1 + }, + }); + } + + async run(accessor: ServicesAccessor, id: string): Promise { + accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, true); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.undoIgnoredRecommendation', + title: { value: localize('workbench.extensions.action.undoIgnoredRecommendation', "Undo Ignored Recommendation"), original: `Undo Ignored Recommendation` }, + menu: { + id: MenuId.ExtensionContext, + group: '3_recommendations', + when: ContextKeyExpr.has('isUserIgnoredRecommendation'), + order: 1 + }, + }); + } + + async run(accessor: ServicesAccessor, id: string): Promise { + accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, false); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addExtensionToWorkspaceRecommendations', + title: { value: localize('workbench.extensions.action.addExtensionToWorkspaceRecommendations', "Add to Workspace Recommendations"), original: `Add to Workspace Recommendations` }, + menu: { + id: MenuId.ExtensionContext, + group: '3_recommendations', + when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended').negate(), ContextKeyExpr.has('isUserIgnoredRecommendation').negate()), + order: 2 + }, + }); + } + + run(accessor: ServicesAccessor, id: string): Promise { + return accessor.get(IWorkpsaceExtensionsConfigService).toggleRecommendation(id); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', + title: { value: localize('workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', "Remove from Workspace Recommendations"), original: `Remove from Workspace Recommendations` }, + menu: { + id: MenuId.ExtensionContext, + group: '3_recommendations', + when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended')), + order: 2 + }, + }); + } + + run(accessor: ServicesAccessor, id: string): Promise { + return accessor.get(IWorkpsaceExtensionsConfigService).toggleRecommendation(id); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addToWorkspaceRecommendations', + title: { value: localize('workbench.extensions.action.addToWorkspaceRecommendations', "Add Extension to Workspace Recommendations"), original: `Add Extension to Workspace Recommendations` }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), + }, + }); + } + + async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const workpsaceExtensionsConfigService = accessor.get(IWorkpsaceExtensionsConfigService); + if (!(editorService.activeEditor instanceof ExtensionsInput)) { + return; + } + const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase(); + const recommendations = await workpsaceExtensionsConfigService.getRecommendations(); + if (recommendations.includes(extensionId)) { + return; + } + await workpsaceExtensionsConfigService.toggleRecommendation(extensionId); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addToWorkspaceFolderRecommendations', + title: { value: localize('workbench.extensions.action.addToWorkspaceFolderRecommendations', "Add Extension to Workspace Folder Recommendations"), original: `Add Extension to Workspace Folder Recommendations` }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), + }, + }); + } + + async run(accessor: ServicesAccessor): Promise { + return accessor.get(ICommandService).executeCommand('workbench.extensions.action.addToWorkspaceRecommendations'); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addToWorkspaceIgnoredRecommendations', + title: { value: localize('workbench.extensions.action.addToWorkspaceIgnoredRecommendations', "Add Extension to Workspace Ignored Recommendations"), original: `Add Extension to Workspace Ignored Recommendations` }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), + }, + }); + } + + async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const workpsaceExtensionsConfigService = accessor.get(IWorkpsaceExtensionsConfigService); + if (!(editorService.activeEditor instanceof ExtensionsInput)) { + return; + } + const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase(); + const unwatedRecommendations = await workpsaceExtensionsConfigService.getUnwantedRecommendations(); + if (unwatedRecommendations.includes(extensionId)) { + return; + } + await workpsaceExtensionsConfigService.toggleUnwantedRecommendation(extensionId); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', + title: { value: localize('workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', "Add Extension to Workspace Folder Ignored Recommendations"), original: `Add Extension to Workspace Folder Ignored Recommendations` }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), + }, + }); + } + + run(accessor: ServicesAccessor): Promise { + return accessor.get(ICommandService).executeCommand('workbench.extensions.action.addToWorkspaceIgnoredRecommendations'); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: ConfigureWorkspaceRecommendedExtensionsAction.ID, + title: { value: ConfigureWorkspaceRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace)' }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: WorkbenchStateContext.isEqualTo('workspace'), + }, + }); + } + + run(accessor: ServicesAccessor): Promise { + return accessor.get(IInstantiationService).createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL).run(); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, + title: { value: ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace Folder)' }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: WorkbenchStateContext.notEqualsTo('empty'), + }, + }); + } + + run(accessor: ServicesAccessor): Promise { + return accessor.get(IInstantiationService).createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL).run(); + } + }); } } @@ -936,7 +1152,6 @@ const workbenchRegistry = Registry.as(Workbench workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Starting); workbenchRegistry.registerWorkbenchContribution(StatusUpdater, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(MaliciousExtensionChecker, LifecyclePhase.Eventually); -workbenchRegistry.registerWorkbenchContribution(ConfigureRecommendedExtensionsCommandsContributor, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Starting); workbenchRegistry.registerWorkbenchContribution(ExtensionActivationProgress, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 91765c8c58e..39f82943ef4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -11,12 +11,12 @@ import * as DOM from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import * as json from 'vs/base/common/json'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { dispose, Disposable } from 'vs/base/common/lifecycle'; +import { dispose } from 'vs/base/common/lifecycle'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { IExtensionIgnoredRecommendationsService, IExtensionsConfigContent } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -36,16 +36,14 @@ import { Color } from 'vs/base/common/color'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { MenuRegistry, MenuId, IMenuService } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { MenuId, IMenuService } from 'vs/platform/actions/common/actions'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { IQuickPickItem, IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -61,7 +59,7 @@ import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/ import { Codicon } from 'vs/base/common/codicons'; import { IViewsService } from 'vs/workbench/common/views'; import { IActionViewItemOptions, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { EXTENSIONS_CONFIG } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; +import { EXTENSIONS_CONFIG, IExtensionsConfigContent } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { ActionWithDropdownActionViewItem, IActionWithDropdownActionViewItemOptions } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; @@ -834,29 +832,37 @@ export class DropDownMenuActionViewItem extends ExtensionActionViewItem { } } -export function getContextMenuActions(menuService: IMenuService, contextKeyService: IContextKeyService, instantiationService: IInstantiationService, extension: IExtension | undefined | null): IAction[][] { - const scopedContextKeyService = contextKeyService.createScoped(); - if (extension) { - scopedContextKeyService.createKey('extension', extension.identifier.id); - scopedContextKeyService.createKey('isBuiltinExtension', extension.isBuiltin); - scopedContextKeyService.createKey('extensionHasConfiguration', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.configuration); - if (extension.state === ExtensionState.Installed) { - scopedContextKeyService.createKey('extensionStatus', 'installed'); +export function getContextMenuActions(extension: IExtension | undefined | null, instantiationService: IInstantiationService): IAction[][] { + return instantiationService.invokeFunction(accessor => { + const scopedContextKeyService = accessor.get(IContextKeyService).createScoped(); + const menuService = accessor.get(IMenuService); + const extensionRecommendationsService = accessor.get(IExtensionRecommendationsService); + const extensionIgnoredRecommendationsService = accessor.get(IExtensionIgnoredRecommendationsService); + if (extension) { + scopedContextKeyService.createKey('extension', extension.identifier.id); + scopedContextKeyService.createKey('isBuiltinExtension', extension.isBuiltin); + scopedContextKeyService.createKey('extensionHasConfiguration', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.configuration); + scopedContextKeyService.createKey('isExtensionRecommended', !!extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]); + scopedContextKeyService.createKey('isExtensionWorkspaceRecommended', extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]?.reasonId === ExtensionRecommendationReason.Workspace); + scopedContextKeyService.createKey('isUserIgnoredRecommendation', extensionIgnoredRecommendationsService.globalIgnoredRecommendations.some(e => e === extension.identifier.id.toLowerCase())); + if (extension.state === ExtensionState.Installed) { + scopedContextKeyService.createKey('extensionStatus', 'installed'); + } } - } - const groups: IAction[][] = []; - const menu = menuService.createMenu(MenuId.ExtensionContext, scopedContextKeyService); - menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => { - if (action instanceof SubmenuAction) { - return action; - } - return instantiationService.createInstance(MenuItemExtensionAction, action); - }))); - menu.dispose(); - scopedContextKeyService.dispose(); + const groups: IAction[][] = []; + const menu = menuService.createMenu(MenuId.ExtensionContext, scopedContextKeyService); + menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => { + if (action instanceof SubmenuAction) { + return action; + } + return instantiationService.createInstance(MenuItemExtensionAction, action); + }))); + menu.dispose(); + scopedContextKeyService.dispose(); - return groups; + return groups; + }); } export class ManageExtensionAction extends ExtensionDropDownAction { @@ -870,8 +876,6 @@ export class ManageExtensionAction extends ExtensionDropDownAction { @IInstantiationService instantiationService: IInstantiationService, @IExtensionService private readonly extensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, - @IMenuService private readonly menuService: IMenuService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { super(ManageExtensionAction.ID, '', '', true, true, instantiationService); @@ -911,7 +915,7 @@ export class ManageExtensionAction extends ExtensionDropDownAction { groups.push([this.instantiationService.createInstance(UninstallAction)]); groups.push([this.instantiationService.createInstance(InstallAnotherVersionAction)]); - getContextMenuActions(this.menuService, this.contextKeyService, this.instantiationService, this.extension).forEach(actions => groups.push(actions)); + getContextMenuActions(this.extension, this.instantiationService).forEach(actions => groups.push(actions)); groups.forEach(group => group.forEach(extensionAction => { if (extensionAction instanceof ExtensionAction) { @@ -2134,124 +2138,6 @@ export class ChangeSortAction extends Action { } } -export class ConfigureRecommendedExtensionsCommandsContributor extends Disposable implements IWorkbenchContribution { - - private workspaceContextKey = new RawContextKey('workspaceRecommendations', true); - private workspaceFolderContextKey = new RawContextKey('workspaceFolderRecommendations', true); - private addToWorkspaceRecommendationsContextKey = new RawContextKey('addToWorkspaceRecommendations', false); - private addToWorkspaceFolderRecommendationsContextKey = new RawContextKey('addToWorkspaceFolderRecommendations', false); - - constructor( - @IContextKeyService contextKeyService: IContextKeyService, - @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, - @IEditorService editorService: IEditorService - ) { - super(); - const boundWorkspaceContextKey = this.workspaceContextKey.bindTo(contextKeyService); - boundWorkspaceContextKey.set(workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE); - this._register(workspaceContextService.onDidChangeWorkbenchState(() => boundWorkspaceContextKey.set(workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE))); - - const boundWorkspaceFolderContextKey = this.workspaceFolderContextKey.bindTo(contextKeyService); - boundWorkspaceFolderContextKey.set(workspaceContextService.getWorkspace().folders.length > 0); - this._register(workspaceContextService.onDidChangeWorkspaceFolders(() => boundWorkspaceFolderContextKey.set(workspaceContextService.getWorkspace().folders.length > 0))); - - const boundAddToWorkspaceRecommendationsContextKey = this.addToWorkspaceRecommendationsContextKey.bindTo(contextKeyService); - boundAddToWorkspaceRecommendationsContextKey.set(editorService.activeEditor instanceof ExtensionsInput && workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE); - this._register(editorService.onDidActiveEditorChange(() => boundAddToWorkspaceRecommendationsContextKey.set( - editorService.activeEditor instanceof ExtensionsInput && workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE))); - this._register(workspaceContextService.onDidChangeWorkbenchState(() => boundAddToWorkspaceRecommendationsContextKey.set( - editorService.activeEditor instanceof ExtensionsInput && workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE))); - - const boundAddToWorkspaceFolderRecommendationsContextKey = this.addToWorkspaceFolderRecommendationsContextKey.bindTo(contextKeyService); - boundAddToWorkspaceFolderRecommendationsContextKey.set(editorService.activeEditor instanceof ExtensionsInput); - this._register(editorService.onDidActiveEditorChange(() => boundAddToWorkspaceFolderRecommendationsContextKey.set(editorService.activeEditor instanceof ExtensionsInput))); - - this.registerCommands(); - } - - private registerCommands(): void { - CommandsRegistry.registerCommand(ConfigureWorkspaceRecommendedExtensionsAction.ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService).createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL).run(); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: ConfigureWorkspaceRecommendedExtensionsAction.ID, - title: { value: ConfigureWorkspaceRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace)' }, - category: localize('extensions', "Extensions") - }, - when: this.workspaceContextKey - }); - - CommandsRegistry.registerCommand(ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService).createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL).run(); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, - title: { value: ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace Folder)' }, - category: localize('extensions', "Extensions") - }, - when: this.workspaceFolderContextKey - }); - - CommandsRegistry.registerCommand(AddToWorkspaceRecommendationsAction.ADD_ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService) - .createInstance(AddToWorkspaceRecommendationsAction, AddToWorkspaceRecommendationsAction.ADD_ID, AddToWorkspaceRecommendationsAction.ADD_LABEL) - .run(AddToWorkspaceRecommendationsAction.ADD); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: AddToWorkspaceRecommendationsAction.ADD_ID, - title: { value: AddToWorkspaceRecommendationsAction.ADD_LABEL, original: 'Add to Recommended Extensions (Workspace)' }, - category: localize('extensions', "Extensions") - }, - when: this.addToWorkspaceRecommendationsContextKey - }); - - CommandsRegistry.registerCommand(AddToWorkspaceFolderRecommendationsAction.ADD_ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService) - .createInstance(AddToWorkspaceFolderRecommendationsAction, AddToWorkspaceFolderRecommendationsAction.ADD_ID, AddToWorkspaceFolderRecommendationsAction.ADD_LABEL) - .run(AddToWorkspaceRecommendationsAction.ADD); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: AddToWorkspaceFolderRecommendationsAction.ADD_ID, - title: { value: AddToWorkspaceFolderRecommendationsAction.ADD_LABEL, original: 'Extensions: Add to Recommended Extensions (Workspace Folder)' }, - category: localize('extensions', "Extensions") - }, - when: this.addToWorkspaceFolderRecommendationsContextKey - }); - - CommandsRegistry.registerCommand(AddToWorkspaceRecommendationsAction.IGNORE_ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService) - .createInstance(AddToWorkspaceRecommendationsAction, AddToWorkspaceRecommendationsAction.IGNORE_ID, AddToWorkspaceRecommendationsAction.IGNORE_LABEL) - .run(AddToWorkspaceRecommendationsAction.IGNORE); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: AddToWorkspaceRecommendationsAction.IGNORE_ID, - title: { value: AddToWorkspaceRecommendationsAction.IGNORE_LABEL, original: 'Extensions: Ignore Recommended Extension (Workspace)' }, - category: localize('extensions', "Extensions") - }, - when: this.addToWorkspaceRecommendationsContextKey - }); - - CommandsRegistry.registerCommand(AddToWorkspaceFolderRecommendationsAction.IGNORE_ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService) - .createInstance(AddToWorkspaceFolderRecommendationsAction, AddToWorkspaceFolderRecommendationsAction.IGNORE_ID, AddToWorkspaceFolderRecommendationsAction.IGNORE_LABEL) - .run(AddToWorkspaceRecommendationsAction.IGNORE); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: AddToWorkspaceFolderRecommendationsAction.IGNORE_ID, - title: { value: AddToWorkspaceFolderRecommendationsAction.IGNORE_LABEL, original: 'Extensions: Ignore Recommended Extension (Workspace Folder)' }, - category: localize('extensions', "Extensions") - }, - when: this.addToWorkspaceFolderRecommendationsContextKey - }); - } -} - export abstract class AbstractConfigureRecommendedExtensionsAction extends Action { constructor( @@ -2293,83 +2179,6 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio })); } - protected addExtensionToWorkspaceConfig(workspaceConfigurationFile: URI, extensionId: string, shouldRecommend: boolean) { - return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile) - .then(content => { - const extensionIdLowerCase = extensionId.toLowerCase(); - const workspaceExtensionsConfigContent: IExtensionsConfigContent = (json.parse(content.value.toString()) || {})['extensions'] || {}; - let insertInto = shouldRecommend ? workspaceExtensionsConfigContent.recommendations || [] : workspaceExtensionsConfigContent.unwantedRecommendations || []; - let removeFrom = shouldRecommend ? workspaceExtensionsConfigContent.unwantedRecommendations || [] : workspaceExtensionsConfigContent.recommendations || []; - - if (insertInto.some(e => e.toLowerCase() === extensionIdLowerCase)) { - return Promise.resolve(null); - } - - insertInto.push(extensionId); - removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase); - - return this.jsonEditingService.write(workspaceConfigurationFile, - [{ - path: ['extensions'], - value: { - recommendations: shouldRecommend ? insertInto : removeFrom, - unwantedRecommendations: shouldRecommend ? removeFrom : insertInto - } - }], - true); - }); - } - - protected addExtensionToWorkspaceFolderConfig(extensionsFileResource: URI, extensionId: string, shouldRecommend: boolean): Promise { - return this.getOrCreateExtensionsFile(extensionsFileResource) - .then(({ content }) => { - const extensionIdLowerCase = extensionId.toLowerCase(); - const extensionsConfigContent: IExtensionsConfigContent = json.parse(content) || {}; - let insertInto = shouldRecommend ? extensionsConfigContent.recommendations || [] : extensionsConfigContent.unwantedRecommendations || []; - let removeFrom = shouldRecommend ? extensionsConfigContent.unwantedRecommendations || [] : extensionsConfigContent.recommendations || []; - - if (insertInto.some(e => e.toLowerCase() === extensionIdLowerCase)) { - return Promise.resolve(null); - } - - insertInto.push(extensionId); - - let removeFromPromise: Promise = Promise.resolve(); - if (removeFrom.some(e => e.toLowerCase() === extensionIdLowerCase)) { - removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase); - removeFromPromise = this.jsonEditingService.write(extensionsFileResource, - [{ - path: shouldRecommend ? ['unwantedRecommendations'] : ['recommendations'], - value: removeFrom - }], - true); - } - - return removeFromPromise.then(() => - this.jsonEditingService.write(extensionsFileResource, - [{ - path: shouldRecommend ? ['recommendations'] : ['unwantedRecommendations'], - value: insertInto - }], - true) - ); - }); - } - - protected getWorkspaceExtensionsConfigContent(extensionsFileResource: URI): Promise { - return Promise.resolve(this.fileService.readFile(extensionsFileResource)) - .then(content => { - return (json.parse(content.value.toString()) || {})['extensions'] || {}; - }, err => ({ recommendations: [], unwantedRecommendations: [] })); - } - - protected getWorkspaceFolderExtensionsConfigContent(extensionsFileResource: URI): Promise { - return Promise.resolve(this.fileService.readFile(extensionsFileResource)) - .then(content => { - return (json.parse(content.value.toString()) || {}); - }, err => ({ recommendations: [], unwantedRecommendations: [] })); - } - private getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise { return Promise.resolve(this.fileService.readFile(workspaceConfigurationFile)) .then(content => { @@ -2488,161 +2297,6 @@ export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends Abstrac } } -export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigureRecommendedExtensionsAction { - static readonly ADD = true; - static readonly IGNORE = false; - static readonly ADD_ID = 'workbench.extensions.action.addToWorkspaceFolderRecommendations'; - static readonly ADD_LABEL = localize('addToWorkspaceFolderRecommendations', "Add to Recommended Extensions (Workspace Folder)"); - static readonly IGNORE_ID = 'workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations'; - static readonly IGNORE_LABEL = localize('addToWorkspaceFolderIgnoredRecommendations', "Ignore Recommended Extension (Workspace Folder)"); - - constructor( - id: string, - label: string, - @IFileService fileService: IFileService, - @ITextFileService textFileService: ITextFileService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IEditorService editorService: IEditorService, - @IJSONEditingService jsonEditingService: IJSONEditingService, - @ITextModelService textModelResolverService: ITextModelService, - @ICommandService private readonly commandService: ICommandService, - @INotificationService private readonly notificationService: INotificationService - ) { - super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService); - } - - run(shouldRecommend: boolean): Promise { - if (!(this.editorService.activeEditor instanceof ExtensionsInput) || !this.editorService.activeEditor.extension) { - return Promise.resolve(); - } - const folders = this.contextService.getWorkspace().folders; - if (!folders || !folders.length) { - this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.noWorkspace', 'There are no workspace folders open to add recommendations.')); - return Promise.resolve(); - } - - const extensionId = this.editorService.activeEditor.extension.identifier; - const pickFolderPromise = folders.length === 1 - ? Promise.resolve(folders[0]) - : this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID); - return Promise.resolve(pickFolderPromise) - .then(workspaceFolder => { - if (!workspaceFolder) { - return Promise.resolve(); - } - const configurationFile = workspaceFolder.toResource(EXTENSIONS_CONFIG); - return this.getWorkspaceFolderExtensionsConfigContent(configurationFile).then(content => { - const extensionIdLowerCase = extensionId.id.toLowerCase(); - if (shouldRecommend) { - if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { - this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.alreadyExists', 'This extension is already present in this workspace folder\'s recommendations.')); - return Promise.resolve(); - } - - return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => { - this.notificationService.prompt(Severity.Info, - localize('AddToWorkspaceFolderRecommendations.success', 'The extension was successfully added to this workspace folder\'s recommendations.'), - [{ - label: localize('viewChanges', "View Changes"), - run: () => this.openExtensionsFile(configurationFile) - }]); - }, err => { - this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err)); - }); - } - else { - if ((content.unwantedRecommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { - this.notificationService.info(localize('AddToWorkspaceFolderIgnoredRecommendations.alreadyExists', 'This extension is already present in this workspace folder\'s unwanted recommendations.')); - return Promise.resolve(); - } - - return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => { - this.notificationService.prompt(Severity.Info, - localize('AddToWorkspaceFolderIgnoredRecommendations.success', 'The extension was successfully added to this workspace folder\'s unwanted recommendations.'), - [{ - label: localize('viewChanges', "View Changes"), - run: () => this.openExtensionsFile(configurationFile) - }]); - }, err => { - this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err)); - }); - } - }); - }); - } -} - -export class AddToWorkspaceRecommendationsAction extends AbstractConfigureRecommendedExtensionsAction { - static readonly ADD = true; - static readonly IGNORE = false; - static readonly ADD_ID = 'workbench.extensions.action.addToWorkspaceRecommendations'; - static readonly ADD_LABEL = localize('addToWorkspaceRecommendations', "Add to Recommended Extensions (Workspace)"); - static readonly IGNORE_ID = 'workbench.extensions.action.addToWorkspaceIgnoredRecommendations'; - static readonly IGNORE_LABEL = localize('addToWorkspaceIgnoredRecommendations', "Ignore Recommended Extension (Workspace)"); - - constructor( - id: string, - label: string, - @IFileService fileService: IFileService, - @ITextFileService textFileService: ITextFileService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IEditorService editorService: IEditorService, - @IJSONEditingService jsonEditingService: IJSONEditingService, - @ITextModelService textModelResolverService: ITextModelService, - @INotificationService private readonly notificationService: INotificationService - ) { - super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService); - } - - run(shouldRecommend: boolean): Promise { - const workspaceConfig = this.contextService.getWorkspace().configuration; - - if (!(this.editorService.activeEditor instanceof ExtensionsInput) || !this.editorService.activeEditor.extension || !workspaceConfig) { - return Promise.resolve(); - } - - const extensionId = this.editorService.activeEditor.extension.identifier; - - return this.getWorkspaceExtensionsConfigContent(workspaceConfig).then(content => { - const extensionIdLowerCase = extensionId.id.toLowerCase(); - if (shouldRecommend) { - if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { - this.notificationService.info(localize('AddToWorkspaceRecommendations.alreadyExists', 'This extension is already present in workspace recommendations.')); - return Promise.resolve(); - } - - return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => { - this.notificationService.prompt(Severity.Info, - localize('AddToWorkspaceRecommendations.success', 'The extension was successfully added to this workspace\'s recommendations.'), - [{ - label: localize('viewChanges', "View Changes"), - run: () => this.openWorkspaceConfigurationFile(workspaceConfig) - }]); - - }, err => { - this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err)); - }); - } else { - if ((content.unwantedRecommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { - this.notificationService.info(localize('AddToWorkspaceUnwantedRecommendations.alreadyExists', 'This extension is already present in workspace unwanted recommendations.')); - return Promise.resolve(); - } - - return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => { - this.notificationService.prompt(Severity.Info, - localize('AddToWorkspaceUnwantedRecommendations.success', 'The extension was successfully added to this workspace\'s unwanted recommendations.'), - [{ - label: localize('viewChanges', "View Changes"), - run: () => this.openWorkspaceConfigurationFile(workspaceConfig) - }]); - }, err => { - this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err)); - }); - } - }); - } -} - export class StatusLabelAction extends Action implements IExtensionContainer { private static readonly ENABLED_CLASS = `${ExtensionAction.TEXT_ACTION_CLASS} extension-status-label`; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 8dc0c106914..ceaf99d305a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -44,7 +44,6 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { IMenuService } from 'vs/platform/actions/common/actions'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; @@ -112,7 +111,6 @@ export class ExtensionsListView extends ViewPane { @IProductService protected readonly productService: IProductService, @IContextKeyService contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IMenuService private readonly menuService: IMenuService, @IOpenerService openerService: IOpenerService, @IPreferencesService private readonly preferencesService: IPreferencesService, ) { @@ -251,7 +249,7 @@ export class ExtensionsListView extends ViewPane { getActions: () => actions.slice(0, actions.length - 1) }); } else if (e.element) { - const groups = getContextMenuActions(this.menuService, this.contextKeyService.createScoped(), this.instantiationService, e.element); + const groups = getContextMenuActions(e.element, this.instantiationService); groups.forEach(group => group.forEach(extensionAction => { if (extensionAction instanceof ExtensionAction) { extensionAction.extension = e.element!; @@ -890,7 +888,6 @@ export class ServerExtensionsView extends ExtensionsListView { @IWorkbenchExtensioManagementService extensionManagementService: IWorkbenchExtensioManagementService, @IProductService productService: IProductService, @IContextKeyService contextKeyService: IContextKeyService, - @IMenuService menuService: IMenuService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, @IPreferencesService preferencesService: IPreferencesService, @@ -898,7 +895,7 @@ export class ServerExtensionsView extends ExtensionsListView { options.server = server; super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, tipsService, telemetryService, configurationService, contextService, experimentService, extensionManagementServerService, extensionManagementService, productService, - contextKeyService, viewDescriptorService, menuService, openerService, preferencesService); + contextKeyService, viewDescriptorService, openerService, preferencesService); this._register(onDidChangeTitle(title => this.updateTitle(title))); } diff --git a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts index 231aa8f4b47..013e981bf18 100644 --- a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts @@ -7,12 +7,12 @@ import { EXTENSION_IDENTIFIER_PATTERN, IExtensionGalleryService } from 'vs/platf import { distinct, flatten } from 'vs/base/common/arrays'; import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IExtensionsConfigContent, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; -import { IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; +import { IExtensionsConfigContent, IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; export class WorkspaceRecommendations extends ExtensionRecommendations { @@ -51,23 +51,28 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { this.notificationService.warn(`The ${invalidRecommendations.length} extension(s) below, in workspace recommendations have issues:\n${message}`); } + this._recommendations = []; this._ignoredRecommendations = []; for (const extensionsConfig of extensionsConfigs) { - for (const unwantedRecommendation of extensionsConfig.unwantedRecommendations) { - if (invalidRecommendations.indexOf(unwantedRecommendation) === -1) { - this._ignoredRecommendations.push(unwantedRecommendation); + if (extensionsConfig.unwantedRecommendations) { + for (const unwantedRecommendation of extensionsConfig.unwantedRecommendations) { + if (invalidRecommendations.indexOf(unwantedRecommendation) === -1) { + this._ignoredRecommendations.push(unwantedRecommendation); + } } } - for (const extensionId of extensionsConfig.recommendations) { - if (invalidRecommendations.indexOf(extensionId) === -1) { - this._recommendations.push({ - extensionId, - reason: { - reasonId: ExtensionRecommendationReason.Workspace, - reasonText: localize('workspaceRecommendation', "This extension is recommended by users of the current workspace.") - } - }); + if (extensionsConfig.recommendations) { + for (const extensionId of extensionsConfig.recommendations) { + if (invalidRecommendations.indexOf(extensionId) === -1) { + this._recommendations.push({ + extensionId, + reason: { + reasonId: ExtensionRecommendationReason.Workspace, + reasonText: localize('workspaceRecommendation', "This extension is recommended by users of the current workspace.") + } + }); + } } } } @@ -114,12 +119,8 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { } private async onDidChangeExtensionsConfigs(): Promise { - const oldWorkspaceRecommended = this._recommendations; await this.fetch(); - // Suggest only if at least one of the newly added recommendations was not suggested before - if (this._recommendations.some(current => oldWorkspaceRecommended.every(old => current.extensionId !== old.extensionId))) { - this._onDidChangeRecommendations.fire(); - } + this._onDidChangeRecommendations.fire(); } } 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 2f44fabf0ae..311037fc22c 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 @@ -304,14 +304,14 @@ suite('ExtensionRecommendationsService Test', () => { }, null, '\t')); const myWorkspace = testWorkspace(URI.from({ scheme: 'file', path: folderDir })); + const fileService = new FileService(new NullLogService()); + fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); + instantiationService.stub(IFileService, fileService); workspaceService = new TestContextService(myWorkspace); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IWorkpsaceExtensionsConfigService, instantiationService.createInstance(WorkspaceExtensionsConfigService)); instantiationService.stub(IExtensionIgnoredRecommendationsService, instantiationService.createInstance(ExtensionIgnoredRecommendationsService)); instantiationService.stub(IExtensionRecommendationNotificationService, instantiationService.createInstance(ExtensionRecommendationNotificationService)); - const fileService = new FileService(new NullLogService()); - fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); - instantiationService.stub(IFileService, fileService); } function testNoPromptForValidRecommendations(recommendations: string[]) { diff --git a/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts b/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts index 21fb2dcf206..0d76094d021 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts @@ -7,11 +7,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IStringDictionary } from 'vs/base/common/collections'; import { Event } from 'vs/base/common/event'; -export interface IExtensionsConfigContent { - recommendations: string[]; - unwantedRecommendations: string[]; -} - export type DynamicRecommendation = 'dynamic'; export type ConfigRecommendation = 'config'; export type ExecutableRecommendation = 'executable'; diff --git a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts index dfcdaf50147..dc16b357514 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts @@ -3,20 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { coalesce, distinct, flatten } from 'vs/base/common/arrays'; +import { distinct, flatten } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; import { parse } from 'vs/base/common/json'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IFileService } from 'vs/platform/files/common/files'; +import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; +import { FileKind, IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { localize } from 'vs/nls'; +import { URI } from 'vs/base/common/uri'; +import { IJSONEditingService, IJSONValue } from 'vs/workbench/services/configuration/common/jsonEditing'; +import { ResourceMap } from 'vs/base/common/map'; export const EXTENSIONS_CONFIG = '.vscode/extensions.json'; export interface IExtensionsConfigContent { - recommendations: string[]; - unwantedRecommendations: string[]; + recommendations?: string[]; + unwantedRecommendations?: string[]; } export const IWorkpsaceExtensionsConfigService = createDecorator('IWorkpsaceExtensionsConfigService'); @@ -26,8 +34,11 @@ export interface IWorkpsaceExtensionsConfigService { onDidChangeExtensionsConfigs: Event; getExtensionsConfigs(): Promise; + getRecommendations(): Promise; getUnwantedRecommendations(): Promise; + toggleRecommendation(extensionId: string): Promise; + toggleUnwantedRecommendation(extensionId: string): Promise; } export class WorkspaceExtensionsConfigService extends Disposable implements IWorkpsaceExtensionsConfigService { @@ -40,53 +51,217 @@ export class WorkspaceExtensionsConfigService extends Disposable implements IWor constructor( @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IFileService private readonly fileService: IFileService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IModelService private readonly modelService: IModelService, + @IModeService private readonly modeService: IModeService, + @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, ) { super(); - this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(e => this._onDidChangeExtensionsConfigs.fire())); + this._register(workspaceContextService.onDidChangeWorkspaceFolders(e => this._onDidChangeExtensionsConfigs.fire())); + this._register(fileService.onDidFilesChange(e => { + const workspace = workspaceContextService.getWorkspace(); + if ((workspace.configuration && e.affects(workspace.configuration)) + || workspace.folders.some(folder => e.affects(folder.toResource(EXTENSIONS_CONFIG))) + ) { + this._onDidChangeExtensionsConfigs.fire(); + } + })); } async getExtensionsConfigs(): Promise { const workspace = this.workspaceContextService.getWorkspace(); - const result = await Promise.all([ - this.resolveWorkspaceExtensionConfig(workspace), - ...workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderExtensionConfig(workspaceFolder)) - ]); - return coalesce(result); + const result: IExtensionsConfigContent[] = []; + const workspaceExtensionsConfigContent = workspace.configuration ? await this.resolveWorkspaceExtensionConfig(workspace.configuration) : undefined; + if (workspaceExtensionsConfigContent) { + result.push(workspaceExtensionsConfigContent); + } + result.push(...await Promise.all(workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderExtensionConfig(workspaceFolder)))); + return result; + } + + async getRecommendations(): Promise { + const configs = await this.getExtensionsConfigs(); + return distinct(flatten(configs.map(c => c.recommendations ? c.recommendations.map(c => c.toLowerCase()) : []))); } async getUnwantedRecommendations(): Promise { const configs = await this.getExtensionsConfigs(); - return distinct(flatten(configs.map(c => c.unwantedRecommendations.map(c => c.toLowerCase())))); + return distinct(flatten(configs.map(c => c.unwantedRecommendations ? c.unwantedRecommendations.map(c => c.toLowerCase()) : []))); } - private async resolveWorkspaceExtensionConfig(workspace: IWorkspace): Promise { - try { - if (workspace.configuration) { - const content = await this.fileService.readFile(workspace.configuration); - const extensionsConfigContent = parse(content.value.toString())['extensions']; - return this.parseExtensionConfig(extensionsConfigContent); + async toggleRecommendation(extensionId: string): Promise { + const workspace = this.workspaceContextService.getWorkspace(); + const workspaceExtensionsConfigContent = workspace.configuration ? await this.resolveWorkspaceExtensionConfig(workspace.configuration) : undefined; + const workspaceFolderExtensionsConfigContents = new ResourceMap(); + await Promise.all(workspace.folders.map(async workspaceFolder => { + const extensionsConfigContent = await this.resolveWorkspaceFolderExtensionConfig(workspaceFolder); + workspaceFolderExtensionsConfigContents.set(workspaceFolder.uri, extensionsConfigContent); + })); + + const isWorkspaceRecommended = workspaceExtensionsConfigContent && workspaceExtensionsConfigContent.recommendations?.some(r => r === extensionId); + const recommendedWorksapceFolders = workspace.folders.filter(workspaceFolder => workspaceFolderExtensionsConfigContents.get(workspaceFolder.uri)?.recommendations?.some(r => r === extensionId)); + const isRecommended = isWorkspaceRecommended || recommendedWorksapceFolders.length > 0; + + const workspaceOrFolders = isRecommended + ? await this.pickWorkspaceOrFolders(recommendedWorksapceFolders, isWorkspaceRecommended ? workspace : undefined, localize('select for remove', "Remove extension recommendation from")) + : await this.pickWorkspaceOrFolders(workspace.folders, workspace.configuration ? workspace : undefined, localize('select for add', "Add extension recommendation to")); + + for (const workspaceOrWorkspaceFolder of workspaceOrFolders) { + if (IWorkspace.isIWorkspace(workspaceOrWorkspaceFolder)) { + await this.addOrRemoveWorkspaceRecommendation(extensionId, workspaceOrWorkspaceFolder, workspaceExtensionsConfigContent, !isRecommended); + } else { + await this.addOrRemoveWorkspaceFolderRecommendation(extensionId, workspaceOrWorkspaceFolder, workspaceFolderExtensionsConfigContents.get(workspaceOrWorkspaceFolder.uri)!, !isRecommended); } - } catch (e) { /* Ignore */ } - return null; + } } - private async resolveWorkspaceFolderExtensionConfig(workspaceFolder: IWorkspaceFolder): Promise { + async toggleUnwantedRecommendation(extensionId: string): Promise { + const workspace = this.workspaceContextService.getWorkspace(); + const workspaceExtensionsConfigContent = workspace.configuration ? await this.resolveWorkspaceExtensionConfig(workspace.configuration) : undefined; + const workspaceFolderExtensionsConfigContents = new ResourceMap(); + await Promise.all(workspace.folders.map(async workspaceFolder => { + const extensionsConfigContent = await this.resolveWorkspaceFolderExtensionConfig(workspaceFolder); + workspaceFolderExtensionsConfigContents.set(workspaceFolder.uri, extensionsConfigContent); + })); + + const isWorkspaceUnwanted = workspaceExtensionsConfigContent && workspaceExtensionsConfigContent.unwantedRecommendations?.some(r => r === extensionId); + const unWantedWorksapceFolders = workspace.folders.filter(workspaceFolder => workspaceFolderExtensionsConfigContents.get(workspaceFolder.uri)?.unwantedRecommendations?.some(r => r === extensionId)); + const isUnwanted = isWorkspaceUnwanted || unWantedWorksapceFolders.length > 0; + + const workspaceOrFolders = isUnwanted + ? await this.pickWorkspaceOrFolders(unWantedWorksapceFolders, isWorkspaceUnwanted ? workspace : undefined, localize('select for remove', "Remove extension recommendation from")) + : await this.pickWorkspaceOrFolders(workspace.folders, workspace.configuration ? workspace : undefined, localize('select for add', "Add extension recommendation to")); + + for (const workspaceOrWorkspaceFolder of workspaceOrFolders) { + if (IWorkspace.isIWorkspace(workspaceOrWorkspaceFolder)) { + await this.addOrRemoveWorkspaceUnwantedRecommendation(extensionId, workspaceOrWorkspaceFolder, workspaceExtensionsConfigContent, !isUnwanted); + } else { + await this.addOrRemoveWorkspaceFolderUnwantedRecommendation(extensionId, workspaceOrWorkspaceFolder, workspaceFolderExtensionsConfigContents.get(workspaceOrWorkspaceFolder.uri)!, !isUnwanted); + } + } + } + + private async addOrRemoveWorkspaceFolderRecommendation(extensionId: string, workspaceFolder: IWorkspaceFolder, extensionsConfigContent: IExtensionsConfigContent, add: boolean): Promise { + const values: IJSONValue[] = []; + if (add) { + values.push({ path: ['recommendations'], value: [...extensionsConfigContent.recommendations || [], extensionId] }); + if (extensionsConfigContent.unwantedRecommendations && extensionsConfigContent.unwantedRecommendations.some(e => e === extensionId)) { + values.push({ path: ['unwantedRecommendations'], value: extensionsConfigContent.unwantedRecommendations.filter(e => e !== extensionId) }); + } + } else if (extensionsConfigContent.recommendations) { + values.push({ path: ['recommendations'], value: extensionsConfigContent.recommendations.filter(e => e !== extensionId) }); + } + + if (values.length) { + return this.jsonEditingService.write(workspaceFolder.toResource(EXTENSIONS_CONFIG), values, true); + } + } + + private async addOrRemoveWorkspaceRecommendation(extensionId: string, workspace: IWorkspace, extensionsConfigContent: IExtensionsConfigContent | undefined, add: boolean): Promise { + const values: IJSONValue[] = []; + if (extensionsConfigContent) { + if (add) { + values.push({ path: ['recommendations'], value: [...extensionsConfigContent.recommendations || [], extensionId] }); + if (extensionsConfigContent.unwantedRecommendations && extensionsConfigContent.unwantedRecommendations.some(e => e === extensionId)) { + values.push({ path: ['unwantedRecommendations'], value: extensionsConfigContent.unwantedRecommendations.filter(e => e !== extensionId) }); + } + } else if (extensionsConfigContent.recommendations) { + values.push({ path: ['recommendations'], value: extensionsConfigContent.recommendations.filter(e => e !== extensionId) }); + } + } else if (add) { + values.push({ path: ['extensions'], value: { recommendations: [extensionId] } }); + } + + if (values.length) { + return this.jsonEditingService.write(workspace.configuration!, values, true); + } + } + + private async addOrRemoveWorkspaceFolderUnwantedRecommendation(extensionId: string, workspaceFolder: IWorkspaceFolder, extensionsConfigContent: IExtensionsConfigContent, add: boolean): Promise { + const values: IJSONValue[] = []; + if (add) { + values.push({ path: ['unwantedRecommendations'], value: [...extensionsConfigContent.unwantedRecommendations || [], extensionId] }); + if (extensionsConfigContent.recommendations && extensionsConfigContent.recommendations.some(e => e === extensionId)) { + values.push({ path: ['recommendations'], value: extensionsConfigContent.recommendations.filter(e => e !== extensionId) }); + } + } else if (extensionsConfigContent.unwantedRecommendations) { + values.push({ path: ['unwantedRecommendations'], value: extensionsConfigContent.unwantedRecommendations.filter(e => e !== extensionId) }); + } + if (values.length) { + return this.jsonEditingService.write(workspaceFolder.toResource(EXTENSIONS_CONFIG), values, true); + } + } + + private async addOrRemoveWorkspaceUnwantedRecommendation(extensionId: string, workspace: IWorkspace, extensionsConfigContent: IExtensionsConfigContent | undefined, add: boolean): Promise { + const values: IJSONValue[] = []; + if (extensionsConfigContent) { + if (add) { + values.push({ path: ['unwantedRecommendations'], value: [...extensionsConfigContent.unwantedRecommendations || [], extensionId] }); + if (extensionsConfigContent.recommendations && extensionsConfigContent.recommendations.some(e => e === extensionId)) { + values.push({ path: ['recommendations'], value: extensionsConfigContent.recommendations.filter(e => e !== extensionId) }); + } + } else if (extensionsConfigContent.unwantedRecommendations) { + values.push({ path: ['unwantedRecommendations'], value: extensionsConfigContent.unwantedRecommendations.filter(e => e !== extensionId) }); + } + } else if (add) { + values.push({ path: ['extensions'], value: { unwantedRecommendations: [extensionId] } }); + } + + if (values.length) { + return this.jsonEditingService.write(workspace.configuration!, values, true); + } + } + + private async pickWorkspaceOrFolders(workspaceFolders: IWorkspaceFolder[], workspace: IWorkspace | undefined, placeHolder: string): Promise<(IWorkspace | IWorkspaceFolder)[]> { + const workspaceOrFolders = workspace ? [...workspaceFolders, workspace] : [...workspaceFolders]; + if (workspaceOrFolders.length === 1) { + return workspaceOrFolders; + } + + const folderPicks: (IQuickPickItem & { workspaceOrFolder: IWorkspace | IWorkspaceFolder } | IQuickPickSeparator)[] = workspaceFolders.map(workspaceFolder => { + return { + label: workspaceFolder.name, + description: localize('workspace folder', "Workspace Folder"), + workspaceOrFolder: workspaceFolder, + iconClasses: getIconClasses(this.modelService, this.modeService, workspaceFolder.uri, FileKind.ROOT_FOLDER) + }; + }); + + if (workspace) { + folderPicks.push({ type: 'separator' }); + folderPicks.push({ + label: localize('workspace', "Workspace"), + workspaceOrFolder: workspace, + }); + } + + const result = await this.quickInputService.pick(folderPicks, { placeHolder, canPickMany: true }) || []; + return result.map(r => r.workspaceOrFolder!); + } + + private async resolveWorkspaceExtensionConfig(workspaceConfigurationResource: URI): Promise { + try { + const content = await this.fileService.readFile(workspaceConfigurationResource); + const extensionsConfigContent = parse(content.value.toString())['extensions']; + return extensionsConfigContent ? this.parseExtensionConfig(extensionsConfigContent) : undefined; + } catch (e) { /* Ignore */ } + return undefined; + } + + private async resolveWorkspaceFolderExtensionConfig(workspaceFolder: IWorkspaceFolder): Promise { try { const content = await this.fileService.readFile(workspaceFolder.toResource(EXTENSIONS_CONFIG)); const extensionsConfigContent = parse(content.value.toString()); return this.parseExtensionConfig(extensionsConfigContent); } catch (e) { /* ignore */ } - return null; + return {}; } - private parseExtensionConfig(extensionsConfigContent: IExtensionsConfigContent | undefined): IExtensionsConfigContent | null { - if (extensionsConfigContent) { - return { - recommendations: distinct((extensionsConfigContent.recommendations || []).map(e => e.toLowerCase())), - unwantedRecommendations: distinct((extensionsConfigContent.unwantedRecommendations || []).map(e => e.toLowerCase())) - }; - } - return null; + private parseExtensionConfig(extensionsConfigContent: IExtensionsConfigContent): IExtensionsConfigContent { + return { + recommendations: distinct((extensionsConfigContent.recommendations || []).map(e => e.toLowerCase())), + unwantedRecommendations: distinct((extensionsConfigContent.unwantedRecommendations || []).map(e => e.toLowerCase())) + }; } } From e3754e6f8dbf9c661ded171b352daddf10e45eca Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 9 Nov 2020 15:40:53 +0100 Subject: [PATCH 050/103] group uninstall and install another version actions into same group --- .../contrib/extensions/browser/extensionsActions.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 39f82943ef4..dfb356bf10d 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -912,8 +912,10 @@ export class ManageExtensionAction extends ExtensionDropDownAction { this.instantiationService.createInstance(DisableGloballyAction, runningExtensions), this.instantiationService.createInstance(DisableForWorkspaceAction, runningExtensions) ]); - groups.push([this.instantiationService.createInstance(UninstallAction)]); - groups.push([this.instantiationService.createInstance(InstallAnotherVersionAction)]); + groups.push([ + this.instantiationService.createInstance(UninstallAction), + this.instantiationService.createInstance(InstallAnotherVersionAction) + ]); getContextMenuActions(this.extension, this.instantiationService).forEach(actions => groups.push(actions)); From 7e5609afa98f49fb5a56ef37872efa20346d08f7 Mon Sep 17 00:00:00 2001 From: Scott Davis Date: Mon, 9 Nov 2020 15:00:44 +0000 Subject: [PATCH 051/103] feat: implement Git: Push Tags command (#110096) Related to #109799 --- extensions/git/package.json | 9 +++++++++ extensions/git/package.nls.json | 1 + extensions/git/src/commands.ts | 10 ++++++++++ extensions/git/src/git.ts | 8 ++++++-- extensions/git/src/repository.ts | 8 ++++++-- 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index e0659e26395..ef145a305d1 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -379,6 +379,11 @@ "title": "%command.pushToForce%", "category": "Git" }, + { + "command": "git.pushTags", + "title": "%command.pushTags%", + "category": "Git" + }, { "command": "git.pushWithTags", "title": "%command.pushFollowTags%", @@ -784,6 +789,10 @@ "command": "git.pushWithTagsForce", "when": "config.git.enabled && !git.missing && config.git.allowForcePush && gitOpenRepositoryCount != 0" }, + { + "command": "git.pushTags", + "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 7b9d6cc1166..eb578a4db75 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -68,6 +68,7 @@ "command.pushToForce": "Push to... (Force)", "command.pushFollowTags": "Push (Follow Tags)", "command.pushFollowTagsForce": "Push (Follow Tags, Force)", + "command.pushTags": "Push Tags", "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 ea5891403df..737a32e6c8b 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -271,6 +271,7 @@ enum PushType { Push, PushTo, PushFollowTags, + PushTags } interface PushOptions { @@ -2222,6 +2223,10 @@ export class CommandCenter { return; } + if (pushOptions.pushType === PushType.PushTags) { + await repository.pushTags(undefined, forcePushMode); + } + if (!repository.HEAD || !repository.HEAD.name) { if (!pushOptions.silent) { window.showWarningMessage(localize('nobranch', "Please check out a branch to push to a remote.")); @@ -2303,6 +2308,11 @@ export class CommandCenter { await this._push(repository, { pushType: PushType.PushTo, forcePush: true }); } + @command('git.pushTags', { repository: true }) + async pushTags(repository: Repository): Promise { + await this._push(repository, { pushType: PushType.PushTags }); + } + @command('git.addRemote', { repository: true }) async addRemote(repository: Repository): Promise { const url = await pickRemoteSource(this.model, { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 8b1bf30dd34..df7cb3e1c44 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1649,7 +1649,7 @@ export class Repository { } } - async push(remote?: string, name?: string, setUpstream: boolean = false, tags = false, forcePushMode?: ForcePushMode): Promise { + async push(remote?: string, name?: string, setUpstream: boolean = false, followTags = false, forcePushMode?: ForcePushMode, tags = false): Promise { const args = ['push']; if (forcePushMode === ForcePushMode.ForceWithLease) { @@ -1662,10 +1662,14 @@ export class Repository { args.push('-u'); } - if (tags) { + if (followTags) { args.push('--follow-tags'); } + if (tags) { + args.push('--tags'); + } + if (remote) { args.push(remote); } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index a3840cc6cd8..690162add21 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1221,6 +1221,10 @@ export class Repository implements Disposable { await this.run(Operation.Push, () => this._push(remote, undefined, false, true, forcePushMode)); } + async pushTags(remote?: string, forcePushMode?: ForcePushMode): Promise { + await this.run(Operation.Push, () => this._push(remote, undefined, false, false, forcePushMode, true)); + } + async blame(path: string): Promise { return await this.run(Operation.Blame, () => this.repository.blame(path)); } @@ -1440,9 +1444,9 @@ export class Repository implements Disposable { return ignored; } - private async _push(remote?: string, refspec?: string, setUpstream: boolean = false, tags = false, forcePushMode?: ForcePushMode): Promise { + private async _push(remote?: string, refspec?: string, setUpstream: boolean = false, followTags = false, forcePushMode?: ForcePushMode, tags = false): Promise { try { - await this.repository.push(remote, refspec, setUpstream, tags, forcePushMode); + await this.repository.push(remote, refspec, setUpstream, followTags, forcePushMode, tags); } catch (err) { if (!remote || !refspec) { throw err; From 961cbd66b706f535431b896cfd8adbf5beb089f5 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 9 Nov 2020 16:07:07 +0100 Subject: [PATCH 052/103] CLI help: consider to separate --file-uri and --folder-uri. Fixes #110206 --- src/vs/platform/environment/node/argv.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index d2d8ca659a8..e7b4a45e43d 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -43,8 +43,6 @@ export const OPTIONS: OptionDescriptions> = { 'goto': { type: 'boolean', cat: 'o', alias: 'g', args: 'file:line[:character]', description: localize('goto', "Open a file at the path on the specified line and character position.") }, 'new-window': { type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindow', "Force to open a new window.") }, 'reuse-window': { type: 'boolean', cat: 'o', alias: 'r', description: localize('reuseWindow', "Force to open a file or folder in an already opened window.") }, - 'folder-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") }, - 'file-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") }, 'wait': { type: 'boolean', cat: 'o', alias: 'w', description: localize('wait', "Wait for the files to be closed before returning.") }, 'waitMarkerFilePath': { type: 'string' }, 'locale': { type: 'string', cat: 'o', args: 'locale', description: localize('locale', "The locale to use (e.g. en-US or zh-TW).") }, @@ -79,6 +77,9 @@ export const OPTIONS: OptionDescriptions> = { 'telemetry': { type: 'boolean', cat: 't', description: localize('telemetry', "Shows all telemetry events which VS code collects.") }, 'remote': { type: 'string' }, + 'folder-uri': { type: 'string[]', cat: 'o', args: 'uri' }, + 'file-uri': { type: 'string[]', cat: 'o', args: 'uri' }, + 'locate-extension': { type: 'string[]' }, 'extensionDevelopmentPath': { type: 'string[]' }, 'extensionTestsPath': { type: 'string' }, From 309cabdf512900ff20feccee68a42e4669e1f934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 9 Nov 2020 16:08:03 +0100 Subject: [PATCH 053/103] :lipstick: --- extensions/git/package.json | 4 ++-- extensions/git/package.nls.json | 2 +- extensions/git/src/repository.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 36b0685c2b3..bf64cbad47d 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1797,11 +1797,11 @@ "description": "%config.enableStatusBarSync%", "scope": "resource" }, - "git.pushTags": { + "git.followTagsWhenSync": { "type": "boolean", "scope": "resource", "default": false, - "description": "%config.pushTags%" + "description": "%config.followTagsWhenSync%" }, "git.promptToSaveFilesBeforeStash": { "type": "string", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 5029cbd5cb4..68bef77a393 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -123,7 +123,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.followTagsWhenSync": "Follow push all tags when running the sync command.", "config.promptToSaveFilesBeforeStash": "Controls whether Git should check for unsaved files before stashing changes.", "config.promptToSaveFilesBeforeStash.always": "Check for any unsaved files.", "config.promptToSaveFilesBeforeStash.staged": "Check only for unsaved staged files.", diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 81e1e077a6e..5442f3db15e 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1255,7 +1255,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 followTags = config.get('followTagsWhenSync'); const supportCancellation = config.get('supportCancellation'); const fn = fetchOnPull @@ -1283,7 +1283,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, false, pushTags); + await this._push(remoteName, pushBranch, false, followTags); } }); }); From 61500a468c7cba0af9208e345cfce1346a1197d9 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 9 Nov 2020 16:18:23 +0100 Subject: [PATCH 054/103] Fix #94289 --- .../extensions/browser/extensionEditor.ts | 51 +++++----------- .../browser/extensions.contribution.ts | 2 +- .../extensions/browser/extensionsActions.ts | 58 +++++++++++++++++-- .../extensions/browser/extensionsViews.ts | 2 +- 4 files changed, 68 insertions(+), 45 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 6a8a397c2f6..285311deafc 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -27,7 +27,12 @@ import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { UpdateAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, ExtensionToolTipAction, SystemDisabledWarningAction, LocalInstallAction, ToggleSyncExtensionAction, SetProductIconThemeAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { + UpdateAction, ReloadAction, MaliciousStatusLabelAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, + RemoteInstallAction, ExtensionToolTipAction, SystemDisabledWarningAction, LocalInstallAction, ToggleSyncExtensionAction, SetProductIconThemeAction, + ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, + InstallAnotherVersionAction, ExtensionEditorManageExtensionAction +} from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; @@ -407,7 +412,6 @@ export class ExtensionEditor extends EditorPane { const combinedInstallAction = this.instantiationService.createInstance(InstallDropdownAction); const systemDisabledWarningAction = this.instantiationService.createInstance(SystemDisabledWarningAction); const actions = [ - this.instantiationService.createInstance(ToggleSyncExtensionAction), reloadAction, this.instantiationService.createInstance(StatusLabelAction), this.instantiationService.createInstance(UpdateAction), @@ -421,8 +425,13 @@ export class ExtensionEditor extends EditorPane { this.instantiationService.createInstance(LocalInstallAction), combinedInstallAction, this.instantiationService.createInstance(InstallingLabelAction), - this.instantiationService.createInstance(UninstallAction), + this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.uninstall', UninstallAction.UninstallLabel, [ + this.instantiationService.createInstance(UninstallAction), + this.instantiationService.createInstance(InstallAnotherVersionAction), + ]), + this.instantiationService.createInstance(ToggleSyncExtensionAction), systemDisabledWarningAction, + this.instantiationService.createInstance(ExtensionEditorManageExtensionAction), this.instantiationService.createInstance(ExtensionToolTipAction, systemDisabledWarningAction, reloadAction), this.instantiationService.createInstance(MaliciousStatusLabelAction, true), ]; @@ -435,7 +444,7 @@ export class ExtensionEditor extends EditorPane { this.transientDisposables.add(disposable); } - this.setSubText(extension, reloadAction, template); + this.setSubText(extension, template); template.content.innerText = ''; // Clear content before setting navbar actions. template.navbar.clear(); @@ -466,56 +475,24 @@ export class ExtensionEditor extends EditorPane { this.editorLoadComplete = true; } - private setSubText(extension: IExtension, reloadAction: ReloadAction, template: IExtensionEditorTemplate): void { + private setSubText(extension: IExtension, template: IExtensionEditorTemplate): void { hide(template.subtextContainer); - const ignoreAction = this.instantiationService.createInstance(IgnoreExtensionRecommendationAction, extension); - const undoIgnoreAction = this.instantiationService.createInstance(UndoIgnoreExtensionRecommendationAction, extension); - ignoreAction.enabled = false; - undoIgnoreAction.enabled = false; - - template.ignoreActionbar.clear(); - template.ignoreActionbar.push([ignoreAction, undoIgnoreAction], { icon: true, label: true }); - this.transientDisposables.add(ignoreAction); - this.transientDisposables.add(undoIgnoreAction); - const updateRecommendationFn = () => { const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); if (extRecommendations[extension.identifier.id.toLowerCase()]) { - ignoreAction.enabled = true; - undoIgnoreAction.enabled = false; template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; show(template.subtextContainer); } else if (this.extensionIgnoredRecommendationsService.globalIgnoredRecommendations.indexOf(extension.identifier.id.toLowerCase()) !== -1) { - ignoreAction.enabled = false; - undoIgnoreAction.enabled = true; template.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); show(template.subtextContainer); } else { - ignoreAction.enabled = false; - undoIgnoreAction.enabled = false; template.subtext.textContent = ''; hide(template.subtextContainer); } }; updateRecommendationFn(); this.transientDisposables.add(this.extensionRecommendationsService.onDidChangeRecommendations(() => updateRecommendationFn())); - - this.transientDisposables.add(reloadAction.onDidChange(e => { - if (e.tooltip) { - template.subtext.textContent = reloadAction.tooltip; - show(template.subtextContainer); - ignoreAction.enabled = false; - undoIgnoreAction.enabled = false; - } - if (e.enabled === true) { - show(template.subtextContainer); - } - if (e.enabled === false) { - hide(template.subtextContainer); - } - this.layout(); - })); } clearInput(): void { diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 82f662d9d4c..212bd38ee4d 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -918,7 +918,7 @@ class ExtensionsContributions implements IWorkbenchContribution { menu: { id: MenuId.ExtensionContext, group: '2_configure', - when: CONTEXT_SYNC_ENABLEMENT + when: ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT, ContextKeyExpr.has('inExtensionEditor').negate()) }, }); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index dfb356bf10d..cc3c2c835c0 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -167,7 +167,7 @@ export abstract class ExtensionAction extends Action implements IExtensionContai abstract update(): void; } -export abstract class ActionWithDropDownAction extends ExtensionAction { +export class ActionWithDropDownAction extends ExtensionAction { private action: IAction | undefined; @@ -577,7 +577,7 @@ export class LocalInstallAction extends InstallInOtherServerAction { export class UninstallAction extends ExtensionAction { - private static readonly UninstallLabel = localize('uninstallAction', "Uninstall"); + static readonly UninstallLabel = localize('uninstallAction', "Uninstall"); private static readonly UninstallingLabel = localize('Uninstalling', "Uninstalling"); private static readonly UninstallClass = `${ExtensionAction.LABEL_ACTION_CLASS} uninstall`; @@ -832,7 +832,7 @@ export class DropDownMenuActionViewItem extends ExtensionActionViewItem { } } -export function getContextMenuActions(extension: IExtension | undefined | null, instantiationService: IInstantiationService): IAction[][] { +export function getContextMenuActions(extension: IExtension | undefined | null, inExtensionEditor: boolean, instantiationService: IInstantiationService): IAction[][] { return instantiationService.invokeFunction(accessor => { const scopedContextKeyService = accessor.get(IContextKeyService).createScoped(); const menuService = accessor.get(IMenuService); @@ -845,6 +845,7 @@ export function getContextMenuActions(extension: IExtension | undefined | null, scopedContextKeyService.createKey('isExtensionRecommended', !!extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]); scopedContextKeyService.createKey('isExtensionWorkspaceRecommended', extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]?.reasonId === ExtensionRecommendationReason.Workspace); scopedContextKeyService.createKey('isUserIgnoredRecommendation', extensionIgnoredRecommendationsService.globalIgnoredRecommendations.some(e => e === extension.identifier.id.toLowerCase())); + scopedContextKeyService.createKey('inExtensionEditor', inExtensionEditor); if (extension.state === ExtensionState.Installed) { scopedContextKeyService.createKey('extensionStatus', 'installed'); } @@ -917,7 +918,7 @@ export class ManageExtensionAction extends ExtensionDropDownAction { this.instantiationService.createInstance(InstallAnotherVersionAction) ]); - getContextMenuActions(this.extension, this.instantiationService).forEach(actions => groups.push(actions)); + getContextMenuActions(this.extension, false, this.instantiationService).forEach(actions => groups.push(actions)); groups.forEach(group => group.forEach(extensionAction => { if (extensionAction instanceof ExtensionAction) { @@ -945,6 +946,51 @@ export class ManageExtensionAction extends ExtensionDropDownAction { } } +export class ExtensionEditorManageExtensionAction extends ExtensionDropDownAction { + + private static readonly ID = 'extensionEditor.manageExtension'; + + private static readonly Class = `${ExtensionAction.ICON_ACTION_CLASS} manage codicon-gear`; + private static readonly HideManageExtensionClass = `${ExtensionEditorManageExtensionAction.Class} hide`; + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + @IExtensionService private readonly extensionService: IExtensionService + ) { + + super(ExtensionEditorManageExtensionAction.ID, '', '', true, true, instantiationService); + this.tooltip = localize('manage', "Manage"); + this.update(); + } + + async getActionGroups(runningExtensions: IExtensionDescription[]): Promise { + const groups: IAction[][] = []; + getContextMenuActions(this.extension, true, this.instantiationService).forEach(actions => groups.push(actions)); + groups.forEach(group => group.forEach(extensionAction => { + if (extensionAction instanceof ExtensionAction) { + extensionAction.extension = this.extension; + } + })); + return groups; + } + + async run(): Promise { + const runtimeExtensions = await this.extensionService.getExtensions(); + return super.run({ actionGroups: await this.getActionGroups(runtimeExtensions), disposeActionsOnHide: true }); + } + + update(): void { + this.class = ExtensionEditorManageExtensionAction.HideManageExtensionClass; + this.enabled = false; + if (this.extension) { + const state = this.extension.state; + this.enabled = state === ExtensionState.Installed; + this.class = this.enabled || state === ExtensionState.Uninstalling ? ExtensionEditorManageExtensionAction.Class : ExtensionEditorManageExtensionAction.HideManageExtensionClass; + this.tooltip = state === ExtensionState.Uninstalling ? localize('ManageExtensionAction.uninstallingTooltip', "Uninstalling") : ''; + } + } +} + export class MenuItemExtensionAction extends ExtensionAction { constructor( @@ -981,12 +1027,12 @@ export class InstallAnotherVersionAction extends ExtensionAction { @IQuickInputService private readonly quickInputService: IQuickInputService, @IInstantiationService private readonly instantiationService: IInstantiationService, ) { - super(InstallAnotherVersionAction.ID, InstallAnotherVersionAction.LABEL); + super(InstallAnotherVersionAction.ID, InstallAnotherVersionAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS); this.update(); } update(): void { - this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery; + this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery && this.extension.state !== ExtensionState.Uninstalling && this.extension.state !== ExtensionState.Installing; } run(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index ceaf99d305a..c5fc5e44a5c 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -249,7 +249,7 @@ export class ExtensionsListView extends ViewPane { getActions: () => actions.slice(0, actions.length - 1) }); } else if (e.element) { - const groups = getContextMenuActions(e.element, this.instantiationService); + const groups = getContextMenuActions(e.element, false, this.instantiationService); groups.forEach(group => group.forEach(extensionAction => { if (extensionAction instanceof ExtensionAction) { extensionAction.extension = e.element!; From 98e0e93ae0c719c314d4f16272aa8d1de3af3473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 9 Nov 2020 16:19:13 +0100 Subject: [PATCH 055/103] :lipstick: --- extensions/git/src/repository.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index fddf502a38f..d68a63bc3bc 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1260,7 +1260,6 @@ export class Repository implements Disposable { const supportCancellation = config.get('supportCancellation'); const fn = async (cancellationToken?: CancellationToken) => { - // When fetchOnPull is enabled, fetch all branches when pulling if (fetchOnPull) { await this.repository.fetch({ all: true, cancellationToken }); From 7509a0103e5ead86e6b892e099edaee13aeb7b9d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 9 Nov 2020 16:38:07 +0100 Subject: [PATCH 056/103] Fix #103941 --- .../workbench/contrib/extensions/browser/extensionsActions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index cc3c2c835c0..78dd465dc6b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -2167,13 +2167,15 @@ export class ChangeSortAction extends Action { this.query = Query.parse(''); this.enabled = false; + this.checked = false; this._register(onSearchChange(this.onSearchChange, this)); } private onSearchChange(value: string): void { const query = Query.parse(value); this.query = new Query(query.value, this.sortBy || query.sortBy, query.groupBy); - this.enabled = !!value && this.query.isValid() && !this.query.equals(query); + this.enabled = !!value && this.query.isValid(); + this.checked = this.enabled && this.query.equals(query); } run(): Promise { From f0580d497eb1917bd896647fe1f48161b7d5c5d7 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 9 Nov 2020 17:07:43 +0100 Subject: [PATCH 057/103] Fix #101441 --- .../contrib/extensions/browser/extensions.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 212bd38ee4d..450d7bf5ec6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -236,8 +236,8 @@ CommandsRegistry.registerCommand({ .then(async (extensions) => { for (const extension of extensions) { const requireReload = !(extension.local && extensionService.canAddExtension(toExtensionDescription(extension.local))); - const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.displayName || extension.name) - : localize('InstallVSIXAction.success', "Completed installing the extension {0}.", extension.displayName || extension.name); + const message = requireReload ? localize('InstallVSIXAction.successReload', "Completed installing {0} extension from VSIX. Please reload Visual Studio Code to enable it.", extension.displayName || extension.name) + : localize('InstallVSIXAction.success', "Completed installing {0} extension from VSIX.", extension.displayName || extension.name); const actions = requireReload ? [{ label: localize('InstallVSIXAction.reloadNow', "Reload Now"), run: () => hostService.reload() From 6e6654a6792c49a35ae604999482bf578504caba Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 9 Nov 2020 17:53:49 +0100 Subject: [PATCH 058/103] Fix #91534 --- .../electron-sandbox/fileActions.contribution.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts b/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts index 086d9c9dd57..6dd0c4da584 100644 --- a/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts @@ -23,9 +23,11 @@ import { ResourceContextKey } from 'vs/workbench/common/resources'; import { appendToCommandPalette, appendEditorTitleContextMenuItem } from 'vs/workbench/contrib/files/browser/fileActions.contribution'; import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { SideBySideEditor, EditorResourceAccessor } from 'vs/workbench/common/editor'; +import { ContextKeyOrExpr } from 'vs/platform/contextkey/common/contextkey'; const REVEAL_IN_OS_COMMAND_ID = 'revealFileInOS'; const REVEAL_IN_OS_LABEL = isWindows ? nls.localize('revealInWindows', "Reveal in File Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder"); +const REVEAL_IN_OS_WHEN_CONTEXT = ContextKeyOrExpr.create([ResourceContextKey.Scheme.isEqualTo(Schemas.file), ResourceContextKey.Scheme.isEqualTo(Schemas.userData)]); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: REVEAL_IN_OS_COMMAND_ID, @@ -57,19 +59,19 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ResourceContextKey.Scheme.isEqualTo(Schemas.file)); +appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, REVEAL_IN_OS_WHEN_CONTEXT); // Menu registration - open editors const revealInOsCommand = { id: REVEAL_IN_OS_COMMAND_ID, - title: isWindows ? nls.localize('revealInWindows', "Reveal in File Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder") + title: REVEAL_IN_OS_LABEL }; MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: 'navigation', order: 20, command: revealInOsCommand, - when: ResourceContextKey.IsFileSystemResource + when: REVEAL_IN_OS_WHEN_CONTEXT }); // Menu registration - explorer @@ -78,10 +80,10 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { group: 'navigation', order: 20, command: revealInOsCommand, - when: ResourceContextKey.Scheme.isEqualTo(Schemas.file) + when: REVEAL_IN_OS_WHEN_CONTEXT }); // Command Palette const category = { value: nls.localize('filesCategory', "File"), original: 'File' }; -appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in File Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category, ResourceContextKey.Scheme.isEqualTo(Schemas.file)); +appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in File Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category, REVEAL_IN_OS_WHEN_CONTEXT); From 1c7d982b93a5018104e64355f9fe15609f875104 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 9 Nov 2020 10:22:42 -0800 Subject: [PATCH 059/103] fixes #105201 Co-authored-by: rebornix --- .../contrib/colorPicker/colorPickerWidget.ts | 2 +- .../editor/contrib/hover/modesContentHover.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts index 4bcdaa3974d..454b3d33992 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts @@ -335,7 +335,7 @@ export class ColorPickerWidget extends Widget { body: ColorPickerBody; - constructor(container: Node, private readonly model: ColorPickerModel, private pixelRatio: number, themeService: IThemeService) { + constructor(container: Node, readonly model: ColorPickerModel, private pixelRatio: number, themeService: IThemeService) { super(); this._register(onDidChangeZoomLevel(() => this.layout())); diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 2cb7e7938c5..4a8c03b7135 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -251,6 +251,23 @@ export class ModesContentHoverWidget extends ContentHoverWidget { })); this._register(TokenizationRegistry.onDidChange((e) => { if (this.isVisible && this._lastRange && this._messages.length > 0) { + this._messages = this._messages.map(msg => { + // If a color hover is visible, we need to update the message that + // created it so that the color matches the last chosen color + if (msg instanceof ColorHover && !!this._lastRange?.intersectRanges(msg.range) && this._colorPicker?.model.color) { + const color = this._colorPicker.model.color; + const newColor = { + red: color.rgba.r / 255, + green: color.rgba.g / 255, + blue: color.rgba.b / 255, + alpha: color.rgba.a + }; + return new ColorHover(msg.range, newColor, msg.provider); + } else { + return msg; + } + }); + this._hover.contentsDomNode.textContent = ''; this._renderMessages(this._lastRange, this._messages); } From 876af4ccfdb6b7179a25d504e7e9c85d3955c904 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 9 Nov 2020 19:31:01 +0100 Subject: [PATCH 060/103] no max width for welcome buttons fixes #94101 --- src/vs/workbench/contrib/views/browser/media/views.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/views/browser/media/views.css b/src/vs/workbench/contrib/views/browser/media/views.css index 8b30fccc585..7ff3318d576 100644 --- a/src/vs/workbench/contrib/views/browser/media/views.css +++ b/src/vs/workbench/contrib/views/browser/media/views.css @@ -64,7 +64,6 @@ } .monaco-workbench .pane > .pane-body > .welcome-view .monaco-button { - max-width: 260px; margin-left: auto; margin-right: auto; } From f1ebde547ca1041be33e47762dfb69da93477269 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 9 Nov 2020 19:50:50 +0100 Subject: [PATCH 061/103] fixes #109781 --- .../extensions/electron-browser/runtimeExtensionsEditor.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index d4803919342..0e4312af5ea 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -538,9 +538,7 @@ export class ReportExtensionIssueAction extends Action { @INativeHostService private readonly nativeHostService: INativeHostService ) { super(ReportExtensionIssueAction._id, ReportExtensionIssueAction._label, 'extension-action report-issue'); - this.enabled = extension.marketplaceInfo - && extension.marketplaceInfo.type === ExtensionType.User - && !!extension.description.repository && !!extension.description.repository.url; + this.enabled = !!extension.description.repository && !!extension.description.repository.url; } async run(): Promise { From ea6ee515f138bf5d6297b893938b3edf8837fd54 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 9 Nov 2020 10:53:48 -0800 Subject: [PATCH 062/103] Add color tokens for warning/info bg --- src/vs/editor/browser/widget/codeEditorWidget.ts | 11 ++++++++++- src/vs/platform/theme/common/colorRegistry.ts | 4 +++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 9d70939e7b5..eb86a1de662 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -37,7 +37,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import * as modes from 'vs/editor/common/modes'; import { editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity } from 'vs/editor/common/view/editorColorRegistry'; -import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground, editorErrorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground, editorErrorBackground, editorInfoBackground, editorWarningBackground } from 'vs/platform/theme/common/colorRegistry'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; @@ -1989,6 +1989,7 @@ registerThemingParticipant((theme, collector) => { if (errorBackground) { collector.addRule(`.monaco-editor .${ClassName.EditorErrorDecoration}::before { display: block; content: ''; width: 100%; height: 100%; background: ${errorBackground}; }`); } + const warningBorderColor = theme.getColor(editorWarningBorder); if (warningBorderColor) { collector.addRule(`.monaco-editor .${ClassName.EditorWarningDecoration} { border-bottom: 4px double ${warningBorderColor}; }`); @@ -1997,6 +1998,10 @@ registerThemingParticipant((theme, collector) => { if (warningForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorWarningDecoration} { background: url("data:image/svg+xml,${getSquigglySVGData(warningForeground)}") repeat-x bottom left; }`); } + const warningBackground = theme.getColor(editorWarningBackground); + if (warningBackground) { + collector.addRule(`.monaco-editor .${ClassName.EditorWarningDecoration}::before { display: block; content: ''; width: 100%; height: 100%; background: ${warningBackground}; }`); + } const infoBorderColor = theme.getColor(editorInfoBorder); if (infoBorderColor) { @@ -2006,6 +2011,10 @@ registerThemingParticipant((theme, collector) => { if (infoForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorInfoDecoration} { background: url("data:image/svg+xml,${getSquigglySVGData(infoForeground)}") repeat-x bottom left; }`); } + const infoBackground = theme.getColor(editorInfoBackground); + if (infoBackground) { + collector.addRule(`.monaco-editor .${ClassName.EditorInfoDecoration}::before { display: block; content: ''; width: 100%; height: 100%; background: ${infoBackground}; }`); + } const hintBorderColor = theme.getColor(editorHintBorder); if (hintBorderColor) { diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 67c3936497d..e682f79b8e4 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -243,13 +243,15 @@ export const scrollbarSliderActiveBackground = registerColor('scrollbarSlider.ac export const progressBarBackground = registerColor('progressBar.background', { dark: Color.fromHex('#0E70C0'), light: Color.fromHex('#0E70C0'), hc: contrastBorder }, nls.localize('progressBarBackground', "Background color of the progress bar that can show for long running operations.")); -export const editorErrorBackground = registerColor('editorError.background', { dark: null, light: null, hc: null }, nls.localize('editorError.background', 'Background color of error text in the editor.')); +export const editorErrorBackground = registerColor('editorError.background', { dark: null, light: null, hc: null }, nls.localize('editorError.background', 'Background color of error text in the editor. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorErrorForeground = registerColor('editorError.foreground', { dark: '#F48771', light: '#E51400', hc: null }, nls.localize('editorError.foreground', 'Foreground color of error squigglies in the editor.')); export const editorErrorBorder = registerColor('editorError.border', { dark: null, light: null, hc: Color.fromHex('#E47777').transparent(0.8) }, nls.localize('errorBorder', 'Border color of error boxes in the editor.')); +export const editorWarningBackground = registerColor('editorWarning.background', { dark: null, light: null, hc: null }, nls.localize('editorWarning.background', 'Background color of warning text in the editor. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorWarningForeground = registerColor('editorWarning.foreground', { dark: '#CCA700', light: '#E9A700', hc: null }, nls.localize('editorWarning.foreground', 'Foreground color of warning squigglies in the editor.')); export const editorWarningBorder = registerColor('editorWarning.border', { dark: null, light: null, hc: Color.fromHex('#FFCC00').transparent(0.8) }, nls.localize('warningBorder', 'Border color of warning boxes in the editor.')); +export const editorInfoBackground = registerColor('editorInfo.background', { dark: null, light: null, hc: null }, nls.localize('editorInfo.background', 'Background color of info text in the editor. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorInfoForeground = registerColor('editorInfo.foreground', { dark: '#75BEFF', light: '#75BEFF', hc: null }, nls.localize('editorInfo.foreground', 'Foreground color of info squigglies in the editor.')); export const editorInfoBorder = registerColor('editorInfo.border', { dark: null, light: null, hc: Color.fromHex('#75BEFF').transparent(0.8) }, nls.localize('infoBorder', 'Border color of info boxes in the editor.')); From 87d49a7b66ea94ed8b96918000560b9566016330 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 9 Nov 2020 11:07:06 -0800 Subject: [PATCH 063/103] Fix #108300 --- .../contrib/extensions/browser/media/extensionEditor.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index 3a49d109175..b79ec641beb 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -76,7 +76,6 @@ .extension-editor > .header > .details > .title > .identifier { margin-left: 10px; font-size: 14px; - opacity: 0.6; background: rgba(173, 173, 173, 0.31); padding: 0px 4px; border-radius: 4px; From a45abdbd1b6c9fb6a9a81ee538933da6194ed739 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 9 Nov 2020 11:15:03 -0800 Subject: [PATCH 064/103] Update seti --- extensions/theme-seti/cgmanifest.json | 2 +- extensions/theme-seti/icons/seti.woff | Bin 35436 -> 35272 bytes .../theme-seti/icons/vs-seti-icon-theme.json | 290 ++++++++++-------- 3 files changed, 159 insertions(+), 133 deletions(-) diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index 20f54daa8ba..4548b98125d 100644 --- a/extensions/theme-seti/cgmanifest.json +++ b/extensions/theme-seti/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "seti-ui", "repositoryUrl": "https://github.com/jesseweed/seti-ui", - "commitHash": "7ea773d195eac3f40261897b49a2499815e9346c" + "commitHash": "4bbf2132df28c71302e305077ce20a811bf7d64b" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff index 021e527020083f1efad65391f16c21f9951fe533..a7c47a3ce0c5aa65cdf6c6f79e8b49c630216529 100644 GIT binary patch delta 34083 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

V|ziHcgq-mY|)dN)hOeR7W4uDJKWL=`vo< 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 HyrP-5gY#6c diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json index bca70445720..cbfc6578024 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -438,6 +438,14 @@ "fontCharacter": "\\E032", "fontColor": "#41535b" }, + "_github_light": { + "fontCharacter": "\\E035", + "fontColor": "#bfc2c1" + }, + "_github": { + "fontCharacter": "\\E035", + "fontColor": "#d4d7d6" + }, "_go_light": { "fontCharacter": "\\E036", "fontColor": "#498ba7" @@ -871,526 +879,536 @@ "fontColor": "#e37933" }, "_nim_light": { - "fontCharacter": "\\E061", "fontColor": "#b7b73b" }, "_nim": { - "fontCharacter": "\\E061", "fontColor": "#cbcb41" }, + "_notebook_light": { + "fontColor": "#498ba7" + }, + "_notebook": { + "fontColor": "#519aba" + }, "_npm_light": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E061", "fontColor": "#3b4b52" }, "_npm": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E061", "fontColor": "#41535b" }, "_npm_1_light": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E061", "fontColor": "#b8383d" }, "_npm_1": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E061", "fontColor": "#cc3e44" }, "_npm_ignored_light": { - "fontCharacter": "\\E063", + "fontCharacter": "\\E062", "fontColor": "#3b4b52" }, "_npm_ignored": { - "fontCharacter": "\\E063", + "fontCharacter": "\\E062", "fontColor": "#41535b" }, "_nunjucks_light": { - "fontCharacter": "\\E064", + "fontCharacter": "\\E063", "fontColor": "#7fae42" }, "_nunjucks": { - "fontCharacter": "\\E064", + "fontCharacter": "\\E063", "fontColor": "#8dc149" }, "_ocaml_light": { - "fontCharacter": "\\E065", + "fontCharacter": "\\E064", "fontColor": "#cc6d2e" }, "_ocaml": { - "fontCharacter": "\\E065", + "fontCharacter": "\\E064", "fontColor": "#e37933" }, "_odata_light": { - "fontCharacter": "\\E066", + "fontCharacter": "\\E065", "fontColor": "#cc6d2e" }, "_odata": { - "fontCharacter": "\\E066", + "fontCharacter": "\\E065", "fontColor": "#e37933" }, "_pddl_light": { - "fontCharacter": "\\E067", + "fontCharacter": "\\E066", "fontColor": "#9068b0" }, "_pddl": { - "fontCharacter": "\\E067", + "fontCharacter": "\\E066", "fontColor": "#a074c4" }, "_pdf_light": { - "fontCharacter": "\\E068", + "fontCharacter": "\\E067", "fontColor": "#b8383d" }, "_pdf": { - "fontCharacter": "\\E068", + "fontCharacter": "\\E067", "fontColor": "#cc3e44" }, "_perl_light": { - "fontCharacter": "\\E069", + "fontCharacter": "\\E068", "fontColor": "#498ba7" }, "_perl": { - "fontCharacter": "\\E069", + "fontCharacter": "\\E068", "fontColor": "#519aba" }, "_photoshop_light": { - "fontCharacter": "\\E06A", + "fontCharacter": "\\E069", "fontColor": "#498ba7" }, "_photoshop": { - "fontCharacter": "\\E06A", + "fontCharacter": "\\E069", "fontColor": "#519aba" }, "_php_light": { - "fontCharacter": "\\E06B", + "fontCharacter": "\\E06A", "fontColor": "#9068b0" }, "_php": { - "fontCharacter": "\\E06B", + "fontCharacter": "\\E06A", "fontColor": "#a074c4" }, "_plan_light": { - "fontCharacter": "\\E06C", + "fontCharacter": "\\E06B", "fontColor": "#7fae42" }, "_plan": { - "fontCharacter": "\\E06C", + "fontCharacter": "\\E06B", "fontColor": "#8dc149" }, "_platformio_light": { - "fontCharacter": "\\E06D", + "fontCharacter": "\\E06C", "fontColor": "#cc6d2e" }, "_platformio": { - "fontCharacter": "\\E06D", + "fontCharacter": "\\E06C", "fontColor": "#e37933" }, "_powershell_light": { - "fontCharacter": "\\E06E", + "fontCharacter": "\\E06D", "fontColor": "#498ba7" }, "_powershell": { - "fontCharacter": "\\E06E", + "fontCharacter": "\\E06D", + "fontColor": "#519aba" + }, + "_prisma_light": { + "fontColor": "#498ba7" + }, + "_prisma": { "fontColor": "#519aba" }, "_prolog_light": { - "fontCharacter": "\\E070", + "fontCharacter": "\\E06F", "fontColor": "#cc6d2e" }, "_prolog": { - "fontCharacter": "\\E070", + "fontCharacter": "\\E06F", "fontColor": "#e37933" }, "_pug_light": { - "fontCharacter": "\\E071", + "fontCharacter": "\\E070", "fontColor": "#b8383d" }, "_pug": { - "fontCharacter": "\\E071", + "fontCharacter": "\\E070", "fontColor": "#cc3e44" }, "_puppet_light": { - "fontCharacter": "\\E072", + "fontCharacter": "\\E071", "fontColor": "#b7b73b" }, "_puppet": { - "fontCharacter": "\\E072", + "fontCharacter": "\\E071", "fontColor": "#cbcb41" }, "_python_light": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E072", "fontColor": "#498ba7" }, "_python": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E072", "fontColor": "#519aba" }, "_react_light": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E074", "fontColor": "#498ba7" }, "_react": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E074", "fontColor": "#519aba" }, "_react_1_light": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E074", "fontColor": "#cc6d2e" }, "_react_1": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E074", "fontColor": "#e37933" }, "_react_2_light": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E074", "fontColor": "#b7b73b" }, "_react_2": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E074", "fontColor": "#cbcb41" }, "_reasonml_light": { - "fontCharacter": "\\E076", + "fontCharacter": "\\E075", "fontColor": "#b8383d" }, "_reasonml": { - "fontCharacter": "\\E076", + "fontCharacter": "\\E075", "fontColor": "#cc3e44" }, "_rollup_light": { - "fontCharacter": "\\E077", + "fontCharacter": "\\E076", "fontColor": "#b8383d" }, "_rollup": { - "fontCharacter": "\\E077", + "fontCharacter": "\\E076", "fontColor": "#cc3e44" }, "_ruby_light": { - "fontCharacter": "\\E078", + "fontCharacter": "\\E077", "fontColor": "#b8383d" }, "_ruby": { - "fontCharacter": "\\E078", + "fontCharacter": "\\E077", "fontColor": "#cc3e44" }, "_rust_light": { - "fontCharacter": "\\E079", + "fontCharacter": "\\E078", "fontColor": "#627379" }, "_rust": { - "fontCharacter": "\\E079", + "fontCharacter": "\\E078", "fontColor": "#6d8086" }, "_salesforce_light": { - "fontCharacter": "\\E07A", + "fontCharacter": "\\E079", "fontColor": "#498ba7" }, "_salesforce": { - "fontCharacter": "\\E07A", + "fontCharacter": "\\E079", "fontColor": "#519aba" }, "_sass_light": { - "fontCharacter": "\\E07B", + "fontCharacter": "\\E07A", "fontColor": "#dd4b78" }, "_sass": { - "fontCharacter": "\\E07B", + "fontCharacter": "\\E07A", "fontColor": "#f55385" }, "_sbt_light": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E07B", "fontColor": "#498ba7" }, "_sbt": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E07B", "fontColor": "#519aba" }, "_scala_light": { - "fontCharacter": "\\E07D", + "fontCharacter": "\\E07C", "fontColor": "#b8383d" }, "_scala": { - "fontCharacter": "\\E07D", + "fontCharacter": "\\E07C", "fontColor": "#cc3e44" }, "_shell_light": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E07F", "fontColor": "#455155" }, "_shell": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E07F", "fontColor": "#4d5a5e" }, "_slim_light": { - "fontCharacter": "\\E081", + "fontCharacter": "\\E080", "fontColor": "#cc6d2e" }, "_slim": { - "fontCharacter": "\\E081", + "fontCharacter": "\\E080", "fontColor": "#e37933" }, "_smarty_light": { - "fontCharacter": "\\E082", + "fontCharacter": "\\E081", "fontColor": "#b7b73b" }, "_smarty": { - "fontCharacter": "\\E082", + "fontCharacter": "\\E081", "fontColor": "#cbcb41" }, "_spring_light": { - "fontCharacter": "\\E083", + "fontCharacter": "\\E082", "fontColor": "#7fae42" }, "_spring": { - "fontCharacter": "\\E083", + "fontCharacter": "\\E082", "fontColor": "#8dc149" }, "_stylelint_light": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E083", "fontColor": "#bfc2c1" }, "_stylelint": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E083", "fontColor": "#d4d7d6" }, "_stylelint_1_light": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E083", "fontColor": "#455155" }, "_stylelint_1": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E083", "fontColor": "#4d5a5e" }, "_stylus_light": { - "fontCharacter": "\\E085", + "fontCharacter": "\\E084", "fontColor": "#7fae42" }, "_stylus": { - "fontCharacter": "\\E085", + "fontCharacter": "\\E084", "fontColor": "#8dc149" }, "_sublime_light": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E085", "fontColor": "#cc6d2e" }, "_sublime": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E085", "fontColor": "#e37933" }, "_svg_light": { - "fontCharacter": "\\E087", + "fontCharacter": "\\E086", "fontColor": "#9068b0" }, "_svg": { - "fontCharacter": "\\E087", + "fontCharacter": "\\E086", "fontColor": "#a074c4" }, "_svg_1_light": { - "fontCharacter": "\\E087", + "fontCharacter": "\\E086", "fontColor": "#498ba7" }, "_svg_1": { - "fontCharacter": "\\E087", + "fontCharacter": "\\E086", "fontColor": "#519aba" }, "_swift_light": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E087", "fontColor": "#cc6d2e" }, "_swift": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E087", "fontColor": "#e37933" }, "_terraform_light": { - "fontCharacter": "\\E089", + "fontCharacter": "\\E088", "fontColor": "#9068b0" }, "_terraform": { - "fontCharacter": "\\E089", + "fontCharacter": "\\E088", "fontColor": "#a074c4" }, "_tex_light": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E089", "fontColor": "#498ba7" }, "_tex": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E089", "fontColor": "#519aba" }, "_tex_1_light": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E089", "fontColor": "#b7b73b" }, "_tex_1": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E089", "fontColor": "#cbcb41" }, "_tex_2_light": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E089", "fontColor": "#cc6d2e" }, "_tex_2": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E089", "fontColor": "#e37933" }, "_tex_3_light": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E089", "fontColor": "#bfc2c1" }, "_tex_3": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E089", "fontColor": "#d4d7d6" }, "_todo": { - "fontCharacter": "\\E08C" + "fontCharacter": "\\E08B" }, "_tsconfig_light": { - "fontCharacter": "\\E08D", + "fontCharacter": "\\E08C", "fontColor": "#498ba7" }, "_tsconfig": { - "fontCharacter": "\\E08D", + "fontCharacter": "\\E08C", "fontColor": "#519aba" }, "_twig_light": { - "fontCharacter": "\\E08E", + "fontCharacter": "\\E08D", "fontColor": "#7fae42" }, "_twig": { - "fontCharacter": "\\E08E", + "fontCharacter": "\\E08D", "fontColor": "#8dc149" }, "_typescript_light": { - "fontCharacter": "\\E08F", + "fontCharacter": "\\E08E", "fontColor": "#498ba7" }, "_typescript": { - "fontCharacter": "\\E08F", + "fontCharacter": "\\E08E", "fontColor": "#519aba" }, "_typescript_1_light": { - "fontCharacter": "\\E08F", + "fontCharacter": "\\E08E", "fontColor": "#b7b73b" }, "_typescript_1": { - "fontCharacter": "\\E08F", + "fontCharacter": "\\E08E", "fontColor": "#cbcb41" }, "_vala_light": { - "fontCharacter": "\\E090", + "fontCharacter": "\\E08F", "fontColor": "#627379" }, "_vala": { - "fontCharacter": "\\E090", + "fontCharacter": "\\E08F", "fontColor": "#6d8086" }, "_video_light": { - "fontCharacter": "\\E091", + "fontCharacter": "\\E090", "fontColor": "#dd4b78" }, "_video": { - "fontCharacter": "\\E091", + "fontCharacter": "\\E090", "fontColor": "#f55385" }, "_vue_light": { - "fontCharacter": "\\E092", + "fontCharacter": "\\E091", "fontColor": "#7fae42" }, "_vue": { - "fontCharacter": "\\E092", + "fontCharacter": "\\E091", "fontColor": "#8dc149" }, "_wasm_light": { - "fontCharacter": "\\E093", + "fontCharacter": "\\E092", "fontColor": "#9068b0" }, "_wasm": { - "fontCharacter": "\\E093", + "fontCharacter": "\\E092", "fontColor": "#a074c4" }, "_wat_light": { - "fontCharacter": "\\E094", + "fontCharacter": "\\E093", "fontColor": "#9068b0" }, "_wat": { - "fontCharacter": "\\E094", + "fontCharacter": "\\E093", "fontColor": "#a074c4" }, "_webpack_light": { - "fontCharacter": "\\E095", + "fontCharacter": "\\E094", "fontColor": "#498ba7" }, "_webpack": { - "fontCharacter": "\\E095", + "fontCharacter": "\\E094", "fontColor": "#519aba" }, "_wgt_light": { - "fontCharacter": "\\E096", + "fontCharacter": "\\E095", "fontColor": "#498ba7" }, "_wgt": { - "fontCharacter": "\\E096", + "fontCharacter": "\\E095", "fontColor": "#519aba" }, "_windows_light": { - "fontCharacter": "\\E097", + "fontCharacter": "\\E096", "fontColor": "#498ba7" }, "_windows": { - "fontCharacter": "\\E097", + "fontCharacter": "\\E096", "fontColor": "#519aba" }, "_word_light": { - "fontCharacter": "\\E098", + "fontCharacter": "\\E097", "fontColor": "#498ba7" }, "_word": { - "fontCharacter": "\\E098", + "fontCharacter": "\\E097", "fontColor": "#519aba" }, "_xls_light": { - "fontCharacter": "\\E099", + "fontCharacter": "\\E098", "fontColor": "#7fae42" }, "_xls": { - "fontCharacter": "\\E099", + "fontCharacter": "\\E098", "fontColor": "#8dc149" }, "_xml_light": { - "fontCharacter": "\\E09A", + "fontCharacter": "\\E099", "fontColor": "#cc6d2e" }, "_xml": { - "fontCharacter": "\\E09A", + "fontCharacter": "\\E099", "fontColor": "#e37933" }, "_yarn_light": { - "fontCharacter": "\\E09B", + "fontCharacter": "\\E09A", "fontColor": "#498ba7" }, "_yarn": { - "fontCharacter": "\\E09B", + "fontCharacter": "\\E09A", "fontColor": "#519aba" }, "_yml_light": { - "fontCharacter": "\\E09C", + "fontCharacter": "\\E09B", "fontColor": "#9068b0" }, "_yml": { - "fontCharacter": "\\E09C", + "fontCharacter": "\\E09B", "fontColor": "#a074c4" }, "_zip_light": { - "fontCharacter": "\\E09D", + "fontCharacter": "\\E09C", "fontColor": "#b8383d" }, "_zip": { - "fontCharacter": "\\E09D", + "fontCharacter": "\\E09C", "fontColor": "#cc3e44" }, "_zip_1_light": { - "fontCharacter": "\\E09D", + "fontCharacter": "\\E09C", "fontColor": "#627379" }, "_zip_1": { - "fontCharacter": "\\E09D", + "fontCharacter": "\\E09C", "fontColor": "#6d8086" } }, @@ -1449,6 +1467,7 @@ "gsp": "_grails", "gql": "_graphql", "graphql": "_graphql", + "graphqls": "_graphql", "haml": "_haml", "hs": "_haskell", "lhs": "_haskell", @@ -1480,6 +1499,8 @@ "stache": "_mustache", "nim": "_nim", "nims": "_nim", + "github-issues": "_github", + "ipynb": "_notebook", "njk": "_nunjucks", "nunjucks": "_nunjucks", "nunjs": "_nunjucks", @@ -1496,6 +1517,7 @@ "pddl": "_pddl", "plan": "_plan", "happenings": "_happenings", + "prisma": "_prisma", "pp": "_puppet", "epp": "_puppet", "spec.jsx": "_react_1", @@ -1786,6 +1808,7 @@ "gsp": "_grails_light", "gql": "_graphql_light", "graphql": "_graphql_light", + "graphqls": "_graphql_light", "haml": "_haml_light", "hs": "_haskell_light", "lhs": "_haskell_light", @@ -1817,6 +1840,8 @@ "stache": "_mustache_light", "nim": "_nim_light", "nims": "_nim_light", + "github-issues": "_github_light", + "ipynb": "_notebook_light", "njk": "_nunjucks_light", "nunjucks": "_nunjucks_light", "nunjs": "_nunjucks_light", @@ -1833,6 +1858,7 @@ "pddl": "_pddl_light", "plan": "_plan_light", "happenings": "_happenings_light", + "prisma": "_prisma_light", "pp": "_puppet_light", "epp": "_puppet_light", "spec.jsx": "_react_1_light", @@ -2066,5 +2092,5 @@ "npm-debug.log": "_npm_ignored_light" } }, - "version": "https://github.com/jesseweed/seti-ui/commit/7ea773d195eac3f40261897b49a2499815e9346c" + "version": "https://github.com/jesseweed/seti-ui/commit/4bbf2132df28c71302e305077ce20a811bf7d64b" } \ No newline at end of file From ef6b4387694fd20b28d66e006337e5a2e2cfe38d Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Mon, 9 Nov 2020 17:26:14 +0000 Subject: [PATCH 065/103] Fix #88703 --- .../common/preferencesValidation.ts | 24 ++++++++++++++++--- .../test/common/preferencesValidation.test.ts | 16 +++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/preferences/common/preferencesValidation.ts b/src/vs/workbench/services/preferences/common/preferencesValidation.ts index 40b916d16ae..80d97ac9190 100644 --- a/src/vs/workbench/services/preferences/common/preferencesValidation.ts +++ b/src/vs/workbench/services/preferences/common/preferencesValidation.ts @@ -92,10 +92,12 @@ function valueValidatesAsType(value: any, type: string): boolean { } function getStringValidators(prop: IConfigurationPropertySchema) { + const uriRegex = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; let patternRegex: RegExp | undefined; if (typeof prop.pattern === 'string') { patternRegex = new RegExp(prop.pattern); } + return [ { enabled: prop.maxLength !== undefined, @@ -116,7 +118,25 @@ function getStringValidators(prop: IConfigurationPropertySchema) { enabled: prop.format === 'color-hex', isValid: ((value: string) => Color.Format.CSS.parseHex(value)), message: nls.localize('validations.colorFormat', "Invalid color format. Use #RGB, #RGBA, #RRGGBB or #RRGGBBAA.") - } + }, + { + enabled: prop.format === 'uri' || prop.format === 'uri-reference', + isValid: ((value: string) => !!value.length), + message: nls.localize('validations.uriEmpty', "URI expected.") + }, + { + enabled: prop.format === 'uri' || prop.format === 'uri-reference', + isValid: ((value: string) => uriRegex.test(value)), + message: nls.localize('validations.uriMissing', "URI is expected.") + }, + { + enabled: prop.format === 'uri', + isValid: ((value: string) => { + const matches = value.match(uriRegex); + return !!(matches && matches[2]); + }), + message: nls.localize('validations.uriSchemeMissing', "URI with a scheme is expected.") + }, ].filter(validation => validation.enabled); } @@ -249,5 +269,3 @@ function getArrayOfStringValidator(prop: IConfigurationPropertySchema): ((value: return null; } - - diff --git a/src/vs/workbench/services/preferences/test/common/preferencesValidation.test.ts b/src/vs/workbench/services/preferences/test/common/preferencesValidation.test.ts index 9fcbd932494..7a8e0c4872a 100644 --- a/src/vs/workbench/services/preferences/test/common/preferencesValidation.test.ts +++ b/src/vs/workbench/services/preferences/test/common/preferencesValidation.test.ts @@ -373,4 +373,20 @@ suite('Preferences Validation', () => { testInvalidTypeError([null], 'null', false); testInvalidTypeError('null', 'null', false); }); + + test('uri checks work', () => { + const tester = new Tester({ type: 'string', format: 'uri' }); + tester.rejects('example.com'); + tester.rejects('example.com/example'); + tester.rejects('example/example.html'); + tester.rejects('www.example.com'); + tester.rejects(''); + tester.rejects(' '); + tester.rejects('example'); + + tester.accepts('https:'); + tester.accepts('https://'); + tester.accepts('https://example.com'); + tester.accepts('https://www.example.com'); + }); }); From 98a3c4fe64893e4aa08468cfd104bd98b5e23f4a Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 9 Nov 2020 11:25:36 -0800 Subject: [PATCH 066/103] reduce work when menu is not visible fixes #108712 --- .../browser/parts/titlebar/menubarControl.ts | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index d7e75f38c6e..46de06abcb9 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -122,7 +122,7 @@ export abstract class MenubarControl extends Disposable { this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); // Listen to update service - this.updateService.onStateChange(() => this.updateMenubar()); + this.updateService.onStateChange(() => this.onUpdateStateChange()); // Listen for changes in recently opened menu this._register(this.workspacesService.onRecentlyOpenedChange(() => { this.onRecentlyOpenedChange(); })); @@ -148,6 +148,14 @@ export abstract class MenubarControl extends Disposable { return label; } + protected onUpdateStateChange(): void { + this.updateMenubar(); + } + + protected onUpdateKeybindings(): void { + this.updateMenubar(); + } + protected getOpenRecentActions(): (Separator | IAction & { uri: URI })[] { if (!this.recentlyOpened) { return []; @@ -193,7 +201,7 @@ export abstract class MenubarControl extends Disposable { } } - private onRecentlyOpenedChange(): void { + protected onRecentlyOpenedChange(): void { this.workspacesService.getRecentlyOpened().then(recentlyOpened => { this.recentlyOpened = recentlyOpened; this.updateMenubar(); @@ -266,6 +274,7 @@ export class CustomMenubarControl extends MenubarControl { private container: HTMLElement | undefined; private alwaysOnMnemonics: boolean = false; private focusInsideMenubar: boolean = false; + private visible: boolean = true; private readonly _onVisibilityChange: Emitter; private readonly _onFocusStateChange: Emitter; @@ -530,6 +539,12 @@ export class CustomMenubarControl extends MenubarControl { return currentSidebarLocation === 'right' ? Direction.Left : Direction.Right; } + private onDidVisibilityChange(visible: boolean): void { + this.visible = visible; + this.onRecentlyOpenedChange(); + this._onVisibilityChange.fire(visible); + } + private setupCustomMenubar(firstTime: boolean): void { // If there is no container, we cannot setup the menubar if (!this.container) { @@ -554,7 +569,7 @@ export class CustomMenubarControl extends MenubarControl { } })); - this._register(this.menubar.onVisibilityChange(e => this._onVisibilityChange.fire(e))); + this._register(this.menubar.onVisibilityChange(e => this.onDidVisibilityChange(e))); // Before we focus the menubar, stop updates to it so that focus-related context keys will work this._register(DOM.addDisposableListener(this.container, DOM.EventType.FOCUS_IN, () => { @@ -668,6 +683,10 @@ export class CustomMenubarControl extends MenubarControl { } protected onDidChangeWindowFocus(hasFocus: boolean): void { + if (!this.visible) { + return; + } + super.onDidChangeWindowFocus(hasFocus); if (this.container) { @@ -682,6 +701,30 @@ export class CustomMenubarControl extends MenubarControl { } } + protected onUpdateStateChange(): void { + if (!this.visible) { + return; + } + + super.onUpdateStateChange(); + } + + protected onRecentlyOpenedChange(): void { + if (!this.visible) { + return; + } + + super.onRecentlyOpenedChange(); + } + + protected onUpdateKeybindings(): void { + if (!this.visible) { + return; + } + + super.onUpdateKeybindings(); + } + protected registerListeners(): void { super.registerListeners(); From 62d39a7dbacb2230dbd598c7bf6b47fc93cf5047 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 9 Nov 2020 11:38:58 -0800 Subject: [PATCH 067/103] Adopt storagetarget in Exp services refs #109967 --- .../workbench/contrib/experiments/common/experimentService.ts | 4 ++-- .../workbench/services/experiment/common/experimentService.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index ee883ca582e..ba3b3147a83 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -5,7 +5,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Emitter, Event } from 'vs/base/common/event'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -222,7 +222,7 @@ export class ExperimentService extends Disposable implements IExperimentService const storageKey = 'experiments.' + experimentId; const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); experimentState.state = ExperimentState.Complete; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); + this.storageService.store2(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); } protected async getExperiments(): Promise { diff --git a/src/vs/workbench/services/experiment/common/experimentService.ts b/src/vs/workbench/services/experiment/common/experimentService.ts index 29ac3b835a3..7671335308c 100644 --- a/src/vs/workbench/services/experiment/common/experimentService.ts +++ b/src/vs/workbench/services/experiment/common/experimentService.ts @@ -8,7 +8,7 @@ import type { IKeyValueStorage, IExperimentationTelemetry, IExperimentationFilte import { MementoObject, Memento } from 'vs/workbench/common/memento'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryData } from 'vs/base/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -207,7 +207,7 @@ export class ExperimentService implements ITASExperimentService { ); const memento = new Memento(ExperimentService.MEMENTO_ID, this.storageService); - const keyValueStorage = new MementoKeyValueStorage(memento.legacygetMemento(StorageScope.GLOBAL)); + const keyValueStorage = new MementoKeyValueStorage(memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE)); const telemetry = new ExperimentServiceTelemetry(this.telemetryService); From e5530fc4f01552496e74f40a8cd8dc6ce165a5f8 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 9 Nov 2020 11:39:15 -0800 Subject: [PATCH 068/103] fix #105920. --- extensions/git/package.json | 6 +++--- src/vs/editor/browser/widget/diffEditorWidget.ts | 6 ++++++ src/vs/editor/common/config/editorOptions.ts | 5 +++++ src/vs/monaco.d.ts | 5 +++++ .../contrib/notebook/browser/diff/cellComponents.ts | 3 ++- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index c76a5ba50e2..cfa7c3b05e8 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1312,17 +1312,17 @@ { "command": "git.stageSelectedRanges", "group": "2_git@1", - "when": "isInDiffRightEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" }, { "command": "git.unstageSelectedRanges", "group": "2_git@2", - "when": "isInDiffRightEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" }, { "command": "git.revertSelectedRanges", "group": "2_git@3", - "when": "isInDiffRightEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" } ], "scm/change/title": [ diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index ada211784bf..0e39aa77037 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -296,6 +296,12 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._modifiedCodeLens = Boolean(options.modifiedCodeLens); } + if (typeof options.isInEmbeddedEditor !== 'undefined') { + this._contextKeyService.createKey('isInEmbeddedDiffEditor', options.isInEmbeddedEditor); + } else { + this._contextKeyService.createKey('isInEmbeddedDiffEditor', false); + } + this._updateDecorationsRunner = this._register(new RunOnceScheduler(() => this._updateDecorations(), 0)); this._containerDomElement = document.createElement('div'); diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index c509ff3da7c..2659f2f51dc 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -657,6 +657,11 @@ export interface IDiffEditorOptions extends IEditorOptions { * Defaults to false. */ modifiedCodeLens?: boolean; + /** + * Is the diff editor inside another editor + * Defaults to false + */ + isInEmbeddedEditor?: boolean; } //#endregion diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 3de2c0f2cbb..7df3f0f8636 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3190,6 +3190,11 @@ declare namespace monaco.editor { * Defaults to false. */ modifiedCodeLens?: boolean; + /** + * Is the diff editor inside another editor + * Defaults to false + */ + isInEmbeddedEditor?: boolean; } /** diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 43a3d299a5d..3af3a0c7832 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -62,7 +62,8 @@ const fixedDiffEditorOptions: IDiffEditorOptions = { glyphMargin: true, enableSplitViewResizing: false, renderIndicators: false, - readOnly: false + readOnly: false, + isInEmbeddedEditor: true }; From d29487617b57039dee1acecb4185202668ed3a46 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 9 Nov 2020 11:46:09 -0800 Subject: [PATCH 069/103] update language --- .../telemetry/browser/telemetry.contribution.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 004f9217c13..511c8296399 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -31,7 +31,7 @@ type TelemetryData = { ext: string; path: number; reason?: number; - whitelistedjson?: string; + allowlistedjson?: string; }; type FileTelemetryDataFragment = { @@ -39,13 +39,13 @@ type FileTelemetryDataFragment = { ext: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; path: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; reason?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; - whitelistedjson?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + allowlistedjson?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; }; export class TelemetryContribution extends Disposable implements IWorkbenchContribution { - private static WHITELIST_JSON = ['package.json', 'package-lock.json', 'tsconfig.json', 'jsconfig.json', 'bower.json', '.eslintrc.json', 'tslint.json', 'composer.json']; - private static WHITELIST_WORKSPACE_JSON = ['settings.json', 'extensions.json', 'tasks.json', 'launch.json']; + private static ALLOWLIST_JSON = ['package.json', 'package-lock.json', 'tsconfig.json', 'jsconfig.json', 'bower.json', '.eslintrc.json', 'tslint.json', 'composer.json']; + private static ALLOWLIST_WORKSPACE_JSON = ['settings.json', 'extensions.json', 'tasks.json', 'launch.json']; constructor( @ITelemetryService private readonly telemetryService: ITelemetryService, @@ -184,7 +184,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr for (const folder of folders) { if (isEqualOrParent(resource, folder.toResource('.vscode'))) { const filename = basename(resource); - if (TelemetryContribution.WHITELIST_WORKSPACE_JSON.indexOf(filename) > -1) { + if (TelemetryContribution.ALLOWLIST_WORKSPACE_JSON.indexOf(filename) > -1) { return `.vscode/${filename}`; } } @@ -202,11 +202,11 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr ext, path: hash(path), reason, - whitelistedjson: undefined as string | undefined + allowlistedjson: undefined as string | undefined }; - if (ext === '.json' && TelemetryContribution.WHITELIST_JSON.indexOf(fileName) > -1) { - telemetryData['whitelistedjson'] = fileName; + if (ext === '.json' && TelemetryContribution.ALLOWLIST_JSON.indexOf(fileName) > -1) { + telemetryData['allowlistedjson'] = fileName; } return telemetryData; From b5f6a521e0ef6170de3b8de4d1382832eac24ee9 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Mon, 9 Nov 2020 20:03:38 +0000 Subject: [PATCH 070/103] Fix vscode-emmet-helper issue #1 --- extensions/emmet/yarn.lock | 49 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index bb19fbcd08a..fc2815cf4b3 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -55,9 +55,9 @@ integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI= "@types/node@^12.11.7": - version "12.19.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.2.tgz#9565ed5c72ba96038fc3add643edd5e7820598e7" - integrity sha512-SRH6QM0IMOBBFmDiJ75vlhcbUEYEquvSuhsVW9ijG20JvdFTfOrB1p6ddZxz5y/JNnbf+9HoHhjhOVSX2hsJyA== + version "12.19.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.4.tgz#cdfbb62e26c7435ed9aab9c941393cc3598e9b46" + integrity sha512-o3oj1bETk8kBwzz1WlO6JWL/AfAA3Vm6J1B3C9CsdxHYp7XgPiH7OEXPUbZTndHlRaIElrANkQfe6ZmfJb3H2w== ajv@^6.12.3: version "6.12.6" @@ -178,9 +178,9 @@ aws-sign2@~0.7.0: integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" - integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== balanced-match@^1.0.0: version "1.0.0" @@ -392,12 +392,12 @@ debug@^2.2.0: dependencies: ms "2.0.0" -debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== +debug@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== dependencies: - ms "^2.1.1" + ms "2.1.2" decamelize@^1.1.2: version "1.2.0" @@ -994,9 +994,9 @@ is-buffer@^1.1.5, is-buffer@~1.1.6: integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-core-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.0.0.tgz#58531b70aed1db7c0e8d4eb1a0a2d1ddd64bd12d" - integrity sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.1.0.tgz#a4cc031d9b1aca63eecbd18a650e13cb4eeab946" + integrity sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA== dependencies: has "^1.0.3" @@ -1410,7 +1410,7 @@ lodash.values@~2.4.1: dependencies: lodash.keys "~2.4.1" -lodash@^4.16.4: +lodash@^4.17.15: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -1572,12 +1572,12 @@ mocha-junit-reporter@^1.17.0: xml "^1.0.0" mocha-multi-reporters@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82" - integrity sha1-zH8/TTL0eFIJQdhSq7ZNmYhYfYI= + version "1.5.1" + resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.5.1.tgz#c73486bed5519e1d59c9ce39ac7a9792600e5676" + integrity sha512-Yb4QJOaGLIcmB0VY7Wif5AjvLMUFAdV57D2TWEva1Y0kU/3LjKpeRVmlMIfuO1SVbauve459kgtIizADqxMWPg== dependencies: - debug "^3.1.0" - lodash "^4.16.4" + debug "^4.1.1" + lodash "^4.17.15" mocha@^2.3.3: version "2.5.3" @@ -1605,7 +1605,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -2413,14 +2413,15 @@ vinyl@~2.0.1: replace-ext "^1.0.0" vscode-emmet-helper@~2.0.0: - version "2.0.8" - resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.0.8.tgz#7c3cf8027d1a75d29625e029d516da7bc56c1fdb" - integrity sha512-Wyf+b5pua+13eZSHCpmob1x915x/od4z6lIia9T2N4v7+CUYNxDisBu3/ShIM3qg3YiYvTCOm+Yx/CLd2khcVw== + version "2.0.9" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.0.9.tgz#16244c087cba4e379116f268384bb644649db6ad" + integrity sha512-S6RjnR9gUicl8LsYnQAMNqqOxolud9gcj+NpPyEnxfxp1YIBuC9oetj6l6N9VMZBWu6tL77wmf+/EJsRx1PDPA== dependencies: emmet "^2.1.5" jsonc-parser "^2.3.0" vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "^3.15.1" + vscode-nls "^5.0.0" vscode-uri "^2.1.2" vscode-html-languageservice@^3.0.3: From e300dfcdd251983c03e7defe07de26a5ea4fd3d6 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 9 Nov 2020 12:24:18 -0800 Subject: [PATCH 071/103] terminal: allow excluding programs by name from typeahead Fixes https://github.com/microsoft/vscode/issues/110109 --- .../browser/terminalTypeAheadAddon.ts | 25 ++++++++++--- .../contrib/terminal/common/terminal.ts | 3 ++ .../terminal/common/terminalConfiguration.ts | 11 +++++- .../test/browser/terminalTypeahead.test.ts | 35 +++++++++++++++++-- 4 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index 793b5ae848c..2de671a97e7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -8,10 +8,11 @@ import { Color } from 'vs/base/common/color'; import { debounce } from 'vs/base/common/decorators'; import { Emitter } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { XTermAttributes, XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; -import { IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { DEFAULT_LOCAL_ECHO_EXCLUDE, IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import type { IBuffer, IBufferCell, IDisposable, ITerminalAddon, Terminal } from 'xterm'; const ESC = '\x1b'; @@ -1178,11 +1179,16 @@ class TypeAheadStyle implements IDisposable { } } +const compileExcludeRegexp = (programs = DEFAULT_LOCAL_ECHO_EXCLUDE) => + new RegExp(`\\b(${programs.map(escapeRegExpCharacters).join('|')})\\b`, 'i'); + export class TypeAheadAddon extends Disposable implements ITerminalAddon { private typeaheadStyle?: TypeAheadStyle; private typeaheadThreshold = this.config.config.localEchoLatencyThreshold; + private excludeProgramRe = compileExcludeRegexp(this.config.config.localEchoExcludePrograms); protected lastRow?: { y: number; startingX: number }; - private timeline?: PredictionTimeline; + protected timeline?: PredictionTimeline; + private terminalTitle = ''; public stats?: PredictionStats; /** @@ -1206,6 +1212,10 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { timeline.setShowPredictions(this.typeaheadThreshold === 0); this._register(terminal.onData(e => this.onUserData(e))); + this._register(terminal.onTitleChange(title => { + this.terminalTitle = title; + this.reevaluatePredictorState(stats, timeline); + })); this._register(terminal.onResize(() => { timeline.setShowPredictions(false); timeline.clearCursor(); @@ -1214,6 +1224,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { this._register(this.config.onConfigChanged(() => { style.onUpdate(this.config.config.localEchoStyle); this.typeaheadThreshold = this.config.config.localEchoLatencyThreshold; + this.excludeProgramRe = compileExcludeRegexp(this.config.config.localEchoExcludePrograms); this.reevaluatePredictorState(stats, timeline); })); this._register(this.processManager.onBeforeProcessData(e => this.onBeforeProcessData(e))); @@ -1260,8 +1271,14 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { * terminal cursor is not updated, causes issues. */ @debounce(100) - private reevaluatePredictorState(stats: PredictionStats, timeline: PredictionTimeline) { - if (this.typeaheadThreshold < 0) { + protected reevaluatePredictorState(stats: PredictionStats, timeline: PredictionTimeline) { + this.reevaluatePredictorStateNow(stats, timeline); + } + + protected reevaluatePredictorStateNow(stats: PredictionStats, timeline: PredictionTimeline) { + if (this.excludeProgramRe.test(this.terminalTitle)) { + timeline.setShowPredictions(false); + } else if (this.typeaheadThreshold < 0) { timeline.setShowPredictions(false); } else if (this.typeaheadThreshold === 0) { timeline.setShowPredictions(true); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 3b32d9e9fcb..73a390bdf8f 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -136,11 +136,14 @@ export interface ITerminalConfiguration { unicodeVersion: '6' | '11'; experimentalLinkProvider: boolean; localEchoLatencyThreshold: number; + localEchoExcludePrograms: ReadonlyArray; localEchoStyle: 'bold' | 'dim' | 'italic' | 'underlined' | 'inverted' | string; serverSpawn: boolean; enablePersistentSessions: boolean; } +export const DEFAULT_LOCAL_ECHO_EXCLUDE: ReadonlyArray = ['vim', 'vi', 'nano', 'tmux']; + export interface ITerminalConfigHelper { config: ITerminalConfiguration; diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 9b1a6b4af4d..61cfd0b0bac 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -6,7 +6,7 @@ import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; import { localize } from 'vs/nls'; import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; -import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT } from 'vs/workbench/contrib/terminal/common/terminal'; +import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, DEFAULT_LOCAL_ECHO_EXCLUDE } from 'vs/workbench/contrib/terminal/common/terminal'; import { isMacintosh, isWindows, Platform } from 'vs/base/common/platform'; export const terminalConfiguration: IConfigurationNode = { @@ -358,6 +358,15 @@ export const terminalConfiguration: IConfigurationNode = { minimum: -1, default: 30, }, + 'terminal.integrated.localEchoExcludePrograms': { + description: localize('terminal.integrated.localEchoExcludePrograms', "Experimental: local echo will be disabled when any of these program names are found in the terminal title."), + type: 'array', + items: { + type: 'string', + uniqueItems: true + }, + default: DEFAULT_LOCAL_ECHO_EXCLUDE, + }, 'terminal.integrated.localEchoStyle': { description: localize('terminal.integrated.localEchoStyle', "Experimental: terminal style of locally echoed text; either a font style or an RGB color."), default: 'dim', diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts index 3434cd4211a..a0a66dfe4a7 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts @@ -8,9 +8,10 @@ import { Terminal } from 'xterm'; import { SinonStub, stub, useFakeTimers } from 'sinon'; import { Emitter } from 'vs/base/common/event'; import { IPrediction, PredictionStats, TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon'; -import { IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { DEFAULT_LOCAL_ECHO_EXCLUDE, IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { timeout } from 'vs/base/common/async'; const CSI = `\x1b[`; @@ -92,7 +93,8 @@ suite('Workbench - Terminal Typeahead', () => { setup(() => { config = upcastPartial({ localEchoStyle: 'italic', - localEchoLatencyThreshold: 0 + localEchoLatencyThreshold: 0, + localEchoExcludePrograms: DEFAULT_LOCAL_ECHO_EXCLUDE, }); publicLog = stub(); addon = new TestTypeAheadAddon( @@ -260,6 +262,24 @@ suite('Workbench - Terminal Typeahead', () => { onBeforeProcessData.fire({ data: 'o' }); } }); + + test('disables on title change', async () => { + const t = createMockTerminal({ lines: ['hello|'] }); + addon.activate(t.terminal); + + await timeout(1000); + + addon.reevaluateNow(); + assert.strictEqual(addon.isShowing, true, 'expected to show initially'); + + t.onTitleChange.fire('foo - VIM.exe'); + addon.reevaluateNow(); + assert.strictEqual(addon.isShowing, false, 'expected to hide when vim is open'); + + t.onTitleChange.fire('foo - git.exe'); + addon.reevaluateNow(); + assert.strictEqual(addon.isShowing, true, 'expected to show again after vim closed'); + }); }); }); @@ -267,6 +287,14 @@ class TestTypeAheadAddon extends TypeAheadAddon { public unlockLeftNavigating() { this.lastRow = { y: 1, startingX: 1 }; } + + public reevaluateNow() { + this.reevaluatePredictorStateNow(this.stats!, this.timeline!); + } + + public get isShowing() { + return !!this.timeline?.isShowingPredictions; + } } function upcastPartial(v: Partial): T { @@ -292,6 +320,7 @@ function createMockTerminal({ lines, cursorAttrs }: { }) { const written: string[] = []; const cursor = { y: 1, x: 1 }; + const onTitleChange = new Emitter(); const onData = new Emitter(); const csiEmitter = new Emitter(); @@ -315,11 +344,13 @@ function createMockTerminal({ lines, cursorAttrs }: { clearWritten: () => written.splice(0, written.length), onData: (s: string) => onData.fire(s), csiEmitter, + onTitleChange, terminal: { cols: 80, rows: 5, onResize: new Emitter().event, onData: onData.event, + onTitleChange: onTitleChange.event, parser: { registerCsiHandler(_: unknown, callback: () => void) { csiEmitter.event(callback); From fcef0e3db40a155da88e6197fb9e949eabbf0189 Mon Sep 17 00:00:00 2001 From: David Sanders Date: Mon, 9 Nov 2020 12:46:26 -0800 Subject: [PATCH 072/103] Use Array.flat() (#110189) --- .../src/features/foldingProvider.ts | 3 +-- .../src/features/workspaceSymbolProvider.ts | 3 +-- extensions/markdown-language-features/src/util/arrays.ts | 4 ---- extensions/markdown-language-features/tsconfig.json | 1 + 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/extensions/markdown-language-features/src/features/foldingProvider.ts b/extensions/markdown-language-features/src/features/foldingProvider.ts index 553c1a3257b..62c245c9ad2 100644 --- a/extensions/markdown-language-features/src/features/foldingProvider.ts +++ b/extensions/markdown-language-features/src/features/foldingProvider.ts @@ -7,7 +7,6 @@ import { Token } from 'markdown-it'; import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; import { TableOfContentsProvider } from '../tableOfContentsProvider'; -import { flatten } from '../util/arrays'; const rangeLimit = 5000; @@ -27,7 +26,7 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi this.getHeaderFoldingRanges(document), this.getBlockFoldingRanges(document) ]); - return flatten(foldables).slice(0, rangeLimit); + return foldables.flat().slice(0, rangeLimit); } private async getRegions(document: vscode.TextDocument): Promise { diff --git a/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts b/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts index f97bb021914..91f9dfcd3ec 100644 --- a/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts +++ b/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts @@ -9,7 +9,6 @@ import { isMarkdownFile } from '../util/file'; import { Lazy, lazy } from '../util/lazy'; import MDDocumentSymbolProvider from './documentSymbolProvider'; import { SkinnyTextDocument, SkinnyTextLine } from '../tableOfContentsProvider'; -import { flatten } from '../util/arrays'; export interface WorkspaceMarkdownDocumentProvider { getAllMarkdownDocuments(): Thenable>; @@ -136,7 +135,7 @@ export default class MarkdownWorkspaceSymbolProvider extends Disposable implemen } const allSymbolsSets = await Promise.all(Array.from(this._symbolCache.values()).map(x => x.value)); - const allSymbols = flatten(allSymbolsSets); + const allSymbols = allSymbolsSets.flat(); return allSymbols.filter(symbolInformation => symbolInformation.name.toLowerCase().indexOf(query.toLowerCase()) !== -1); } diff --git a/extensions/markdown-language-features/src/util/arrays.ts b/extensions/markdown-language-features/src/util/arrays.ts index ec0ed25c55d..b778a24ec9d 100644 --- a/extensions/markdown-language-features/src/util/arrays.ts +++ b/extensions/markdown-language-features/src/util/arrays.ts @@ -15,8 +15,4 @@ export function equals(one: ReadonlyArray, other: ReadonlyArray, itemEq } return true; -} - -export function flatten(arr: ReadonlyArray[]): T[] { - return ([] as T[]).concat.apply([], arr); } \ No newline at end of file diff --git a/extensions/markdown-language-features/tsconfig.json b/extensions/markdown-language-features/tsconfig.json index a5bb9b92b49..b02362c2cbe 100644 --- a/extensions/markdown-language-features/tsconfig.json +++ b/extensions/markdown-language-features/tsconfig.json @@ -6,6 +6,7 @@ "lib": [ "es6", "es2015.promise", + "es2019.array", "es2020.string", "dom" ] From 5e8f9cf1d205baeaf6c169f399ebc2fe5a532c05 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Nov 2020 20:15:53 +0100 Subject: [PATCH 073/103] :lipstick: code lens --- src/vs/editor/contrib/codelens/codelensWidget.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index 215e48f22de..fdd8b8c4ad7 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -25,9 +25,9 @@ class CodeLensViewZone implements IViewZone { afterLineNumber: number; private _lastHeight?: number; - private readonly _onHeight: Function; + private readonly _onHeight: () => void; - constructor(afterLineNumber: number, onHeight: Function) { + constructor(afterLineNumber: number, onHeight: () => void) { this.afterLineNumber = afterLineNumber; this._onHeight = onHeight; @@ -179,8 +179,8 @@ export class CodeLensWidget { private readonly _editor: IActiveCodeEditor; private readonly _className: string; - private readonly _viewZone!: CodeLensViewZone; - private readonly _viewZoneId!: string; + private readonly _viewZone: CodeLensViewZone; + private readonly _viewZoneId: string; private _contentWidget?: CodeLensContentWidget; private _decorationIds: string[]; @@ -193,7 +193,7 @@ export class CodeLensWidget { className: string, helper: CodeLensHelper, viewZoneChangeAccessor: IViewZoneChangeAccessor, - updateCallback: Function + updateCallback: () => void ) { this._editor = editor; this._className = className; From 51eb0901d4ee685a0750ea02bb516eb9f1c0db2a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Nov 2020 21:56:04 +0100 Subject: [PATCH 074/103] add setting for code lens font family and size, https://github.com/microsoft/vscode/issues/16038 --- src/vs/editor/common/config/editorOptions.ts | 21 ++ .../common/standalone/standaloneEnums.ts | 218 ++++++++--------- .../contrib/codelens/codelensController.ts | 43 +++- .../editor/contrib/codelens/codelensWidget.ts | 19 +- src/vs/monaco.d.ts | 228 +++++++++--------- 5 files changed, 297 insertions(+), 232 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 2659f2f51dc..1886fd56a09 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -495,6 +495,14 @@ export interface IEditorOptions { * Defaults to true. */ codeLens?: boolean; + /** + * Code lens font family. Defaults to editor font family. + */ + codeLensFontFamily?: string; + /** + * Code lens font size. Default to 90% of the editor font size + */ + codeLensFontSize?: number; /** * Control the behavior and rendering of the code action lightbulb. */ @@ -3619,6 +3627,8 @@ export const enum EditorOption { automaticLayout, autoSurround, codeLens, + codeLensFontFamily, + codeLensFontSize, colorDecorators, columnSelection, comments, @@ -3848,6 +3858,17 @@ export const EditorOptions = { EditorOption.codeLens, 'codeLens', true, { description: nls.localize('codeLens', "Controls whether the editor shows CodeLens.") } )), + codeLensFontFamily: register(new EditorStringOption( + EditorOption.codeLensFontFamily, 'codeLensFontFamily', '', + { description: nls.localize('codeLensFontFamily', "Controls the font family for CodeLens.") } + )), + codeLensFontSize: register(new EditorIntOption(EditorOption.codeLensFontSize, 'codeLensFontSize', 0, 0, 100, { + type: 'number', + default: 0, + minimum: 0, + maximum: 100, + description: nls.localize('codeLensFontSize', "Controls the font size in pixels for CodeLens. When set to `0`, the 90% of `#editor.fontSize#` is used.") + })), colorDecorators: register(new EditorBooleanOption( EditorOption.colorDecorators, 'colorDecorators', true, { description: nls.localize('colorDecorators', "Controls whether the editor should render the inline color decorators and color picker.") } diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 91c07a481a7..c8a841bd212 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -179,114 +179,116 @@ export enum EditorOption { automaticLayout = 9, autoSurround = 10, codeLens = 11, - colorDecorators = 12, - columnSelection = 13, - comments = 14, - contextmenu = 15, - copyWithSyntaxHighlighting = 16, - cursorBlinking = 17, - cursorSmoothCaretAnimation = 18, - cursorStyle = 19, - cursorSurroundingLines = 20, - cursorSurroundingLinesStyle = 21, - cursorWidth = 22, - disableLayerHinting = 23, - disableMonospaceOptimizations = 24, - dragAndDrop = 25, - emptySelectionClipboard = 26, - extraEditorClassName = 27, - fastScrollSensitivity = 28, - find = 29, - fixedOverflowWidgets = 30, - folding = 31, - foldingStrategy = 32, - foldingHighlight = 33, - unfoldOnClickAfterEndOfLine = 34, - fontFamily = 35, - fontInfo = 36, - fontLigatures = 37, - fontSize = 38, - fontWeight = 39, - formatOnPaste = 40, - formatOnType = 41, - glyphMargin = 42, - gotoLocation = 43, - hideCursorInOverviewRuler = 44, - highlightActiveIndentGuide = 45, - hover = 46, - inDiffEditor = 47, - letterSpacing = 48, - lightbulb = 49, - lineDecorationsWidth = 50, - lineHeight = 51, - lineNumbers = 52, - lineNumbersMinChars = 53, - links = 54, - matchBrackets = 55, - minimap = 56, - mouseStyle = 57, - mouseWheelScrollSensitivity = 58, - mouseWheelZoom = 59, - multiCursorMergeOverlapping = 60, - multiCursorModifier = 61, - multiCursorPaste = 62, - occurrencesHighlight = 63, - overviewRulerBorder = 64, - overviewRulerLanes = 65, - padding = 66, - parameterHints = 67, - peekWidgetDefaultFocus = 68, - definitionLinkOpensInPeek = 69, - quickSuggestions = 70, - quickSuggestionsDelay = 71, - readOnly = 72, - renameOnType = 73, - renderControlCharacters = 74, - renderIndentGuides = 75, - renderFinalNewline = 76, - renderLineHighlight = 77, - renderLineHighlightOnlyWhenFocus = 78, - renderValidationDecorations = 79, - renderWhitespace = 80, - revealHorizontalRightPadding = 81, - roundedSelection = 82, - rulers = 83, - scrollbar = 84, - scrollBeyondLastColumn = 85, - scrollBeyondLastLine = 86, - scrollPredominantAxis = 87, - selectionClipboard = 88, - selectionHighlight = 89, - selectOnLineNumbers = 90, - showFoldingControls = 91, - showUnused = 92, - snippetSuggestions = 93, - smartSelect = 94, - smoothScrolling = 95, - stopRenderingLineAfter = 96, - suggest = 97, - suggestFontSize = 98, - suggestLineHeight = 99, - suggestOnTriggerCharacters = 100, - suggestSelection = 101, - tabCompletion = 102, - tabIndex = 103, - unusualLineTerminators = 104, - useTabStops = 105, - wordSeparators = 106, - wordWrap = 107, - wordWrapBreakAfterCharacters = 108, - wordWrapBreakBeforeCharacters = 109, - wordWrapColumn = 110, - wordWrapMinified = 111, - wrappingIndent = 112, - wrappingStrategy = 113, - showDeprecated = 114, - editorClassName = 115, - pixelRatio = 116, - tabFocusMode = 117, - layoutInfo = 118, - wrappingInfo = 119 + codeLensFontFamily = 12, + codeLensFontSize = 13, + colorDecorators = 14, + columnSelection = 15, + comments = 16, + contextmenu = 17, + copyWithSyntaxHighlighting = 18, + cursorBlinking = 19, + cursorSmoothCaretAnimation = 20, + cursorStyle = 21, + cursorSurroundingLines = 22, + cursorSurroundingLinesStyle = 23, + cursorWidth = 24, + disableLayerHinting = 25, + disableMonospaceOptimizations = 26, + dragAndDrop = 27, + emptySelectionClipboard = 28, + extraEditorClassName = 29, + fastScrollSensitivity = 30, + find = 31, + fixedOverflowWidgets = 32, + folding = 33, + foldingStrategy = 34, + foldingHighlight = 35, + unfoldOnClickAfterEndOfLine = 36, + fontFamily = 37, + fontInfo = 38, + fontLigatures = 39, + fontSize = 40, + fontWeight = 41, + formatOnPaste = 42, + formatOnType = 43, + glyphMargin = 44, + gotoLocation = 45, + hideCursorInOverviewRuler = 46, + highlightActiveIndentGuide = 47, + hover = 48, + inDiffEditor = 49, + letterSpacing = 50, + lightbulb = 51, + lineDecorationsWidth = 52, + lineHeight = 53, + lineNumbers = 54, + lineNumbersMinChars = 55, + links = 56, + matchBrackets = 57, + minimap = 58, + mouseStyle = 59, + mouseWheelScrollSensitivity = 60, + mouseWheelZoom = 61, + multiCursorMergeOverlapping = 62, + multiCursorModifier = 63, + multiCursorPaste = 64, + occurrencesHighlight = 65, + overviewRulerBorder = 66, + overviewRulerLanes = 67, + padding = 68, + parameterHints = 69, + peekWidgetDefaultFocus = 70, + definitionLinkOpensInPeek = 71, + quickSuggestions = 72, + quickSuggestionsDelay = 73, + readOnly = 74, + renameOnType = 75, + renderControlCharacters = 76, + renderIndentGuides = 77, + renderFinalNewline = 78, + renderLineHighlight = 79, + renderLineHighlightOnlyWhenFocus = 80, + renderValidationDecorations = 81, + renderWhitespace = 82, + revealHorizontalRightPadding = 83, + roundedSelection = 84, + rulers = 85, + scrollbar = 86, + scrollBeyondLastColumn = 87, + scrollBeyondLastLine = 88, + scrollPredominantAxis = 89, + selectionClipboard = 90, + selectionHighlight = 91, + selectOnLineNumbers = 92, + showFoldingControls = 93, + showUnused = 94, + snippetSuggestions = 95, + smartSelect = 96, + smoothScrolling = 97, + stopRenderingLineAfter = 98, + suggest = 99, + suggestFontSize = 100, + suggestLineHeight = 101, + suggestOnTriggerCharacters = 102, + suggestSelection = 103, + tabCompletion = 104, + tabIndex = 105, + unusualLineTerminators = 106, + useTabStops = 107, + wordSeparators = 108, + wordWrap = 109, + wordWrapBreakAfterCharacters = 110, + wordWrapBreakBeforeCharacters = 111, + wordWrapColumn = 112, + wordWrapMinified = 113, + wrappingIndent = 114, + wrappingStrategy = 115, + showDeprecated = 116, + editorClassName = 117, + pixelRatio = 118, + tabFocusMode = 119, + layoutInfo = 120, + wrappingInfo = 121 } /** diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 360889a81e2..2a2c871c23c 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -53,7 +53,7 @@ export class CodeLensContribution implements IEditorContribution { this._disposables.add(this._editor.onDidChangeModel(() => this._onModelChange())); this._disposables.add(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); this._disposables.add(this._editor.onDidChangeConfiguration((e) => { - if (e.hasChanged(EditorOption.fontInfo)) { + if (e.hasChanged(EditorOption.fontInfo) || e.hasChanged(EditorOption.codeLensFontSize) || e.hasChanged(EditorOption.codeLensFontFamily)) { this._updateLensStyle(); } if (e.hasChanged(EditorOption.codeLens)) { @@ -77,21 +77,41 @@ export class CodeLensContribution implements IEditorContribution { this._disposables.dispose(); this._oldCodeLensModels.dispose(); this._currentCodeLensModel?.dispose(); + this._styleElement.remove(); + } + + private _getLayoutInfo() { + let fontSize = this._editor.getOption(EditorOption.codeLensFontSize); + let codeLensHeight: number; + if (!fontSize || fontSize < 5) { + fontSize = (this._editor.getOption(EditorOption.fontSize) * .9) | 0; + codeLensHeight = this._editor.getOption(EditorOption.lineHeight); + } else { + codeLensHeight = (fontSize * Math.max(1.3, this._editor.getOption(EditorOption.lineHeight) / this._editor.getOption(EditorOption.fontSize))) | 0; + } + return { codeLensHeight, fontSize }; } private _updateLensStyle(): void { - const options = this._editor.getOptions(); - const fontInfo = options.get(EditorOption.fontInfo); - const lineHeight = options.get(EditorOption.lineHeight); + const { codeLensHeight, fontSize } = this._getLayoutInfo(); + const fontFamily = this._editor.getOption(EditorOption.codeLensFontFamily); - const height = Math.round(lineHeight * 1.1); - const fontSize = Math.round(fontInfo.fontSize * 0.9); - const newStyle = ` - .monaco-editor .codelens-decoration.${this._styleClassName} { height: ${height}px; line-height: ${lineHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;} - .monaco-editor .codelens-decoration.${this._styleClassName} > a > .codicon { line-height: ${lineHeight}px; font-size: ${fontSize}px; } + let newStyle = ` + .monaco-editor .codelens-decoration.${this._styleClassName} { line-height: ${codeLensHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontSize * 0.5)}px;} + .monaco-editor .codelens-decoration.${this._styleClassName} > a > .codicon { line-height: ${codeLensHeight}px; font-size: ${fontSize}px; } `; + if (fontFamily) { + newStyle += `.monaco-editor .codelens-decoration.${this._styleClassName} { font-family: ${fontFamily}}`; + } this._styleElement.textContent = newStyle; + + // + this._editor.changeViewZones(accessor => { + for (let lens of this._lenses) { + lens.updateHeight(codeLensHeight, accessor); + } + }); } private _localDispose(): void { @@ -283,6 +303,7 @@ export class CodeLensContribution implements IEditorContribution { } const scrollState = StableEditorScrollState.capture(this._editor); + const layoutInfo = this._getLayoutInfo(); this._editor.changeDecorations(decorationsAccessor => { this._editor.changeViewZones(viewZoneAccessor => { @@ -304,7 +325,7 @@ export class CodeLensContribution implements IEditorContribution { groupsIndex++; codeLensIndex++; } else { - this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._resolveCodeLensesInViewportSoon())); + this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, layoutInfo.codeLensHeight, () => this._resolveCodeLensesInViewportSoon())); codeLensIndex++; groupsIndex++; } @@ -318,7 +339,7 @@ export class CodeLensContribution implements IEditorContribution { // Create extra symbols while (groupsIndex < groups.length) { - this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._resolveCodeLensesInViewportSoon())); + this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, layoutInfo.codeLensHeight, () => this._resolveCodeLensesInViewportSoon())); groupsIndex++; } diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index fdd8b8c4ad7..cf6d754271f 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -18,20 +18,20 @@ import { renderCodicons } from 'vs/base/browser/codicons'; class CodeLensViewZone implements IViewZone { - readonly heightInLines: number; readonly suppressMouseDown: boolean; readonly domNode: HTMLElement; afterLineNumber: number; + heightInPx: number; private _lastHeight?: number; private readonly _onHeight: () => void; - constructor(afterLineNumber: number, onHeight: () => void) { + constructor(afterLineNumber: number, heightInPx: number, onHeight: () => void) { this.afterLineNumber = afterLineNumber; - this._onHeight = onHeight; + this.heightInPx = heightInPx; - this.heightInLines = 1; + this._onHeight = onHeight; this.suppressMouseDown = true; this.domNode = document.createElement('div'); } @@ -193,6 +193,7 @@ export class CodeLensWidget { className: string, helper: CodeLensHelper, viewZoneChangeAccessor: IViewZoneChangeAccessor, + heightInPx: number, updateCallback: () => void ) { this._editor = editor; @@ -224,7 +225,7 @@ export class CodeLensWidget { } }); - this._viewZone = new CodeLensViewZone(range!.startLineNumber - 1, updateCallback); + this._viewZone = new CodeLensViewZone(range!.startLineNumber - 1, heightInPx, updateCallback); this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone); if (lenses.length > 0) { @@ -277,6 +278,14 @@ export class CodeLensWidget { }); } + updateHeight(height: number, viewZoneChangeAccessor: IViewZoneChangeAccessor): void { + this._viewZone.heightInPx = height; + viewZoneChangeAccessor.layoutZone(this._viewZoneId); + if (this._contentWidget) { + this._editor.layoutContentWidget(this._contentWidget); + } + } + computeIfNecessary(model: ITextModel): CodeLensItem[] | null { if (!this._viewZone.domNode.hasAttribute('monaco-visible-view-zone')) { return null; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 7df3f0f8636..a5a2fa1c36e 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3034,6 +3034,14 @@ declare namespace monaco.editor { * Defaults to true. */ codeLens?: boolean; + /** + * Code lens font family. Defaults to editor font family. + */ + codeLensFontFamily?: string; + /** + * Code lens font size. Default to 90% of the editor font size + */ + codeLensFontSize?: number; /** * Control the behavior and rendering of the code action lightbulb. */ @@ -3884,114 +3892,116 @@ declare namespace monaco.editor { automaticLayout = 9, autoSurround = 10, codeLens = 11, - colorDecorators = 12, - columnSelection = 13, - comments = 14, - contextmenu = 15, - copyWithSyntaxHighlighting = 16, - cursorBlinking = 17, - cursorSmoothCaretAnimation = 18, - cursorStyle = 19, - cursorSurroundingLines = 20, - cursorSurroundingLinesStyle = 21, - cursorWidth = 22, - disableLayerHinting = 23, - disableMonospaceOptimizations = 24, - dragAndDrop = 25, - emptySelectionClipboard = 26, - extraEditorClassName = 27, - fastScrollSensitivity = 28, - find = 29, - fixedOverflowWidgets = 30, - folding = 31, - foldingStrategy = 32, - foldingHighlight = 33, - unfoldOnClickAfterEndOfLine = 34, - fontFamily = 35, - fontInfo = 36, - fontLigatures = 37, - fontSize = 38, - fontWeight = 39, - formatOnPaste = 40, - formatOnType = 41, - glyphMargin = 42, - gotoLocation = 43, - hideCursorInOverviewRuler = 44, - highlightActiveIndentGuide = 45, - hover = 46, - inDiffEditor = 47, - letterSpacing = 48, - lightbulb = 49, - lineDecorationsWidth = 50, - lineHeight = 51, - lineNumbers = 52, - lineNumbersMinChars = 53, - links = 54, - matchBrackets = 55, - minimap = 56, - mouseStyle = 57, - mouseWheelScrollSensitivity = 58, - mouseWheelZoom = 59, - multiCursorMergeOverlapping = 60, - multiCursorModifier = 61, - multiCursorPaste = 62, - occurrencesHighlight = 63, - overviewRulerBorder = 64, - overviewRulerLanes = 65, - padding = 66, - parameterHints = 67, - peekWidgetDefaultFocus = 68, - definitionLinkOpensInPeek = 69, - quickSuggestions = 70, - quickSuggestionsDelay = 71, - readOnly = 72, - renameOnType = 73, - renderControlCharacters = 74, - renderIndentGuides = 75, - renderFinalNewline = 76, - renderLineHighlight = 77, - renderLineHighlightOnlyWhenFocus = 78, - renderValidationDecorations = 79, - renderWhitespace = 80, - revealHorizontalRightPadding = 81, - roundedSelection = 82, - rulers = 83, - scrollbar = 84, - scrollBeyondLastColumn = 85, - scrollBeyondLastLine = 86, - scrollPredominantAxis = 87, - selectionClipboard = 88, - selectionHighlight = 89, - selectOnLineNumbers = 90, - showFoldingControls = 91, - showUnused = 92, - snippetSuggestions = 93, - smartSelect = 94, - smoothScrolling = 95, - stopRenderingLineAfter = 96, - suggest = 97, - suggestFontSize = 98, - suggestLineHeight = 99, - suggestOnTriggerCharacters = 100, - suggestSelection = 101, - tabCompletion = 102, - tabIndex = 103, - unusualLineTerminators = 104, - useTabStops = 105, - wordSeparators = 106, - wordWrap = 107, - wordWrapBreakAfterCharacters = 108, - wordWrapBreakBeforeCharacters = 109, - wordWrapColumn = 110, - wordWrapMinified = 111, - wrappingIndent = 112, - wrappingStrategy = 113, - showDeprecated = 114, - editorClassName = 115, - pixelRatio = 116, - tabFocusMode = 117, - layoutInfo = 118, - wrappingInfo = 119 + codeLensFontFamily = 12, + codeLensFontSize = 13, + colorDecorators = 14, + columnSelection = 15, + comments = 16, + contextmenu = 17, + copyWithSyntaxHighlighting = 18, + cursorBlinking = 19, + cursorSmoothCaretAnimation = 20, + cursorStyle = 21, + cursorSurroundingLines = 22, + cursorSurroundingLinesStyle = 23, + cursorWidth = 24, + disableLayerHinting = 25, + disableMonospaceOptimizations = 26, + dragAndDrop = 27, + emptySelectionClipboard = 28, + extraEditorClassName = 29, + fastScrollSensitivity = 30, + find = 31, + fixedOverflowWidgets = 32, + folding = 33, + foldingStrategy = 34, + foldingHighlight = 35, + unfoldOnClickAfterEndOfLine = 36, + fontFamily = 37, + fontInfo = 38, + fontLigatures = 39, + fontSize = 40, + fontWeight = 41, + formatOnPaste = 42, + formatOnType = 43, + glyphMargin = 44, + gotoLocation = 45, + hideCursorInOverviewRuler = 46, + highlightActiveIndentGuide = 47, + hover = 48, + inDiffEditor = 49, + letterSpacing = 50, + lightbulb = 51, + lineDecorationsWidth = 52, + lineHeight = 53, + lineNumbers = 54, + lineNumbersMinChars = 55, + links = 56, + matchBrackets = 57, + minimap = 58, + mouseStyle = 59, + mouseWheelScrollSensitivity = 60, + mouseWheelZoom = 61, + multiCursorMergeOverlapping = 62, + multiCursorModifier = 63, + multiCursorPaste = 64, + occurrencesHighlight = 65, + overviewRulerBorder = 66, + overviewRulerLanes = 67, + padding = 68, + parameterHints = 69, + peekWidgetDefaultFocus = 70, + definitionLinkOpensInPeek = 71, + quickSuggestions = 72, + quickSuggestionsDelay = 73, + readOnly = 74, + renameOnType = 75, + renderControlCharacters = 76, + renderIndentGuides = 77, + renderFinalNewline = 78, + renderLineHighlight = 79, + renderLineHighlightOnlyWhenFocus = 80, + renderValidationDecorations = 81, + renderWhitespace = 82, + revealHorizontalRightPadding = 83, + roundedSelection = 84, + rulers = 85, + scrollbar = 86, + scrollBeyondLastColumn = 87, + scrollBeyondLastLine = 88, + scrollPredominantAxis = 89, + selectionClipboard = 90, + selectionHighlight = 91, + selectOnLineNumbers = 92, + showFoldingControls = 93, + showUnused = 94, + snippetSuggestions = 95, + smartSelect = 96, + smoothScrolling = 97, + stopRenderingLineAfter = 98, + suggest = 99, + suggestFontSize = 100, + suggestLineHeight = 101, + suggestOnTriggerCharacters = 102, + suggestSelection = 103, + tabCompletion = 104, + tabIndex = 105, + unusualLineTerminators = 106, + useTabStops = 107, + wordSeparators = 108, + wordWrap = 109, + wordWrapBreakAfterCharacters = 110, + wordWrapBreakBeforeCharacters = 111, + wordWrapColumn = 112, + wordWrapMinified = 113, + wrappingIndent = 114, + wrappingStrategy = 115, + showDeprecated = 116, + editorClassName = 117, + pixelRatio = 118, + tabFocusMode = 119, + layoutInfo = 120, + wrappingInfo = 121 } export const EditorOptions: { acceptSuggestionOnCommitCharacter: IEditorOption; @@ -4006,6 +4016,8 @@ declare namespace monaco.editor { automaticLayout: IEditorOption; autoSurround: IEditorOption; codeLens: IEditorOption; + codeLensFontFamily: IEditorOption; + codeLensFontSize: IEditorOption; colorDecorators: IEditorOption; columnSelection: IEditorOption; comments: IEditorOption; From 5ce31a6e8b4feeb1079985aff2d1dce34dcd6876 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 9 Nov 2020 13:53:24 -0800 Subject: [PATCH 075/103] fixes build break --- .../test/electron-browser/experimentalPrompts.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts index b0e18778a2e..df2c5a34a0b 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts @@ -68,6 +68,11 @@ suite('Experimental Prompts', () => { if (a === 'experiments.experiment1') { storageData = JSON.parse(b + ''); } + }, + store2: (a, b, c, d) => { + if (a === 'experiments.experiment1') { + storageData = JSON.parse(b + ''); + } } }); instantiationService.stub(INotificationService, new TestNotificationService()); From 0efbfd919011cc3ca9d52288ffdf38ec0d137902 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 9 Nov 2020 14:39:06 -0800 Subject: [PATCH 076/103] fix #108765. --- extensions/git/src/commands.ts | 7 +++++-- src/vs/workbench/api/common/extHostTypeConverters.ts | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 3b63452eed0..c666b0cf968 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -425,7 +425,7 @@ export class CommandCenter { } if (!left) { - await commands.executeCommand('vscode.open', right, opts, title); + await commands.executeCommand('vscode.open', right, { ...opts, override: resource.type === Status.BOTH_MODIFIED ? false : undefined }, title); } else { await commands.executeCommand('vscode.diff', left, right, title, opts); } @@ -828,7 +828,10 @@ export class CommandCenter { try { document = await workspace.openTextDocument(uri); } catch (error) { - await commands.executeCommand('vscode.open', uri, opts); + await commands.executeCommand('vscode.open', uri, { + ...opts, + override: arg instanceof Resource && arg.type === Status.BOTH_MODIFIED ? false : undefined + }); continue; } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index c0eb2a21f6a..ee870895848 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1183,6 +1183,7 @@ export namespace FoldingRangeKind { export interface TextEditorOpenOptions extends vscode.TextDocumentShowOptions { background?: boolean; + override?: boolean; } export namespace TextEditorOpenOptions { @@ -1194,6 +1195,7 @@ export namespace TextEditorOpenOptions { inactive: options.background, preserveFocus: options.preserveFocus, selection: typeof options.selection === 'object' ? Range.from(options.selection) : undefined, + override: typeof options.override === 'boolean' ? false : undefined }; } From 840bc2ef1c7f15eb23932fa4219bc6eb627c11a5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 9 Nov 2020 15:59:22 -0800 Subject: [PATCH 077/103] Set explicit StorageTarget for webviews For #109967 --- .../contrib/customEditor/common/contributedCustomEditors.ts | 6 +++--- .../contrib/webviewView/browser/webviewViewPane.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts b/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts index 960c3335d1b..ac834f0fabb 100644 --- a/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts +++ b/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts @@ -8,12 +8,12 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Memento } from 'vs/workbench/common/memento'; import { CustomEditorDescriptor, CustomEditorInfo, CustomEditorPriority } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { customEditorsExtensionPoint, ICustomEditorsExtensionPoint } from 'vs/workbench/contrib/customEditor/common/extensionPoint'; -import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { DEFAULT_EDITOR_ID } from 'vs/workbench/services/editor/common/editorOpenWith'; +import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in"); @@ -40,7 +40,7 @@ export class ContributedCustomEditors extends Disposable { this._memento = new Memento(ContributedCustomEditors.CUSTOM_EDITORS_STORAGE_ID, storageService); - const mementoObject = this._memento.legacygetMemento(StorageScope.GLOBAL); + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); for (const info of (mementoObject[ContributedCustomEditors.CUSTOM_EDITORS_ENTRY_ID] || []) as CustomEditorDescriptor[]) { this.add(new CustomEditorInfo(info)); } diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts index 049b3a0dd0d..df90ede801c 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts @@ -16,7 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -70,7 +70,7 @@ export class WebviewViewPane extends ViewPane { this.defaultTitle = this.title; this.memento = new Memento(`webviewView.${this.id}`, storageService); - this.viewState = this.memento.legacygetMemento(StorageScope.WORKSPACE); + this.viewState = this.memento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); this._register(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility())); From c9bebe2cc9cea7ac54254094432cdf67acc30508 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Tue, 10 Nov 2020 00:09:34 +0000 Subject: [PATCH 078/103] Fix #98041 --- extensions/emmet/package.json | 2 +- .../emmet/src/evaluateMathExpression.ts | 35 +++++++++---- .../src/test/evaluateMathExpression.test.ts | 52 +++++++++++++++++++ .../src/typings/emmetio__math-expression.d.ts | 13 ----- extensions/emmet/yarn.lock | 13 +++-- 5 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 extensions/emmet/src/test/evaluateMathExpression.test.ts delete mode 100644 extensions/emmet/src/typings/emmetio__math-expression.d.ts diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index fbb17884ad9..9898e0de97a 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -433,7 +433,7 @@ "dependencies": { "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", "@emmetio/html-matcher": "^0.3.3", - "@emmetio/math-expression": "^0.1.1", + "@emmetio/math-expression": "^1.0.4", "image-size": "^0.5.2", "vscode-emmet-helper": "~2.0.0", "vscode-html-languageservice": "^3.0.3" diff --git a/extensions/emmet/src/evaluateMathExpression.ts b/extensions/emmet/src/evaluateMathExpression.ts index 8a7de6ca423..588d4dce9ba 100644 --- a/extensions/emmet/src/evaluateMathExpression.ts +++ b/extensions/emmet/src/evaluateMathExpression.ts @@ -6,24 +6,42 @@ /* Based on @sergeche's work in his emmet plugin */ import * as vscode from 'vscode'; -import evaluate from '@emmetio/math-expression'; +import evaluate, { extract } from '@emmetio/math-expression'; import { DocumentStreamReader } from './bufferStream'; -export function evaluateMathExpression() { +export function evaluateMathExpression(): Thenable { if (!vscode.window.activeTextEditor) { vscode.window.showInformationMessage('No editor is active'); - return; + return Promise.resolve(false); } const editor = vscode.window.activeTextEditor; const stream = new DocumentStreamReader(editor.document); - editor.edit(editBuilder => { + return editor.edit(editBuilder => { editor.selections.forEach(selection => { - const pos = selection.isReversed ? selection.anchor : selection.active; - stream.pos = pos; + // startpos always comes before endpos + const startpos = selection.isReversed ? selection.active : selection.anchor; + const endpos = selection.isReversed ? selection.anchor : selection.active; + const selectionText = stream.substring(startpos, endpos); try { - const result = String(evaluate(stream, true)); - editBuilder.replace(new vscode.Range(stream.pos, pos), result); + if (selectionText) { + // respect selections + const result = String(evaluate(selectionText)); + editBuilder.replace(new vscode.Range(startpos, endpos), result); + } else { + // no selection made, extract expression from line + const lineToSelectionEnd = stream.substring(new vscode.Position(selection.end.line, 0), endpos); + const extractedIndices = extract(lineToSelectionEnd); + if (!extractedIndices) { + throw new Error('Invalid extracted indices'); + } + const result = String(evaluate(lineToSelectionEnd.substr(extractedIndices[0], extractedIndices[1]))); + const rangeToReplace = new vscode.Range( + new vscode.Position(selection.end.line, extractedIndices[0]), + new vscode.Position(selection.end.line, extractedIndices[1]) + ); + editBuilder.replace(rangeToReplace, result); + } } catch (err) { vscode.window.showErrorMessage('Could not evaluate expression'); // Ignore error since most likely it’s because of non-math expression @@ -31,5 +49,4 @@ export function evaluateMathExpression() { } }); }); - } diff --git a/extensions/emmet/src/test/evaluateMathExpression.test.ts b/extensions/emmet/src/test/evaluateMathExpression.test.ts new file mode 100644 index 00000000000..553d0417123 --- /dev/null +++ b/extensions/emmet/src/test/evaluateMathExpression.test.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'mocha'; +import * as assert from 'assert'; +import { Position, Selection } from 'vscode'; +import { withRandomFileEditor, closeAllEditors } from './testUtils'; +import { evaluateMathExpression } from '../evaluateMathExpression'; + +suite('Tests for Evaluate Math Expression', () => { + teardown(closeAllEditors); + + function testEvaluateMathExpression(fileContents: string, selection: [number, number] | number, expectedFileContents: string): Thenable { + return withRandomFileEditor(fileContents, 'html', async (editor, _doc) => { + const selectionToUse = typeof selection === 'number' ? + new Selection(new Position(0, selection), new Position(0, selection)) : + new Selection(new Position(0, selection[0]), new Position(0, selection[1])); + editor.selection = selectionToUse; + + await evaluateMathExpression(); + + assert.strictEqual(editor.document.getText(), expectedFileContents); + return Promise.resolve(); + }); + } + + test('Selected sanity check', () => { + return testEvaluateMathExpression('1 + 2', [0, 5], '3'); + }); + + test('Selected with surrounding text', () => { + return testEvaluateMathExpression('test1 + 2test', [4, 9], 'test3test'); + }); + + test('Selected with number not part of selection', () => { + return testEvaluateMathExpression('test3 1+2', [6, 9], 'test3 3'); + }); + + test('Non-selected sanity check', () => { + return testEvaluateMathExpression('1 + 2', 5, '3'); + }); + + test('Non-selected midway', () => { + return testEvaluateMathExpression('1 + 2', 1, '1 + 2'); + }); + + test('Non-selected with surrounding text', () => { + return testEvaluateMathExpression('test1 + 3test', 9, 'test4test'); + }); +}); diff --git a/extensions/emmet/src/typings/emmetio__math-expression.d.ts b/extensions/emmet/src/typings/emmetio__math-expression.d.ts deleted file mode 100644 index 4c3d862b0d4..00000000000 --- a/extensions/emmet/src/typings/emmetio__math-expression.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -declare module '@emmetio/math-expression' { - import { BufferStream } from 'EmmetNode'; - - function index(stream: BufferStream, backward: boolean): number; - - export default index; -} - diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index fc2815cf4b3..d956865f4e7 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -31,13 +31,12 @@ "@emmetio/stream-reader" "^2.0.0" "@emmetio/stream-reader-utils" "^0.1.0" -"@emmetio/math-expression@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@emmetio/math-expression/-/math-expression-0.1.1.tgz#1ff2c7f05800f64c57ca89038ee18bce9f5776dc" - integrity sha1-H/LH8FgA9kxXyokDjuGLzp9Xdtw= +"@emmetio/math-expression@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@emmetio/math-expression/-/math-expression-1.0.4.tgz#cb657ed944f82b3728f863bf5ece1b1ff3ae7497" + integrity sha512-1m7y8/VeXCAfgFoPGTerbqCIadApcIINujd3TaM/LRLPPKiod8aT1PPmh542spnsUSsSnZJjbuF7xiO4WFA42g== dependencies: - "@emmetio/stream-reader" "^2.0.1" - "@emmetio/stream-reader-utils" "^0.1.0" + "@emmetio/scanner" "^1.0.0" "@emmetio/scanner@^1.0.0": version "1.0.0" @@ -49,7 +48,7 @@ resolved "https://registry.yarnpkg.com/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz#244cb02c77ec2e74f78a9bd318218abc9c500a61" integrity sha1-JEywLHfsLnT3ipvTGCGKvJxQCmE= -"@emmetio/stream-reader@^2.0.0", "@emmetio/stream-reader@^2.0.1", "@emmetio/stream-reader@^2.2.0": +"@emmetio/stream-reader@^2.0.0", "@emmetio/stream-reader@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz#46cffea119a0a003312a21c2d9b5628cb5fcd442" integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI= From ffb8c08dd9e6cf41cc64e0f24b535857c55f13a5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 9 Nov 2020 17:09:55 -0800 Subject: [PATCH 079/103] Enable IgnoreMenuShortcuts for iframe based webviews For #109702 Iframe based webviews need to redispatch keypresses so that VS Code commands are supported. However, if this command is also in the native menu bars, this can end up triggering the command twice (once from the menubar and once from the webview's dispatch) We already fixed this for `` based webview. This change also fixes the issue for iframe based webviews --- .../webview/common/webviewManagerService.ts | 10 ++- .../electron-main/webviewMainService.ts | 27 +++++-- .../electron-browser/webviewElement.ts | 76 ++----------------- .../webviewIgnoreMenuShortcutsManager.ts | 74 ++++++++++++++++++ .../electron-sandbox/iframeWebviewElement.ts | 20 +++++ .../windowIgnoreMenuShortcutsManager.ts | 46 +++++++++++ 6 files changed, 177 insertions(+), 76 deletions(-) create mode 100644 src/vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager.ts create mode 100644 src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts diff --git a/src/vs/platform/webview/common/webviewManagerService.ts b/src/vs/platform/webview/common/webviewManagerService.ts index 8963865f3b6..476201f8fc6 100644 --- a/src/vs/platform/webview/common/webviewManagerService.ts +++ b/src/vs/platform/webview/common/webviewManagerService.ts @@ -11,6 +11,14 @@ import { IWebviewPortMapping } from 'vs/platform/webview/common/webviewPortMappi export const IWebviewManagerService = createDecorator('webviewManagerService'); +export interface WebviewWebContentsId { + readonly webContentsId: number; +} + +export interface WebviewWindowId { + readonly windowId: number; +} + export interface IWebviewManagerService { _serviceBrand: unknown; @@ -20,7 +28,7 @@ export interface IWebviewManagerService { didLoadResource(requestId: number, content: VSBuffer | undefined): void; - setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise; + setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise; } export interface RegisterWebviewMetadata { diff --git a/src/vs/platform/webview/electron-main/webviewMainService.ts b/src/vs/platform/webview/electron-main/webviewMainService.ts index c3b49724aba..03a6e306cac 100644 --- a/src/vs/platform/webview/electron-main/webviewMainService.ts +++ b/src/vs/platform/webview/electron-main/webviewMainService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { webContents } from 'electron'; +import { WebContents, webContents } from 'electron'; import { VSBuffer } from 'vs/base/common/buffer'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { IRequestService } from 'vs/platform/request/common/request'; -import { IWebviewManagerService, RegisterWebviewMetadata } from 'vs/platform/webview/common/webviewManagerService'; +import { IWebviewManagerService, RegisterWebviewMetadata, WebviewWebContentsId, WebviewWindowId } from 'vs/platform/webview/common/webviewManagerService'; import { WebviewPortMappingProvider } from 'vs/platform/webview/electron-main/webviewPortMappingProvider'; import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; @@ -26,7 +26,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer @IFileService fileService: IFileService, @IRequestService requestService: IRequestService, @ITunnelService tunnelService: ITunnelService, - @IWindowsMainService windowsMainService: IWindowsMainService, + @IWindowsMainService private readonly windowsMainService: IWindowsMainService, ) { super(); this.protocolProvider = this._register(new WebviewProtocolProvider(fileService, requestService, windowsMainService)); @@ -70,11 +70,24 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer }); } - public async setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise { - const contents = webContents.fromId(webContentsId); - if (!contents) { - throw new Error(`Invalid webContentsId: ${webContentsId}`); + public async setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise { + let contents: WebContents | undefined; + + if (typeof (id as WebviewWindowId).windowId === 'number') { + const { windowId } = (id as WebviewWindowId); + const window = this.windowsMainService.getWindowById(windowId); + if (!window) { + throw new Error(`Invalid windowId: ${windowId}`); + } + contents = window.win.webContents; + } else { + const { webContentsId } = (id as WebviewWebContentsId); + contents = webContents.fromId(webContentsId); + if (!contents) { + throw new Error(`Invalid webContentsId: ${webContentsId}`); + } } + if (!contents.isDestroyed()) { contents.setIgnoreMenuShortcuts(enabled); } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 6ac6a86d729..d11fa227334 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -8,96 +8,35 @@ import { addDisposableListener } from 'vs/base/browser/dom'; import { ThrottledDelayer } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { once } from 'vs/base/common/functional'; -import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { FileAccess, Schemas } from 'vs/base/common/network'; -import { isMacintosh } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { ILogService } from 'vs/platform/log/common/log'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader'; -import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; import { BaseWebview, WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { WebviewFindDelegate, WebviewFindWidget } from 'vs/workbench/contrib/webview/browser/webviewFindWidget'; -import { WebviewResourceRequestManager, rewriteVsCodeResourceUrls } from 'vs/workbench/contrib/webview/electron-sandbox/resourceLoading'; - -class WebviewKeyboardHandler { - - private readonly _webviews = new Set(); - private readonly _isUsingNativeTitleBars: boolean; - - private readonly webviewMainService: IWebviewManagerService; - - constructor( - configurationService: IConfigurationService, - mainProcessService: IMainProcessService, - ) { - this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') === 'native'; - - this.webviewMainService = createChannelSender(mainProcessService.getChannel('webview')); - } - - public add(webview: WebviewTag): IDisposable { - this._webviews.add(webview); - - const disposables = new DisposableStore(); - - if (this.shouldToggleMenuShortcutsEnablement) { - this.setIgnoreMenuShortcutsForWebview(webview, true); - } - - disposables.add(addDisposableListener(webview, 'ipc-message', (event) => { - switch (event.channel) { - case 'did-focus': - this.setIgnoreMenuShortcuts(true); - break; - - case 'did-blur': - this.setIgnoreMenuShortcuts(false); - return; - } - })); - - return toDisposable(() => { - disposables.dispose(); - this._webviews.delete(webview); - }); - } - - private get shouldToggleMenuShortcutsEnablement() { - return isMacintosh || this._isUsingNativeTitleBars; - } - - private setIgnoreMenuShortcuts(value: boolean) { - for (const webview of this._webviews) { - this.setIgnoreMenuShortcutsForWebview(webview, value); - } - } - - private setIgnoreMenuShortcutsForWebview(webview: WebviewTag, value: boolean) { - if (this.shouldToggleMenuShortcutsEnablement) { - this.webviewMainService.setIgnoreMenuShortcuts(webview.getWebContentsId(), value); - } - } -} +import { WebviewIgnoreMenuShortcutsManager } from 'vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager'; +import { rewriteVsCodeResourceUrls, WebviewResourceRequestManager } from 'vs/workbench/contrib/webview/electron-sandbox/resourceLoading'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export class ElectronWebviewBasedWebview extends BaseWebview implements Webview, WebviewFindDelegate { - private static _webviewKeyboardHandler: WebviewKeyboardHandler | undefined; + private static _webviewKeyboardHandler: WebviewIgnoreMenuShortcutsManager | undefined; private static getWebviewKeyboardHandler( configService: IConfigurationService, mainProcessService: IMainProcessService, ) { if (!this._webviewKeyboardHandler) { - this._webviewKeyboardHandler = new WebviewKeyboardHandler(configService, mainProcessService); + this._webviewKeyboardHandler = new WebviewIgnoreMenuShortcutsManager(configService, mainProcessService); } return this._webviewKeyboardHandler; } @@ -124,6 +63,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme @IConfigurationService configurationService: IConfigurationService, @IMainProcessService mainProcessService: IMainProcessService, @INotificationService noficationService: INotificationService, + @INativeHostService nativeHostService: INativeHostService, ) { super(id, options, contentOptions, extension, _webviewThemeDataProvider, noficationService, _myLogService, telemetryService, environmentService); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager.ts new file mode 100644 index 00000000000..5b5e4dd75ac --- /dev/null +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { WebviewTag } from 'electron'; +import { addDisposableListener } from 'vs/base/browser/dom'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { isMacintosh } from 'vs/base/common/platform'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; +import { WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; + +export class WebviewIgnoreMenuShortcutsManager { + + private readonly _webviews = new Set(); + private readonly _isUsingNativeTitleBars: boolean; + + private readonly webviewMainService: IWebviewManagerService; + + constructor( + configurationService: IConfigurationService, + mainProcessService: IMainProcessService, + ) { + this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') === 'native'; + + this.webviewMainService = createChannelSender(mainProcessService.getChannel('webview')); + } + + public add(webview: WebviewTag): IDisposable { + this._webviews.add(webview); + + const disposables = new DisposableStore(); + + if (this.shouldToggleMenuShortcutsEnablement) { + this.setIgnoreMenuShortcutsForWebview(webview, true); + } + + disposables.add(addDisposableListener(webview, 'ipc-message', (event) => { + switch (event.channel) { + case WebviewMessageChannels.didFocus: + this.setIgnoreMenuShortcuts(true); + break; + + case WebviewMessageChannels.didBlur: + this.setIgnoreMenuShortcuts(false); + return; + } + })); + + return toDisposable(() => { + disposables.dispose(); + this._webviews.delete(webview); + }); + } + + private get shouldToggleMenuShortcutsEnablement() { + return isMacintosh || this._isUsingNativeTitleBars; + } + + private setIgnoreMenuShortcuts(value: boolean) { + for (const webview of this._webviews) { + this.setIgnoreMenuShortcutsForWebview(webview, value); + } + } + + private setIgnoreMenuShortcutsForWebview(webview: WebviewTag, value: boolean) { + if (this.shouldToggleMenuShortcutsEnablement) { + this.webviewMainService.setIgnoreMenuShortcuts({ webContentsId: webview.getWebContentsId() }, value); + } + } +} diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts b/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts index 3b9c5e75a9c..da47d47f957 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts @@ -6,18 +6,23 @@ import { ThrottledDelayer } from 'vs/base/common/async'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { ILogService } from 'vs/platform/log/common/log'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { IRequestService } from 'vs/platform/request/common/request'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; import { WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; import { rewriteVsCodeResourceUrls, WebviewResourceRequestManager } from 'vs/workbench/contrib/webview/electron-sandbox/resourceLoading'; +import { WindowIgnoreMenuShortcutsManager } from 'vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; /** @@ -31,6 +36,8 @@ export class ElectronIframeWebview extends IFrameWebview { private readonly _focusDelayer = this._register(new ThrottledDelayer(10)); private _elementFocusImpl!: (options?: FocusOptions | undefined) => void; + private readonly _webviewKeyboardHandler: WindowIgnoreMenuShortcutsManager; + constructor( id: string, options: WebviewOptions, @@ -45,12 +52,25 @@ export class ElectronIframeWebview extends IFrameWebview { @IRemoteAuthorityResolverService _remoteAuthorityResolverService: IRemoteAuthorityResolverService, @ILogService logService: ILogService, @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService, + @IMainProcessService mainProcessService: IMainProcessService, @INotificationService noficationService: INotificationService, + @INativeHostService nativeHostService: INativeHostService, ) { super(id, options, contentOptions, extension, webviewThemeDataProvider, noficationService, tunnelService, fileService, requestService, telemetryService, environmentService, _remoteAuthorityResolverService, logService); this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.content.options)); + + this._webviewKeyboardHandler = new WindowIgnoreMenuShortcutsManager(configurationService, mainProcessService, nativeHostService); + + this._register(this.on(WebviewMessageChannels.didFocus, () => { + this._webviewKeyboardHandler.didFocus(); + })); + + this._register(this.on(WebviewMessageChannels.didBlur, () => { + this._webviewKeyboardHandler.didBlur(); + })); } protected createElement(options: WebviewOptions, contentOptions: WebviewContentOptions) { diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts b/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts new file mode 100644 index 00000000000..00da271c3e8 --- /dev/null +++ b/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isMacintosh } from 'vs/base/common/platform'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; + +export class WindowIgnoreMenuShortcutsManager { + + private readonly _isUsingNativeTitleBars: boolean; + + private readonly webviewMainService: IWebviewManagerService; + + constructor( + configurationService: IConfigurationService, + mainProcessService: IMainProcessService, + private readonly nativeHostService: INativeHostService + ) { + this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') === 'native'; + + this.webviewMainService = createChannelSender(mainProcessService.getChannel('webview')); + } + + public didFocus(): void { + this.setIgnoreMenuShortcuts(true); + } + + public didBlur(): void { + this.setIgnoreMenuShortcuts(false); + } + + private get shouldToggleMenuShortcutsEnablement() { + return isMacintosh || this._isUsingNativeTitleBars; + } + + protected setIgnoreMenuShortcuts(value: boolean) { + if (this.shouldToggleMenuShortcutsEnablement) { + this.webviewMainService.setIgnoreMenuShortcuts({ windowId: this.nativeHostService.windowId }, value); + } + } +} From d04956f6a7118679a35e8eeaa72892a68af05cd0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 9 Nov 2020 18:00:46 -0800 Subject: [PATCH 080/103] Switches the 'Fix all' quick fix provider to use resolveCodeAction (#107853) Made while testing #106410 --- .../src/languageFeatures/quickFix.ts | 85 ++++++++++++------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/quickFix.ts b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts index 0965a574721..2af23b2700d 100644 --- a/extensions/typescript-language-features/src/languageFeatures/quickFix.ts +++ b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts @@ -51,6 +51,9 @@ class ApplyCodeActionCommand implements Command { } } +type ApplyFixAllCodeAction_args = { + readonly action: VsCodeFixAllCodeAction; +}; class ApplyFixAllCodeAction implements Command { public static readonly ID = '_typescript.applyFixAllCodeAction'; @@ -61,14 +64,7 @@ class ApplyFixAllCodeAction implements Command { private readonly telemetryReporter: TelemetryReporter, ) { } - public async execute( - file: string, - tsAction: Proto.CodeFixAction, - ): Promise { - if (!tsAction.fixId) { - return; - } - + public async execute(args: ApplyFixAllCodeAction_args): Promise { /* __GDPR__ "quickFixAll.execute" : { "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, @@ -78,25 +74,12 @@ class ApplyFixAllCodeAction implements Command { } */ this.telemetryReporter.logTelemetry('quickFixAll.execute', { - fixName: tsAction.fixName + fixName: args.action.tsAction.fixName }); - const args: Proto.GetCombinedCodeFixRequestArgs = { - scope: { - type: 'file', - args: { file } - }, - fixId: tsAction.fixId, - }; - - const response = await this.client.execute('getCombinedCodeFix', args, nulToken); - if (response.type !== 'response' || !response.body) { - return undefined; + if (args.action.combinedResponse) { + await applyCodeActionCommands(this.client, args.action.combinedResponse.body.commands, nulToken); } - - const edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, response.body.changes); - await vscode.workspace.applyEdit(edit); - await applyCodeActionCommands(this.client, response.body.commands, nulToken); } } @@ -134,13 +117,25 @@ class VsCodeCodeAction extends vscode.CodeAction { constructor( public readonly tsAction: Proto.CodeFixAction, title: string, - kind: vscode.CodeActionKind, - public readonly isFixAll: boolean, + kind: vscode.CodeActionKind ) { super(title, kind); } } +class VsCodeFixAllCodeAction extends VsCodeCodeAction { + constructor( + tsAction: Proto.CodeFixAction, + public readonly file: string, + title: string, + kind: vscode.CodeActionKind + ) { + super(tsAction, title, kind); + } + + public combinedResponse?: Proto.GetCombinedCodeFixResponse; +} + class CodeActionSet { private readonly _actions = new Set(); private readonly _fixAllActions = new Map<{}, VsCodeCodeAction>(); @@ -202,7 +197,7 @@ class SupportedCodeActionProvider { } } -class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { +class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { public static readonly metadata: vscode.CodeActionProviderMetadata = { providedCodeActionKinds: [vscode.CodeActionKind.QuickFix] @@ -257,6 +252,28 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { return allActions; } + public async resolveCodeAction(codeAction: VsCodeCodeAction, token: vscode.CancellationToken): Promise { + if (!(codeAction instanceof VsCodeFixAllCodeAction) || !codeAction.tsAction.fixId) { + return codeAction; + } + + const arg: Proto.GetCombinedCodeFixRequestArgs = { + scope: { + type: 'file', + args: { file: codeAction.file } + }, + fixId: codeAction.tsAction.fixId, + }; + + const response = await this.client.execute('getCombinedCodeFix', arg, token); + if (response.type === 'response') { + codeAction.combinedResponse = response; + codeAction.edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, response.body.changes); + } + + return codeAction; + } + private async getFixesForDiagnostic( document: vscode.TextDocument, file: string, @@ -295,7 +312,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { diagnostic: vscode.Diagnostic, tsAction: Proto.CodeFixAction ): VsCodeCodeAction { - const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix, false); + const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix); codeAction.edit = getEditForCodeAction(this.client, tsAction); codeAction.diagnostics = [diagnostic]; codeAction.command = { @@ -328,14 +345,16 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { return results; } - const action = new VsCodeCodeAction( + const action = new VsCodeFixAllCodeAction( tsAction, + file, tsAction.fixAllDescription || localize('fixAllInFileLabel', '{0} (Fix all in file)', tsAction.description), - vscode.CodeActionKind.QuickFix, true); + vscode.CodeActionKind.QuickFix); + action.diagnostics = [diagnostic]; action.command = { command: ApplyFixAllCodeAction.ID, - arguments: [file, tsAction], + arguments: [{ action }], title: '' }; results.addFixAllAction(tsAction.fixId, action); @@ -370,7 +389,7 @@ function isPreferredFix( action: VsCodeCodeAction, allActions: readonly VsCodeCodeAction[] ): boolean { - if (action.isFixAll) { + if (action instanceof VsCodeFixAllCodeAction) { return false; } @@ -384,7 +403,7 @@ function isPreferredFix( return true; } - if (otherAction.isFixAll) { + if (otherAction instanceof VsCodeFixAllCodeAction) { return true; } From 4247b296eb143c94e48ca2629a1945f429e8768c Mon Sep 17 00:00:00 2001 From: Suman B K Date: Tue, 10 Nov 2020 07:32:38 +0530 Subject: [PATCH 081/103] Including ` as part of foldEndPairCharacters (#110108) * Including '`' as part of foldEndPairCharacters * Removed test code, which was unrelated --- .../src/languageFeatures/folding.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/folding.ts b/extensions/typescript-language-features/src/languageFeatures/folding.ts index 31c3169fa70..5dc6dbd1cca 100644 --- a/extensions/typescript-language-features/src/languageFeatures/folding.ts +++ b/extensions/typescript-language-features/src/languageFeatures/folding.ts @@ -58,7 +58,7 @@ class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { return new vscode.FoldingRange(start, end, kind); } - private static readonly foldEndPairCharacters = ['}', ']', ')']; + private static readonly foldEndPairCharacters = ['}', ']', ')', '`']; private adjustFoldingEnd(range: vscode.Range, document: vscode.TextDocument) { // workaround for #47240 From 2815e732f7666108246e3d392f272a6752584a30 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 9 Nov 2020 19:22:14 -0800 Subject: [PATCH 082/103] Fix refocusing clearing existing forcus for iframe based webviews #83188 --- .../contrib/webview/browser/pre/main.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 0f0773fce5a..c088c40d384 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -439,10 +439,18 @@ // propagate focus host.onMessage('focus', () => { - const target = getActiveFrame(); - if (target) { - target.contentWindow.focus(); + const activeFrame = getActiveFrame(); + if (!activeFrame || !activeFrame.contentWindow) { + return; } + + if (document.activeElement === activeFrame) { + // We are already focused on the iframe (or one of its children) so no need + // to refocus. + return; + } + + activeFrame.contentWindow.focus(); }); // update iframe-contents @@ -573,6 +581,8 @@ newFrame.contentWindow.focus(); } + + contentWindow.addEventListener('scroll', handleInnerScroll); contentWindow.addEventListener('wheel', handleWheel); From 6c32ada9b54ac3927fa13fcebf3b111d94e82ab0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 10 Nov 2020 07:30:17 +0100 Subject: [PATCH 083/103] show extension editor manage action always --- .../extensions/browser/extensionsActions.ts | 44 ++++--------------- 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 78dd465dc6b..4663cc13565 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -948,47 +948,21 @@ export class ManageExtensionAction extends ExtensionDropDownAction { export class ExtensionEditorManageExtensionAction extends ExtensionDropDownAction { - private static readonly ID = 'extensionEditor.manageExtension'; - - private static readonly Class = `${ExtensionAction.ICON_ACTION_CLASS} manage codicon-gear`; - private static readonly HideManageExtensionClass = `${ExtensionEditorManageExtensionAction.Class} hide`; - constructor( - @IInstantiationService instantiationService: IInstantiationService, - @IExtensionService private readonly extensionService: IExtensionService + @IInstantiationService instantiationService: IInstantiationService ) { - - super(ExtensionEditorManageExtensionAction.ID, '', '', true, true, instantiationService); + super('extensionEditor.manageExtension', '', `${ExtensionAction.ICON_ACTION_CLASS} manage codicon-gear`, true, true, instantiationService); this.tooltip = localize('manage', "Manage"); - this.update(); } - async getActionGroups(runningExtensions: IExtensionDescription[]): Promise { - const groups: IAction[][] = []; - getContextMenuActions(this.extension, true, this.instantiationService).forEach(actions => groups.push(actions)); - groups.forEach(group => group.forEach(extensionAction => { - if (extensionAction instanceof ExtensionAction) { - extensionAction.extension = this.extension; - } - })); - return groups; + update(): void { } + + run(): Promise { + const actionGroups: IAction[][] = []; + getContextMenuActions(this.extension, true, this.instantiationService).forEach(actions => actionGroups.push(actions)); + return super.run({ actionGroups, disposeActionsOnHide: true }); } - async run(): Promise { - const runtimeExtensions = await this.extensionService.getExtensions(); - return super.run({ actionGroups: await this.getActionGroups(runtimeExtensions), disposeActionsOnHide: true }); - } - - update(): void { - this.class = ExtensionEditorManageExtensionAction.HideManageExtensionClass; - this.enabled = false; - if (this.extension) { - const state = this.extension.state; - this.enabled = state === ExtensionState.Installed; - this.class = this.enabled || state === ExtensionState.Uninstalling ? ExtensionEditorManageExtensionAction.Class : ExtensionEditorManageExtensionAction.HideManageExtensionClass; - this.tooltip = state === ExtensionState.Uninstalling ? localize('ManageExtensionAction.uninstallingTooltip', "Uninstalling") : ''; - } - } } export class MenuItemExtensionAction extends ExtensionAction { @@ -1032,7 +1006,7 @@ export class InstallAnotherVersionAction extends ExtensionAction { } update(): void { - this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery && this.extension.state !== ExtensionState.Uninstalling && this.extension.state !== ExtensionState.Installing; + this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery && this.extension.state === ExtensionState.Installed; } run(): Promise { From 65f805d98e798b979901b74d4c8810c05455b792 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 10 Nov 2020 08:17:14 +0100 Subject: [PATCH 084/103] fix build --- src/bootstrap-amd.js | 2 +- src/bootstrap-fork.js | 8 ++++++-- src/bootstrap-window.js | 2 +- src/main.js | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index 22422e26b57..228c3d59e63 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -29,7 +29,7 @@ if (process.env['ELECTRON_RUN_AS_NODE'] || process.versions['electron']) { } // Pseudo NLS support -if (nlsConfig?.pseudo) { +if (nlsConfig && nlsConfig.pseudo) { loader(['vs/nls'], function (nlsPlugin) { nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); }); diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js index 711578cecd8..0cd75027286 100644 --- a/src/bootstrap-fork.js +++ b/src/bootstrap-fork.js @@ -81,7 +81,9 @@ function pipeLoggingToParent() { // to start the stacktrace where the console message was being written if (process.env.VSCODE_LOG_STACK === 'true') { const stack = new Error().stack; - argsArray.push({ __$stack: stack?.split('\n').slice(3).join('\n') }); + if (stack) { + argsArray.push({ __$stack: stack.split('\n').slice(3).join('\n') }); + } } try { @@ -114,7 +116,9 @@ function pipeLoggingToParent() { */ function safeSend(arg) { try { - process.send?.(arg); + if (process.send) { + process.send(arg); + } } catch (error) { // Can happen if the parent channel is closed meanwhile } diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index 7dcd0badbe1..38867c15c49 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -61,7 +61,7 @@ const enableDeveloperTools = (safeProcess.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath; let developerToolsUnbind; if (enableDeveloperTools || (options && options.forceEnableDeveloperKeybindings)) { - developerToolsUnbind = registerDeveloperKeybindings(options?.disallowReloadKeybinding); + developerToolsUnbind = registerDeveloperKeybindings(options && options.disallowReloadKeybinding); } // Correctly inherit the parent's environment (TODO@sandbox non-sandboxed only) diff --git a/src/main.js b/src/main.js index 16b7ca5f1e4..b75dd6ad33f 100644 --- a/src/main.js +++ b/src/main.js @@ -107,7 +107,7 @@ crashReporter.start({ // to ensure that no 'logs' folder is created on disk at a // location outside of the portable directory // (https://github.com/microsoft/vscode/issues/56651) -if (portable?.isPortable) { +if (portable && portable.isPortable) { app.setAppLogsPath(path.join(userDataPath, 'logs')); } From 1357f6d4940ee109222d3469b88363e35caa2d73 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 10 Nov 2020 08:48:40 +0100 Subject: [PATCH 085/103] editor preview - ensure simple file dialog opens pinned too --- src/vs/editor/browser/services/openerService.ts | 9 ++++++++- src/vs/platform/opener/common/opener.ts | 6 ++++++ .../dialogs/browser/abstractFileDialogService.ts | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index cb61a91ef72..84adca8dd55 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -74,7 +74,14 @@ class EditorOpener implements IOpener { } await this._editorService.openCodeEditor( - { resource: target, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } }, + { + resource: target, + options: { + selection, + context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API, + ...options?.editorOptions + } + }, this._editorService.getFocusedCodeEditor(), options?.openToSide ); diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 977cb79b80d..d82651f93be 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -7,6 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { equalsIgnoreCase, startsWithIgnoreCase } from 'vs/base/common/strings'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; export const IOpenerService = createDecorator('openerService'); @@ -18,6 +19,11 @@ type OpenInternalOptions = { */ readonly openToSide?: boolean; + /** + * Extra editor options to apply in case an editor is used to open. + */ + readonly editorOptions?: IEditorOptions; + /** * Signals that the editor to open was triggered through a user * action, such as keyboard or mouse usage. diff --git a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts index bdb558b4244..508de8442bf 100644 --- a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts @@ -164,7 +164,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { if (stat.isDirectory || options.forceNewWindow || preferNewWindow) { return this.hostService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow }); } else { - return this.openerService.open(uri, { fromUserGesture: true }); + return this.openerService.open(uri, { fromUserGesture: true, editorOptions: { pinned: true } }); } } } @@ -181,7 +181,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { if (options.forceNewWindow || preferNewWindow) { return this.hostService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow }); } else { - return this.openerService.open(uri, { fromUserGesture: true }); + return this.openerService.open(uri, { fromUserGesture: true, editorOptions: { pinned: true } }); } } } From 0216c71896dc5aba9a7dc67027892a00790cacb0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Nov 2020 08:56:33 +0100 Subject: [PATCH 086/103] Revert "Use editor base weight when contributing peek commands, fixes https://github.com/microsoft/vscode/issues/109727#issuecomment-720986472" This reverts commit 7601718ccc234602284632e387cabd9d74d7aae9. --- src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts index a7aaa87c526..b0231c8fa34 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts @@ -367,7 +367,7 @@ KeybindingsRegistry.registerKeybindingRule({ }); KeybindingsRegistry.registerKeybindingRule({ id: 'closeReferenceSearch', - weight: KeybindingWeight.EditorContrib + 50, + weight: KeybindingWeight.WorkbenchContrib + 50, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], when: ContextKeyExpr.and(ctxReferenceSearchVisible, ContextKeyExpr.not('config.editor.stablePeek')) @@ -376,7 +376,7 @@ KeybindingsRegistry.registerKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'revealReference', - weight: KeybindingWeight.EditorContrib, + weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.Enter, mac: { primary: KeyCode.Enter, From 351e6172e11f6544adcab5f135705144575406b5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Nov 2020 09:22:37 +0100 Subject: [PATCH 087/103] update code lens when editor gains focus, https://github.com/microsoft/vscode/issues/83363 --- src/vs/editor/contrib/codelens/codelensController.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 2a2c871c23c..77955b25ac9 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -224,6 +224,9 @@ export class CodeLensContribution implements IEditorContribution { // Ask for all references again scheduler.schedule(); })); + this._localToDispose.add(this._editor.onDidFocusEditorWidget(() => { + scheduler.schedule(); + })); this._localToDispose.add(this._editor.onDidScrollChange(e => { if (e.scrollTopChanged && this._lenses.length > 0) { this._resolveCodeLensesInViewportSoon(); From c0c4ceed4426971b5f3ebe7904e55aba6062eb1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 10 Nov 2020 09:33:00 +0100 Subject: [PATCH 088/103] fixes #110290 --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index c4ecfc2d53b..f89bce50ece 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1038,7 +1038,11 @@ class ViewModel { } for (const repository of this.scmViewService.visibleRepositories) { - const item = this.items.get(repository)!; + const item = this.items.get(repository); + + if (!item) { + continue; + } // go backwards from last group for (let j = item.groupItems.length - 1; j >= 0; j--) { From b3cc19b819d2d4bf79c6b95d043acffcb04255e5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Nov 2020 09:45:44 +0100 Subject: [PATCH 089/103] :lipstick: use async-await for code lens provider logic --- .../api/browser/mainThreadLanguageFeatures.ts | 19 ++--- .../api/common/extHostLanguageFeatures.ts | 84 +++++++++---------- 2 files changed, 47 insertions(+), 56 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 64a3f19a3be..33824992142 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -165,16 +165,15 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerCodeLensSupport(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void { const provider = { - provideCodeLenses: (model: ITextModel, token: CancellationToken): Promise => { - return this._proxy.$provideCodeLenses(handle, model.uri, token).then(listDto => { - if (!listDto) { - return undefined; - } - return { - lenses: listDto.lenses, - dispose: () => listDto.cacheId && this._proxy.$releaseCodeLenses(handle, listDto.cacheId) - }; - }); + provideCodeLenses: async (model: ITextModel, token: CancellationToken): Promise => { + const listDto = await this._proxy.$provideCodeLenses(handle, model.uri, token); + if (!listDto) { + return undefined; + } + return { + lenses: listDto.lenses, + dispose: () => listDto.cacheId && this._proxy.$releaseCodeLenses(handle, listDto.cacheId) + }; }, resolveCodeLens: (_model: ITextModel, codeLens: modes.CodeLens, token: CancellationToken): Promise => { return this._proxy.$resolveCodeLens(handle, codeLens, token); diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index e4fab08b3fb..77c0b391594 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -116,65 +116,57 @@ class CodeLensAdapter { private readonly _provider: vscode.CodeLensProvider ) { } - provideCodeLenses(resource: URI, token: CancellationToken): Promise { + async provideCodeLenses(resource: URI, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - return asPromise(() => this._provider.provideCodeLenses(doc, token)).then(lenses => { - - if (!lenses || token.isCancellationRequested) { - return undefined; - } - - const cacheId = this._cache.add(lenses); - const disposables = new DisposableStore(); - this._disposables.set(cacheId, disposables); - - const result: extHostProtocol.ICodeLensListDto = { - cacheId, - lenses: [], - }; - - for (let i = 0; i < lenses.length; i++) { - result.lenses.push({ - cacheId: [cacheId, i], - range: typeConvert.Range.from(lenses[i].range), - command: this._commands.toInternal(lenses[i].command, disposables) - }); - } - - return result; - }); + const lenses = await this._provider.provideCodeLenses(doc, token); + if (!lenses || token.isCancellationRequested) { + return undefined; + } + const cacheId = this._cache.add(lenses); + const disposables = new DisposableStore(); + this._disposables.set(cacheId, disposables); + const result: extHostProtocol.ICodeLensListDto = { + cacheId, + lenses: [], + }; + for (let i = 0; i < lenses.length; i++) { + result.lenses.push({ + cacheId: [cacheId, i], + range: typeConvert.Range.from(lenses[i].range), + command: this._commands.toInternal(lenses[i].command, disposables) + }); + } + return result; } - resolveCodeLens(symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise { + async resolveCodeLens(symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise { const lens = symbol.cacheId && this._cache.get(...symbol.cacheId); if (!lens) { - return Promise.resolve(undefined); + return undefined; } - let resolve: Promise; + let resolvedLens: vscode.CodeLens | undefined | null; if (typeof this._provider.resolveCodeLens !== 'function' || lens.isResolved) { - resolve = Promise.resolve(lens); + resolvedLens = lens; } else { - resolve = asPromise(() => this._provider.resolveCodeLens!(lens, token)); + resolvedLens = await this._provider.resolveCodeLens(lens, token); + } + if (!resolvedLens) { + resolvedLens = lens; } - return resolve.then(newLens => { - if (token.isCancellationRequested) { - return undefined; - } - - const disposables = symbol.cacheId && this._disposables.get(symbol.cacheId[0]); - if (!disposables) { - // We've already been disposed of - return undefined; - } - - newLens = newLens || lens; - symbol.command = this._commands.toInternal(newLens.command || CodeLensAdapter._badCmd, disposables); - return symbol; - }); + if (token.isCancellationRequested) { + return undefined; + } + const disposables = symbol.cacheId && this._disposables.get(symbol.cacheId[0]); + if (!disposables) { + // disposed in the meantime + return undefined; + } + symbol.command = this._commands.toInternal(resolvedLens.command ?? CodeLensAdapter._badCmd, disposables); + return symbol; } releaseCodeLenses(cachedId: number): void { From 9b0522f3dc3c2441c89cf30fa9ea17ed07dc63e6 Mon Sep 17 00:00:00 2001 From: Andreas Ziegler Date: Tue, 10 Nov 2020 09:50:13 +0100 Subject: [PATCH 090/103] scmViewPane: do not render whitespace in commit message input field (#107913) When entering a commit message we don't need to render whitespace, so let's disable the corresponding option in the editorOptions hash. Fixes: #107689 --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index f89bce50ece..0c7d7dca4b2 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1486,7 +1486,8 @@ class SCMInputWidget extends Disposable { padding: { top: 3, bottom: 3 }, quickSuggestions: false, scrollbar: { alwaysConsumeMouseWheel: false }, - overflowWidgetsDomNode + overflowWidgetsDomNode, + renderWhitespace: 'none' }; const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { From 21c84a435159466bdb7ecc8b2d60038fac4281b3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Nov 2020 18:13:07 +0100 Subject: [PATCH 091/103] restore old case ignore behaviour unless explicitly set --- src/vs/base/common/map.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 39b03f06fd6..deaf56955e8 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -6,6 +6,8 @@ import { URI } from 'vs/base/common/uri'; import { CharCode } from 'vs/base/common/charCode'; import { compareSubstringIgnoreCase, compare, compareSubstring, compareIgnoreCase } from 'vs/base/common/strings'; +import { isLinux } from 'vs/base/common/platform'; +import { Schemas } from 'vs/base/common/network'; export function getOrSet(map: Map, key: K, value: V): V { let result = map.get(key); @@ -138,7 +140,7 @@ export class UriIterator implements IKeyIterator { private _states: UriIteratorState[] = []; private _stateIdx: number = 0; - constructor(private readonly _ignorePathCasing: boolean) { } + constructor(private readonly _ignorePathCasing: boolean | undefined) { } reset(key: URI): this { this._value = key; @@ -150,7 +152,10 @@ export class UriIterator implements IKeyIterator { this._states.push(UriIteratorState.Authority); } if (this._value.path) { - this._pathIterator = new PathIterator(false, !this._ignorePathCasing); + this._pathIterator = new PathIterator(false, this._ignorePathCasing === undefined + ? key.scheme === Schemas.file && isLinux + : !this._ignorePathCasing + ); this._pathIterator.reset(key.path); if (this._pathIterator.value()) { this._states.push(UriIteratorState.Path); @@ -226,7 +231,7 @@ class TernarySearchTreeNode { export class TernarySearchTree { - static forUris(ignorePathCasing: boolean = false): TernarySearchTree { + static forUris(ignorePathCasing?: boolean): TernarySearchTree { return new TernarySearchTree(new UriIterator(ignorePathCasing)); } From 3bf2afdc01c4c3485f08f8b68ed97f9909200987 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Nov 2020 10:04:01 +0100 Subject: [PATCH 092/103] add TernarySearchTree#forUri2 which allow correct path casing handling, https://github.com/microsoft/vscode/issues/110241 --- src/vs/base/common/map.ts | 14 +++++++++----- src/vs/base/test/common/map.test.ts | 18 ++++++++++++++---- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index deaf56955e8..6ab0597e4b8 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -140,7 +140,7 @@ export class UriIterator implements IKeyIterator { private _states: UriIteratorState[] = []; private _stateIdx: number = 0; - constructor(private readonly _ignorePathCasing: boolean | undefined) { } + constructor(private readonly _ignorePathCasing: (uri: URI) => boolean) { } reset(key: URI): this { this._value = key; @@ -152,10 +152,7 @@ export class UriIterator implements IKeyIterator { this._states.push(UriIteratorState.Authority); } if (this._value.path) { - this._pathIterator = new PathIterator(false, this._ignorePathCasing === undefined - ? key.scheme === Schemas.file && isLinux - : !this._ignorePathCasing - ); + this._pathIterator = new PathIterator(false, !this._ignorePathCasing(key)); this._pathIterator.reset(key.path); if (this._pathIterator.value()) { this._states.push(UriIteratorState.Path); @@ -231,7 +228,14 @@ class TernarySearchTreeNode { export class TernarySearchTree { + /** + * @deprecated + */ static forUris(ignorePathCasing?: boolean): TernarySearchTree { + return new TernarySearchTree(new UriIterator(key => ignorePathCasing ?? (key.scheme === Schemas.file && isLinux))); + } + + static forUris2(ignorePathCasing: (key: URI) => boolean): TernarySearchTree { return new TernarySearchTree(new UriIterator(ignorePathCasing)); } diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 2615f098e4c..90b43529204 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -368,7 +368,7 @@ suite('Map', () => { }); test('URIIterator', function () { - const iter = new UriIterator(false); + const iter = new UriIterator(() => false); iter.reset(URI.parse('file:///usr/bin/file.txt')); assert.equal(iter.value(), 'file'); @@ -680,7 +680,7 @@ suite('Map', () => { }); test('TernarySearchTree (URI) - basics', function () { - let trie = new TernarySearchTree(new UriIterator(false)); + let trie = new TernarySearchTree(new UriIterator(() => false)); trie.set(URI.file('/user/foo/bar'), 1); trie.set(URI.file('/user/foo'), 2); @@ -700,7 +700,7 @@ suite('Map', () => { test('TernarySearchTree (URI) - lookup', function () { - const map = new TernarySearchTree(new UriIterator(false)); + const map = new TernarySearchTree(new UriIterator(() => false)); map.set(URI.parse('http://foo.bar/user/foo/bar'), 1); map.set(URI.parse('http://foo.bar/user/foo?query'), 2); map.set(URI.parse('http://foo.bar/user/foo?QUERY'), 3); @@ -715,9 +715,19 @@ suite('Map', () => { assert.equal(map.get(URI.parse('http://foo.bar/user/foo/bar/boo')), undefined); }); + test('TernarySearchTree (URI) - lookup, casing', function () { + + const map = new TernarySearchTree(new UriIterator(uri => /^https?$/.test(uri.scheme))); + map.set(URI.parse('http://foo.bar/user/foo/bar'), 1); + assert.equal(map.get(URI.parse('http://foo.bar/USER/foo/bar')), 1); + + map.set(URI.parse('foo://foo.bar/user/foo/bar'), 1); + assert.equal(map.get(URI.parse('foo://foo.bar/USER/foo/bar')), undefined); + }); + test('TernarySearchTree (PathSegments) - superstr', function () { - const map = new TernarySearchTree(new UriIterator(false)); + const map = new TernarySearchTree(new UriIterator(() => false)); map.set(URI.file('/user/foo/bar'), 1); map.set(URI.file('/user/foo'), 2); map.set(URI.file('/user/foo/flip/flop'), 3); From f5e9a01715868f32cc1899abd75f6cd44ffe7099 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Nov 2020 10:09:47 +0100 Subject: [PATCH 093/103] make default not ignore path casing, https://github.com/microsoft/vscode/issues/110241 --- 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 6ab0597e4b8..09430ded67d 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -235,7 +235,7 @@ export class TernarySearchTree { return new TernarySearchTree(new UriIterator(key => ignorePathCasing ?? (key.scheme === Schemas.file && isLinux))); } - static forUris2(ignorePathCasing: (key: URI) => boolean): TernarySearchTree { + static forUris2(ignorePathCasing: (key: URI) => boolean = () => false): TernarySearchTree { return new TernarySearchTree(new UriIterator(ignorePathCasing)); } From f2a9b63c64e0d59481961d2100e73041f6fe74c0 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 10 Nov 2020 10:11:41 +0100 Subject: [PATCH 094/103] Do not show hover on icon label after click Fixes #110284 --- src/vs/base/browser/ui/iconLabel/iconLabel.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 6ef2c31036f..0e1ba77f691 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -201,10 +201,11 @@ export class IconLabel extends Disposable { function mouseMove(this: HTMLElement, e: MouseEvent): any { mouseX = e.x; } - function mouseLeave(this: HTMLElement, e: MouseEvent): any { + function mouseLeaveOrDown(this: HTMLElement, e: MouseEvent): any { isHovering = false; } - const mouseLeaveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_LEAVE, true)(mouseLeave.bind(htmlElement)); + const mouseLeaveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_LEAVE, true)(mouseLeaveOrDown.bind(htmlElement)); + const mouseDownDisposable = domEvent(htmlElement, dom.EventType.MOUSE_DOWN, true)(mouseLeaveOrDown.bind(htmlElement)); const mouseMoveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_MOVE, true)(mouseMove.bind(htmlElement)); setTimeout(async () => { if (isHovering && tooltip) { @@ -232,6 +233,7 @@ export class IconLabel extends Disposable { } mouseMoveDisposable.dispose(); mouseLeaveDisposable.dispose(); + mouseDownDisposable.dispose(); }, hoverDelay); } const mouseOverDisposable = this._register(domEvent(htmlElement, dom.EventType.MOUSE_OVER, true)(mouseOver.bind(htmlElement))); From d751b6bcd238bddf91e540eb462f9467bf58b091 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Nov 2020 10:35:08 +0100 Subject: [PATCH 095/103] add IExtUri#ignorePathCasing, use forUris2 in decorations service, https://github.com/microsoft/vscode/issues/110241 --- src/vs/base/common/resources.ts | 9 +++++++++ .../decorations/browser/decorationsService.ts | 13 ++++++++++--- .../test/browser/decorationsService.test.ts | 10 +++++++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 621c97368c7..3d68dc55942 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -58,6 +58,11 @@ export interface IExtUri { */ getComparisonKey(uri: URI, ignoreFragment?: boolean): string; + /** + * Whether the casing of the path-component of the uri should be ignored. + */ + ignorePathCasing(uri: URI): boolean; + // --- path math basenameOrAuthority(resource: URI): string; @@ -161,6 +166,10 @@ export class ExtUri implements IExtUri { }).toString(); } + ignorePathCasing(uri: URI): boolean { + return this._ignorePathCasing(uri); + } + isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment: boolean = false): boolean { if (base.scheme === parentCandidate.scheme) { if (base.scheme === Schemas.file) { diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index fda885169d6..842849af346 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -18,6 +18,7 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { hash } from 'vs/base/common/hash'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; class DecorationRule { @@ -168,10 +169,10 @@ class DecorationStyles { class FileDecorationChangeEvent implements IResourceDecorationChangeEvent { - private readonly _data = TernarySearchTree.forUris(); + private readonly _data = TernarySearchTree.forUris2(_uri => true); // events ignore all path casings affectsResource(uri: URI): boolean { - return this._data.get(uri) || this._data.findSuperstr(uri) !== undefined; + return this._data.get(uri) ?? this._data.findSuperstr(uri) !== undefined; } static debouncer(last: FileDecorationChangeEvent | undefined, current: URI | URI[]) { @@ -201,14 +202,18 @@ class DecorationDataRequest { class DecorationProviderWrapper { - readonly data = TernarySearchTree.forUris(); + readonly data: TernarySearchTree; private readonly _dispoable: IDisposable; constructor( readonly provider: IDecorationsProvider, + uriIdentityService: IUriIdentityService, private readonly _uriEmitter: Emitter, private readonly _flushEmitter: Emitter ) { + + this.data = TernarySearchTree.forUris2(uri => uriIdentityService.extUri.ignorePathCasing(uri)); + this._dispoable = this.provider.onDidChange(uris => { if (!uris) { // flush event -> drop all data, can affect everything @@ -326,6 +331,7 @@ export class DecorationsService implements IDecorationsService { constructor( @IThemeService themeService: IThemeService, + @IUriIdentityService private readonly _uriIdentityService: IUriIdentityService, ) { this._decorationStyles = new DecorationStyles(themeService); } @@ -340,6 +346,7 @@ export class DecorationsService implements IDecorationsService { const wrapper = new DecorationProviderWrapper( provider, + this._uriIdentityService, this._onDidChangeDecorationsDelayed, this._onDidChangeDecorations ); diff --git a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts index 9cbdbb2a632..5a4b76eb6e3 100644 --- a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts +++ b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts @@ -8,8 +8,11 @@ import { DecorationsService } from 'vs/workbench/services/decorations/browser/de import { IDecorationsProvider, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; +import * as resources from 'vs/base/common/resources'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { mock } from 'vs/base/test/common/mock'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; suite('DecorationsService', function () { @@ -19,7 +22,12 @@ suite('DecorationsService', function () { if (service) { service.dispose(); } - service = new DecorationsService(new TestThemeService()); + service = new DecorationsService( + new TestThemeService(), + new class extends mock() { + extUri = resources.extUri; + } + ); }); test('Async provider, async/evented result', function () { From 156d702f5acfad0fe140548d0099347dbc1ecaf8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 10 Nov 2020 10:43:51 +0100 Subject: [PATCH 096/103] Diff editor: show path as description (fix #109224) --- .../parts/editor/editor.contribution.ts | 10 ++-- .../browser/parts/editor/textDiffEditor.ts | 2 +- src/vs/workbench/common/editor.ts | 2 +- .../common/editor/diffEditorInput.ts | 54 +++++++++++++++++-- .../bulkEdit/browser/preview/bulkEditPane.ts | 3 +- .../customEditor/browser/customEditors.ts | 3 +- .../browser/preferences.contribution.ts | 2 +- .../contrib/search/browser/replaceService.ts | 6 ++- .../userDataSync/browser/userDataSync.ts | 1 + .../browser/userDataSyncMergesView.ts | 1 + .../userDataSync/browser/userDataSyncViews.ts | 1 + .../services/editor/browser/editorService.ts | 22 ++------ .../test/browser/parts/editor/editor.test.ts | 2 +- .../parts/editor/editorDiffModel.test.ts | 2 +- .../browser/parts/editor/editorGroups.test.ts | 7 +-- .../browser/parts/editor/editorInput.test.ts | 11 ++-- 16 files changed, 89 insertions(+), 40 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 609ed7d5a8c..802966c808e 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -238,27 +238,27 @@ export abstract class AbstractSideBySideEditorInputFactory implements IEditorInp const secondaryInput = secondaryInputFactory.deserialize(instantiationService, deserialized.secondarySerialized); if (primaryInput && secondaryInput) { - return this.createEditorInput(deserialized.name, deserialized.description, secondaryInput, primaryInput); + return this.createEditorInput(instantiationService, deserialized.name, deserialized.description, secondaryInput, primaryInput); } } return undefined; } - protected abstract createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput; + protected abstract createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput; } class SideBySideEditorInputFactory extends AbstractSideBySideEditorInputFactory { - protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { return new SideBySideEditorInput(name, description, secondaryInput, primaryInput); } } class DiffEditorInputFactory extends AbstractSideBySideEditorInputFactory { - protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { - return new DiffEditorInput(name, description, secondaryInput, primaryInput); + protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + return instantiationService.createInstance(DiffEditorInput, name, description, secondaryInput, primaryInput, undefined); } } diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 115d6ee98d4..be8c620ce46 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -175,7 +175,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan const originalInput = input.originalInput; const modifiedInput = input.modifiedInput; - const binaryDiffInput = new DiffEditorInput(input.getName(), input.getDescription(), originalInput, modifiedInput, true); + const binaryDiffInput = this.instantiationService.createInstance(DiffEditorInput, input.getName(), input.getDescription(), originalInput, modifiedInput, true); // Forward binary flag to input if supported const fileEditorInputFactory = Registry.as(EditorInputExtensions.EditorInputFactories).getFileEditorInputFactory(); diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 11b96aea9a5..2dfe74eed7d 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -734,7 +734,7 @@ export class SideBySideEditorInput extends EditorInput { constructor( protected readonly name: string | undefined, - private readonly description: string | undefined, + protected readonly description: string | undefined, private readonly _secondary: EditorInput, private readonly _primary: EditorInput ) { diff --git a/src/vs/workbench/common/editor/diffEditorInput.ts b/src/vs/workbench/common/editor/diffEditorInput.ts index 4274f2eb876..e4ac4d07e3a 100644 --- a/src/vs/workbench/common/editor/diffEditorInput.ts +++ b/src/vs/workbench/common/editor/diffEditorInput.ts @@ -3,11 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditorModel, EditorInput, SideBySideEditorInput, TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID } from 'vs/workbench/common/editor'; +import { EditorModel, EditorInput, SideBySideEditorInput, TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID, Verbosity } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel'; import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel'; import { localize } from 'vs/nls'; +import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; +import { dirname } from 'vs/base/common/resources'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IFileService } from 'vs/platform/files/common/files'; +import { URI } from 'vs/base/common/uri'; /** * The base editor input for the diff editor. It is made up of two editor inputs, the original version @@ -21,10 +26,12 @@ export class DiffEditorInput extends SideBySideEditorInput { constructor( protected name: string | undefined, - description: string | undefined, + protected description: string | undefined, public readonly originalInput: EditorInput, public readonly modifiedInput: EditorInput, - private readonly forceOpenAsBinary?: boolean + private readonly forceOpenAsBinary: boolean | undefined, + @ILabelService private readonly labelService: ILabelService, + @IFileService private readonly fileService: IFileService ) { super(name, description, originalInput, modifiedInput); } @@ -35,12 +42,53 @@ export class DiffEditorInput extends SideBySideEditorInput { getName(): string { if (!this.name) { + + // Craft a name from original and modified input that includes the + // relative path in case both sides have different parents and we + // compare file resources. + const fileResources = this.asFileResources(); + if (fileResources && dirname(fileResources.original).path !== dirname(fileResources.modified).path + ) { + return `${this.labelService.getUriLabel(fileResources.original, { relative: true })} ↔ ${this.labelService.getUriLabel(fileResources.modified, { relative: true })}`; + } + return localize('sideBySideLabels', "{0} ↔ {1}", this.originalInput.getName(), this.modifiedInput.getName()); } return this.name; } + getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string | undefined { + if (typeof this.description !== 'string') { + + // Pass the description of the modified side in case both original + // and modified input have the same parent and we compare file resources. + const fileResources = this.asFileResources(); + if (fileResources && dirname(fileResources.original).path === dirname(fileResources.modified).path + ) { + return this.modifiedInput.getDescription(verbosity); + } + } + + return this.description; + } + + private asFileResources(): { original: URI, modified: URI } | undefined { + if ( + this.originalInput instanceof AbstractTextResourceEditorInput && + this.modifiedInput instanceof AbstractTextResourceEditorInput && + this.fileService.canHandleResource(this.originalInput.preferredResource) && + this.fileService.canHandleResource(this.modifiedInput.preferredResource) + ) { + return { + original: this.originalInput.preferredResource, + modified: this.modifiedInput.preferredResource + }; + } + + return undefined; + } + async resolve(): Promise { // Create Model - we never reuse our cached model if refresh is true because we cannot diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index 1f196dd3f0f..e9e3d55b8c5 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -26,7 +26,7 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; -import { basename } from 'vs/base/common/resources'; +import { basename, dirname } from 'vs/base/common/resources'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IAction } from 'vs/base/common/actions'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -356,6 +356,7 @@ export class BulkEditPane extends ViewPane { leftResource, rightResource: previewUri, label, + description: this._labelService.getUriLabel(dirname(leftResource), { relative: true }), options }, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 3bcaaacdba0..c67d70f1a95 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -447,6 +447,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo constructor( @IEditorService private readonly editorService: IEditorService, @ICustomEditorService private readonly customEditorService: ICustomEditorService, + @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); @@ -651,7 +652,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo if (modifiedOverride || originalOverride) { return { override: (async () => { - const input = new DiffEditorInput(editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput, true); + const input = this.instantiationService.createInstance(DiffEditorInput, editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput, true); return this.editorService.openEditor(input, { ...options, override: false }, group); })(), }; diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 8c59d791150..5f5e5c47554 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -95,7 +95,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( // Register Preferences Editor Input Factory class PreferencesEditorInputFactory extends AbstractSideBySideEditorInputFactory { - protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { return new PreferencesEditorInput(name, description, secondaryInput, primaryInput); } } diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index 83a6db8d711..c85764d0724 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -24,6 +24,8 @@ import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/b import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { mergeSort } from 'vs/base/common/arrays'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { dirname } from 'vs/base/common/resources'; const REPLACE_PREVIEW = 'replacePreview'; @@ -93,7 +95,8 @@ export class ReplaceService implements IReplaceService { @ITextFileService private readonly textFileService: ITextFileService, @IEditorService private readonly editorService: IEditorService, @ITextModelService private readonly textModelResolverService: ITextModelService, - @IBulkEditService private readonly bulkEditorService: IBulkEditService + @IBulkEditService private readonly bulkEditorService: IBulkEditService, + @ILabelService private readonly labelService: ILabelService ) { } replace(match: Match): Promise; @@ -113,6 +116,7 @@ export class ReplaceService implements IReplaceService { leftResource: fileMatch.resource, rightResource: toReplaceResource(fileMatch.resource), label: nls.localize('fileReplaceChanges', "{0} ↔ {1} (Replace Preview)", fileMatch.name(), fileMatch.name()), + description: this.labelService.getUriLabel(dirname(fileMatch.resource), { relative: true }), options: { preserveFocus, pinned, diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 660bbeb865f..f48dcb3b246 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -661,6 +661,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo leftResource: conflict.remoteResource, rightResource: conflict.previewResource, label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), + description: localize('sideBySideDescription', "Settings Sync"), options: { preserveFocus: false, pinned: true, diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts index 69e0cf9e60a..3610b1ca5ac 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts @@ -318,6 +318,7 @@ export class UserDataSyncMergesViewPane extends TreeViewPane { leftResource, rightResource, label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), + description: localize('sideBySideDescription', "Settings Sync"), options: { preserveFocus: true, revealIfVisible: true, diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts index 74bac346ea3..a29cb807d85 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts @@ -279,6 +279,7 @@ export class UserDataSyncDataViews extends Disposable { leftResource, rightResource, label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), + description: localize('sideBySideDescription', "Settings Sync"), options: { preserveFocus: true, revealIfVisible: true, diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index a50d37b302f..0bffc4cb803 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -24,7 +24,6 @@ import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from import { coalesce, distinct, insert } from 'vs/base/common/arrays'; import { isCodeEditor, isDiffEditor, ICodeEditor, IDiffEditor, isCompositeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorGroupView, IEditorOpeningEvent, EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; -import { ILabelService } from 'vs/platform/label/common/label'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; import { EditorsObserver } from 'vs/workbench/browser/parts/editor/editorsObserver'; @@ -73,7 +72,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IUntitledTextEditorService private readonly untitledTextEditorService: IUntitledTextEditorService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @ILabelService private readonly labelService: ILabelService, @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @@ -817,11 +815,12 @@ export class EditorService extends Disposable implements EditorServiceImpl { const leftInput = this.createEditorInput({ resource: resourceDiffInput.leftResource, forceFile: resourceDiffInput.forceFile }); const rightInput = this.createEditorInput({ resource: resourceDiffInput.rightResource, forceFile: resourceDiffInput.forceFile }); - return new DiffEditorInput( - resourceDiffInput.label || this.toSideBySideLabel(leftInput, rightInput), + return this.instantiationService.createInstance(DiffEditorInput, + resourceDiffInput.label, resourceDiffInput.description, leftInput, - rightInput + rightInput, + undefined ); } @@ -968,19 +967,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { return input; } - 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 })} ↔ ${this.labelService.getUriLabel(rightInput.preferredResource, { relative: true })}`; - } - - // Signal back that the label should be computed from within the editor - return undefined; - } - //#endregion //#region save/revert diff --git a/src/vs/workbench/test/browser/parts/editor/editor.test.ts b/src/vs/workbench/test/browser/parts/editor/editor.test.ts index aad14e56a3c..8ff03965ebd 100644 --- a/src/vs/workbench/test/browser/parts/editor/editor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editor.test.ts @@ -75,7 +75,7 @@ suite('Workbench editor', () => { assert.equal(EditorResourceAccessor.getOriginalUri(file, { filterByScheme: [Schemas.file, Schemas.untitled] })!.toString(), file.resource.toString()); assert.ok(!EditorResourceAccessor.getOriginalUri(file, { filterByScheme: Schemas.untitled })); - const diffEditorInput = new DiffEditorInput('name', 'description', untitled, file); + const diffEditorInput = instantiationService.createInstance(DiffEditorInput, 'name', 'description', untitled, file, undefined); assert.ok(!EditorResourceAccessor.getCanonicalUri(diffEditorInput)); assert.ok(!EditorResourceAccessor.getCanonicalUri(diffEditorInput, { filterByScheme: Schemas.file })); diff --git a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts index 6333219069c..cf541542d35 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts @@ -37,7 +37,7 @@ suite('Workbench editor model', () => { let input = instantiationService.createInstance(ResourceEditorInput, URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), 'name', 'description', undefined); let otherInput = instantiationService.createInstance(ResourceEditorInput, URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), 'name2', 'description', undefined); - let diffInput = new DiffEditorInput('name', 'description', input, otherInput); + let diffInput = instantiationService.createInstance(DiffEditorInput, 'name', 'description', input, otherInput, undefined); let model = await diffInput.resolve() as TextDiffEditorModel; diff --git a/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts b/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts index 2059826812e..4d2fbf4ec6e 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { EditorGroup, ISerializedEditorGroup, EditorCloseEvent } from 'vs/workbench/common/editor/editorGroup'; import { Extensions as EditorExtensions, IEditorInputFactoryRegistry, EditorInput, IFileEditorInput, IEditorInputFactory, CloseDirection, EditorsOrder } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; -import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { TestLifecycleService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -269,12 +269,13 @@ suite('Workbench editor groups', () => { test('contains()', function () { const group = createGroup(); + const instantiationService = workbenchInstantiationService(); const input1 = input(); const input2 = input(); - const diffInput1 = new DiffEditorInput('name', 'description', input1, input2); - const diffInput2 = new DiffEditorInput('name', 'description', input2, input1); + const diffInput1 = instantiationService.createInstance(DiffEditorInput, 'name', 'description', input1, input2, undefined); + const diffInput2 = instantiationService.createInstance(DiffEditorInput, 'name', 'description', input2, input1, undefined); group.openEditor(input1, { pinned: true, active: true }); diff --git a/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts index 8e00169fc0c..21c86ec8fda 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { EditorInput } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; class MyEditorInput extends EditorInput { readonly resource = undefined; @@ -36,6 +37,8 @@ suite('Workbench editor input', () => { }); test('DiffEditorInput', () => { + const instantiationService = workbenchInstantiationService(); + let counter = 0; let input = new MyEditorInput(); input.onDispose(() => { @@ -49,7 +52,7 @@ suite('Workbench editor input', () => { counter++; }); - let diffInput = new DiffEditorInput('name', 'description', input, otherInput); + let diffInput = instantiationService.createInstance(DiffEditorInput, 'name', 'description', input, otherInput, undefined); assert.equal(diffInput.originalInput, input); assert.equal(diffInput.modifiedInput, otherInput); @@ -62,11 +65,13 @@ suite('Workbench editor input', () => { }); test('DiffEditorInput disposes when input inside disposes', function () { + const instantiationService = workbenchInstantiationService(); + let counter = 0; let input = new MyEditorInput(); let otherInput = new MyEditorInput(); - let diffInput = new DiffEditorInput('name', 'description', input, otherInput); + let diffInput = instantiationService.createInstance(DiffEditorInput, 'name', 'description', input, otherInput, undefined); diffInput.onDispose(() => { counter++; assert(true); @@ -77,7 +82,7 @@ suite('Workbench editor input', () => { input = new MyEditorInput(); otherInput = new MyEditorInput(); - let diffInput2 = new DiffEditorInput('name', 'description', input, otherInput); + let diffInput2 = instantiationService.createInstance(DiffEditorInput, 'name', 'description', input, otherInput, undefined); diffInput2.onDispose(() => { counter++; assert(true); From 438f6108606ba657eac707c7fafab0546aea183e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Nov 2020 10:43:54 +0100 Subject: [PATCH 097/103] strict path casing in extension host profiler, https://github.com/microsoft/vscode/issues/110241 --- .../extensions/electron-browser/extensionHostProfiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts index 342ec2dc402..b8171e2f3b4 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts @@ -30,7 +30,7 @@ export class ExtensionHostProfiler { } private distill(profile: Profile, extensions: IExtensionDescription[]): IExtensionHostProfile { - let searchTree = TernarySearchTree.forUris(); + let searchTree = TernarySearchTree.forUris2(); for (let extension of extensions) { if (extension.extensionLocation.scheme === Schemas.file) { searchTree.set(URI.file(realpathSync(extension.extensionLocation.fsPath)), extension); From cb6374447511d2c9647ce197488ee853e1177afa Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 10 Nov 2020 11:07:00 +0100 Subject: [PATCH 098/103] Adopt TernarySearchTree#forUris2 (#110241) --- src/vs/platform/files/common/fileService.ts | 3 ++- src/vs/platform/files/common/files.ts | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index b36a5aac681..8648504a3c5 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -180,6 +180,7 @@ export class FileService extends Disposable implements IFileService { private async doResolveFile(resource: URI, options?: IResolveFileOptions): Promise; private async doResolveFile(resource: URI, options?: IResolveFileOptions): Promise { const provider = await this.withProvider(resource); + const isPathCaseSensitive = this.isPathCaseSensitive(provider); const resolveTo = options?.resolveTo; const resolveSingleChildDescendants = options?.resolveSingleChildDescendants; @@ -193,7 +194,7 @@ export class FileService extends Disposable implements IFileService { // lazy trie to check for recursive resolving if (!trie) { - trie = TernarySearchTree.forUris(); + trie = TernarySearchTree.forUris2(() => !isPathCaseSensitive); trie.set(resource, true); if (isNonEmptyArray(resolveTo)) { resolveTo.forEach(uri => trie!.set(uri, true)); diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index ed480b1c068..4232528124a 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -521,19 +521,19 @@ export class FileChangesEvent { switch (change.type) { case FileChangeType.ADDED: if (!this.added) { - this.added = TernarySearchTree.forUris(this.ignorePathCasing); + this.added = TernarySearchTree.forUris2(() => this.ignorePathCasing); } this.added.set(change.resource, change); break; case FileChangeType.UPDATED: if (!this.updated) { - this.updated = TernarySearchTree.forUris(this.ignorePathCasing); + this.updated = TernarySearchTree.forUris2(() => this.ignorePathCasing); } this.updated.set(change.resource, change); break; case FileChangeType.DELETED: if (!this.deleted) { - this.deleted = TernarySearchTree.forUris(this.ignorePathCasing); + this.deleted = TernarySearchTree.forUris2(() => this.ignorePathCasing); } this.deleted.set(change.resource, change); break; From 55fdf94d67dfc72eaaf25655b7b0d9ff16d2e301 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Nov 2020 11:12:20 +0100 Subject: [PATCH 099/103] fix https://github.com/microsoft/vscode/issues/109776 --- .../contrib/message/messageController.css | 16 +++++++++ .../contrib/message/messageController.ts | 35 +++++++++++++------ 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/message/messageController.css b/src/vs/editor/contrib/message/messageController.css index a3910415dd4..924349d112e 100644 --- a/src/vs/editor/contrib/message/messageController.css +++ b/src/vs/editor/contrib/message/messageController.css @@ -8,6 +8,12 @@ z-index: 10000; } +.monaco-editor .monaco-editor-overlaymessage.below { + padding-bottom: 0; + padding-top: 8px; + z-index: 10000; +} + @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } @@ -37,3 +43,13 @@ border-width: 8px; position: absolute; } + +.monaco-editor .monaco-editor-overlaymessage:not(.below) .anchor.top, +.monaco-editor .monaco-editor-overlaymessage.below .anchor.below { + display: none; +} + +.monaco-editor .monaco-editor-overlaymessage.below .anchor.top { + display: inherit; + top: -8px; +} diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index 6bec24fd00f..319f0d521d7 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -7,7 +7,7 @@ import 'vs/css!./messageController'; import * as nls from 'vs/nls'; import { TimeoutTimer } from 'vs/base/common/async'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { IDisposable, Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; @@ -20,7 +20,7 @@ import { inputValidationInfoBorder, inputValidationInfoBackground, inputValidati import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ColorScheme } from 'vs/platform/theme/common/theme'; -export class MessageController extends Disposable implements IEditorContribution { +export class MessageController implements IEditorContribution { public static readonly ID = 'editor.contrib.messageController'; @@ -32,21 +32,24 @@ export class MessageController extends Disposable implements IEditorContribution private readonly _editor: ICodeEditor; private readonly _visible: IContextKey; - private readonly _messageWidget = this._register(new MutableDisposable()); - private readonly _messageListeners = this._register(new DisposableStore()); + private readonly _messageWidget = new MutableDisposable(); + private readonly _messageListeners = new DisposableStore(); + private readonly _editorListener: IDisposable; constructor( editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService ) { - super(); + this._editor = editor; this._visible = MessageController.MESSAGE_VISIBLE.bindTo(contextKeyService); - this._register(this._editor.onDidAttemptReadOnlyEdit(() => this._onDidAttemptReadOnlyEdit())); + this._editorListener = this._editor.onDidAttemptReadOnlyEdit(() => this._onDidAttemptReadOnlyEdit()); } dispose(): void { - super.dispose(); + this._editorListener.dispose(); + this._messageListeners.dispose(); + this._messageWidget.dispose(); this._visible.reset(); } @@ -150,14 +153,18 @@ class MessageWidget implements IContentWidget { this._domNode = document.createElement('div'); this._domNode.classList.add('monaco-editor-overlaymessage'); + const anchorTop = document.createElement('div'); + anchorTop.classList.add('anchor', 'top'); + this._domNode.appendChild(anchorTop); + const message = document.createElement('div'); message.classList.add('message'); message.textContent = text; this._domNode.appendChild(message); - const anchor = document.createElement('div'); - anchor.classList.add('anchor'); - this._domNode.appendChild(anchor); + const anchorBottom = document.createElement('div'); + anchorBottom.classList.add('anchor', 'below'); + this._domNode.appendChild(anchorBottom); this._editor.addContentWidget(this); this._domNode.classList.add('fadeIn'); @@ -178,6 +185,11 @@ class MessageWidget implements IContentWidget { getPosition(): IContentWidgetPosition { return { position: this._position, preference: [ContentWidgetPositionPreference.ABOVE, ContentWidgetPositionPreference.BELOW] }; } + + afterRender(position: ContentWidgetPositionPreference | null): void { + this._domNode.classList.toggle('below', position === ContentWidgetPositionPreference.BELOW); + } + } registerEditorContribution(MessageController.ID, MessageController); @@ -186,7 +198,8 @@ registerThemingParticipant((theme, collector) => { const border = theme.getColor(inputValidationInfoBorder); if (border) { let borderWidth = theme.type === ColorScheme.HIGH_CONTRAST ? 2 : 1; - collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor { border-top-color: ${border}; }`); + collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor.below { border-top-color: ${border}; }`); + collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor.top { border-bottom-color: ${border}; }`); collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .message { border: ${borderWidth}px solid ${border}; }`); } const background = theme.getColor(inputValidationInfoBackground); From 64ef9869b69c922e253e42d90caf2f30b5da063b Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 10 Nov 2020 11:17:27 +0100 Subject: [PATCH 100/103] Fix filtering on * for simple file dialog Fixes #110265 --- src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 19876953254..ff3fe29fd60 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -924,7 +924,8 @@ export class SimpleFileDialog { const ext = resources.extname(file); for (let i = 0; i < this.options.filters.length; i++) { for (let j = 0; j < this.options.filters[i].extensions.length; j++) { - if (ext === ('.' + this.options.filters[i].extensions[j])) { + const testExt = this.options.filters[i].extensions[j]; + if ((testExt === '*') || (ext === ('.' + testExt))) { return true; } } From 2c1272b79482683f75c47bc99e187107d1e01192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 10 Nov 2020 11:22:12 +0100 Subject: [PATCH 101/103] fixes #104945 --- 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 c666b0cf968..7301afc566d 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1488,7 +1488,7 @@ export class CommandCenter { ? localize('unsaved files single', "The following file has unsaved changes which won't be included in the commit if you proceed: {0}.\n\nWould you like to save it before committing?", path.basename(documents[0].uri.fsPath)) : localize('unsaved files', "There are {0} unsaved files.\n\nWould you like to save them before committing?", documents.length); const saveAndCommit = localize('save and commit', "Save All & Commit"); - const commit = localize('commit', "Commit Anyway"); + const commit = localize('commit', "Commit Staged Changes"); const pick = await window.showWarningMessage(message, { modal: true }, saveAndCommit, commit); if (pick === saveAndCommit) { From 77a57165e4f155145ab880f2998e75de2392a1ad Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Nov 2020 11:23:10 +0100 Subject: [PATCH 102/103] debt - race more minimal edits computation against 1sec timeout --- src/vs/editor/common/services/editorWorkerServiceImpl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index 5976df3c226..5099af91d90 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IntervalTimer } from 'vs/base/common/async'; +import { IntervalTimer, timeout } from 'vs/base/common/async'; import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { SimpleWorkerClient, logOnceWebWorkerWarning, IWorkerClient } from 'vs/base/common/worker/simpleWorker'; @@ -105,7 +105,7 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker const sw = StopWatch.create(true); const result = this._workerManager.withWorker().then(client => client.computeMoreMinimalEdits(resource, edits)); result.finally(() => this._logService.trace('FORMAT#computeMoreMinimalEdits', resource.toString(true), sw.elapsed())); - return result; + return Promise.race([result, timeout(1000).then(() => edits)]); } else { return Promise.resolve(undefined); From 6a2da6d725a073bd0bc357c8c98f37e78deec278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 10 Nov 2020 11:28:44 +0100 Subject: [PATCH 103/103] :lipstick: --- extensions/git/src/remoteSource.ts | 66 ++++++++++++++++-------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/extensions/git/src/remoteSource.ts b/extensions/git/src/remoteSource.ts index 1ecdc71c25e..03c890757f7 100644 --- a/extensions/git/src/remoteSource.ts +++ b/extensions/git/src/remoteSource.ts @@ -87,42 +87,46 @@ export async function pickRemoteSource(model: Model, options: PickRemoteSourceOp 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) { - 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 })); + if (options.providerName) { + const provider = model.getRemoteProviders() + .filter(provider => provider.name === options.providerName)[0]; - quickpick.placeholder = providers.length === 0 - ? localize('provide url', "Provide repository URL") - : localize('provide url or pick', "Provide repository URL or pick a repository source."); + if (provider) { + return await pickProviderSource(provider); + } + } - 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; - } - }; + const providers = model.getRemoteProviders() + .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + (options.providerLabel ? options.providerLabel(provider) : provider.name), alwaysShow: true, provider })); - quickpick.onDidChangeValue(updatePicks); - updatePicks(); + 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 result = await getQuickPickResult(quickpick); + 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; + } + }; - if (result) { - if (result.url) { - return result.url; - } else if (result.provider) { - return await pickProviderSource(result.provider); - } + 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); } }